diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000000..6e93655bd8 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,18 @@ +github: qos-ch +tidelift: maven/ch.qos.logback:logback-core +thanks-dev: gh/ceki + + +#github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +#patreon: # Replace with a single Patreon username +#open_collective: # Replace with a single Open Collective username +#ko_fi: # Replace with a single Ko-fi username +#tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +#community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +#liberapay: # Replace with a single Liberapay username +#issuehunt: # Replace with a single IssueHunt username +#lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +#polar: # Replace with a single Polar username +#buy_me_a_coffee: # Replace with a single Buy Me a Coffee username +#thanks_dev: # Replace with a single thanks.dev username +#custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] \ No newline at end of file diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000000..02afb85ba4 --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,47 @@ +name: CI + +on: + workflow_dispatch: # ← this enables manual runs + + # Trigger on push to any branch + #push: + # branches: + # - '*' + +# https://help.github.com/en/actions/automating-your-workflow-with-github-actions/software-installed-on-github-hosted-runners + +concurrency: + # On master/release, we don't want any jobs cancelled so the sha is used to name the group + # On PR branches, we cancel the job if new commits are pushed + # More info: https://stackoverflow.com/a/68422069/253468 + group: ${{ (github.ref == 'refs/heads/branch_1.2.18' || github.ref == 'refs/heads/main' || github.ref == 'refs/heads/release' ) && format('ci-main-{0}', github.sha) || format('ci-main-{0}', github.ref) }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + Test: + name: JDK ${{ matrix.jdk }}, ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + jdk: [21] + os: [ubuntu-latest, windows-latest, macos-latest] + fail-fast: true + max-parallel: 4 + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 50 + - name: Set up Java ${{ matrix.jdk }} + uses: actions/setup-java@v4 + with: + distribution: 'temurin' + java-version: ${{ matrix.jdk }} + cache: maven # This enables Maven dependency caching + - name: Install + # download dependencies, etc, so test log looks better + run: mvn -B install + + diff --git a/.gitignore b/.gitignore index 7584f38cb9..029ed6ea83 100755 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ logback-classic/jdbc:hsqldb:mem:test.log logback-classic/jdbc:hsqldb:mem:test.properties logback-classic/jdbc:hsqldb:mem:test.script cobertura.ser +# ignore files with pound characters +\#*\# \ No newline at end of file diff --git a/.travis.yml b/.travis.yml index dc28573fe4..79ade6b348 100755 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,19 @@ dist: trusty + language: java -jdk: oraclejdk9 + +jdk: + - oraclejdk11 + notifications: email: - notification@qos.ch + +install: /bin/true + +script: + - mvn clean + - mvn install +cache: + directories: + - $HOME/.m2 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c5cf650b5a..62bd3b5af4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -47,9 +47,9 @@ meets all requirements outlined in this document.** 3. Submit a pull-request based off your topic branch, following the patch rules below. 4. If your patch is non-trivial and you haven't submitted a [signed CLA](http://logback.qos.ch/cla.txt), - please email it to ceki@qos.ch and tony19@gmail.com with **\[logback] - signed CLA** in the subject line. Trivial bug fixes (less than ~30 lines) - do not require a CLA. + please email it to ceki@qos.ch with **\[logback] signed CLA** in the subject line. Trivial bug + fixes (less than ~30 lines) do not require a CLA. + ## Patch rules diff --git a/LICENSE.txt b/LICENSE.txt index af39fcb95d..55db596024 100755 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -2,10 +2,10 @@ Logback LICENSE --------------- Logback: the reliable, generic, fast and flexible logging framework. -Copyright (C) 1999-2015, QOS.ch. All rights reserved. +Copyright (C) 1999-2024, QOS.ch. All rights reserved. This program and the accompanying materials are dual-licensed under -either the terms of the Eclipse Public License v1.0 as published by +either the terms of the Eclipse Public License v2.0 as published by the Eclipse Foundation or (per the licensee's choosing) diff --git a/README.md b/README.md index 50b5688a88..93745d6971 100755 --- a/README.md +++ b/README.md @@ -8,50 +8,77 @@ The Logback documentation can be found on the [project web-site](https://logback.qos.ch/documentation.html) as well as under the docs/ folder of the logback distribution. +## On the 1.5.x series + +The 1.5.x series is a direct descendant of and a drop-in replacement +for the 1.4.x series. It differs from the 1.4.x series by the +relocation of the logback-access module which was moved to its [own +separate github repository](https://github.com/qos-ch/logback-access). + +Here is a summary of 1.5.x dependencies: + +|Logback version |github branch |SLF4J version | JDK at runtime | JDK during build | Enterprise Edition (optional)| +|:---------------:|:--------:|:---------:|:-------:|:--------:|------------------------------| +| 1.5.x | master | 2.0.x | 11 | 21 | Jakarta EE (jakarta.* namespace)| + # Building logback -Building logback is documented at: +Version 1.5.x requires Java 21 to compile and build. + +More details on building logback is documented at: https://logback.qos.ch/setup.html#ide # In case of problems In case of problems please do not hesitate to post an e-mail message -on the logback-user@qos.ch mailing list. However, please do not -directly e-mail logback developers. The answer to your question might -be useful to other users. Moreover, there are many knowledgeable users -on the logback-user mailing lists who can quickly answer your -questions. - +on the logback-user@qos.ch mailing list. You may also post message on the +[github discussions](https://github.com/qos-ch/logback/discussions) forum. +However, please do not directly e-mail logback developers. +The answer to your question might be useful to other users. Moreover, +there are many knowledgeable users on the logback-user mailing lists +who can quickly answer your questions. -# Pull requests +# Urgent issues -If you are interested in improving logback, great! The logback community -looks forward to your contribution. Please follow this process: +For urgent issues do not hesitate to [champion a +release](https://github.com/sponsors/qos-ch/sponsorships?tier_id=543501). +In principle, most championed issues are solved within 3 business days +followed up by a release. -1. Please file a [bug - report](https://logback.qos.ch/bugreport.html). Pull requests with - an associated JIRA issue will get more attention. +# Pull requests - Optional: Start a discussion on the [logback-dev mailing - list](https://logback.qos.ch/mailinglist.html) about your proposed - change. +If you are interested in improving logback, that is great! The logback +community looks forward to your contribution. Please follow this process: -2. Fork qos-ch/logback. Ideally, create a new branch from your fork for +1. Fork qos-ch/logback. Ideally, create a new branch from your fork for your contribution to make it easier to merge your changes back. -3. Make your changes on the branch you hopefuly created in Step 2. Be +2. Make the effort to explain the aim of your proposed change. + +3. Make your changes on the branch you hopefully created in Step 2. Be sure that your code passes existing unit tests. 4. Please add unit tests for your work if appropriate. It usually is. -5. Push your changes to your fork/branch in github. Don't push it to +5. Push your changes to your fork/branch in GitHub. Don't push it to your master! If you do it will make it harder to submit new changes later. -6. Submit a pull request to logback from from your commit page on - github. +6. Submit a pull request to logback from your commit page on GitHub. + All commits must have signed off by the contributor attesting to + [Developer Certificate of Origin (DCO)](https://developercertificate.org/). + Commits without sign off will be automatically rejected by the [DCO GitHub + check](https://probot.github.io/apps/dco/) application. + +7. Do not forget to explain your proposed changes. + + -# Build Status -[![Build Status](https://travis-ci.org/qos-ch/slf4j.png)](https://travis-ci.org/qos-ch/slf4j) diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000000..3e37f80975 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,68 @@ + +## Reporting security issues + +Please report security issues related to the logback project to the +following email address: + + support(at)qos.ch + + +## Verifying contents + +All logback project artifacts published on Maven central are signed. For each +artifact, there is an associated signature file with the .asc suffix. + +The cryptographic key was updated 2022-08-08 to use a more modern +Elliptic curve algorithm instead of RSA previously. + +### After 2022-08-08 + +To verify the signature use [this public key](https://www.slf4j.org/public-keys/60200AC4AE761F1614D6C46766D68DAA073BE985.gpg). Here is its fingerprint: +``` +pub nistp521 2022-08-08 [SC] + 60200AC4AE761F1614D6C46766D68DAA073BE985 +uid Ceki Gulcu +sub nistp521 2022-08-08 [E] +``` + +A copy of this key is stored on the +[keys.openpgp.org](https://keys.openpgp.org) keyserver. To add it to +your public key ring use the following command: + +``` +> FINGER_PRINT=60200AC4AE761F1614D6C46766D68DAA073BE985 +> gpg --keyserver hkps://keys.openpgp.org --recv-keys $FINGER_PRINT +``` + +### Before 2022-08-08 + +To verify the signature use [this public key](https://www.slf4j.org/public-keys/ceki-public-key.pgp). Here is its fingerprint: +``` +pub 2048R/A511E325 2012-04-26 +Key fingerprint = 475F 3B8E 59E6 E63A A780 6748 2C7B 12F2 A511 E325 +uid Ceki Gulcu +sub 2048R/7FBFA159 2012-04-26 +``` + +A copy of this key is stored on the +[keys.openpgp.org](https://keys.openpgp.org) keyserver. To add it to +your public key ring use the following command: + +``` +> FINGER_PRINT=475F3B8E59E6E63AA78067482C7B12F2A511E325 +> gpg --keyserver hkps://keys.openpgp.org --recv-keys $FINGER_PRINT +``` + + +## Preventing commit history overwrite + +In order to prevent loss of commit history, developers of the project +are highly encouraged to deny branch deletions or history overwrites +by invoking the following two commands on their local copy of the +repository. + + +``` +git config receive.denyDelete true +git config receive.denyNonFastForwards true +``` \ No newline at end of file diff --git a/javaConventions.xml b/javaConventions.xml new file mode 100644 index 0000000000..5294c233e8 --- /dev/null +++ b/javaConventions.xml @@ -0,0 +1,390 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logback-access/LICENSE.txt b/logback-access/LICENSE.txt new file mode 100644 index 0000000000..af39fcb95d --- /dev/null +++ b/logback-access/LICENSE.txt @@ -0,0 +1,15 @@ +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2015, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v1.0 as published by +the Eclipse Foundation + + or (per the licensee's choosing) + +under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation. + diff --git a/logback-access/build.xml b/logback-access/build.xml deleted file mode 100644 index e7728ead92..0000000000 --- a/logback-access/build.xml +++ /dev/null @@ -1,90 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - These are the targets supported by this ANT build scpript: - - build - compile all project files, if a certain library is missing, - then the compilation of its dependents are skipped. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - -
-
-
-
- - -
diff --git a/logback-access/keywords.html b/logback-access/keywords.html deleted file mode 100644 index 91d456c9fc..0000000000 --- a/logback-access/keywords.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
keyword LabelJettyResinTomcatObservations
aremote IP addressNANArequest.getRemoteAddr() [S] RemoteIPAddressConverter
Alocal IP address InetAddress.getLocalHost().getHostAddress() [S] LocalIPAddressConverter
bContent lengthrequest.getConnection().getGenerator().getContentWritten();Unless statusCode==304, response.getContentLength()response.getContentCount();JettyContentLengthConverter, ResinContentLengthConverter
cCookierequest cookies (dumps all of them}dumps cookies by name, request cookie first, if - unavailable then response cookierequest cookies, by nameRequestCookieConverter
hremote IP addrrequest.getRemoteAddr() [S]request.printRemoteAddr, IP address string in textual presentationrequest.getRemoteHost() [S]RemoteHostConverter
Hrequest protocolrequest.getProtocol()RequestProtocolConverter
iIncoming headerNArequest.getHeader(name) [S]request.getHeader(header) [S]RequestHeaderConverter
lRemote logical username from identdalways '-'always '-'always '-'NAConverter
nattribute in the ServletRequestNArequest.getAttribute(name) [S] NARequestAttributeConverter, in tomcat %r is used
oresponse headerreponse.getHeader(name) [NS]not implemented altough response.setHeader() method is available
rFirst line of the request (method and request URI)request.getMethod()+ request.getUri() [NS] +request.getProtocol()request.getMethod() + request.getUriBuffer() [NS] + request.getProtocol()request.getMethod() + request.getRequestURI() + request.getQueryString() + '?' + request.getProtocol();RequestURLConverter
{}rattribute in the ServletRequestNANArequest.getAttribute(name) [S]RequestAttributeConverter, in resin %n is used
sstatus coderesponse.getStatus() [NS] response.getStatusCode() [NS]response.getStatus() [NS]suprisingly, not in the Servlet API
{}trequest time stampgetTimeStamp() [NS]based on current timebased on current timeDateConverter
Trequest processing duration in secondsNArequest.getStartTime() - Alarm.getExactTime()Valve measures invocation time
Drequest processing duration in millisrequest.getStartTime() - Alarm.getExactTime()Valve measures invocation time
uremote userrequest.getRemoteUser() [S]request.getRemoteUser() [S]request.getRemoteUser() [S]RemoteUserConverter
URequested URI pathNA request.getRequestURI() [S} request.getRequestURI() [S}RequestURIConverter
- - -Tomcat -====== -%a - Remote IP address -%A - Local IP address -%b - Bytes sent, excluding HTTP headers, or '-' if zero -%B - Bytes sent, excluding HTTP headers -%h - Remote host name (or IP address if resolveHosts is false) -%H - Request protocol -%l - Remote logical username from identd (always returns '-') -%m - Request method (GET, POST, etc.) -%p - Local port on which this request was received -%q - Query string (prepended with a '?' if it exists) -%r - First line of the request (method and request URI) -%s - HTTP status code of the response -%S - User session ID -%t - Date and time, in Common Log Format -%u - Remote user that was authenticated (if any), else '-' -%U - Requested URL path -%v - Local server name -%D - Time taken to process the request, in millis -%T - Time taken to process the request, in seconds - -Resin -===== - - - - \ No newline at end of file diff --git a/logback-access/lib/jetty.jar b/logback-access/lib/jetty.jar deleted file mode 100644 index 4d22ecbbbd..0000000000 Binary files a/logback-access/lib/jetty.jar and /dev/null differ diff --git a/logback-access/lib/servlet-api-2.5.jar b/logback-access/lib/servlet-api-2.5.jar deleted file mode 100644 index d259b6d0f9..0000000000 Binary files a/logback-access/lib/servlet-api-2.5.jar and /dev/null differ diff --git a/logback-access/pom.xml b/logback-access/pom.xml old mode 100755 new mode 100644 index 6fb0f4cc3c..5f1f106af5 --- a/logback-access/pom.xml +++ b/logback-access/pom.xml @@ -1,176 +1,31 @@ - + 4.0.0 ch.qos.logback logback-parent - 1.3.0-alpha5-SNAPSHOT + 1.5.28-SNAPSHOT - + logback-access - jar - Logback Access Module - logback-access module + pom + + Logback Access Module Relocated + logback-access module relocated to its own repository + https://github.com/qos-ch/logback-access - - - ch.qos.logback - logback-core - compile - - - ch.qos.logback - logback-core - test-jar - test - - - org.apache.tomcat - tomcat-catalina - compile - true - - - org.apache.tomcat - tomcat-coyote - compile - true - - - org.eclipse.jetty - jetty-server - compile - true - - - org.codehaus.janino - janino - compile - true - - - org.hsqldb - hsqldb - test - - - javax.mail - javax.mail-api - compile - true - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - once - - plain - true - - **/AllAccessTest.java - **/PackageTest.java - **/SerializationPerfTest.java - - - - - org.apache.maven.plugins - maven-jar-plugin - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - bundle-test-jar - package - - test-jar - - - - - - org.apache.felix - maven-bundle-plugin - true - - - bundle-manifest - process-classes - - manifest - - - - - - ch.qos.logback.access.* - - - ch.qos.logback.core.rolling, - ch.qos.logback.core.rolling.helper, - javax.servlet.*;version="2.5", - javax.*;resolution:=optional, - org.apache.catalina.*;version="${tomcat.version}";resolution:=optional, - org.eclipse.jetty.*;version="${jetty.version}";resolution:=optional, - * - - JavaSE-1.6 - - - - - - + + + ch.qos.logback.access + logback-access-common + 2.0.6 + + - - - - host-orion - - - mysql - mysql-connector-java - 5.0.8 - test - - - postgresql - postgresql - 8.2-507.jdbc3 - - - - com.microsoft.sqlserver - sqljdbc4 - 2.0 - test - - - - com.oracle - ojdbc14 - 10.2.0.1 - test - - - - diff --git a/logback-access/src/main/java/ch/qos/logback/access/AccessConstants.java b/logback-access/src/main/java/ch/qos/logback/access/AccessConstants.java deleted file mode 100644 index 722744af7e..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/AccessConstants.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -public class AccessConstants { - - public static final String LOGBACK_STATUS_MANAGER_KEY = "LOGBACK_STATUS_MANAGER"; - public static final String LB_INPUT_BUFFER = "LB_INPUT_BUFFER"; - public static final String LB_OUTPUT_BUFFER = "LB_OUTPUT_BUFFER"; - - public static final String X_WWW_FORM_URLECODED = "application/x-www-form-urlencoded"; - - public static final String IMAGE_CONTENT_TYPE = "image/"; - public static final String IMAGE_JPEG = "image/jpeg"; - public static final String IMAGE_GIF = "image/gif"; - public static final String IMAGE_PNG = "image/png"; - - public static final String TEE_FILTER_INCLUDES_PARAM = "includes"; - public static final String TEE_FILTER_EXCLUDES_PARAM = "excludes"; - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java b/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java deleted file mode 100644 index b2cb713365..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/PatternLayout.java +++ /dev/null @@ -1,217 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -import ch.qos.logback.access.pattern.ContentLengthConverter; -import ch.qos.logback.access.pattern.DateConverter; -import ch.qos.logback.access.pattern.ElapsedSecondsConverter; -import ch.qos.logback.access.pattern.ElapsedTimeConverter; -import ch.qos.logback.access.pattern.EnsureLineSeparation; -import ch.qos.logback.access.pattern.FullRequestConverter; -import ch.qos.logback.access.pattern.FullResponseConverter; -import ch.qos.logback.access.pattern.LineSeparatorConverter; -import ch.qos.logback.access.pattern.LocalIPAddressConverter; -import ch.qos.logback.access.pattern.LocalPortConverter; -import ch.qos.logback.access.pattern.NAConverter; -import ch.qos.logback.access.pattern.QueryStringConverter; -import ch.qos.logback.access.pattern.RemoteHostConverter; -import ch.qos.logback.access.pattern.RemoteIPAddressConverter; -import ch.qos.logback.access.pattern.RemoteUserConverter; -import ch.qos.logback.access.pattern.RequestAttributeConverter; -import ch.qos.logback.access.pattern.RequestContentConverter; -import ch.qos.logback.access.pattern.RequestCookieConverter; -import ch.qos.logback.access.pattern.RequestHeaderConverter; -import ch.qos.logback.access.pattern.RequestMethodConverter; -import ch.qos.logback.access.pattern.RequestParameterConverter; -import ch.qos.logback.access.pattern.RequestProtocolConverter; -import ch.qos.logback.access.pattern.RequestURIConverter; -import ch.qos.logback.access.pattern.RequestURLConverter; -import ch.qos.logback.access.pattern.ResponseContentConverter; -import ch.qos.logback.access.pattern.ResponseHeaderConverter; -import ch.qos.logback.access.pattern.ServerNameConverter; -import ch.qos.logback.access.pattern.SessionIDConverter; -import ch.qos.logback.access.pattern.StatusCodeConverter; -import ch.qos.logback.access.pattern.ThreadNameConverter; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.pattern.PatternLayoutBase; -import ch.qos.logback.core.pattern.color.*; -import ch.qos.logback.core.pattern.parser.Parser; - -import java.util.HashMap; -import java.util.Map; - -/** - *

- * This class is a module-specific implementation of - * {@link ch.qos.logback.access.PatternLayout} to allow http-specific patterns - * to be used. The ch.qos.logback.access.PatternLayout provides a - * way to format the logging output that is just as easy and flexible as the - * usual PatternLayout. - *

- *

- * For more information about this layout, please refer to the online manual at - * http://logback.qos.ch/manual/layouts.html#AccessPatternLayout - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class PatternLayout extends PatternLayoutBase { - - public static final Map defaultConverterMap = new HashMap(); - public static final String HEADER_PREFIX = "#logback.access pattern: "; - - public static final String CLF_PATTERN = "%h %l %u [%t] \"%r\" %s %b"; - public static final String CLF_PATTERN_NAME = "common"; - public static final String CLF_PATTERN_NAME_2 = "clf"; - public static final String COMBINED_PATTERN = "%h %l %u [%t] \"%r\" %s %b \"%i{Referer}\" \"%i{User-Agent}\""; - public static final String COMBINED_PATTERN_NAME = "combined"; - - static { - defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); - - defaultConverterMap.put("a", RemoteIPAddressConverter.class.getName()); - defaultConverterMap.put("remoteIP", RemoteIPAddressConverter.class.getName()); - - defaultConverterMap.put("A", LocalIPAddressConverter.class.getName()); - defaultConverterMap.put("localIP", LocalIPAddressConverter.class.getName()); - - defaultConverterMap.put("b", ContentLengthConverter.class.getName()); - defaultConverterMap.put("B", ContentLengthConverter.class.getName()); - defaultConverterMap.put("bytesSent", ContentLengthConverter.class.getName()); - - defaultConverterMap.put("h", RemoteHostConverter.class.getName()); - defaultConverterMap.put("clientHost", RemoteHostConverter.class.getName()); - - defaultConverterMap.put("H", RequestProtocolConverter.class.getName()); - defaultConverterMap.put("protocol", RequestProtocolConverter.class.getName()); - - defaultConverterMap.put("i", RequestHeaderConverter.class.getName()); - defaultConverterMap.put("header", RequestHeaderConverter.class.getName()); - - defaultConverterMap.put("I", ThreadNameConverter.class.getName()); - defaultConverterMap.put("threadName", ThreadNameConverter.class.getName()); - - defaultConverterMap.put("l", NAConverter.class.getName()); - - defaultConverterMap.put("m", RequestMethodConverter.class.getName()); - defaultConverterMap.put("requestMethod", RequestMethodConverter.class.getName()); - - defaultConverterMap.put("q", QueryStringConverter.class.getName()); - defaultConverterMap.put("queryString", QueryStringConverter.class.getName()); - - defaultConverterMap.put("r", RequestURLConverter.class.getName()); - defaultConverterMap.put("requestURL", RequestURLConverter.class.getName()); - - defaultConverterMap.put("s", StatusCodeConverter.class.getName()); - defaultConverterMap.put("statusCode", StatusCodeConverter.class.getName()); - - defaultConverterMap.put("S", SessionIDConverter.class.getName()); - defaultConverterMap.put("sessionID", SessionIDConverter.class.getName()); - - defaultConverterMap.put("t", DateConverter.class.getName()); - defaultConverterMap.put("date", DateConverter.class.getName()); - - defaultConverterMap.put("u", RemoteUserConverter.class.getName()); - defaultConverterMap.put("user", RemoteUserConverter.class.getName()); - - defaultConverterMap.put("U", RequestURIConverter.class.getName()); - defaultConverterMap.put("requestURI", RequestURIConverter.class.getName()); - - defaultConverterMap.put("v", ServerNameConverter.class.getName()); - defaultConverterMap.put("server", ServerNameConverter.class.getName()); - - defaultConverterMap.put("localPort", LocalPortConverter.class.getName()); - - defaultConverterMap.put("requestAttribute", RequestAttributeConverter.class.getName()); - defaultConverterMap.put("reqAttribute", RequestAttributeConverter.class.getName()); - - defaultConverterMap.put("reqCookie", RequestCookieConverter.class.getName()); - defaultConverterMap.put("requestCookie", RequestCookieConverter.class.getName()); - - defaultConverterMap.put("responseHeader", ResponseHeaderConverter.class.getName()); - - defaultConverterMap.put("requestParameter", RequestParameterConverter.class.getName()); - defaultConverterMap.put("reqParameter", RequestParameterConverter.class.getName()); - - defaultConverterMap.put("requestContent", RequestContentConverter.class.getName()); - - defaultConverterMap.put("responseContent", ResponseContentConverter.class.getName()); - - defaultConverterMap.put("fullRequest", FullRequestConverter.class.getName()); - defaultConverterMap.put("fullResponse", FullResponseConverter.class.getName()); - - defaultConverterMap.put("elapsedTime", ElapsedTimeConverter.class.getName()); - defaultConverterMap.put("D", ElapsedTimeConverter.class.getName()); - - defaultConverterMap.put("elapsedSeconds", ElapsedSecondsConverter.class.getName()); - defaultConverterMap.put("T", ElapsedSecondsConverter.class.getName()); - - defaultConverterMap.put("n", LineSeparatorConverter.class.getName()); - - defaultConverterMap.put("black", BlackCompositeConverter.class.getName()); - defaultConverterMap.put("red", RedCompositeConverter.class.getName()); - defaultConverterMap.put("green", GreenCompositeConverter.class.getName()); - defaultConverterMap.put("yellow", YellowCompositeConverter.class.getName()); - defaultConverterMap.put("blue", BlueCompositeConverter.class.getName()); - defaultConverterMap.put("magenta", MagentaCompositeConverter.class.getName()); - defaultConverterMap.put("cyan", CyanCompositeConverter.class.getName()); - defaultConverterMap.put("white", WhiteCompositeConverter.class.getName()); - defaultConverterMap.put("gray", GrayCompositeConverter.class.getName()); - defaultConverterMap.put("boldRed", BoldRedCompositeConverter.class.getName()); - defaultConverterMap.put("boldGreen", BoldGreenCompositeConverter.class.getName()); - defaultConverterMap.put("boldYellow", BoldYellowCompositeConverter.class.getName()); - defaultConverterMap.put("boldBlue", BoldBlueCompositeConverter.class.getName()); - defaultConverterMap.put("boldMagenta", BoldMagentaCompositeConverter.class.getName()); - defaultConverterMap.put("boldCyan", BoldCyanCompositeConverter.class.getName()); - defaultConverterMap.put("boldWhite", BoldWhiteCompositeConverter.class.getName()); - } - - public PatternLayout() { - // set a default value for pattern - setPattern(CLF_PATTERN); - // by default postCompileProcessor the is an EnsureLineSeparation instance - this.postCompileProcessor = new EnsureLineSeparation(); - } - - /** - * Returns the default converter map for this instance. - */ - @Override - public Map getDefaultConverterMap() { - return defaultConverterMap; - } - - @Override - public String doLayout(IAccessEvent event) { - if (!isStarted()) { - return null; - } - return writeLoopOnConverters(event); - } - - @Override - public void start() { - if (getPattern().equalsIgnoreCase(CLF_PATTERN_NAME) || getPattern().equalsIgnoreCase(CLF_PATTERN_NAME_2)) { - setPattern(CLF_PATTERN); - } else if (getPattern().equalsIgnoreCase(COMBINED_PATTERN_NAME)) { - setPattern(COMBINED_PATTERN); - } - super.start(); - } - - @Override - protected String getPresentationHeaderPrefix() { - return HEADER_PREFIX; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/PatternLayoutEncoder.java b/logback-access/src/main/java/ch/qos/logback/access/PatternLayoutEncoder.java deleted file mode 100644 index 92c4a019fc..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/PatternLayoutEncoder.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.pattern.PatternLayoutEncoderBase; - -public class PatternLayoutEncoder extends PatternLayoutEncoderBase { - - @Override - public void start() { - PatternLayout patternLayout = new PatternLayout(); - patternLayout.setContext(context); - patternLayout.setPattern(getPattern()); - patternLayout.start(); - this.layout = patternLayout; - super.start(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/ViewStatusMessagesServlet.java b/logback-access/src/main/java/ch/qos/logback/access/ViewStatusMessagesServlet.java deleted file mode 100644 index ce4d448a2f..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/ViewStatusMessagesServlet.java +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.status.ViewStatusMessagesServletBase; - -public class ViewStatusMessagesServlet extends ViewStatusMessagesServletBase { - - private static final long serialVersionUID = 443878494348593337L; - - @Override - protected StatusManager getStatusManager(HttpServletRequest req, HttpServletResponse resp) { - - ServletContext sc = getServletContext(); - return (StatusManager) sc.getAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY); - - // if (result != null) { - // System.out.println("from ServletContext"); - // return result; - // } else { - // HttpSession httpSession = req.getSession(true); - // - // System.out.println("from httpSession"); - // return (StatusManager) httpSession - // .getAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY); - // } - } - - @Override - protected String getPageTitle(HttpServletRequest req, HttpServletResponse resp) { - return "

Status messages for logback-access

\r\n"; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/boolex/JaninoEventEvaluator.java b/logback-access/src/main/java/ch/qos/logback/access/boolex/JaninoEventEvaluator.java deleted file mode 100644 index 7ba5a4bbff..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/boolex/JaninoEventEvaluator.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.boolex; - -import java.util.ArrayList; -import java.util.List; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.boolex.JaninoEventEvaluatorBase; -import ch.qos.logback.core.boolex.Matcher; - -public class JaninoEventEvaluator extends JaninoEventEvaluatorBase { - - public final static List DEFAULT_PARAM_NAME_LIST = new ArrayList<>(); - public final static List> DEFAULT_PARAM_TYPE_LIST = new ArrayList<>(); - - static { - DEFAULT_PARAM_NAME_LIST.add("event"); - DEFAULT_PARAM_TYPE_LIST.add(IAccessEvent.class); - } - - @Override - protected String getDecoratedExpression() { - String expression = getExpression(); - if (!expression.contains("return")) { - expression = "return " + expression + ";"; - addInfo("Adding [return] prefix and a semicolon suffix. Expression becomes [" + expression + "]"); - addInfo("See also " + CoreConstants.CODES_URL + "#block"); - } - return expression; - } - - @Override - protected String[] getParameterNames() { - List fullNameList = new ArrayList(); - fullNameList.addAll(DEFAULT_PARAM_NAME_LIST); - - for (int i = 0; i < matcherList.size(); i++) { - Matcher m = (Matcher) matcherList.get(i); - fullNameList.add(m.getName()); - } - - return (String[]) fullNameList.toArray(CoreConstants.EMPTY_STRING_ARRAY); - } - - @Override - protected Class[] getParameterTypes() { - List> fullTypeList = new ArrayList<>(); - fullTypeList.addAll(DEFAULT_PARAM_TYPE_LIST); - for (int i = 0; i < matcherList.size(); i++) { - fullTypeList.add(Matcher.class); - } - return (Class[]) fullTypeList.toArray(CoreConstants.EMPTY_CLASS_ARRAY); - } - - @Override - protected Object[] getParameterValues(IAccessEvent accessEvent) { - final int matcherListSize = matcherList.size(); - - int i = 0; - Object[] values = new Object[DEFAULT_PARAM_NAME_LIST.size() + matcherListSize]; - - values[i++] = accessEvent; - - for (int j = 0; j < matcherListSize; j++) { - values[i++] = matcherList.get(j); - } - - return values; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/db/DBAppender.java b/logback-access/src/main/java/ch/qos/logback/access/db/DBAppender.java deleted file mode 100644 index 64ccd689c7..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/db/DBAppender.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.db; - -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.Enumeration; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.db.DBAppenderBase; - -/** - * The DBAppender inserts access events into three database tables in a format - * independent of the Java programming language. - * - * For more information about this appender, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#AccessDBAppender - * - * @author Ceki Gülcü - * @author Ray DeCampo - * @author Sébastien Pennec - */ -public class DBAppender extends DBAppenderBase { - protected static final String insertSQL; - protected final String insertHeaderSQL = "INSERT INTO access_event_header (event_id, header_key, header_value) VALUES (?, ?, ?)"; - protected static final Method GET_GENERATED_KEYS_METHOD; - - private boolean insertHeaders = false; - - static { - StringBuilder sql = new StringBuilder(); - sql.append("INSERT INTO access_event ("); - sql.append("timestmp, "); - sql.append("requestURI, "); - sql.append("requestURL, "); - sql.append("remoteHost, "); - sql.append("remoteUser, "); - sql.append("remoteAddr, "); - sql.append("protocol, "); - sql.append("method, "); - sql.append("serverName, "); - sql.append("postContent) "); - sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?)"); - insertSQL = sql.toString(); - - Method getGeneratedKeysMethod; - try { - getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[]) null); - } catch (Exception ex) { - getGeneratedKeysMethod = null; - } - GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod; - } - - @Override - protected void subAppend(IAccessEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable { - - addAccessEvent(insertStatement, event); - - int updateCount = insertStatement.executeUpdate(); - if (updateCount != 1) { - addWarn("Failed to insert access event"); - } - } - - @Override - protected void secondarySubAppend(IAccessEvent event, Connection connection, long eventId) throws Throwable { - if (insertHeaders) { - addRequestHeaders(event, connection, eventId); - } - } - - void addAccessEvent(PreparedStatement stmt, IAccessEvent event) throws SQLException { - stmt.setLong(1, event.getTimeStamp()); - stmt.setString(2, event.getRequestURI()); - stmt.setString(3, event.getRequestURL()); - stmt.setString(4, event.getRemoteHost()); - stmt.setString(5, event.getRemoteUser()); - stmt.setString(6, event.getRemoteAddr()); - stmt.setString(7, event.getProtocol()); - stmt.setString(8, event.getMethod()); - stmt.setString(9, event.getServerName()); - stmt.setString(10, event.getRequestContent()); - } - - void addRequestHeaders(IAccessEvent event, Connection connection, long eventId) throws SQLException { - Enumeration names = event.getRequestHeaderNames(); - if (names.hasMoreElements()) { - PreparedStatement insertHeaderStatement = connection.prepareStatement(insertHeaderSQL); - - while (names.hasMoreElements()) { - String key = (String) names.nextElement(); - String value = (String) event.getRequestHeader(key); - - insertHeaderStatement.setLong(1, eventId); - insertHeaderStatement.setString(2, key); - insertHeaderStatement.setString(3, value); - - if (cnxSupportsBatchUpdates) { - insertHeaderStatement.addBatch(); - } else { - insertHeaderStatement.execute(); - } - } - - if (cnxSupportsBatchUpdates) { - insertHeaderStatement.executeBatch(); - } - - insertHeaderStatement.close(); - } - } - - @Override - protected Method getGeneratedKeysMethod() { - return GET_GENERATED_KEYS_METHOD; - } - - @Override - protected String getInsertSQL() { - return insertSQL; - } - - public void setInsertHeaders(boolean insertHeaders) { - this.insertHeaders = insertHeaders; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/db/package.html b/logback-access/src/main/java/ch/qos/logback/access/db/package.html deleted file mode 100644 index b6fab1396d..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/db/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - -

The ch.qos.logback.access.db package provides means to append access events -into various databases. -

- -

Most popular database systems, such as PostgreSQL, MySQL, Oracle, DB2 and MsSQL -are supported. -

- -

Just as importantly, the way for obtaining JDBC connections is pluggable. Connections can -be obtained through the tradinal way of DriverManager, or alternatively as a DataSource. -A DataSource can be instantiated directly or it can obtained through JNDI. -

- - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/CountingFilter.java b/logback-access/src/main/java/ch/qos/logback/access/filter/CountingFilter.java deleted file mode 100644 index 21932ca120..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/CountingFilter.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.spi.FilterReply; - -import javax.management.MBeanServer; -import javax.management.ObjectName; -import javax.management.StandardMBean; -import java.lang.management.ManagementFactory; - -public class CountingFilter extends Filter { - - long total = 0; - final StatisticalViewImpl accessStatsImpl; - - String domain = "ch.qos.logback.access"; - - public CountingFilter() { - accessStatsImpl = new StatisticalViewImpl(this); - } - - @Override - public FilterReply decide(IAccessEvent event) { - total++; - accessStatsImpl.update(); - return FilterReply.NEUTRAL; - } - - public long getTotal() { - return total; - } - - @Override - public void start() { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - try { - ObjectName on = new ObjectName(domain + ":Name=" + getName()); - StandardMBean mbean = new StandardMBean(accessStatsImpl, StatisticalView.class); - if (mbs.isRegistered(on)) { - mbs.unregisterMBean(on); - } - mbs.registerMBean(mbean, on); - super.start(); - } catch (Exception e) { - addError("Failed to create mbean", e); - } - } - - @Override - public void stop() { - super.stop(); - try { - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - ObjectName on = new ObjectName("totp:Filter=1"); - mbs.unregisterMBean(on); - } catch (Exception e) { - addError("Failed to unregister mbean", e); - } - } - - public String getDomain() { - return domain; - } - - public void setDomain(String domain) { - this.domain = domain; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/PeriodicStats.java b/logback-access/src/main/java/ch/qos/logback/access/filter/PeriodicStats.java deleted file mode 100644 index f1019564b1..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/PeriodicStats.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -abstract public class PeriodicStats { - - private long nextPeriodBegins = 0; - private long lastTotal = 0; - private long lastCount = 0; - - private double average; - private int n; - - PeriodicStats() { - this(System.currentTimeMillis()); - } - - PeriodicStats(long now) { - nextPeriodBegins = computeStartOfNextPeriod(now); - } - - void update(long now, long total) { - if (now > nextPeriodBegins) { - lastCount = total - lastTotal; - lastTotal = total; - average = (average * n + lastCount) / (++n); - nextPeriodBegins = computeStartOfNextPeriod(now); - } - } - - public double getAverage() { - return average; - } - - public long getLastCount() { - return lastCount; - } - - void reset(long now) { - nextPeriodBegins = computeStartOfNextPeriod(now); - lastTotal = 0; - lastCount = 0; - average = 0.0; - n = 0; - } - - void reset() { - reset(System.currentTimeMillis()); - } - - abstract long computeStartOfNextPeriod(long now); - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalView.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalView.java deleted file mode 100644 index 9df3028cc8..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalView.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -public interface StatisticalView { - - long getTotal(); - - long getLastMinuteCount(); - - double getMinuteAverage(); - - long getLastHoursCount(); - - double getHourlyAverage(); - - long getLastDaysCount(); - - double getDailyAverage(); - - long getLastWeeksCount(); - - double getWeeklyAverage(); - - long getLastMonthsCount(); - - double getMonthlyAverage(); - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalViewImpl.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalViewImpl.java deleted file mode 100644 index 0b1fb25fae..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatisticalViewImpl.java +++ /dev/null @@ -1,130 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.spi.LifeCycle; - -public class StatisticalViewImpl implements StatisticalView, LifeCycle { - - final CountingFilter countingFilter; - boolean started; - - StatsByMinute statsByMinute = new StatsByMinute(); - StatsByHour statsByHour = new StatsByHour(); - StatsByDay statsByDay = new StatsByDay(); - StatsByWeek statsByWeek = new StatsByWeek(); - StatsByMonth statsByMonth = new StatsByMonth(); - - StatisticalViewImpl(CountingFilter countingFilter) { - this.countingFilter = countingFilter; - } - - @Override - public double getDailyAverage() { - return statsByDay.getAverage(); - } - - @Override - public long getLastDaysCount() { - return statsByDay.getLastCount(); - } - - @Override - public double getMonthlyAverage() { - return statsByMonth.getAverage(); - } - - @Override - public long getLastMonthsCount() { - return statsByMonth.getLastCount(); - } - - @Override - public long getTotal() { - return countingFilter.getTotal(); - } - - @Override - public double getWeeklyAverage() { - return statsByWeek.getAverage(); - } - - @Override - public long getLastWeeksCount() { - return statsByWeek.getLastCount(); - } - - void update(long now) { - long total = getTotal(); - statsByMinute.update(now, total); - statsByHour.update(now, total); - statsByDay.update(now, total); - statsByWeek.update(now, total); - statsByMonth.update(now, total); - - } - - void update() { - long now = System.currentTimeMillis(); - update(now); - } - - @Override - public void start() { - System.out.println("StatisticalViewImpl start called"); - started = true; - long now = System.currentTimeMillis(); - statsByMinute = new StatsByMinute(now); - statsByHour = new StatsByHour(now); - statsByDay = new StatsByDay(now); - statsByWeek = new StatsByWeek(now); - statsByMonth = new StatsByMonth(now); - } - - @Override - public boolean isStarted() { - return started; - } - - @Override - public void stop() { - started = false; - statsByMinute.reset(); - statsByHour.reset(); - statsByDay.reset(); - statsByWeek.reset(); - statsByMonth.reset(); - } - - @Override - public long getLastMinuteCount() { - return statsByMinute.getLastCount(); - } - - @Override - public double getMinuteAverage() { - return statsByMinute.getAverage(); - } - - @Override - public double getHourlyAverage() { - return statsByHour.getAverage(); - } - - @Override - public long getLastHoursCount() { - return statsByHour.getLastCount(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByDay.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByDay.java deleted file mode 100644 index d03f4a7989..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByDay.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByDay extends PeriodicStats { - - StatsByDay() { - super(); - } - - StatsByDay(long now) { - super(now); - } - - @Override - long computeStartOfNextPeriod(long now) { - return TimeUtil.computeStartOfNextDay(now); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByHour.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByHour.java deleted file mode 100644 index 26d82507ad..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByHour.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByHour extends PeriodicStats { - - StatsByHour() { - super(); - } - - StatsByHour(long now) { - super(now); - } - - @Override - long computeStartOfNextPeriod(long now) { - return TimeUtil.computeStartOfNextHour(now); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMinute.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMinute.java deleted file mode 100644 index c29e45c7af..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMinute.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByMinute extends PeriodicStats { - - StatsByMinute() { - super(); - } - - StatsByMinute(long now) { - super(now); - } - - @Override - long computeStartOfNextPeriod(long now) { - return TimeUtil.computeStartOfNextMinute(now); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMonth.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMonth.java deleted file mode 100644 index 74e630e9ef..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByMonth.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByMonth extends PeriodicStats { - - StatsByMonth() { - super(); - } - - StatsByMonth(long now) { - super(now); - } - - @Override - long computeStartOfNextPeriod(long now) { - return TimeUtil.computeStartOfNextMonth(now); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByWeek.java b/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByWeek.java deleted file mode 100644 index 177ba6bbb7..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/filter/StatsByWeek.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByWeek extends PeriodicStats { - - StatsByWeek() { - super(); - } - - StatsByWeek(long now) { - super(now); - } - - @Override - long computeStartOfNextPeriod(long now) { - return TimeUtil.computeStartOfNextWeek(now); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java b/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java deleted file mode 100644 index 18e93578a7..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/html/DefaultCssBuilder.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.html; - -import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; -import ch.qos.logback.core.html.CssBuilder; - -/** - * This class helps the HTMLLayout build the CSS link. It either provides the - * HTMLLayout with a default css file, or builds the link to an external, - * user-specified, file. - * - * @author Sébastien Pennec - */ -public class DefaultCssBuilder implements CssBuilder { - - @Override - public void addCss(StringBuilder sbuf) { - sbuf.append(""); - } -} \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java b/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java deleted file mode 100644 index 932d2ad97b..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/html/HTMLLayout.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.html; - -import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; - -import java.util.Map; - -import ch.qos.logback.access.PatternLayout; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.html.HTMLLayoutBase; -import ch.qos.logback.core.pattern.Converter; - -/** - * - * HTMLLayout outputs events in an HTML table. - *

- * The content of the table columns are specified using a conversion pattern. - * See {@link ch.qos.logback.access.PatternLayout} for documentation on the - * available patterns. - *

- * For more information about this layout, please refer to the online manual at - * http://logback.qos.ch/manual/layouts.html#AccessHTMLLayout - * - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class HTMLLayout extends HTMLLayoutBase { - - /** - * Default pattern string for log output. - */ - static final String DEFAULT_CONVERSION_PATTERN = "%h%l%u%t%r%s%b"; - - /** - * Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN. - * - */ - public HTMLLayout() { - pattern = DEFAULT_CONVERSION_PATTERN; - cssBuilder = new DefaultCssBuilder(); - } - - @Override - protected Map getDefaultConverterMap() { - return PatternLayout.defaultConverterMap; - } - - @Override - public String doLayout(IAccessEvent event) { - StringBuilder buf = new StringBuilder(); - startNewTableIfLimitReached(buf); - - boolean odd = true; - if (((counter++) & 1) == 0) { - odd = false; - } - - buf.append(LINE_SEPARATOR); - buf.append(""); - } else { - buf.append(" even\">"); - } - buf.append(LINE_SEPARATOR); - - Converter c = head; - while (c != null) { - appendEventToBuffer(buf, c, event); - c = c.getNext(); - } - buf.append(""); - buf.append(LINE_SEPARATOR); - - return buf.toString(); - } - - private void appendEventToBuffer(StringBuilder buf, Converter c, IAccessEvent event) { - buf.append(""); - c.write(buf, event); - buf.append(""); - buf.append(LINE_SEPARATOR); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java b/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java deleted file mode 100644 index 77185b3fcf..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/html/UrlCssBuilder.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.html; - -import ch.qos.logback.core.html.CssBuilder; - -/** - * This class helps the HTMLLayout build the CSS link. - * It either provides the HTMLLayout with a default css file, - * or builds the link to an external, user-specified, file. - * - * @author Sébastien Pennec - */ -public class UrlCssBuilder implements CssBuilder { - - String url = "http://logback.qos.ch/css/access.css"; - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - @Override - public void addCss(StringBuilder sbuf) { - sbuf.append(""); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/html/package.html b/logback-access/src/main/java/ch/qos/logback/access/html/package.html deleted file mode 100644 index c988c3f19e..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/html/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

Contains classes to format log output in HTML.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java b/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java deleted file mode 100644 index bff6da05b2..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/jetty/JettyServerAdapter.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import ch.qos.logback.access.spi.ServerAdapter; - -import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Response; - -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Map; - -/** - * A jetty specific implementation of the {@link ServerAdapter} interface. - * - * @author Sébastien Pennec - * @author Ceki Gulcu - */ -public class JettyServerAdapter implements ServerAdapter { - - Request request; - Response response; - - public JettyServerAdapter(Request jettyRequest, Response jettyResponse) { - this.request = jettyRequest; - this.response = jettyResponse; - } - - @Override - public long getContentLength() { - return response.getContentCount(); - } - - @Override - public int getStatusCode() { - return response.getStatus(); - } - - @Override - public long getRequestTimestamp() { - return request.getTimeStamp(); - } - - @Override - public Map buildResponseHeaderMap() { - Map responseHeaderMap = new HashMap(); - HttpFields httpFields = response.getHttpFields(); - Enumeration e = httpFields.getFieldNames(); - while (e.hasMoreElements()) { - String key = (String) e.nextElement(); - String value = response.getHeader(key); - responseHeaderMap.put(key, value); - } - return responseHeaderMap; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogImpl.java b/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogImpl.java deleted file mode 100644 index 6ae1f2a51f..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogImpl.java +++ /dev/null @@ -1,323 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import java.io.File; -import java.net.URL; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; - -import ch.qos.logback.core.status.InfoStatus; -import ch.qos.logback.core.util.FileUtil; -import ch.qos.logback.core.util.StatusPrinter; - -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.RequestLog; -import org.eclipse.jetty.server.Response; - -import ch.qos.logback.access.joran.JoranConfigurator; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.spi.AppenderAttachable; -import ch.qos.logback.core.spi.AppenderAttachableImpl; -import ch.qos.logback.core.spi.FilterAttachable; -import ch.qos.logback.core.spi.FilterAttachableImpl; -import ch.qos.logback.core.spi.FilterReply; -import ch.qos.logback.core.status.ErrorStatus; -import ch.qos.logback.core.util.OptionHelper; - -/** - * This class is logback's implementation of jetty's RequestLog interface.

- * It can be seen as logback classic's LoggerContext. Appenders can be attached - * directly to RequestLogImpl and RequestLogImpl uses the same StatusManager as - * LoggerContext does. It also provides containers for properties.

To - * configure jetty in order to use RequestLogImpl, the following lines must be - * added to the jetty configuration file, namely etc/jetty.xml: - *

- *

- *    <Ref id="requestLog">
- *      <Set name="requestLog">
- *        <New id="requestLogImpl" class="ch.qos.logback.access.jetty.RequestLogImpl"></New>
- *      </Set>
- *    </Ref>
- * 
- *

- * By default, RequestLogImpl looks for a logback configuration file called - * logback-access.xml, in the same folder where jetty.xml is located, that is - * etc/logback-access.xml. The logback-access.xml file is slightly - * different than the usual logback classic configuration file. Most of it is - * the same: Appenders and Layouts are declared the exact same way. However, - * loggers elements are not allowed.

It is possible to put the logback - * configuration file anywhere, as long as it's path is specified. Here is - * another example, with a path to the logback-access.xml file. - *

- *

- *    <Ref id="requestLog">
- *      <Set name="requestLog">
- *        <New id="requestLogImpl" class="ch.qos.logback.access.jetty.RequestLogImpl"></New>
- *          <Set name="fileName">path/to/logback.xml</Set>
- *      </Set>
- *    </Ref>
- * 
- *

- *

Here is a sample logback-access.xml file that can be used right away: - *

- *

- *    <configuration>
- *      <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
- *        <layout class="ch.qos.logback.access.PatternLayout">
- *          <param name="Pattern" value="%date %server %remoteIP %clientHost %user %requestURL" />
- *        </layout>
- *      </appender>
- *
- *      <appender-ref ref="STDOUT" />
- *    </configuration>
- * 
- *

- *

Another configuration file, using SMTPAppender, could be: - *

- *

- *    <configuration>
- *      <appender name="SMTP" class="ch.qos.logback.access.net.SMTPAppender">
- *        <layout class="ch.qos.logback.access.PatternLayout">
- *          <param name="pattern" value="%remoteIP [%date] %requestURL %statusCode %bytesSent" />
- *        </layout>
- *        <param name="From" value="sender@domaine.org" />
- *        <param name="SMTPHost" value="mail.domain.org" />
- *         <param name="Subject" value="Last Event: %statusCode %requestURL" />
- *         <param name="To" value="server_admin@domain.org" />
- *      </appender>
- *      <appender-ref ref="SMTP" />
- *    </configuration>
- * 
- * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class RequestLogImpl extends ContextBase implements RequestLog, AppenderAttachable, FilterAttachable { - - public final static String DEFAULT_CONFIG_FILE = "etc" + File.separatorChar + "logback-access.xml"; - - AppenderAttachableImpl aai = new AppenderAttachableImpl(); - FilterAttachableImpl fai = new FilterAttachableImpl(); - String fileName; - String resource; - boolean started = false; - boolean quiet = false; - - public RequestLogImpl() { - putObject(CoreConstants.EVALUATOR_MAP, new HashMap>()); - } - - @Override - public void log(Request jettyRequest, Response jettyResponse) { - JettyServerAdapter adapter = new JettyServerAdapter(jettyRequest, jettyResponse); - IAccessEvent accessEvent = new AccessEvent(this, jettyRequest, jettyResponse, adapter); - if (getFilterChainDecision(accessEvent) == FilterReply.DENY) { - return; - } - aai.appendLoopOnAppenders(accessEvent); - } - - private void addInfo(String msg) { - getStatusManager().add(new InfoStatus(msg, this)); - } - - private void addError(String msg) { - getStatusManager().add(new ErrorStatus(msg, this)); - } - - @Override - public void start() { - configure(); - if (!isQuiet()) { - StatusPrinter.print(getStatusManager()); - } - started = true; - } - - protected void configure() { - URL configURL = getConfigurationFileURL(); - if (configURL != null) { - runJoranOnFile(configURL); - } else { - addError("Could not find configuration file for logback-access"); - } - } - - protected URL getConfigurationFileURL() { - if (fileName != null) { - addInfo("Will use configuration file [" + fileName + "]"); - File file = new File(fileName); - if (!file.exists()) - return null; - return FileUtil.fileToURL(file); - } - if (resource != null) { - addInfo("Will use configuration resource [" + resource + "]"); - return this.getClass().getResource(resource); - } - - String jettyHomeProperty = OptionHelper.getSystemProperty("jetty.home"); - String defaultConfigFile = DEFAULT_CONFIG_FILE; - if (!OptionHelper.isEmpty(jettyHomeProperty)) { - defaultConfigFile = jettyHomeProperty + File.separatorChar + DEFAULT_CONFIG_FILE; - } else { - addInfo("[jetty.home] system property not set."); - } - File file = new File(defaultConfigFile); - addInfo("Assuming default configuration file [" + defaultConfigFile + "]"); - if (!file.exists()) - return null; - return FileUtil.fileToURL(file); - } - - private void runJoranOnFile(URL configURL) { - try { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(this); - jc.doConfigure(configURL); - if (getName() == null) { - setName("LogbackRequestLog"); - } - } catch (JoranException e) { - // errors have been registered as status messages - } - } - - @Override - public void stop() { - aai.detachAndStopAllAppenders(); - started = false; - } - - @Override - public boolean isRunning() { - return started; - } - - public void setFileName(String fileName) { - this.fileName = fileName; - } - - public void setResource(String resource) { - this.resource = resource; - } - - @Override - public boolean isStarted() { - return started; - } - - @Override - public boolean isStarting() { - return false; - } - - @Override - public boolean isStopping() { - return false; - } - - @Override - public boolean isStopped() { - return !started; - } - - @Override - public boolean isFailed() { - return false; - } - - public boolean isQuiet() { - return quiet; - } - - public void setQuiet(boolean quiet) { - this.quiet = quiet; - } - - @Override - public void addAppender(Appender newAppender) { - aai.addAppender(newAppender); - } - - @Override - public Iterator> iteratorForAppenders() { - return aai.iteratorForAppenders(); - } - - @Override - public Appender getAppender(String name) { - return aai.getAppender(name); - } - - @Override - public boolean isAttached(Appender appender) { - return aai.isAttached(appender); - } - - @Override - public void detachAndStopAllAppenders() { - aai.detachAndStopAllAppenders(); - } - - @Override - public boolean detachAppender(Appender appender) { - return aai.detachAppender(appender); - } - - @Override - public boolean detachAppender(String name) { - return aai.detachAppender(name); - } - - @Override - public void addFilter(Filter newFilter) { - fai.addFilter(newFilter); - } - - @Override - public void clearAllFilters() { - fai.clearAllFilters(); - } - - @Override - public List> getCopyOfAttachedFiltersList() { - return fai.getCopyOfAttachedFiltersList(); - } - - @Override - public FilterReply getFilterChainDecision(IAccessEvent event) { - return fai.getFilterChainDecision(event); - } - - @Override - public void addLifeCycleListener(Listener listener) { - // we'll implement this when asked - } - - @Override - public void removeLifeCycleListener(Listener listener) { - // we'll implement this when asked - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogRegistry.java b/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogRegistry.java deleted file mode 100644 index acdf91dfa8..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/jetty/RequestLogRegistry.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import java.util.HashMap; -import java.util.Map; - -// this class is currently not used -public class RequestLogRegistry { - - private static Map requestLogRegistry = new HashMap(); - - public static void register(RequestLogImpl requestLogImpl) { - requestLogRegistry.put(requestLogImpl.getName(), requestLogImpl); - } - - public static RequestLogImpl get(String key) { - return requestLogRegistry.get(key); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/jetty/package.html b/logback-access/src/main/java/ch/qos/logback/access/jetty/package.html deleted file mode 100644 index bc2c3dc595..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/jetty/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

This is logback access' implementation for Jetty 8.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/joran/JoranConfigurator.java b/logback-access/src/main/java/ch/qos/logback/access/joran/JoranConfigurator.java deleted file mode 100644 index 9a29a98e46..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/joran/JoranConfigurator.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran; - -import ch.qos.logback.access.PatternLayout; -import ch.qos.logback.access.PatternLayoutEncoder; -import ch.qos.logback.access.boolex.JaninoEventEvaluator; -import ch.qos.logback.access.joran.action.ConfigurationAction; -import ch.qos.logback.access.joran.action.EvaluatorAction; -import ch.qos.logback.access.sift.SiftAction; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.AppenderBase; -import ch.qos.logback.core.UnsynchronizedAppenderBase; -import ch.qos.logback.core.filter.EvaluatorFilter; -import ch.qos.logback.core.joran.JoranConfiguratorBase; -import ch.qos.logback.core.joran.action.AppenderRefAction; -import ch.qos.logback.core.joran.action.IncludeAction; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.joran.conditional.ElseAction; -import ch.qos.logback.core.joran.conditional.IfAction; -import ch.qos.logback.core.joran.conditional.ThenAction; -import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.RuleStore; -import ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules; - -/** - * This JoranConfiguratorclass adds rules specific to logback-access. - * - * @author Ceki Gülcü - */ -public class JoranConfigurator extends JoranConfiguratorBase { - - @Override - public void addInstanceRules(RuleStore rs) { - super.addInstanceRules(rs); - - rs.addRule(new ElementSelector("configuration"), new ConfigurationAction()); - rs.addRule(new ElementSelector("configuration/appender-ref"), new AppenderRefAction()); - - rs.addRule(new ElementSelector("configuration/appender/sift"), new SiftAction()); - rs.addRule(new ElementSelector("configuration/appender/sift/*"), new NOPAction()); - - rs.addRule(new ElementSelector("configuration/evaluator"), new EvaluatorAction()); - - // add if-then-else support - rs.addRule(new ElementSelector("*/if"), new IfAction()); - rs.addRule(new ElementSelector("*/if/then"), new ThenAction()); - rs.addRule(new ElementSelector("*/if/then/*"), new NOPAction()); - rs.addRule(new ElementSelector("*/if/else"), new ElseAction()); - rs.addRule(new ElementSelector("*/if/else/*"), new NOPAction()); - - rs.addRule(new ElementSelector("configuration/include"), new IncludeAction()); - } - - @Override - protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { - registry.add(AppenderBase.class, "layout", PatternLayout.class); - registry.add(EvaluatorFilter.class, "evaluator", JaninoEventEvaluator.class); - - registry.add(AppenderBase.class, "encoder", PatternLayoutEncoder.class); - registry.add(UnsynchronizedAppenderBase.class, "encoder", PatternLayoutEncoder.class); - SSLNestedComponentRegistryRules.addDefaultNestedComponentRegistryRules(registry); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/joran/action/ConfigurationAction.java b/logback-access/src/main/java/ch/qos/logback/access/joran/action/ConfigurationAction.java deleted file mode 100644 index a06087b0b0..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/joran/action/ConfigurationAction.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran.action; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.status.OnConsoleStatusListener; -import ch.qos.logback.core.util.OptionHelper; -import ch.qos.logback.core.util.StatusListenerConfigHelper; - -public class ConfigurationAction extends Action { - static final String INTERNAL_DEBUG_ATTR = "debug"; - static final String DEBUG_SYSTEM_PROPERTY_KEY = "logback-access.debug"; - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) { - - // See LBCLASSIC-225 (the system property is looked up first. Thus, it overrides - // the equivalent property in the config file. This reversal of scope priority is justified - // by the use case: the admin trying to chase rogue config file - String debugAttrib = System.getProperty(DEBUG_SYSTEM_PROPERTY_KEY); - if (debugAttrib == null) { - debugAttrib = attributes.getValue(INTERNAL_DEBUG_ATTR); - } - - if (OptionHelper.isEmpty(debugAttrib) || debugAttrib.equals("false") || debugAttrib.equals("null")) { - addInfo(INTERNAL_DEBUG_ATTR + " attribute not set"); - } else { - StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); - } - - // the context is appender attachable, so it is pushed on top of the stack - ec.pushObject(getContext()); - } - - @Override - public void end(InterpretationContext ec, String name) { - addInfo("End of configuration."); - ec.popObject(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/joran/action/EvaluatorAction.java b/logback-access/src/main/java/ch/qos/logback/access/joran/action/EvaluatorAction.java deleted file mode 100644 index 5fa3bff6cf..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/joran/action/EvaluatorAction.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran.action; - -import ch.qos.logback.access.boolex.JaninoEventEvaluator; -import ch.qos.logback.core.joran.action.AbstractEventEvaluatorAction; - -public class EvaluatorAction extends AbstractEventEvaluatorAction { - - @Override - protected String defaultClassName() { - return JaninoEventEvaluator.class.getName(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/joran/package.html b/logback-access/src/main/java/ch/qos/logback/access/joran/package.html deleted file mode 100644 index 3955b621f0..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/joran/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

Contains the Joran configration classes

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/AccessEventPreSerializationTransformer.java b/logback-access/src/main/java/ch/qos/logback/access/net/AccessEventPreSerializationTransformer.java deleted file mode 100644 index b75b4faeef..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/AccessEventPreSerializationTransformer.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.io.Serializable; - -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -public class AccessEventPreSerializationTransformer implements PreSerializationTransformer { - - @Override - public Serializable transform(IAccessEvent event) { - if (event instanceof AccessEvent) { - return (AccessEvent) event; - } else { - throw new IllegalArgumentException("Unsupported type " + event.getClass().getName()); - } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java b/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java deleted file mode 100755 index c0ba6b0109..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/HardenedAccessEventInputStream.java +++ /dev/null @@ -1,15 +0,0 @@ -package ch.qos.logback.access.net; - -import java.io.IOException; -import java.io.InputStream; - -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.core.net.HardenedObjectInputStream; - -public class HardenedAccessEventInputStream extends HardenedObjectInputStream { - - public HardenedAccessEventInputStream(InputStream in) throws IOException { - super(in, new String[] {AccessEvent.class.getName(), String[].class.getName()}); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java deleted file mode 100644 index e40a5afbce..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/SMTPAppender.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import ch.qos.logback.access.PatternLayout; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Layout; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.helpers.CyclicBuffer; -import ch.qos.logback.core.net.SMTPAppenderBase; - -/** - * Send an e-mail when a specific access event occurs, typically when - * certain pages are accessed. - * - * For more information about this appender, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#AccessSMTPAppender - *

- * @author Ceki Gülcü - * @author Sébastien Pennec - * - */ -public class SMTPAppender extends SMTPAppenderBase { - - static final String DEFAULT_SUBJECT_PATTERN = "%m"; - - /** - * The default constructor will instantiate the appender with a - * {@link EventEvaluator} that will trigger on events with level - * ERROR or higher. - */ - public SMTPAppender() { - } - - /** - * Use evaluator passed as parameter as the {@link - * EventEvaluator} for this SMTPAppender. - */ - public SMTPAppender(EventEvaluator evaluator) { - this.eventEvaluator = evaluator; - } - - /** - * Perform SMTPAppender specific appending actions, mainly adding the event to - * the appropriate cyclic buffer. - */ - @Override - protected void subAppend(CyclicBuffer cb, IAccessEvent event) { - cb.add(event); - } - - @Override - protected void fillBuffer(CyclicBuffer cb, StringBuffer sbuf) { - int len = cb.length(); - for (int i = 0; i < len; i++) { - // sbuf.append(MimeUtility.encodeText(layout.format(cb.getOrCreate()))); - IAccessEvent event = cb.get(); - sbuf.append(layout.doLayout(event)); - } - } - - @Override - protected Layout makeSubjectLayout(String subjectStr) { - if (subjectStr == null) { - subjectStr = DEFAULT_SUBJECT_PATTERN; - } - PatternLayout pl = new PatternLayout(); - pl.setPattern(subjectStr); - pl.start(); - return pl; - } - - @Override - protected PatternLayout makeNewToPatternLayout(String toPattern) { - PatternLayout pl = new PatternLayout(); - pl.setPattern(toPattern); - return pl; - } - - @Override - protected boolean eventMarksEndOfLife(IAccessEvent eventObject) { - return false; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SSLSocketAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/SSLSocketAppender.java deleted file mode 100644 index a171715eaf..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/SSLSocketAppender.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.net.AbstractSSLSocketAppender; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * A {@link SocketAppender} that supports SSL. - *

- * For more information on this appender, please refer to the online manual - * at http://logback.qos.ch/manual/appenders.html#SSLSocketAppender - * - * @author Carl Harris - */ -public class SSLSocketAppender extends AbstractSSLSocketAppender { - - private final PreSerializationTransformer pst = new AccessEventPreSerializationTransformer(); - - public SSLSocketAppender() { - } - - @Override - protected void postProcessEvent(IAccessEvent event) { - event.prepareForDeferredProcessing(); - } - - public PreSerializationTransformer getPST() { - return pst; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SimpleSocketServer.java b/logback-access/src/main/java/ch/qos/logback/access/net/SimpleSocketServer.java deleted file mode 100644 index 3360e84341..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/SimpleSocketServer.java +++ /dev/null @@ -1,101 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.net.ServerSocket; -import java.net.Socket; - -import ch.qos.logback.access.joran.JoranConfigurator; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.util.StatusPrinter; - -/** - * A simple {@link SocketNode} based server. - * - *

- *     <b>Usage:</b> java ch.qos.logback.access.net.SimpleSocketServer port configFile
- *    
- *     where
- * 
- * port
- * 
- *     is a part number where the server listens and
- * 
- * configFile
- * 
- *     is an xml configuration file fed to {@link JoranConfigurator}.
- * 
- * - * @author Ceki Gülcü - * @author Sébastien Pennec - * - * @since 0.8.4 - */ -public class SimpleSocketServer { - - static int port; - - private static AccessContext basicContext; - - public static void main(String argv[]) throws Exception { - if (argv.length == 2) { - init(argv[0], argv[1]); - } else { - usage("Wrong number of arguments."); - } - - runServer(); - } - - static void runServer() { - try { - System.out.println("Listening on port " + port); - @SuppressWarnings("resource") - ServerSocket serverSocket = new ServerSocket(port); - while (true) { - System.out.println("Waiting to accept a new client."); - Socket socket = serverSocket.accept(); - System.out.println("Connected to client at " + socket.getInetAddress()); - System.out.println("Starting new socket node."); - new Thread(new SocketNode(socket, basicContext)).start(); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - static void usage(String msg) { - System.err.println(msg); - System.err.println("Usage: java " + SimpleSocketServer.class.getName() + " port configFile"); - System.exit(1); - } - - static void init(String portStr, String configFile) throws JoranException { - try { - port = Integer.parseInt(portStr); - } catch (java.lang.NumberFormatException e) { - e.printStackTrace(); - usage("Could not interpret port number [" + portStr + "]."); - } - - basicContext = new AccessContext(); - if (configFile.endsWith(".xml")) { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(basicContext); - configurator.doConfigure(configFile); - StatusPrinter.print(basicContext); - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java deleted file mode 100644 index 8189aee4b3..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/SocketAppender.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -// Contributors: Dan MacDonald -package ch.qos.logback.access.net; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.net.AbstractSocketAppender; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * Sends {@link IAccessEvent} objects to a remote a log server, usually a - * {@link SocketNode}. - * - * For more information about this appender, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#AccessSocketAppender - * - * @author Ceki Gülcü - * @author Sébastien Pennec - * - */ - -public class SocketAppender extends AbstractSocketAppender { - - PreSerializationTransformer pst = new AccessEventPreSerializationTransformer(); - - public SocketAppender() { - } - - @Override - protected void postProcessEvent(IAccessEvent event) { - event.prepareForDeferredProcessing(); - } - - public PreSerializationTransformer getPST() { - return pst; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java b/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java deleted file mode 100644 index 45b88a6df0..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/SocketNode.java +++ /dev/null @@ -1,88 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.io.BufferedInputStream; -import java.io.IOException; -import java.net.Socket; - -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.spi.FilterReply; - -// Contributors: Moses Hohman - -/** - * Read {@link IAccessEvent} objects sent from a remote client using Sockets - * (TCP). These logging events are logged according to local policy, as if they - * were generated locally. - * - *

- * For example, the socket node might decide to log events to a local file and - * also resent them to a second socket node. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - * - * @since 0.8.4 - */ -public class SocketNode implements Runnable { - - Socket socket; - AccessContext context; - HardenedAccessEventInputStream hardenedOIS; - - public SocketNode(Socket socket, AccessContext context) { - this.socket = socket; - this.context = context; - try { - hardenedOIS = new HardenedAccessEventInputStream(new BufferedInputStream(socket.getInputStream())); - } catch (Exception e) { - System.out.println("Could not open HardenedObjectInputStream to " + socket + e); - } - } - - @Override - public void run() { - IAccessEvent event; - - try { - while (true) { - // read an event from the wire - event = (IAccessEvent) hardenedOIS.readObject(); - // check that the event should be logged - if (context.getFilterChainDecision(event) == FilterReply.DENY) { - break; - } - // send it to the appenders - context.callAppenders(event); - } - } catch (java.io.EOFException e) { - System.out.println("Caught java.io.EOFException closing connection."); - } catch (java.net.SocketException e) { - System.out.println("Caught java.net.SocketException closing connection."); - } catch (IOException e) { - System.out.println("Caught java.io.IOException: " + e); - System.out.println("Closing connection."); - } catch (Exception e) { - System.out.println("Unexpected exception. Closing connection." + e); - } - - try { - hardenedOIS.close(); - } catch (Exception e) { - System.out.println("Could not close connection." + e); - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/URLEvaluator.java b/logback-access/src/main/java/ch/qos/logback/access/net/URLEvaluator.java deleted file mode 100644 index 9abdb9b569..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/URLEvaluator.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.util.ArrayList; -import java.util.List; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.boolex.EvaluationException; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.spi.LifeCycle; - -public class URLEvaluator extends ContextAwareBase implements EventEvaluator, LifeCycle { - - boolean started; - String name; - private List URLList = new ArrayList(); - - public void addURL(String url) { - URLList.add(url); - } - - @Override - public void start() { - if (URLList.size() == 0) { - addWarn("No URL was given to URLEvaluator"); - } else { - started = true; - } - } - - @Override - public boolean evaluate(IAccessEvent event) throws NullPointerException, EvaluationException { - String url = event.getRequestURL(); - for (String expected : URLList) { - if (url.contains(expected)) { - return true; - } - } - return false; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String name) { - this.name = name; - } - - @Override - public boolean isStarted() { - return started; - } - - @Override - public void stop() { - started = false; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/server/SSLServerSocketAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/server/SSLServerSocketAppender.java deleted file mode 100644 index 916818b954..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/server/SSLServerSocketAppender.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net.server; - -import ch.qos.logback.access.net.AccessEventPreSerializationTransformer; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.net.server.SSLServerSocketAppenderBase; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * An appender that listens on a TCP port for connections from remote - * loggers. Each event delivered to this appender is delivered to all - * connected remote loggers. - * - * @author Carl Harris - */ -public class SSLServerSocketAppender extends SSLServerSocketAppenderBase { - - private static final PreSerializationTransformer pst = new AccessEventPreSerializationTransformer(); - - @Override - protected void postProcessEvent(IAccessEvent event) { - event.prepareForDeferredProcessing(); - } - - @Override - protected PreSerializationTransformer getPST() { - return pst; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/server/ServerSocketAppender.java b/logback-access/src/main/java/ch/qos/logback/access/net/server/ServerSocketAppender.java deleted file mode 100644 index 24c021d03c..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/server/ServerSocketAppender.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net.server; - -import ch.qos.logback.access.net.AccessEventPreSerializationTransformer; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.net.server.AbstractServerSocketAppender; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * An appender that listens on a TCP port for connections from remote - * loggers. Each event delivered to this appender is delivered to all - * connected remote loggers. - * - * @author Carl Harris - */ -public class ServerSocketAppender extends AbstractServerSocketAppender { - - private static final PreSerializationTransformer pst = new AccessEventPreSerializationTransformer(); - - @Override - protected void postProcessEvent(IAccessEvent event) { - event.prepareForDeferredProcessing(); - } - - @Override - protected PreSerializationTransformer getPST() { - return pst; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/net/server/package.html b/logback-access/src/main/java/ch/qos/logback/access/net/server/package.html deleted file mode 100644 index 162894cfd9..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/net/server/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - -

Provides a robust appender that serves logging events to remote clients

-

The ServerSocketAppender - listens on a configurable TCP port for connections from remote - loggers. Events that are delivered to the appender are serialized - and sent to each connected remote logger. -

- - diff --git a/logback-access/src/main/java/ch/qos/logback/access/package.html b/logback-access/src/main/java/ch/qos/logback/access/package.html deleted file mode 100644 index 5755fc4968..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

This is logback access' main package.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/AccessConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/AccessConverter.java deleted file mode 100644 index fa5cb645d0..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/AccessConverter.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.pattern.DynamicConverter; -import ch.qos.logback.core.spi.ContextAware; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.status.Status; - -abstract public class AccessConverter extends DynamicConverter implements ContextAware { - - public final static char SPACE_CHAR = ' '; - public final static char QUESTION_CHAR = '?'; - - ContextAwareBase cab = new ContextAwareBase(); - - @Override - public void setContext(Context context) { - cab.setContext(context); - } - - @Override - public Context getContext() { - return cab.getContext(); - } - - @Override - public void addStatus(Status status) { - cab.addStatus(status); - } - - @Override - public void addInfo(String msg) { - cab.addInfo(msg); - } - - @Override - public void addInfo(String msg, Throwable ex) { - cab.addInfo(msg, ex); - } - - @Override - public void addWarn(String msg) { - cab.addWarn(msg); - } - - @Override - public void addWarn(String msg, Throwable ex) { - cab.addWarn(msg, ex); - } - - @Override - public void addError(String msg) { - cab.addError(msg); - } - - @Override - public void addError(String msg, Throwable ex) { - cab.addError(msg, ex); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ContentLengthConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ContentLengthConverter.java deleted file mode 100644 index c040513db2..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ContentLengthConverter.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class ContentLengthConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - long len = accessEvent.getContentLength(); - if (len == IAccessEvent.SENTINEL) { - return IAccessEvent.NA; - } else { - return Long.toString(len); - } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/DateConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/DateConverter.java deleted file mode 100644 index 9009638c58..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/DateConverter.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import java.util.List; -import java.util.TimeZone; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.util.CachingDateFormatter; - -public class DateConverter extends AccessConverter { - - CachingDateFormatter cachingDateFormatter = null; - - @Override - public void start() { - - String datePattern = getFirstOption(); - if (datePattern == null) { - datePattern = CoreConstants.CLF_DATE_PATTERN; - } - - if (datePattern.equals(CoreConstants.ISO8601_STR)) { - datePattern = CoreConstants.ISO8601_PATTERN; - } - - try { - cachingDateFormatter = new CachingDateFormatter(datePattern); - // maximumCacheValidity = CachedDateFormat.getMaximumCacheValidity(pattern); - } catch (IllegalArgumentException e) { - addWarn("Could not instantiate SimpleDateFormat with pattern " + datePattern, e); - addWarn("Defaulting to " + CoreConstants.CLF_DATE_PATTERN); - cachingDateFormatter = new CachingDateFormatter(CoreConstants.CLF_DATE_PATTERN); - } - - List optionList = getOptionList(); - - // if the option list contains a TZ option, then set it. - if (optionList != null && optionList.size() > 1) { - TimeZone tz = TimeZone.getTimeZone((String) optionList.get(1)); - cachingDateFormatter.setTimeZone(tz); - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - long timestamp = accessEvent.getTimeStamp(); - return cachingDateFormatter.format(timestamp); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedSecondsConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedSecondsConverter.java deleted file mode 100644 index 614b367a0e..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedSecondsConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2013, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class ElapsedSecondsConverter extends AccessConverter { - - public String convert(IAccessEvent accessEvent) { - return Long.toString(accessEvent.getElapsedSeconds()); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedTimeConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedTimeConverter.java deleted file mode 100644 index 381d757e76..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ElapsedTimeConverter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class ElapsedTimeConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return Long.toString(accessEvent.getElapsedTime()); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/EnsureLineSeparation.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/EnsureLineSeparation.java deleted file mode 100644 index 2e415d16e5..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/EnsureLineSeparation.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.pattern.Converter; -import ch.qos.logback.core.pattern.ConverterUtil; -import ch.qos.logback.core.pattern.PostCompileProcessor; - -public class EnsureLineSeparation implements PostCompileProcessor { - - /** - * Add a line separator converter so that access event appears on a separate - * line. - */ - @Override - public void process(Context context, Converter head) { - if (head == null) - throw new IllegalArgumentException("Empty converter chain"); - - // if head != null, then tail != null as well - Converter tail = ConverterUtil.findTail(head); - Converter newLineConverter = new LineSeparatorConverter(); - if (!(tail instanceof LineSeparatorConverter)) { - tail.setNext(newLineConverter); - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/FullRequestConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/FullRequestConverter.java deleted file mode 100644 index 9c6be06a87..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/FullRequestConverter.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import java.util.Enumeration; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.CoreConstants; - -/** - * This class is tied to the fullRequest conversion word. - *

- * It has been removed from the {@link ch.qos.logback.access.PatternLayout} since - * it needs further testing before wide use. - *

- * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class FullRequestConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent ae) { - StringBuilder buf = new StringBuilder(); - buf.append(ae.getRequestURL()); - buf.append(CoreConstants.LINE_SEPARATOR); - - Enumeration headerNames = ae.getRequestHeaderNames(); - while (headerNames.hasMoreElements()) { - String name = headerNames.nextElement(); - buf.append(name); - buf.append(": "); - buf.append(ae.getRequestHeader(name)); - buf.append(CoreConstants.LINE_SEPARATOR); - } - buf.append(CoreConstants.LINE_SEPARATOR); - buf.append(ae.getRequestContent()); - return buf.toString(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java deleted file mode 100644 index e0366cb118..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/FullResponseConverter.java +++ /dev/null @@ -1,131 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import java.util.List; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.CoreConstants; - -public class FullResponseConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent ae) { - StringBuilder buf = new StringBuilder(); - - buf.append("HTTP/1.1 "); - int statusCode = ae.getStatusCode(); - buf.append(statusCode); - buf.append(" "); - buf.append(getStatusCodeDescription(statusCode)); - buf.append(CoreConstants.LINE_SEPARATOR); - - List hnList = ae.getResponseHeaderNameList(); - for (String headerName : hnList) { - buf.append(headerName); - buf.append(": "); - buf.append(ae.getResponseHeader(headerName)); - buf.append(CoreConstants.LINE_SEPARATOR); - } - buf.append(CoreConstants.LINE_SEPARATOR); - buf.append(ae.getResponseContent()); - buf.append(CoreConstants.LINE_SEPARATOR); - return buf.toString(); - } - - static String getStatusCodeDescription(int sc) { - switch (sc) { - case 200: - return "OK"; - case 201: - return "Created"; - case 202: - return "Accepted"; - case 203: - return "Non-Authoritative Information"; - case 204: - return "No Content"; - case 205: - return "Reset Content"; - case 206: - return "Partial Content"; - case 300: - return "Multiple Choices"; - case 301: - return "Moved Permanently"; - case 302: - return "Found"; - case 303: - return "See Other"; - case 304: - return "Not Modified"; - case 305: - return "Use Proxy"; - case 306: - return "(Unused)"; - case 307: - return "Temporary Redirect"; - case 400: - return "Bad Request"; - case 401: - return "Unauthorized"; - case 402: - return "Payment Required"; - case 403: - return "Forbidden"; - case 404: - return "Not Found"; - case 405: - return "Method Not Allowed"; - case 406: - return "Not Acceptable"; - case 407: - return "Proxy Authentication Required"; - case 408: - return "Request Timeout"; - case 409: - return "Conflict"; - case 410: - return "Gone"; - case 411: - return "Length Required"; - case 412: - return "Precondition Failed"; - case 413: - return "Request Entity Too Large"; - case 414: - return "Request-URI Too Long"; - case 415: - return "Unsupported Media Type"; - case 416: - return "Requested Range Not Satisfiable"; - case 417: - return "Expectation Failed"; - case 500: - return "Internal Server Error"; - case 501: - return "Not Implemented"; - case 502: - return "Bad Gateway"; - case 503: - return "Service Unavailable"; - case 504: - return "Gateway Timeout"; - case 505: - return "HTTP Version Not Supported"; - default: - return "NA"; - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/LineSeparatorConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/LineSeparatorConverter.java deleted file mode 100644 index 1590a2ab6f..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/LineSeparatorConverter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.CoreConstants; - -public class LineSeparatorConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent event) { - return CoreConstants.LINE_SEPARATOR; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalIPAddressConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalIPAddressConverter.java deleted file mode 100644 index 21f576acb3..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalIPAddressConverter.java +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import java.net.InetAddress; -import java.net.UnknownHostException; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class LocalIPAddressConverter extends AccessConverter { - - String localIPAddressStr; - - public LocalIPAddressConverter() { - try { - localIPAddressStr = InetAddress.getLocalHost().getHostAddress(); - } catch (UnknownHostException uhe) { - localIPAddressStr = "127.0.0.1"; - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - return localIPAddressStr; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalPortConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalPortConverter.java deleted file mode 100644 index 1b0d566690..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/LocalPortConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class LocalPortConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return Integer.toString(accessEvent.getLocalPort()); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/NAConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/NAConverter.java deleted file mode 100644 index e2172186ee..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/NAConverter.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -/** - * Always returns the NA (not available) string which is "-" in the case - * of access conversions. - * - * @author Ceki Gülcü - */ -public class NAConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return IAccessEvent.NA; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/QueryStringConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/QueryStringConverter.java deleted file mode 100644 index 50772421f3..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/QueryStringConverter.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2013, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class QueryStringConverter extends AccessConverter { - - public String convert(IAccessEvent accessEvent) { - return accessEvent.getQueryString(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteHostConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteHostConverter.java deleted file mode 100644 index bad1c898cb..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteHostConverter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class RemoteHostConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getRemoteHost(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteIPAddressConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteIPAddressConverter.java deleted file mode 100644 index 7f91702521..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteIPAddressConverter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class RemoteIPAddressConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getRemoteAddr(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteUserConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteUserConverter.java deleted file mode 100644 index 7b0864a9e9..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RemoteUserConverter.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class RemoteUserConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - - String user = accessEvent.getRemoteUser(); - if (user == null) { - return IAccessEvent.NA; - } else { - return user; - } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestAttributeConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestAttributeConverter.java deleted file mode 100644 index 153797511e..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestAttributeConverter.java +++ /dev/null @@ -1,42 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.util.OptionHelper; - -public class RequestAttributeConverter extends AccessConverter { - - String key; - - @Override - public void start() { - key = getFirstOption(); - if (OptionHelper.isEmpty(key)) { - addWarn("Missing key for the request attribute"); - } else { - super.start(); - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_REQUEST_ATTRIB_CONV"; - } - - return accessEvent.getAttribute(key); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestContentConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestContentConverter.java deleted file mode 100644 index 67e409fb46..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestContentConverter.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -/** - * This class is tied to the requestContent conversion word. - *

- * It has been removed from the {@link ch.qos.logback.access.PatternLayout} since - * it needs further testing before wide use. - *

- * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class RequestContentConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getRequestContent(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestCookieConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestCookieConverter.java deleted file mode 100644 index 0d2f189e8c..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestCookieConverter.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.util.OptionHelper; - -public class RequestCookieConverter extends AccessConverter { - - String key; - - @Override - public void start() { - key = getFirstOption(); - if (OptionHelper.isEmpty(key)) { - addWarn("Missing key for the requested header"); - } else { - super.start(); - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_COOKIE_CONVERTER"; - } - - return accessEvent.getCookie(key); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestHeaderConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestHeaderConverter.java deleted file mode 100644 index c3e22f1890..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestHeaderConverter.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.util.OptionHelper; - -public class RequestHeaderConverter extends AccessConverter { - - String key; - - @Override - public void start() { - key = getFirstOption(); - if (OptionHelper.isEmpty(key)) { - addWarn("Missing key for the requested header. Defaulting to all keys."); - key = null; - } - super.start(); - } - - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_HEADER_CONV"; - } - - if (key != null) { - return accessEvent.getRequestHeader(key); - } else { - return accessEvent.getRequestHeaderMap().toString(); - } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestMethodConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestMethodConverter.java deleted file mode 100644 index 51b9a82b03..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestMethodConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class RequestMethodConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getMethod(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestParameterConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestParameterConverter.java deleted file mode 100644 index 0ae0e430eb..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestParameterConverter.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import java.util.Arrays; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.util.OptionHelper; - -public class RequestParameterConverter extends AccessConverter { - - String key; - - @Override - public void start() { - key = getFirstOption(); - if (OptionHelper.isEmpty(key)) { - addWarn("Missing key for the request parameter"); - } else { - super.start(); - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_REQUEST_PARAM_CONV"; - } - - String[] paramArray = accessEvent.getRequestParameter(key); - if (paramArray.length == 1) { - return paramArray[0]; - } else { - // for an array string {"a", "b"} named 'sa', Array.toString(sa) returns the string - // "[a, b]". - return Arrays.toString(paramArray); - } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestProtocolConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestProtocolConverter.java deleted file mode 100644 index a20f7878b3..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestProtocolConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class RequestProtocolConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getProtocol(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURIConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURIConverter.java deleted file mode 100644 index 7f8cebce05..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURIConverter.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -/** - * The request URI. - * - * @author Ceki Gülcü - */ -public class RequestURIConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getRequestURI(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURLConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURLConverter.java deleted file mode 100644 index fd50d7d4a9..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/RequestURLConverter.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -/** - * The first line of the request. - * - * @author Ceki Gülcü - */ -public class RequestURLConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getRequestURL(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java deleted file mode 100644 index ac5201b860..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseContentConverter.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -/** - * This class is tied to the requestContent conversion word. - *

- * It has been removed from the {@link ch.qos.logback.access.PatternLayout} since - * it needs further testing before wide use. - *

- * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class ResponseContentConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getResponseContent(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseHeaderConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseHeaderConverter.java deleted file mode 100644 index 3838db30bb..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ResponseHeaderConverter.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.util.OptionHelper; - -public class ResponseHeaderConverter extends AccessConverter { - - String key; - - @Override - public void start() { - key = getFirstOption(); - if (OptionHelper.isEmpty(key)) { - addWarn("Missing key for the response header"); - } else { - super.start(); - } - } - - @Override - public String convert(IAccessEvent accessEvent) { - if (!isStarted()) { - return "INACTIVE_REPONSE_HEADER_CONV"; - } - - return accessEvent.getResponseHeader(key); - // return null; - - // HttpServletResponse response = accessEvent.getHttpResponse(); - // - // Object value = null; // = response.getHeader(key); - // if (value == null) { - // return AccessConverter.NA; - // } else { - // return value.toString(); - // } - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ServerNameConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ServerNameConverter.java deleted file mode 100644 index 086e8179a6..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ServerNameConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class ServerNameConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return accessEvent.getServerName(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/SessionIDConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/SessionIDConverter.java deleted file mode 100644 index c403e2f28b..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/SessionIDConverter.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2013, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class SessionIDConverter extends AccessConverter { - - public String convert(IAccessEvent accessEvent) { - return accessEvent.getSessionID(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/StatusCodeConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/StatusCodeConverter.java deleted file mode 100644 index d19807415d..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/StatusCodeConverter.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class StatusCodeConverter extends AccessConverter { - - @Override - public String convert(IAccessEvent accessEvent) { - return Integer.toString(accessEvent.getStatusCode()); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/ThreadNameConverter.java b/logback-access/src/main/java/ch/qos/logback/access/pattern/ThreadNameConverter.java deleted file mode 100644 index cdc26ed5c0..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/ThreadNameConverter.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2013, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import ch.qos.logback.access.spi.IAccessEvent; - -public class ThreadNameConverter extends AccessConverter { - - public String convert(IAccessEvent accessEvent) { - return accessEvent.getThreadName(); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/pattern/package.html b/logback-access/src/main/java/ch/qos/logback/access/pattern/package.html deleted file mode 100644 index 5e4898f941..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/pattern/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

Provides classes implementing format specifiers in conversion patterns.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java deleted file mode 100644 index eed0968579..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeFilter.java +++ /dev/null @@ -1,137 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import static ch.qos.logback.access.AccessConstants.LB_OUTPUT_BUFFER; -import static ch.qos.logback.access.AccessConstants.TEE_FILTER_INCLUDES_PARAM; -import static ch.qos.logback.access.AccessConstants.TEE_FILTER_EXCLUDES_PARAM; - -public class TeeFilter implements Filter { - - boolean active; - - @Override - public void destroy() { - // NOP - } - - @Override - public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { - - if (active && request instanceof HttpServletRequest) { - try { - TeeHttpServletRequest teeRequest = new TeeHttpServletRequest((HttpServletRequest) request); - TeeHttpServletResponse teeResponse = new TeeHttpServletResponse((HttpServletResponse) response); - - // System.out.println("BEFORE TeeFilter. filterChain.doFilter()"); - filterChain.doFilter(teeRequest, teeResponse); - // System.out.println("AFTER TeeFilter. filterChain.doFilter()"); - - teeResponse.finish(); - // let the output contents be available for later use by - // logback-access-logging - teeRequest.setAttribute(LB_OUTPUT_BUFFER, teeResponse.getOutputBuffer()); - } catch (IOException e) { - e.printStackTrace(); - throw e; - } catch (ServletException e) { - e.printStackTrace(); - throw e; - } - } else { - filterChain.doFilter(request, response); - } - - } - - @Override - public void init(FilterConfig filterConfig) throws ServletException { - String includeListAsStr = filterConfig.getInitParameter(TEE_FILTER_INCLUDES_PARAM); - String excludeListAsStr = filterConfig.getInitParameter(TEE_FILTER_EXCLUDES_PARAM); - String localhostName = getLocalhostName(); - - active = computeActivation(localhostName, includeListAsStr, excludeListAsStr); - if (active) - System.out.println("TeeFilter will be ACTIVE on this host [" + localhostName + "]"); - else - System.out.println("TeeFilter will be DISABLED on this host [" + localhostName + "]"); - - } - - static List extractNameList(String nameListAsStr) { - List nameList = new ArrayList(); - if (nameListAsStr == null) { - return nameList; - } - - nameListAsStr = nameListAsStr.trim(); - if (nameListAsStr.length() == 0) { - return nameList; - } - - String[] nameArray = nameListAsStr.split("[,;]"); - for (String n : nameArray) { - n = n.trim(); - nameList.add(n); - } - return nameList; - } - - static String getLocalhostName() { - String hostname = "127.0.0.1"; - - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (UnknownHostException uhe) { - uhe.printStackTrace(); - } - return hostname; - } - - static boolean computeActivation(String hostname, String includeListAsStr, String excludeListAsStr) { - List includeList = extractNameList(includeListAsStr); - List excludeList = extractNameList(excludeListAsStr); - boolean inIncludesList = mathesIncludesList(hostname, includeList); - boolean inExcludesList = mathesExcludesList(hostname, excludeList); - return inIncludesList && (!inExcludesList); - } - - static boolean mathesIncludesList(String hostname, List includeList) { - if (includeList.isEmpty()) - return true; - return includeList.contains(hostname); - } - - static boolean mathesExcludesList(String hostname, List excludesList) { - if (excludesList.isEmpty()) - return false; - return excludesList.contains(hostname); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletRequest.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletRequest.java deleted file mode 100644 index 8ab9c6dad1..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletRequest.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; - -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletRequestWrapper; - -import static ch.qos.logback.access.AccessConstants.LB_INPUT_BUFFER; - -/** - * As the "tee" program on Unix, duplicate the request's input stream. - * - * @author Ceki Gülcü - */ -class TeeHttpServletRequest extends HttpServletRequestWrapper { - - private TeeServletInputStream inStream; - private BufferedReader reader; - boolean postedParametersMode = false; - - TeeHttpServletRequest(HttpServletRequest request) { - super(request); - // we can't access the input stream and access the request parameters - // at the same time - if (Util.isFormUrlEncoded(request)) { - postedParametersMode = true; - } else { - inStream = new TeeServletInputStream(request); - // add the contents of the input buffer as an attribute of the request - request.setAttribute(LB_INPUT_BUFFER, inStream.getInputBuffer()); - reader = new BufferedReader(new InputStreamReader(inStream)); - } - - } - - byte[] getInputBuffer() { - if (postedParametersMode) { - throw new IllegalStateException("Call disallowed in postedParametersMode"); - } - return inStream.getInputBuffer(); - } - - @Override - public ServletInputStream getInputStream() throws IOException { - if (!postedParametersMode) { - return inStream; - } else { - return super.getInputStream(); - } - } - - // - - @Override - public BufferedReader getReader() throws IOException { - if (!postedParametersMode) { - return reader; - } else { - return super.getReader(); - } - } - - public boolean isPostedParametersMode() { - return postedParametersMode; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java deleted file mode 100644 index 6dfd6dd2dd..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeHttpServletResponse.java +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpServletResponseWrapper; - -public class TeeHttpServletResponse extends HttpServletResponseWrapper { - - TeeServletOutputStream teeServletOutputStream; - PrintWriter teeWriter; - - public TeeHttpServletResponse(HttpServletResponse httpServletResponse) { - super(httpServletResponse); - } - - @Override - public ServletOutputStream getOutputStream() throws IOException { - if (teeServletOutputStream == null) { - teeServletOutputStream = new TeeServletOutputStream(this.getResponse()); - } - return teeServletOutputStream; - } - - @Override - public PrintWriter getWriter() throws IOException { - if (this.teeWriter == null) { - this.teeWriter = new PrintWriter(new OutputStreamWriter(getOutputStream(), this.getResponse().getCharacterEncoding()), true); - } - return this.teeWriter; - } - - @Override - public void flushBuffer() { - if (this.teeWriter != null) { - this.teeWriter.flush(); - } - } - - byte[] getOutputBuffer() { - // teeServletOutputStream can be null if the getOutputStream method is never - // called. - if (teeServletOutputStream != null) { - return teeServletOutputStream.getOutputStreamAsByteArray(); - } else { - return null; - } - } - - void finish() throws IOException { - if (this.teeWriter != null) { - this.teeWriter.close(); - } - if (this.teeServletOutputStream != null) { - this.teeServletOutputStream.close(); - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletInputStream.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletInputStream.java deleted file mode 100644 index 207a84f234..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletInputStream.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -import javax.servlet.ReadListener; -import javax.servlet.ServletInputStream; -import javax.servlet.http.HttpServletRequest; - -class TeeServletInputStream extends ServletInputStream { - - InputStream in; - byte[] inputBuffer; - - TeeServletInputStream(HttpServletRequest request) { - duplicateInputStream(request); - } - - private void duplicateInputStream(HttpServletRequest request) { - ServletInputStream originalSIS = null; - try { - originalSIS = request.getInputStream(); - inputBuffer = consumeBufferAndReturnAsByteArray(originalSIS); - this.in = new ByteArrayInputStream(inputBuffer); - } catch (IOException e) { - e.printStackTrace(); - } finally { - closeStream(originalSIS); - } - } - - @Override - public int read() throws IOException { - return in.read(); - } - - byte[] consumeBufferAndReturnAsByteArray(InputStream is) throws IOException { - int len = 1024; - byte[] temp = new byte[len]; - int c = -1; - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while ((c = is.read(temp, 0, len)) != -1) { - baos.write(temp, 0, c); - } - return baos.toByteArray(); - } - - void closeStream(ServletInputStream is) { - if (is != null) { - try { - is.close(); - } catch (IOException e) { - } - } - } - - byte[] getInputBuffer() { - return inputBuffer; - } - - @Override - public boolean isFinished() { - throw new RuntimeException("Not yet implemented"); - } - - @Override - public boolean isReady() { - throw new RuntimeException("Not yet implemented"); - } - - @Override - public void setReadListener(ReadListener listener) { - throw new RuntimeException("Not yet implemented"); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java deleted file mode 100644 index 687eb62167..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/TeeServletOutputStream.java +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import javax.servlet.ServletOutputStream; -import javax.servlet.ServletResponse; -import javax.servlet.WriteListener; - -public class TeeServletOutputStream extends ServletOutputStream { - - final ServletOutputStream underlyingStream; - final ByteArrayOutputStream baosCopy; - - TeeServletOutputStream(ServletResponse httpServletResponse) throws IOException { - // System.out.println("TeeServletOutputStream.constructor() called"); - this.underlyingStream = httpServletResponse.getOutputStream(); - baosCopy = new ByteArrayOutputStream(); - } - - byte[] getOutputStreamAsByteArray() { - return baosCopy.toByteArray(); - } - - @Override - public void write(int val) throws IOException { - if (underlyingStream != null) { - underlyingStream.write(val); - baosCopy.write(val); - } - } - - @Override - public void write(byte[] byteArray) throws IOException { - if (underlyingStream == null) { - return; - } - // System.out.println("WRITE TeeServletOutputStream.write(byte[]) called"); - write(byteArray, 0, byteArray.length); - } - - @Override - public void write(byte byteArray[], int offset, int length) throws IOException { - if (underlyingStream == null) { - return; - } - // System.out.println("WRITE TeeServletOutputStream.write(byte[], int, int) - // called"); - // System.out.println(new String(byteArray, offset, length)); - underlyingStream.write(byteArray, offset, length); - baosCopy.write(byteArray, offset, length); - } - - @Override - public void close() throws IOException { - // System.out.println("CLOSE TeeServletOutputStream.close() called"); - - // If the servlet accessing the stream is using a writer instead of - // an OutputStream, it will probably call os.close() before calling - // writer.close. Thus, the underlying output stream will be called - // before the data sent to the writer could be flushed. - } - - @Override - public void flush() throws IOException { - if (underlyingStream == null) { - return; - } - // System.out.println("FLUSH TeeServletOutputStream.flush() called"); - underlyingStream.flush(); - baosCopy.flush(); - } - - @Override - public boolean isReady() { - throw new RuntimeException("Not yet implemented"); - } - - @Override - public void setWriteListener(WriteListener listener) { - throw new RuntimeException("Not yet implemented"); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/servlet/Util.java b/logback-access/src/main/java/ch/qos/logback/access/servlet/Util.java deleted file mode 100755 index 241d4398f9..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/servlet/Util.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import ch.qos.logback.access.AccessConstants; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -public class Util { - - public static boolean isFormUrlEncoded(HttpServletRequest request) { - - String contentTypeStr = request.getContentType(); - if ("POST".equalsIgnoreCase(request.getMethod()) && contentTypeStr != null && contentTypeStr.startsWith(AccessConstants.X_WWW_FORM_URLECODED)) { - return true; - } else { - return false; - } - } - - public static boolean isImageResponse(HttpServletResponse response) { - - String responseType = response.getContentType(); - - if (responseType != null && responseType.startsWith(AccessConstants.IMAGE_CONTENT_TYPE)) { - return true; - } else { - return false; - } - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java b/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java deleted file mode 100644 index c6eadf0b09..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/sift/AccessEventDiscriminator.java +++ /dev/null @@ -1,191 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpSession; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.sift.AbstractDiscriminator; - -/** - * - * AccessEventDiscriminator's job is to return the value of a designated field - * in an {@link IAccessEvent} instance. - * - *

The field is specified via the {@link FieldName} property. - * - * @author Ceki Gülcü - * - */ -public class AccessEventDiscriminator extends AbstractDiscriminator { - - /** - * At present time the followed fields can be designated: COOKIE, - * REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS, - * LOCAL_PORT,REQUEST_URI - * - *

The first three fields require an additional key. For the - * SESSION_ATTRIBUTE field, the additional key named "id" has special meaning - * as it is mapped to the session id of the current http request. - */ - public enum FieldName { - COOKIE, REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS, LOCAL_PORT, REQUEST_URI - } - - String defaultValue; - String key; - FieldName fieldName; - String additionalKey; - - @Override - public String getDiscriminatingValue(IAccessEvent acccessEvent) { - String rawValue = getRawDiscriminatingValue(acccessEvent); - if (rawValue == null || rawValue.length() == 0) { - return defaultValue; - } else { - return rawValue; - } - } - - public String getRawDiscriminatingValue(IAccessEvent acccessEvent) { - switch (fieldName) { - case COOKIE: - // tested - return acccessEvent.getCookie(additionalKey); - case LOCAL_PORT: - return String.valueOf(acccessEvent.getLocalPort()); - case REQUEST_ATTRIBUTE: - // tested - return getRequestAttribute(acccessEvent); - case SESSION_ATTRIBUTE: - return getSessionAttribute(acccessEvent); - case REMOTE_ADDRESS: - return acccessEvent.getRemoteAddr(); - case REQUEST_URI: - // tested - return getRequestURI(acccessEvent); - default: - return null; - } - } - - private String getRequestAttribute(IAccessEvent acccessEvent) { - String attr = acccessEvent.getAttribute(additionalKey); - if (IAccessEvent.NA.equals(attr)) { - return null; - } else { - return attr; - } - } - - private String getRequestURI(IAccessEvent acccessEvent) { - String uri = acccessEvent.getRequestURI(); - if (uri != null && uri.length() >= 1 && uri.charAt(0) == '/') { - return uri.substring(1); - } else { - return uri; - } - } - - private String getSessionAttribute(IAccessEvent acccessEvent) { - HttpServletRequest req = acccessEvent.getRequest(); - if (req != null) { - HttpSession session = req.getSession(false); - if (session != null) { - if ("id".equalsIgnoreCase(additionalKey)) { - return session.getId(); - } else { - Object v = session.getAttribute(additionalKey); - if (v != null) { - return v.toString(); - } - } - } - } - return null; - } - - @Override - public void start() { - - int errorCount = 0; - - if (defaultValue == null) { - addError("\"DefaultValue\" property must be set."); - } - if (fieldName == null) { - addError("\"FieldName\" property must be set."); - errorCount++; - } - - switch (fieldName) { - case SESSION_ATTRIBUTE: - case REQUEST_ATTRIBUTE: - case COOKIE: - if (additionalKey == null) { - addError("\"OptionalKey\" property is mandatory for field name " + fieldName.toString()); - errorCount++; - } - default: - } - - if (errorCount == 0) { - started = true; - } - } - - public void setFieldName(FieldName fieldName) { - this.fieldName = fieldName; - } - - public FieldName getFieldName() { - return fieldName; - } - - public String getAdditionalKey() { - return additionalKey; - } - - public void setAdditionalKey(String additionalKey) { - this.additionalKey = additionalKey; - } - - /** - * @see #setDefaultValue(String) - * @return - */ - public String getDefaultValue() { - return defaultValue; - } - - /** - * The default value returned by this discriminator in case it cannot compute - * the discriminating value from the access event. - * - * @param defaultValue - */ - public void setDefaultValue(String defaultValue) { - this.defaultValue = defaultValue; - } - - public String getKey() { - return key; - } - - public void setKey(String key) { - this.key = key; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/sift/AppenderFactoryUsingJoran.java b/logback-access/src/main/java/ch/qos/logback/access/sift/AppenderFactoryUsingJoran.java deleted file mode 100644 index 09c1635cb2..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/sift/AppenderFactoryUsingJoran.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import java.util.List; -import java.util.Map; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.sift.AbstractAppenderFactoryUsingJoran; -import ch.qos.logback.core.sift.SiftingJoranConfiguratorBase; - -public class AppenderFactoryUsingJoran extends AbstractAppenderFactoryUsingJoran { - - AppenderFactoryUsingJoran(List eventList, String key, Map parentPropertyMap) { - super(eventList, key, parentPropertyMap); - } - - @Override - public SiftingJoranConfiguratorBase getSiftingJoranConfigurator(String keyValue) { - return new SiftingJoranConfigurator(key, keyValue, parentPropertyMap); - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftAction.java b/logback-access/src/main/java/ch/qos/logback/access/sift/SiftAction.java deleted file mode 100644 index 0e985153f3..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftAction.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.InPlayListener; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -public class SiftAction extends Action implements InPlayListener { - List seList; - - @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - seList = new ArrayList(); - ic.addInPlayListener(this); - } - - @Override - public void end(InterpretationContext ic, String name) throws ActionException { - ic.removeInPlayListener(this); - Object o = ic.peekObject(); - if (o instanceof SiftingAppender) { - SiftingAppender siftingAppender = (SiftingAppender) o; - Map propertyMap = ic.getCopyOfPropertyMap(); - AppenderFactoryUsingJoran appenderFactory = new AppenderFactoryUsingJoran(seList, siftingAppender.getDiscriminatorKey(), propertyMap); - siftingAppender.setAppenderFactory(appenderFactory); - } - } - - @Override - public void inPlay(SaxEvent event) { - seList.add(event); - } - - public List getSeList() { - return seList; - } - -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingAppender.java b/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingAppender.java deleted file mode 100644 index 65eada81bd..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingAppender.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.joran.spi.DefaultClass; -import ch.qos.logback.core.sift.Discriminator; -import ch.qos.logback.core.sift.SiftingAppenderBase; - -/** - * This appender can contains other appenders which it can build dynamically - * depending on MDC values. The built appender is specified as part of a - * configuration file. - * - *

See the logback manual for further details. - * - * - * @author Ceki Gulcu - */ -public class SiftingAppender extends SiftingAppenderBase { - - @Override - public void start() { - super.start(); - } - - @Override - protected long getTimestamp(IAccessEvent event) { - return event.getTimeStamp(); - } - - @Override - protected boolean eventMarksEndOfLife(IAccessEvent event) { - return false; - } - - @Override - @DefaultClass(AccessEventDiscriminator.class) - public void setDiscriminator(Discriminator discriminator) { - super.setDiscriminator(discriminator); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingJoranConfigurator.java b/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingJoranConfigurator.java deleted file mode 100644 index 8e2206874f..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/sift/SiftingJoranConfigurator.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.action.AppenderAction; -import ch.qos.logback.core.joran.spi.ElementPath; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.RuleStore; -import ch.qos.logback.core.sift.SiftingJoranConfiguratorBase; - -public class SiftingJoranConfigurator extends SiftingJoranConfiguratorBase { - - SiftingJoranConfigurator(String key, String value, Map parentPropertyMap) { - super(key, value, parentPropertyMap); - } - - @Override - protected ElementPath initialElementPath() { - return new ElementPath("configuration"); - } - - @Override - protected void addInstanceRules(RuleStore rs) { - rs.addRule(new ElementSelector("configuration/appender"), new AppenderAction()); - } - - @Override - protected void buildInterpreter() { - super.buildInterpreter(); - Map omap = interpreter.getInterpretationContext().getObjectMap(); - omap.put(ActionConst.APPENDER_BAG, new HashMap>()); - //omap.put(ActionConst.FILTER_CHAIN_BAG, new HashMap()); - Map propertiesMap = new HashMap(); - propertiesMap.putAll(parentPropertyMap); - propertiesMap.put(key, value); - interpreter.setInterpretationContextPropertiesMap(propertiesMap); - } - - @SuppressWarnings("unchecked") - @Override - public Appender getAppender() { - Map omap = interpreter.getInterpretationContext().getObjectMap(); - HashMap> appenderMap = (HashMap>) omap.get(ActionConst.APPENDER_BAG); - oneAndOnlyOneCheck(appenderMap); - Collection> values = appenderMap.values(); - if (values.size() == 0) { - return null; - } - return (Appender) values.iterator().next(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/AccessContext.java b/logback-access/src/main/java/ch/qos/logback/access/spi/AccessContext.java deleted file mode 100644 index 569421e240..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/AccessContext.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import java.util.Iterator; -import java.util.List; - -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.spi.AppenderAttachable; -import ch.qos.logback.core.spi.AppenderAttachableImpl; -import ch.qos.logback.core.spi.FilterAttachable; -import ch.qos.logback.core.spi.FilterAttachableImpl; -import ch.qos.logback.core.spi.FilterReply; - -/** - * A minimal context implementation used by certain logback-access components, - * mainly SocketServer. - * - * @author Sébastien Pennec - */ -public class AccessContext extends ContextBase implements AppenderAttachable, FilterAttachable { - - AppenderAttachableImpl aai = new AppenderAttachableImpl(); - FilterAttachableImpl fai = new FilterAttachableImpl(); - - public void callAppenders(IAccessEvent event) { - aai.appendLoopOnAppenders(event); - } - - @Override - public void addAppender(Appender newAppender) { - aai.addAppender(newAppender); - } - - @Override - public void detachAndStopAllAppenders() { - aai.detachAndStopAllAppenders(); - } - - @Override - public boolean detachAppender(Appender appender) { - return aai.detachAppender(appender); - } - - @Override - public boolean detachAppender(String name) { - return aai.detachAppender(name); - } - - @Override - public Appender getAppender(String name) { - return aai.getAppender(name); - } - - @Override - public boolean isAttached(Appender appender) { - return aai.isAttached(appender); - } - - @Override - public Iterator> iteratorForAppenders() { - return aai.iteratorForAppenders(); - } - - @Override - public void addFilter(Filter newFilter) { - fai.addFilter(newFilter); - } - - @Override - public void clearAllFilters() { - fai.clearAllFilters(); - } - - @Override - public List> getCopyOfAttachedFiltersList() { - return fai.getCopyOfAttachedFiltersList(); - } - - @Override - public FilterReply getFilterChainDecision(IAccessEvent event) { - return fai.getFilterChainDecision(event); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java b/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java deleted file mode 100755 index 5863107d82..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/AccessEvent.java +++ /dev/null @@ -1,622 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import ch.qos.logback.access.AccessConstants; -import ch.qos.logback.access.pattern.AccessConverter; -import ch.qos.logback.access.servlet.Util; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.spi.SequenceNumberGenerator; - -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.servlet.http.HttpSession; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import java.util.Vector; - -// Contributors: Joern Huxhorn (see also bug #110) - -/** - * The Access module's internal representation of logging events. When the - * logging component instance is called in the container to log then a - * AccessEvent instance is created. This instance is passed - * around to the different logback components. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class AccessEvent implements Serializable, IAccessEvent { - - private static final String[] NA_STRING_ARRAY = new String[] { NA }; - - private static final long serialVersionUID = 866718993618836343L; - - private static final String EMPTY = ""; - - private transient final HttpServletRequest httpRequest; - private transient final HttpServletResponse httpResponse; - - String queryString; - String requestURI; - String requestURL; - String remoteHost; - String remoteUser; - String remoteAddr; - String threadName; - String protocol; - String method; - String serverName; - String requestContent; - String responseContent; - String sessionID; - long elapsedTime; - - Map requestHeaderMap; - Map requestParameterMap; - Map responseHeaderMap; - Map attributeMap; - - long contentLength = SENTINEL; - int statusCode = SENTINEL; - int localPort = SENTINEL; - - transient ServerAdapter serverAdapter; - - /** - * The number of milliseconds elapsed from 1/1/1970 until logging event was - * created. - */ - private long timeStamp = 0; - - private long sequenceNumber = 0; - - public AccessEvent(Context context, HttpServletRequest httpRequest, HttpServletResponse httpResponse, ServerAdapter adapter) { - this.httpRequest = httpRequest; - this.httpResponse = httpResponse; - this.timeStamp = System.currentTimeMillis(); - - SequenceNumberGenerator sng = context.getSequenceNumberGenerator(); - if (sng != null) { - this.sequenceNumber = sng.nextSequenceNumber(); - } - this.serverAdapter = adapter; - this.elapsedTime = calculateElapsedTime(); - } - - /** - * Returns the underlying HttpServletRequest. After serialization the returned - * value will be null. - * - * @return - */ - @Override - public HttpServletRequest getRequest() { - return httpRequest; - } - - /** - * Returns the underlying HttpServletResponse. After serialization the returned - * value will be null. - * - * @return - */ - @Override - public HttpServletResponse getResponse() { - return httpResponse; - } - - @Override - public long getTimeStamp() { - return timeStamp; - } - - public void setTimeStamp(long timeStamp) { - if (this.timeStamp != 0) { - throw new IllegalStateException("timeStamp has been already set for this event."); - } else { - this.timeStamp = timeStamp; - } - } - - public long getSequenceNumber() { - return sequenceNumber; - } - - public void setSequenceNumber(long sequenceNumber) { - this.sequenceNumber = sequenceNumber; - } - - /** - * @param threadName The threadName to set. - */ - public void setThreadName(String threadName) { - this.threadName = threadName; - } - - @Override - public String getThreadName() { - return threadName == null ? NA : threadName; - } - - @Override - public String getRequestURI() { - if (requestURI == null) { - if (httpRequest != null) { - requestURI = httpRequest.getRequestURI(); - } else { - requestURI = NA; - } - } - return requestURI; - } - - @Override - public String getQueryString() { - if (queryString == null) { - if (httpRequest != null) { - StringBuilder buf = new StringBuilder(); - final String qStr = httpRequest.getQueryString(); - if (qStr != null) { - buf.append(AccessConverter.QUESTION_CHAR); - buf.append(qStr); - } - queryString = buf.toString(); - } else { - queryString = NA; - } - } - return queryString; - } - - /** - * The first line of the request. - */ - @Override - public String getRequestURL() { - if (requestURL == null) { - if (httpRequest != null) { - StringBuilder buf = new StringBuilder(); - buf.append(httpRequest.getMethod()); - buf.append(AccessConverter.SPACE_CHAR); - buf.append(httpRequest.getRequestURI()); - buf.append(getQueryString()); - buf.append(AccessConverter.SPACE_CHAR); - buf.append(httpRequest.getProtocol()); - requestURL = buf.toString(); - } else { - requestURL = NA; - } - } - return requestURL; - } - - @Override - public String getRemoteHost() { - if (remoteHost == null) { - if (httpRequest != null) { - // the underlying implementation of HttpServletRequest will - // determine if remote lookup will be performed - remoteHost = httpRequest.getRemoteHost(); - } else { - remoteHost = NA; - } - } - return remoteHost; - } - - @Override - public String getRemoteUser() { - if (remoteUser == null) { - if (httpRequest != null) { - remoteUser = httpRequest.getRemoteUser(); - } else { - remoteUser = NA; - } - } - return remoteUser; - } - - @Override - public String getProtocol() { - if (protocol == null) { - if (httpRequest != null) { - protocol = httpRequest.getProtocol(); - } else { - protocol = NA; - } - } - return protocol; - } - - @Override - public String getMethod() { - if (method == null) { - if (httpRequest != null) { - method = httpRequest.getMethod(); - } else { - method = NA; - } - } - return method; - } - - @Override - public String getSessionID() { - if (sessionID == null) { - if (httpRequest != null) { - final HttpSession session = httpRequest.getSession(); - if (session != null) { - sessionID = session.getId(); - } - } else { - sessionID = NA; - } - } - return sessionID; - } - - @Override - public String getServerName() { - if (serverName == null) { - if (httpRequest != null) { - serverName = httpRequest.getServerName(); - } else { - serverName = NA; - } - } - return serverName; - } - - @Override - public String getRemoteAddr() { - if (remoteAddr == null) { - if (httpRequest != null) { - remoteAddr = httpRequest.getRemoteAddr(); - } else { - remoteAddr = NA; - } - } - return remoteAddr; - } - - @Override - public String getRequestHeader(String key) { - String result = null; - key = key.toLowerCase(); - if (requestHeaderMap == null) { - if (httpRequest != null) { - buildRequestHeaderMap(); - result = requestHeaderMap.get(key); - } - } else { - result = requestHeaderMap.get(key); - } - - if (result != null) { - return result; - } else { - return NA; - } - } - - @Override - public Enumeration getRequestHeaderNames() { - // post-serialization - if (httpRequest == null) { - Vector list = new Vector(getRequestHeaderMap().keySet()); - return list.elements(); - } - return httpRequest.getHeaderNames(); - } - - @Override - public Map getRequestHeaderMap() { - if (requestHeaderMap == null) { - buildRequestHeaderMap(); - } - return requestHeaderMap; - } - - public void buildRequestHeaderMap() { - // according to RFC 2616 header names are case insensitive - // latest versions of Tomcat return header names in lower-case - requestHeaderMap = new TreeMap(String.CASE_INSENSITIVE_ORDER); - Enumeration e = httpRequest.getHeaderNames(); - if (e == null) { - return; - } - while (e.hasMoreElements()) { - String key = e.nextElement(); - requestHeaderMap.put(key, httpRequest.getHeader(key)); - } - } - - public void buildRequestParameterMap() { - requestParameterMap = new HashMap(); - Enumeration e = httpRequest.getParameterNames(); - if (e == null) { - return; - } - while (e.hasMoreElements()) { - String key = e.nextElement(); - requestParameterMap.put(key, httpRequest.getParameterValues(key)); - } - } - - @Override - public Map getRequestParameterMap() { - if (requestParameterMap == null) { - buildRequestParameterMap(); - } - return requestParameterMap; - } - - @Override - public String getAttribute(String key) { - Object value = null; - if (attributeMap != null) { - // Event was prepared for deferred processing so we have a copy of attribute map and must use that copy - value = attributeMap.get(key); - } else if (httpRequest != null) { - // We have original request so take attribute from it - value = httpRequest.getAttribute(key); - } - - return value != null ? value.toString() : NA; - } - - private void copyAttributeMap() { - - if (httpRequest == null) { - return; - } - - // attributeMap has been copied already. See also LOGBACK-1189 - if (attributeMap != null) { - return; - } - - attributeMap = new HashMap(); - - Enumeration names = httpRequest.getAttributeNames(); - while (names.hasMoreElements()) { - String name = names.nextElement(); - - Object value = httpRequest.getAttribute(name); - if (shouldCopyAttribute(name, value)) { - attributeMap.put(name, value); - } - } - } - - private boolean shouldCopyAttribute(String name, Object value) { - if (AccessConstants.LB_INPUT_BUFFER.equals(name) || AccessConstants.LB_OUTPUT_BUFFER.equals(name)) { - // Do not copy attributes used by logback internally - these are available via other getters anyway - return false; - } else if (value == null) { - // No reasons to copy nulls - Map.get() will return null for missing keys and the list of attribute - // names is not available through IAccessEvent - return false; - } else { - // Only copy what is serializable - return value instanceof Serializable; - } - } - - @Override - public String[] getRequestParameter(String key) { - String[] value = null; - - if (requestParameterMap != null) { - value = requestParameterMap.get(key); - } else if (httpRequest != null) { - value = httpRequest.getParameterValues(key); - } - - return (value != null) ? value : NA_STRING_ARRAY; - } - - @Override - public String getCookie(String key) { - - if (httpRequest != null) { - Cookie[] cookieArray = httpRequest.getCookies(); - if (cookieArray == null) { - return NA; - } - - for (Cookie cookie : cookieArray) { - if (key.equals(cookie.getName())) { - return cookie.getValue(); - } - } - } - return NA; - } - - @Override - public long getContentLength() { - if (contentLength == SENTINEL) { - if (httpResponse != null) { - contentLength = serverAdapter.getContentLength(); - return contentLength; - } - } - return contentLength; - } - - public int getStatusCode() { - if (statusCode == SENTINEL) { - if (httpResponse != null) { - statusCode = serverAdapter.getStatusCode(); - } - } - return statusCode; - } - - public long getElapsedSeconds() { - return elapsedTime < 0 ? elapsedTime : elapsedTime / 1000; - } - - public long getElapsedTime() { - return elapsedTime; - } - - private long calculateElapsedTime() { - if (serverAdapter.getRequestTimestamp() < 0) { - return -1; - } - return getTimeStamp() - serverAdapter.getRequestTimestamp(); - } - - public String getRequestContent() { - if (requestContent != null) { - return requestContent; - } - - if (Util.isFormUrlEncoded(httpRequest)) { - StringBuilder buf = new StringBuilder(); - - Enumeration pramEnumeration = httpRequest.getParameterNames(); - - // example: id=1234&user=cgu - // number=1233&x=1 - int count = 0; - try { - while (pramEnumeration.hasMoreElements()) { - - String key = pramEnumeration.nextElement(); - if (count++ != 0) { - buf.append("&"); - } - buf.append(key); - buf.append("="); - String val = httpRequest.getParameter(key); - if (val != null) { - buf.append(val); - } else { - buf.append(""); - } - } - } catch (Exception e) { - // FIXME Why is try/catch required? - e.printStackTrace(); - } - requestContent = buf.toString(); - } else { - // retrieve the byte array placed by TeeFilter - byte[] inputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_INPUT_BUFFER); - - if (inputBuffer != null) { - requestContent = new String(inputBuffer); - } - - if (requestContent == null || requestContent.length() == 0) { - requestContent = EMPTY; - } - } - - return requestContent; - } - - public String getResponseContent() { - if (responseContent != null) { - return responseContent; - } - - if (Util.isImageResponse(httpResponse)) { - responseContent = "[IMAGE CONTENTS SUPPRESSED]"; - } else { - - // retreive the byte array previously placed by TeeFilter - byte[] outputBuffer = (byte[]) httpRequest.getAttribute(AccessConstants.LB_OUTPUT_BUFFER); - - if (outputBuffer != null) { - responseContent = new String(outputBuffer); - } - if (responseContent == null || responseContent.length() == 0) { - responseContent = EMPTY; - } - } - - return responseContent; - } - - public int getLocalPort() { - if (localPort == SENTINEL) { - if (httpRequest != null) { - localPort = httpRequest.getLocalPort(); - } - - } - return localPort; - } - - public ServerAdapter getServerAdapter() { - return serverAdapter; - } - - public String getResponseHeader(String key) { - buildResponseHeaderMap(); - return responseHeaderMap.get(key); - } - - void buildResponseHeaderMap() { - if (responseHeaderMap == null) { - responseHeaderMap = serverAdapter.buildResponseHeaderMap(); - } - } - - public Map getResponseHeaderMap() { - buildResponseHeaderMap(); - return responseHeaderMap; - } - - public List getResponseHeaderNameList() { - buildResponseHeaderMap(); - return new ArrayList(responseHeaderMap.keySet()); - } - - public void prepareForDeferredProcessing() { - getRequestHeaderMap(); - getRequestParameterMap(); - getResponseHeaderMap(); - getLocalPort(); - getMethod(); - getProtocol(); - getRemoteAddr(); - getRemoteHost(); - getRemoteUser(); - getRequestURI(); - getRequestURL(); - getServerName(); - getTimeStamp(); - getElapsedTime(); - - getStatusCode(); - getContentLength(); - getRequestContent(); - getResponseContent(); - - copyAttributeMap(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/IAccessEvent.java b/logback-access/src/main/java/ch/qos/logback/access/spi/IAccessEvent.java deleted file mode 100644 index 16911af26d..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/IAccessEvent.java +++ /dev/null @@ -1,140 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import ch.qos.logback.core.spi.DeferredProcessingAware; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.util.Enumeration; -import java.util.List; -import java.util.Map; - -// Contributors: Joern Huxhorn (see also bug #110) - -/** - * The Access module's internal representation of logging events. When the - * logging component instance is called in the container to log then a - * AccessEvent instance is created. This instance is passed - * around to the different logback components. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - * @author Jörn Huxhorn - */ -public interface IAccessEvent extends DeferredProcessingAware { - - String NA = "-"; - int SENTINEL = -1; - - /** - * Returns the underlying HttpServletRequest. After serialization the returned - * value will be null. - * - * @return - */ - HttpServletRequest getRequest(); - - /** - * Returns the underlying HttpServletResponse. After serialization the returned - * value will be null. - * - * @return - */ - HttpServletResponse getResponse(); - - /** - * The number of milliseconds elapsed from 1/1/1970 until logging event was - * created. - */ - long getTimeStamp(); - - /** - * The sequence number associated with this event. - * - *

Sequence numbers, if present, should be increasing monotonically. - * - * @since 1.3.0 - */ - - long getSequenceNumber(); - - /** - * The time elapsed between receiving the request and logging it in milliseconds. - */ - long getElapsedTime(); - - /** - * The number of seconds elapsed between receiving the request and logging it. - */ - long getElapsedSeconds(); - - String getRequestURI(); - - /** - * The first line of the request. - */ - String getRequestURL(); - - String getRemoteHost(); - - String getRemoteUser(); - - String getProtocol(); - - String getMethod(); - - String getServerName(); - - String getSessionID(); - - void setThreadName(String threadName); - String getThreadName(); - - String getQueryString(); - - String getRemoteAddr(); - - String getRequestHeader(String key); - - Enumeration getRequestHeaderNames(); - - Map getRequestHeaderMap(); - - Map getRequestParameterMap(); - - String getAttribute(String key); - - String[] getRequestParameter(String key); - - String getCookie(String key); - - long getContentLength(); - - int getStatusCode(); - - String getRequestContent(); - - String getResponseContent(); - - int getLocalPort(); - - ServerAdapter getServerAdapter(); - - String getResponseHeader(String key); - - Map getResponseHeaderMap(); - - List getResponseHeaderNameList(); -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java b/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java deleted file mode 100644 index 72129448ed..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/ServerAdapter.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import java.util.Map; - -/** - * An interface to access server-specific methods from - * the server-independent AccessEvent. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public interface ServerAdapter { - - long getRequestTimestamp(); - - long getContentLength(); - - int getStatusCode(); - - Map buildResponseHeaderMap(); -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/Util.java b/logback-access/src/main/java/ch/qos/logback/access/spi/Util.java deleted file mode 100644 index 815ec60514..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/Util.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; - -public class Util { - static final int BUF_SIZE = 128; - - public static String readToString(InputStream in) throws IOException { - if (in == null) { - return null; - } - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - byte[] buf = new byte[BUF_SIZE]; - int n = 0; - while ((n = in.read(buf, 0, BUF_SIZE)) != -1) { - baos.write(buf, 0, n); - } - return baos.toString(); - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/spi/package.html b/logback-access/src/main/java/ch/qos/logback/access/spi/package.html deleted file mode 100644 index 08b45592c1..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/spi/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

Contains the core classes of logback access.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/ch/qos/logback/access/tomcat/LogbackValve.java b/logback-access/src/main/java/ch/qos/logback/access/tomcat/LogbackValve.java deleted file mode 100644 index 7b0b053499..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/tomcat/LogbackValve.java +++ /dev/null @@ -1,471 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.tomcat; - -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; - -import javax.servlet.ServletContext; -import javax.servlet.ServletException; - -import org.apache.catalina.Lifecycle; -import org.apache.catalina.LifecycleException; -import org.apache.catalina.LifecycleListener; -import org.apache.catalina.LifecycleState; -import org.apache.catalina.connector.Request; -import org.apache.catalina.connector.Response; -import org.apache.catalina.valves.ValveBase; - -import ch.qos.logback.access.AccessConstants; -import ch.qos.logback.access.joran.JoranConfigurator; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.BasicStatusManager; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.LifeCycleManager; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.filter.Filter; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.spi.AppenderAttachable; -import ch.qos.logback.core.spi.AppenderAttachableImpl; -import ch.qos.logback.core.spi.FilterAttachable; -import ch.qos.logback.core.spi.FilterAttachableImpl; -import ch.qos.logback.core.spi.FilterReply; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.spi.LogbackLock; -import ch.qos.logback.core.spi.SequenceNumberGenerator; -import ch.qos.logback.core.status.ErrorStatus; -import ch.qos.logback.core.status.InfoStatus; -import ch.qos.logback.core.status.OnConsoleStatusListener; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.status.WarnStatus; -import ch.qos.logback.core.util.ExecutorServiceUtil; -import ch.qos.logback.core.util.Loader; -import ch.qos.logback.core.util.OptionHelper; -import ch.qos.logback.core.util.StatusListenerConfigHelper; - -//import org.apache.catalina.Lifecycle; - -/** - * This class is an implementation of tomcat's Valve interface, by extending - * ValveBase. - * - *

- * For more information on using LogbackValve please refer to the online - * documentation on logback-acces and tomcat. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ -public class LogbackValve extends ValveBase implements Lifecycle, Context, AppenderAttachable, FilterAttachable { - - public final static String DEFAULT_FILENAME = "logback-access.xml"; - public final static String DEFAULT_CONFIG_FILE = "conf" + File.separatorChar + DEFAULT_FILENAME; - final static String CATALINA_BASE_KEY = "catalina.base"; - final static String CATALINA_HOME_KEY = "catalina.home"; - - private final LifeCycleManager lifeCycleManager = new LifeCycleManager(); - - private long birthTime = System.currentTimeMillis(); - - - LogbackLock configurationLock = new LogbackLock(); - - // Attributes from ContextBase: - private String name; - StatusManager sm = new BasicStatusManager(); - // TODO propertyMap should be observable so that we can be notified - // when it changes so that a new instance of propertyMap can be - // serialized. For the time being, we ignore this shortcoming. - Map propertyMap = new HashMap(); - Map objectMap = new HashMap(); - private FilterAttachableImpl fai = new FilterAttachableImpl(); - - AppenderAttachableImpl aai = new AppenderAttachableImpl(); - String filenameOption; - boolean quiet; - boolean started; - boolean alreadySetLogbackStatusManager = false; - private SequenceNumberGenerator sequenceNumberGenerator; - - - private ScheduledExecutorService scheduledExecutorService; - - public LogbackValve() { - putObject(CoreConstants.EVALUATOR_MAP, new HashMap>()); - } - - public boolean isStarted() { - return started; - } - - @Override - public void startInternal() throws LifecycleException { - scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService(); - - String filename; - - if (filenameOption != null) { - filename = filenameOption; - } else { - addInfo("filename property not set. Assuming [" + DEFAULT_CONFIG_FILE + "]"); - filename = DEFAULT_CONFIG_FILE; - } - - // String catalinaBase = OptionHelper.getSystemProperty(CATALINA_BASE_KEY); - // String catalinaHome = OptionHelper.getSystemProperty(CATALINA_BASE_KEY); - - File configFile = searchForConfigFileTomcatProperty(filename, CATALINA_BASE_KEY); - if (configFile == null) { - configFile = searchForConfigFileTomcatProperty(filename, CATALINA_HOME_KEY); - } - - URL resourceURL; - if (configFile != null) - resourceURL = fileToUrl(configFile); - else - resourceURL = searchAsResource(filename); - - if (resourceURL != null) { - configureAsResource(resourceURL); - } else { - addWarn("Failed to find valid logback-access configuration file."); - } - - if (!quiet) { - StatusListenerConfigHelper.addOnConsoleListenerInstance(this, new OnConsoleStatusListener()); - } - - started = true; - setState(LifecycleState.STARTING); - } - - private URL fileToUrl(File configFile) { - try { - return configFile.toURI().toURL(); - } catch (MalformedURLException e) { - throw new IllegalStateException("File to URL conversion failed", e); - } - } - - private URL searchAsResource(String filename) { - URL result = Loader.getResource(filename, getClass().getClassLoader()); - if (result != null) - addInfo("Found [" + filename + "] as a resource."); - else - addInfo("Could NOT find [" + filename + "] as a resource."); - return result; - } - - private File searchForConfigFileTomcatProperty(String filename, String propertyKey) { - String propertyValue = OptionHelper.getSystemProperty(propertyKey); - String candidatePath = propertyValue + File.separatorChar + filename; - if (propertyValue == null) { - addInfo("System property \"" + propertyKey + "\" is not set. Skipping configuration file search with ${" + propertyKey + "} path prefix."); - return null; - } - File candidateFile = new File(candidatePath); - if (candidateFile.exists()) { - addInfo("Found configuration file [" + candidatePath + "] using property \"" + propertyKey + "\""); - return candidateFile; - } else { - addInfo("Could NOT configuration file [" + candidatePath + "] using property \"" + propertyKey + "\""); - return null; - } - } - - public void addStatus(Status status) { - StatusManager sm = getStatusManager(); - if (sm != null) { - sm.add(status); - } - } - - public void addInfo(String msg) { - addStatus(new InfoStatus(msg, this)); - } - - public void addWarn(String msg) { - addStatus(new WarnStatus(msg, this)); - } - - public void addError(String msg, Throwable t) { - addStatus(new ErrorStatus(msg, this, t)); - } - - private void configureAsResource(URL resourceURL) { - try { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(this); - jc.doConfigure(resourceURL); - addInfo("Done configuring"); - } catch (JoranException e) { - addError("Failed to configure LogbackValve", e); - } - } - - public String getFilename() { - return filenameOption; - } - - public void setFilename(String filename) { - this.filenameOption = filename; - } - - public boolean isQuiet() { - return quiet; - } - - public void setQuiet(boolean quiet) { - this.quiet = quiet; - } - - @Override - public void invoke(Request request, Response response) throws IOException, ServletException { - try { - if (!alreadySetLogbackStatusManager) { - alreadySetLogbackStatusManager = true; - org.apache.catalina.Context tomcatContext = request.getContext(); - if (tomcatContext != null) { - ServletContext sc = tomcatContext.getServletContext(); - if (sc != null) { - sc.setAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY, getStatusManager()); - } - } - } - - getNext().invoke(request, response); - - TomcatServerAdapter adapter = new TomcatServerAdapter(request, response); - IAccessEvent accessEvent = new AccessEvent(this, request, response, adapter); - - addThreadName(accessEvent); - - if (getFilterChainDecision(accessEvent) == FilterReply.DENY) { - return; - } - - // TODO better exception handling - aai.appendLoopOnAppenders(accessEvent); - } finally { - request.removeAttribute(AccessConstants.LOGBACK_STATUS_MANAGER_KEY); - } - } - - private void addThreadName(IAccessEvent accessEvent) { - try { - final String threadName = Thread.currentThread().getName(); - if (threadName != null) { - accessEvent.setThreadName(threadName); - } - } catch (Exception ignored) { - } - } - - @Override - protected void stopInternal() throws LifecycleException { - started = false; - setState(LifecycleState.STOPPING); - lifeCycleManager.reset(); - if (scheduledExecutorService != null) { - ExecutorServiceUtil.shutdown(scheduledExecutorService); - scheduledExecutorService = null; - } - } - - @Override - public void addAppender(Appender newAppender) { - aai.addAppender(newAppender); - } - - @Override - public Iterator> iteratorForAppenders() { - return aai.iteratorForAppenders(); - } - - @Override - public Appender getAppender(String name) { - return aai.getAppender(name); - } - - @Override - public boolean isAttached(Appender appender) { - return aai.isAttached(appender); - } - - @Override - public void detachAndStopAllAppenders() { - aai.detachAndStopAllAppenders(); - - } - - @Override - public boolean detachAppender(Appender appender) { - return aai.detachAppender(appender); - } - - @Override - public boolean detachAppender(String name) { - return aai.detachAppender(name); - } - - public String getInfo() { - return "Logback's implementation of ValveBase"; - } - - // Methods from ContextBase: - @Override - public StatusManager getStatusManager() { - return sm; - } - - public Map getPropertyMap() { - return propertyMap; - } - - @Override - public void putProperty(String key, String val) { - this.propertyMap.put(key, val); - } - - @Override - public String getProperty(String key) { - return (String) this.propertyMap.get(key); - } - - @Override - public Map getCopyOfPropertyMap() { - return new HashMap(this.propertyMap); - } - - @Override - public Object getObject(String key) { - return objectMap.get(key); - } - - @Override - public void putObject(String key, Object value) { - objectMap.put(key, value); - } - - @Override - public void addFilter(Filter newFilter) { - fai.addFilter(newFilter); - } - - @Override - public void clearAllFilters() { - fai.clearAllFilters(); - } - - @Override - public List> getCopyOfAttachedFiltersList() { - return fai.getCopyOfAttachedFiltersList(); - } - - @Override - public FilterReply getFilterChainDecision(IAccessEvent event) { - return fai.getFilterChainDecision(event); - } - - @Override - public ExecutorService getExecutorService() { - return getScheduledExecutorService(); - } - - @Override - public String getName() { - return name; - } - - @Override - public void setName(String name) { - if (this.name != null) { - throw new IllegalStateException("LogbackValve has been already given a name"); - } - this.name = name; - } - - @Override - public long getBirthTime() { - return birthTime; - } - - @Override - public Object getConfigurationLock() { - return configurationLock; - } - - @Override - public void register(LifeCycle component) { - lifeCycleManager.register(component); - } - - // ====== Methods from catalina Lifecycle ===== - - @Override - public void addLifecycleListener(LifecycleListener arg0) { - // dummy NOP implementation - } - - @Override - public LifecycleListener[] findLifecycleListeners() { - return new LifecycleListener[0]; - } - - @Override - public void removeLifecycleListener(LifecycleListener arg0) { - // dummy NOP implementation - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(this.getClass().getName()); - sb.append('['); - sb.append(getName()); - sb.append(']'); - return sb.toString(); - } - - @Override - public ScheduledExecutorService getScheduledExecutorService() { - return scheduledExecutorService; - } - - @Override - public void addScheduledFuture(ScheduledFuture scheduledFuture) { - throw new UnsupportedOperationException(); - } - - public SequenceNumberGenerator getSequenceNumberGenerator() { - return sequenceNumberGenerator; - } - - public void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGenerator) { - this.sequenceNumberGenerator = sequenceNumberGenerator; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java b/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java deleted file mode 100644 index a08f572c94..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/tomcat/TomcatServerAdapter.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.tomcat; - -import ch.qos.logback.access.spi.ServerAdapter; - -import org.apache.catalina.connector.Request; -import org.apache.catalina.connector.Response; - -import java.util.HashMap; -import java.util.Map; - -/** - * A tomcat specific implementation of the {@link ServerAdapter} interface. - * - * @author Sébastien Pennec - */ -public class TomcatServerAdapter implements ServerAdapter { - - Request request; - Response response; - - public TomcatServerAdapter(Request tomcatRequest, Response tomcatResponse) { - this.request = tomcatRequest; - this.response = tomcatResponse; - } - - @Override - public long getContentLength() { - return response.getContentLength(); - } - - @Override - public int getStatusCode() { - return response.getStatus(); - } - - @Override - public long getRequestTimestamp() { - return request.getCoyoteRequest().getStartTime(); - } - - @Override - public Map buildResponseHeaderMap() { - Map responseHeaderMap = new HashMap(); - for (String key : response.getHeaderNames()) { - String value = response.getHeader(key); - responseHeaderMap.put(key, value); - } - return responseHeaderMap; - } -} diff --git a/logback-access/src/main/java/ch/qos/logback/access/tomcat/package.html b/logback-access/src/main/java/ch/qos/logback/access/tomcat/package.html deleted file mode 100644 index c96b85259f..0000000000 --- a/logback-access/src/main/java/ch/qos/logback/access/tomcat/package.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - -

This is logback access' implementation for Tomcat.

- - - \ No newline at end of file diff --git a/logback-access/src/main/java/module-info.DISABLED b/logback-access/src/main/java/module-info.DISABLED deleted file mode 100755 index 644cef17dc..0000000000 --- a/logback-access/src/main/java/module-info.DISABLED +++ /dev/null @@ -1,14 +0,0 @@ -module ch.qos.logback.access { - requires ch.qos.logback.core; - - requires javax.servlet.api; - requires static java.management; - - requires static jetty.server; - - requires static tomcat.coyote; - requires static tomcat.catalina; - - -} - diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2.sql deleted file mode 100644 index 4fedc9dd70..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2.sql +++ /dev/null @@ -1,42 +0,0 @@ -# Logback: the reliable, generic, fast and flexible logging framework. -# Copyright (C) 1999-2010, QOS.ch. All rights reserved. -# -# See http://logback.qos.ch/license.html for the applicable licensing -# conditions. - -# This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender -# -# It is intended for IBM DB2 databases. -# -# WARNING WARNING WARNING WARNING -# ================================= -# This SQL script has not been tested on an actual DB2 -# instance. It may contain errors or even invalid SQL -# statements. - -DROP TABLE access_event_header; -DROP TABLE access_event; - -CREATE TABLE access_event - ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id INTEGER GENERATED ALWAYS AS IDENTITY (START WITH 1) - ); - -CREATE TABLE access_event_header - ( - event_id INTEGER NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value VARCHAR(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ); diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2l.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2l.sql deleted file mode 100644 index dbf008c288..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/db2l.sql +++ /dev/null @@ -1,32 +0,0 @@ -# This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender -# -# It is intended for PostgreSQL databases. - -DROP TABLE access_event_header; -DROP TABLE access_event; - -CREATE SEQUENCE access_event_id_seq MINVALUE 1 START 1; - -CREATE TABLE access_event - ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id INT NOT NULL AUTO_INCREMENT PRIMARY KEY - ); - -CREATE TABLE access_event_header - ( - event_id INT NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value VARCHAR(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ); \ No newline at end of file diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/hsqldb.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/hsqldb.sql deleted file mode 100644 index 459a16b6fb..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/hsqldb.sql +++ /dev/null @@ -1,34 +0,0 @@ -# Logback: the reliable, generic, fast and flexible logging framework. -# Copyright (C) 1999-2010, QOS.ch. All rights reserved. -# -# See http://logback.qos.ch/license.html for the applicable licensing -# conditions. - -# This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender -# -# It is intended for HSQL databases. - - -DROP TABLE access_event_header IF EXISTS; -DROP TABLE access_event IF EXISTS; - -CREATE TABLE access_event ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id BIGINT NOT NULL IDENTITY); - - -CREATE TABLE access_event_header ( - event_id BIGINT NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value LONGVARCHAR, - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id)); \ No newline at end of file diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/msSQLServer.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/msSQLServer.sql deleted file mode 100644 index 7524ad3193..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/msSQLServer.sql +++ /dev/null @@ -1,37 +0,0 @@ --- Logback: the reliable, generic, fast and flexible logging framework. --- Copyright (C) 1999-2010, QOS.ch. All rights reserved. --- --- See http://logback.qos.ch/license.html for the applicable licensing --- conditions. - --- This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender --- - -DROP TABLE access_event_header -DROP TABLE access_event - -CREATE TABLE access_event - ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id INT NOT NULL identity, - PRIMARY KEY(event_id) - ) - -CREATE TABLE access_event_header - ( - event_id INT NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value VARCHAR(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ) - diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/mysql.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/mysql.sql deleted file mode 100644 index 16e0e458e0..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/mysql.sql +++ /dev/null @@ -1,44 +0,0 @@ -# Logback: the reliable, generic, fast and flexible logging framework. -# Copyright (C) 1999-2010, QOS.ch. All rights reserved. -# -# See http://logback.qos.ch/license.html for the applicable licensing -# conditions. - -# This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender. -# -# It is intended for MySQL databases. It has been tested on MySQL 5.5.31 with -# INNODB tables. - - -BEGIN; -DROP TABLE IF EXISTS access_event_header; -DROP TABLE IF EXISTS access_event; -COMMIT; - -BEGIN; -CREATE TABLE access_event - ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY - ); -COMMIT; - -BEGIN; -CREATE TABLE access_event_header - ( - event_id BIGINT NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value VARCHAR(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ); -COMMIT; \ No newline at end of file diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/oracle.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/oracle.sql deleted file mode 100644 index a47dc22fb5..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/oracle.sql +++ /dev/null @@ -1,56 +0,0 @@ --- Logback: the reliable, generic, fast and flexible logging framework. --- Copyright (C) 1999-2010, QOS.ch. All rights reserved. --- --- See http://logback.qos.ch/license.html for the applicable licensing --- conditions. - --- This SQL script creates the required tables by ch.qos.logback.access.db.DBAppender --- --- It is intended for Oracle databases. - - -CREATE SEQUENCE access_event_id_seq MINVALUE 1 START WITH 1; - - -CREATE TABLE access_event - ( - timestmp NUMBER(20) NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id NUMBER(20) PRIMARY KEY - ); - --- the / suffix may or may not be needed depending on your SQL Client --- Some SQL Clients, e.g. SQuirrel SQL has trouble with the following --- trigger creation command, while SQLPlus (the basic SQL Client which --- ships with Oracle) has no trouble at all. - -CREATE TRIGGER access_event_id_seq_trig - BEFORE INSERT ON access_event - FOR EACH ROW - BEGIN - SELECT access_event_id_seq.NEXTVAL - INTO :NEW.event_id - FROM DUAL; - END access_event_id_seq_trig; -/ - -CREATE TABLE access_event_header - ( - event_id NUMBER(20) NOT NULL, - header_key VARCHAR2(254) NOT NULL, - header_value VARCHAR2(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ); - - - - diff --git a/logback-access/src/main/resources/ch/qos/logback/access/db/script/postgresql.sql b/logback-access/src/main/resources/ch/qos/logback/access/db/script/postgresql.sql deleted file mode 100644 index f0e3430915..0000000000 --- a/logback-access/src/main/resources/ch/qos/logback/access/db/script/postgresql.sql +++ /dev/null @@ -1,39 +0,0 @@ -# Logback: the reliable, generic, fast and flexible logging framework. -# Copyright (C) 1999-2010, QOS.ch. All rights reserved. -# -# See http://logback.qos.ch/license.html for the applicable licensing -# conditions. - -# This SQL script creates the required tables by ch.qos.logback.classic.db.DBAppender -# -# It is intended for PostgreSQL databases. - -DROP TABLE access_event_exception; -DROP SEQUENCE access_event_id_seq; -DROP TABLE access_event; - -CREATE SEQUENCE access_event_id_seq MINVALUE 1 START 1; - -CREATE TABLE access_event - ( - timestmp BIGINT NOT NULL, - requestURI VARCHAR(254), - requestURL VARCHAR(254), - remoteHost VARCHAR(254), - remoteUser VARCHAR(254), - remoteAddr VARCHAR(254), - protocol VARCHAR(254), - method VARCHAR(254), - serverName VARCHAR(254), - postContent VARCHAR(254), - event_id BIGINT DEFAULT nextval('access_event_id_seq') PRIMARY KEY - ); - -CREATE TABLE access_event_header -( - event_id BIGINT NOT NULL, - header_key VARCHAR(254) NOT NULL, - header_value VARCHAR(1024), - PRIMARY KEY(event_id, header_key), - FOREIGN KEY (event_id) REFERENCES access_event(event_id) - ); \ No newline at end of file diff --git a/logback-access/src/test/input/integration/db/mysql-with-driver.xml b/logback-access/src/test/input/integration/db/mysql-with-driver.xml deleted file mode 100644 index 81bfdd89b1..0000000000 --- a/logback-access/src/test/input/integration/db/mysql-with-driver.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - com.mysql.jdbc.Driver - jdbc:mysql://localhost:3306/logback - root - - - - - - - diff --git a/logback-access/src/test/input/integration/db/oracle10g-with-driver.xml b/logback-access/src/test/input/integration/db/oracle10g-with-driver.xml deleted file mode 100644 index bf7dc67b4d..0000000000 --- a/logback-access/src/test/input/integration/db/oracle10g-with-driver.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - oracle.jdbc.OracleDriver - jdbc:oracle:thin:@localhost:1522:xe - hr - hr - - - - - - diff --git a/logback-access/src/test/input/integration/db/oracle11g-with-driver.xml b/logback-access/src/test/input/integration/db/oracle11g-with-driver.xml deleted file mode 100644 index 8ed022d986..0000000000 --- a/logback-access/src/test/input/integration/db/oracle11g-with-driver.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - oracle.jdbc.OracleDriver - jdbc:oracle:thin:@localhost:1521:orcl - SCOTT - SCOTT - - - - - - - diff --git a/logback-access/src/test/input/integration/db/postgresql-with-driver.xml b/logback-access/src/test/input/integration/db/postgresql-with-driver.xml deleted file mode 100644 index 34fc95cb75..0000000000 --- a/logback-access/src/test/input/integration/db/postgresql-with-driver.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - org.postgresql.Driver - jdbc:postgresql://192.168.1.5:5432/test - logback - logback - - - - - - diff --git a/logback-access/src/test/input/integration/db/sqlserver-with-driver.xml b/logback-access/src/test/input/integration/db/sqlserver-with-driver.xml deleted file mode 100644 index 85c0ee20b5..0000000000 --- a/logback-access/src/test/input/integration/db/sqlserver-with-driver.xml +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - com.microsoft.sqlserver.jdbc.SQLServerDriver - jdbc:sqlserver://localhost:1987;databaseName=logback; - logback - logback - - - - - - diff --git a/logback-access/src/test/input/jetty/sifting.xml b/logback-access/src/test/input/jetty/sifting.xml deleted file mode 100644 index b4b89b63dd..0000000000 --- a/logback-access/src/test/input/jetty/sifting.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - uri - REQUEST_URI - NA - - - - - - - - \ No newline at end of file diff --git a/logback-access/src/test/input/joran/conditional/conditionalConsole.xml b/logback-access/src/test/input/joran/conditional/conditionalConsole.xml deleted file mode 100644 index 1ce7b68019..0000000000 --- a/logback-access/src/test/input/joran/conditional/conditionalConsole.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - %h %l %u %user %date "%r" %s %b - - - - - - - - - \ No newline at end of file diff --git a/logback-access/src/test/input/joran/conditional/conditionalConsole_ELSE.xml b/logback-access/src/test/input/joran/conditional/conditionalConsole_ELSE.xml deleted file mode 100644 index 4d3bd85f96..0000000000 --- a/logback-access/src/test/input/joran/conditional/conditionalConsole_ELSE.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - %h %l %u %user %date "%r" %s %b - - - - - - - - - - - - \ No newline at end of file diff --git a/logback-access/src/test/input/joran/defaultLayout.xml b/logback-access/src/test/input/joran/defaultLayout.xml deleted file mode 100644 index d486bf7856..0000000000 --- a/logback-access/src/test/input/joran/defaultLayout.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - %requestMethod - - - - - \ No newline at end of file diff --git a/logback-access/src/test/input/joran/smoke.xml b/logback-access/src/test/input/joran/smoke.xml deleted file mode 100644 index a62cdcedbf..0000000000 --- a/logback-access/src/test/input/joran/smoke.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/logback-access/src/test/input/joran/tomcat/logback-access.xml b/logback-access/src/test/input/joran/tomcat/logback-access.xml deleted file mode 100755 index b4d2a9f8ee..0000000000 --- a/logback-access/src/test/input/joran/tomcat/logback-access.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - %h %l %u %user %date "%r" %s %b - - - - - \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/AccessTestConstants.java b/logback-access/src/test/java/ch/qos/logback/access/AccessTestConstants.java deleted file mode 100644 index ea5b625b5d..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/AccessTestConstants.java +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -public class AccessTestConstants { - - public static final String TEST_DIR_PREFIX = "src/test/"; - final static public String INPUT_PREFIX = "src/test/input/"; - final static public String JORAN_INPUT_PREFIX = INPUT_PREFIX + "joran/"; -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/AllAccessTest.java b/logback-access/src/test/java/ch/qos/logback/access/AllAccessTest.java deleted file mode 100755 index 32ca8fe4d2..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/AllAccessTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ch.qos.logback.access.spi.PackageTest.class, ch.qos.logback.access.boolex.PackageTest.class, ch.qos.logback.access.net.PackageTest.class, - ch.qos.logback.access.db.PackageTest.class, ch.qos.logback.access.pattern.PackageTest.class, ch.qos.logback.access.joran.PackageTest.class, - ch.qos.logback.access.jetty.PackageTest.class, ch.qos.logback.access.filter.PackageTest.class, ch.qos.logback.access.servlet.PackageTest.class, - ch.qos.logback.access.sift.PackageTest.class }) -public class AllAccessTest { - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/boolex/JaninoEventEvaluatorTest.java b/logback-access/src/test/java/ch/qos/logback/access/boolex/JaninoEventEvaluatorTest.java deleted file mode 100644 index 449ce04bb1..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/boolex/JaninoEventEvaluatorTest.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.boolex; - -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServerAdapter; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.boolex.EvaluationException; - -public class JaninoEventEvaluatorTest { - - final String expectedURL1 = "testUrl1"; - final String expectedURL2 = "testUrl2"; - //Context context = new ContextBase(); - JaninoEventEvaluator evaluator; - DummyRequest request; - DummyResponse response; - DummyServerAdapter serverAdapter; - AccessContext accessContext = new AccessContext(); - - @Before - public void setUp() throws Exception { - evaluator = new JaninoEventEvaluator(); - evaluator.setContext(accessContext); - request = new DummyRequest(); - response = new DummyResponse(); - serverAdapter = new DummyServerAdapter(request, response); - } - - @Test - public void smoke() throws EvaluationException { - evaluator.setExpression("event.getProtocol().equals(\"testProtocol\")"); - evaluator.start(); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - assertTrue(evaluator.evaluate(ae)); - } - - @Test - public void block() throws EvaluationException { - evaluator.setExpression("String protocol = event.getProtocol();" + "return protocol.equals(\"testProtocol\");"); - evaluator.start(); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - assertTrue(evaluator.evaluate(ae)); - } - - @Test - public void invalidExpression() throws EvaluationException { - evaluator.setExpression("return true"); - evaluator.start(); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - try { - evaluator.evaluate(ae); - fail("Was expecting an exception"); - } catch (IllegalStateException e) { - } - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/boolex/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/boolex/PackageTest.java deleted file mode 100644 index 781c311eef..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/boolex/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.boolex; - -import junit.framework.TestCase; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ JaninoEventEvaluatorTest.class }) -public class PackageTest extends TestCase { -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTest.java b/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTest.java deleted file mode 100755 index 4061a5f276..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTest.java +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import ch.qos.logback.access.spi.IAccessEvent; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServerAdapter; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.core.db.DriverManagerConnectionSource; -import ch.qos.logback.core.util.StatusPrinter; - -public class DBAppenderHSQLTest { - static DBAppenderHSQLTestFixture DB_APPENDER_HSQL_TEST_FIXTURE; - - AccessContext context; - DBAppender appender; - DriverManagerConnectionSource connectionSource; - - int existingEventTableRowCount; - Statement stmt; - - @BeforeClass - static public void fixtureSetUp() throws SQLException { - DB_APPENDER_HSQL_TEST_FIXTURE = new DBAppenderHSQLTestFixture(); - DB_APPENDER_HSQL_TEST_FIXTURE.setUp(); - } - - @AfterClass - static public void fixtureTearDown() throws SQLException { - DB_APPENDER_HSQL_TEST_FIXTURE.tearDown(); - } - - @Before - public void setUp() throws SQLException { - context = new AccessContext(); - context.setName("default"); - appender = new DBAppender(); - appender.setName("DB"); - appender.setContext(context); - connectionSource = new DriverManagerConnectionSource(); - connectionSource.setContext(context); - connectionSource.setDriverClass(DBAppenderHSQLTestFixture.DRIVER_CLASS); - connectionSource.setUrl(DB_APPENDER_HSQL_TEST_FIXTURE.url); - connectionSource.setUser(DB_APPENDER_HSQL_TEST_FIXTURE.user); - connectionSource.setPassword(DB_APPENDER_HSQL_TEST_FIXTURE.password); - connectionSource.start(); - appender.setConnectionSource(connectionSource); - - stmt = connectionSource.getConnection().createStatement(); - existingEventTableRowCount = existingEventTableRowCount(stmt); - } - - @After - public void tearDown() throws SQLException { - context = null; - appender = null; - connectionSource = null; - stmt.close(); - } - - int existingEventTableRowCount(Statement stmt) throws SQLException { - ResultSet rs = stmt.executeQuery("SELECT count(*) FROM access_event"); - int result = -1; - if (rs.next()) { - result = rs.getInt(1); - } - rs.close(); - return result; - } - - private void setInsertHeadersAndStart(boolean insert) { - appender.setInsertHeaders(insert); - appender.start(); - } - - @Test - public void testAppendAccessEvent() throws SQLException { - setInsertHeadersAndStart(false); - - IAccessEvent event = createAccessEvent(); - appender.append(event); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM access_event where EVENT_ID = " + existingEventTableRowCount); - if (rs.next()) { - assertEquals(event.getTimeStamp(), rs.getLong(1)); - assertEquals(event.getRequestURI(), rs.getString(2)); - assertEquals(event.getRequestURL(), rs.getString(3)); - assertEquals(event.getRemoteHost(), rs.getString(4)); - assertEquals(event.getRemoteUser(), rs.getString(5)); - assertEquals(event.getRemoteAddr(), rs.getString(6)); - assertEquals(event.getProtocol(), rs.getString(7)); - assertEquals(event.getMethod(), rs.getString(8)); - assertEquals(event.getServerName(), rs.getString(9)); - assertEquals(event.getRequestContent(), rs.getString(10)); - } else { - fail("No row was inserted in the database"); - } - rs.close(); - stmt.close(); - } - - @Test - public void testCheckNoHeadersAreInserted() throws Exception { - setInsertHeadersAndStart(false); - - IAccessEvent event = createAccessEvent(); - appender.append(event); - StatusPrinter.print(context.getStatusManager()); - - // Check that no headers were inserted - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM access_event_header where EVENT_ID = " + existingEventTableRowCount); - - assertFalse(rs.next()); - rs.close(); - stmt.close(); - } - - @Test - public void testAppendHeaders() throws SQLException { - setInsertHeadersAndStart(true); - - IAccessEvent event = createAccessEvent(); - appender.append(event); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM access_event_header"); - String key; - String value; - if (!rs.next()) { - fail("There should be results to this query"); - } else { - key = rs.getString(2); - value = rs.getString(3); - assertNotNull(key); - assertNotNull(value); - assertEquals(event.getRequestHeader(key), value); - rs.next(); - key = rs.getString(2); - value = rs.getString(3); - assertNotNull(key); - assertNotNull(value); - assertEquals(event.getRequestHeader(key), value); - } - if (rs.next()) { - fail("There should be no more rows available"); - } - - rs.close(); - stmt.close(); - } - - @Test - public void testAppendMultipleEvents() throws SQLException { - setInsertHeadersAndStart(false); - String uri = "testAppendMultipleEvents"; - for (int i = 0; i < 10; i++) { - IAccessEvent event = createAccessEvent(uri); - appender.append(event); - } - - StatusPrinter.print(context); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM access_event where requestURI='" + uri + "'"); - int count = 0; - while (rs.next()) { - count++; - } - assertEquals(10, count); - - rs.close(); - stmt.close(); - } - - private IAccessEvent createAccessEvent() { - return createAccessEvent(""); - } - - private IAccessEvent createAccessEvent(String uri) { - DummyRequest request = new DummyRequest(); - request.setRequestUri(uri); - DummyResponse response = new DummyResponse(); - DummyServerAdapter adapter = new DummyServerAdapter(request, response); - - return new AccessEvent(context, request, response, adapter); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTestFixture.java b/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTestFixture.java deleted file mode 100755 index 69325d330f..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderHSQLTestFixture.java +++ /dev/null @@ -1,127 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.db; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; -import java.sql.Statement; - -import org.hsqldb.Server; - -public class DBAppenderHSQLTestFixture { - - public static final String DRIVER_CLASS = "org.hsqldb.jdbcDriver"; - String serverProps; - String url; - String user = "sa"; - String password = ""; - Server server; - boolean isNetwork = true; - - void setUp() throws SQLException { - if (isNetwork) { - if (url == null) { - url = "jdbc:hsqldb:hsql://localhost/test"; - } - - server = new Server(); - - server.setDatabaseName(0, "test"); - server.setDatabasePath(0, "mem:test;sql.enforce_strict_size=true"); - server.setLogWriter(null); - server.setErrWriter(null); - server.setTrace(false); - server.setSilent(true); - server.start(); - } else { - if (url == null) { - url = "jdbc:hsqldb:file:test;sql.enforce_strict_size=true"; - } - } - - try { - Class.forName(DRIVER_CLASS); - } catch (Exception e) { - e.printStackTrace(); - System.out.println(this + ".setUp() error: " + e.getMessage()); - } - Thread.yield(); - - createTables(); - } - - void tearDown() throws SQLException { - dropTables(); - if (isNetwork) { - server.stop(); - server = null; - } - } - - Connection newConnection() throws SQLException { - return DriverManager.getConnection(url, user, password); - } - - private void createTables() throws SQLException { - Connection conn = newConnection(); - StringBuilder buf = new StringBuilder(); - buf.append("CREATE TABLE access_event ("); - buf.append("timestmp BIGINT NOT NULL,"); - buf.append("requestURI VARCHAR(254),"); - buf.append("requestURL VARCHAR(254),"); - buf.append("remoteHost VARCHAR(254),"); - buf.append("remoteUser VARCHAR(254),"); - buf.append("remoteAddr VARCHAR(254),"); - buf.append("protocol VARCHAR(254),"); - buf.append("method VARCHAR(254),"); - buf.append("serverName VARCHAR(254),"); - buf.append("postContent VARCHAR(254),"); - buf.append("event_id INT NOT NULL IDENTITY);"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("CREATE TABLE access_event_header ("); - buf.append("event_id INT NOT NULL,"); - buf.append("header_key VARCHAR(254) NOT NULL,"); - buf.append("header_value LONGVARCHAR,"); - buf.append("PRIMARY KEY(event_id, header_key),"); - buf.append("FOREIGN KEY (event_id) REFERENCES access_event(event_id));"); - query(conn, buf.toString()); - } - - private void dropTables() throws SQLException { - Connection conn = newConnection(); - - StringBuilder buf = new StringBuilder(); - buf.append("DROP TABLE access_event_header IF EXISTS;"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("DROP TABLE access_event IF EXISTS;"); - query(conn, buf.toString()); - } - - private void query(Connection conn, String expression) throws SQLException { - Statement st = null; - st = conn.createStatement(); - - int i = st.executeUpdate(expression); - if (i == -1) { - System.out.println("db error : " + expression); - } - - st.close(); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderIntegrationTest.java b/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderIntegrationTest.java deleted file mode 100644 index a3c637a447..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/db/DBAppenderIntegrationTest.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.db; - -import static org.junit.Assert.assertEquals; - -import java.util.Random; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.testUtil.EnvUtilForTests; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.EnvUtil; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyAccessEventBuilder; -import ch.qos.logback.access.joran.JoranConfigurator; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.util.StatusPrinter; - -public class DBAppenderIntegrationTest { - - static String LOCAL_HOST_NAME = EnvUtilForTests.getLocalHostName(); - static String[] CONFORMING_HOST_LIST = new String[] { "Orion" }; - - int diff = new Random(System.nanoTime()).nextInt(10000); - AccessContext context = new AccessContext(); - StatusChecker statusChecker = new StatusChecker(context); - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - - } - - public void doTest(String configFile) throws JoranException { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(context); - configurator.doConfigure(configFile); - - Appender appender = context.getAppender("DB"); - - for (int i = 0; i < 10; i++) { - IAccessEvent event = DummyAccessEventBuilder.buildNewAccessEvent(); - appender.doAppend(event); - } - - StatusPrinter.print(context); - - // check that there were no errors - assertEquals(Status.INFO, statusChecker.getHighestLevel(0)); - - } - - static boolean isConformingHostAndJDK16OrHigher() { - if (!EnvUtil.isJDK6OrHigher()) { - return false; - } - return EnvUtilForTests.isLocalHostNameInList(CONFORMING_HOST_LIST); - } - - @Test - public void sqlserver() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/sqlserver-with-driver.xml"); - } - - @Test - @Ignore - public void oracle10g() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/oracle10g-with-driver.xml"); - } - - @Test - @Ignore - public void oracle11g() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/oracle11g-with-driver.xml"); - } - - @Test - public void mysql() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/mysql-with-driver.xml"); - } - - @Test - public void postgres() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/postgresql-with-driver.xml"); - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/db/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/db/PackageTest.java deleted file mode 100755 index 35a4c380c7..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/db/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.db; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ DBAppenderHSQLTest.class, DBAppenderIntegrationTest.class }) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyAccessEventBuilder.java b/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyAccessEventBuilder.java deleted file mode 100644 index 77cf589c7f..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyAccessEventBuilder.java +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.dummy; - -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; - -public class DummyAccessEventBuilder { - - static public IAccessEvent buildNewAccessEvent() { - DummyRequest request = new DummyRequest(); - DummyResponse response = new DummyResponse(); - DummyServerAdapter adapter = new DummyServerAdapter(request, response); - AccessContext accessContext = new AccessContext(); - - return new AccessEvent(accessContext, request, response, adapter); - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyRequest.java b/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyRequest.java deleted file mode 100644 index 0110fd9f9e..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyRequest.java +++ /dev/null @@ -1,356 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.dummy; - -import ch.qos.logback.access.AccessConstants; - -import javax.servlet.*; -import javax.servlet.http.*; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.security.Principal; -import java.util.*; - -public class DummyRequest implements HttpServletRequest { - - public final static String DUMMY_CONTENT_STRING = "request contents"; - public final static byte[] DUMMY_CONTENT_BYTES = DUMMY_CONTENT_STRING.getBytes(); - - public static final Map DUMMY_DEFAULT_ATTR_MAP = new HashMap(); - - public static final String DUMMY_RESPONSE_CONTENT_STRING = "response contents"; - public static final byte[] DUMMY_RESPONSE_CONTENT_BYTES = DUMMY_RESPONSE_CONTENT_STRING.getBytes(); - - Hashtable headerMap; - Hashtable parameterMap; - - String uri; - Map attributes; - - static { - DUMMY_DEFAULT_ATTR_MAP.put("testKey", "testKey"); - DUMMY_DEFAULT_ATTR_MAP.put(AccessConstants.LB_INPUT_BUFFER, DUMMY_CONTENT_BYTES); - DUMMY_DEFAULT_ATTR_MAP.put(AccessConstants.LB_OUTPUT_BUFFER, DUMMY_RESPONSE_CONTENT_BYTES); - } - - public DummyRequest() { - headerMap = new Hashtable(); - headerMap.put("headerName1", "headerValue1"); - headerMap.put("headerName2", "headerValue2"); - - parameterMap = new Hashtable(); - parameterMap.put("param1", new String[] {"value1"}); - - attributes = new HashMap(DUMMY_DEFAULT_ATTR_MAP); - } - - public String getAuthType() { - return null; - } - - public String getContextPath() { - return null; - } - - public Cookie[] getCookies() { - Cookie cookie = new Cookie("testName", "testCookie"); - return new Cookie[] { cookie }; - } - - public long getDateHeader(String arg0) { - return 0; - } - - public String getHeader(String key) { - return headerMap.get(key); - } - - - @Override - public Enumeration getHeaderNames() { - return headerMap.keys(); - } - - @Override - public Enumeration getHeaders(String arg) { - return null; - } - - public Map getHeaders() { - return headerMap; - } - - - public int getIntHeader(String arg0) { - return 0; - } - - public String getMethod() { - return "testMethod"; - } - - public String getPathInfo() { - return null; - } - - public String getPathTranslated() { - return null; - } - - public String getQueryString() { - return null; - } - - public String getRemoteUser() { - return "testUser"; - } - - public String getRequestURI() { - return uri; - } - - public StringBuffer getRequestURL() { - return new StringBuffer(uri); - } - - public String getRequestedSessionId() { - return null; - } - - public String getServletPath() { - return null; - } - - public HttpSession getSession() { - return null; - } - - public HttpSession getSession(boolean arg0) { - return null; - } - - public Principal getUserPrincipal() { - return null; - } - - public boolean isRequestedSessionIdFromCookie() { - return false; - } - - public boolean isRequestedSessionIdFromURL() { - return false; - } - - public boolean isRequestedSessionIdFromUrl() { - return false; - } - - public boolean authenticate(HttpServletResponse response) throws IOException, ServletException { - return false; // To change body of implemented methods use File | Settings | File Templates. - } - - public void login(String username, String password) throws ServletException { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void logout() throws ServletException { - // To change body of implemented methods use File | Settings | File Templates. - } - - public Collection getParts() throws IOException, IllegalStateException, ServletException { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public Part getPart(String name) throws IOException, IllegalStateException, ServletException { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isRequestedSessionIdValid() { - return false; - } - - public boolean isUserInRole(String arg0) { - return false; - } - - public Object getAttribute(String key) { - return attributes.get(key); - } - - public Enumeration getAttributeNames() { - return Collections.enumeration(attributes.keySet()); - } - - public String getCharacterEncoding() { - return null; - } - - public int getContentLength() { - return 0; - } - - public String getContentType() { - return null; - } - - public ServletInputStream getInputStream() throws IOException { - return null; - } - - public String getLocalAddr() { - return null; - } - - public String getLocalName() { - return null; - } - - public int getLocalPort() { - return 11; - } - - public ServletContext getServletContext() { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public AsyncContext startAsync() { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse) { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isAsyncStarted() { - return false; // To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isAsyncSupported() { - return false; // To change body of implemented methods use File | Settings | File Templates. - } - - public AsyncContext getAsyncContext() { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public DispatcherType getDispatcherType() { - return null; // To change body of implemented methods use File | Settings | File Templates. - } - - public Locale getLocale() { - return null; - } - - @Override - public Enumeration getLocales() { - return null; - } - - public String getParameter(String arg) { - String[] stringArray = parameterMap.get(arg); - if(stringArray == null || stringArray.length == 0) - return null; - else - return stringArray[0]; - } - - @Override - public Map getParameterMap() { - return parameterMap; - } - - public Enumeration getParameterNames() { - return parameterMap.keys(); - //eturn Collections.enumeration(parameterMap.keySet()); - } - - public String[] getParameterValues(String arg) { - return parameterMap.get(arg); - } - - public String getProtocol() { - return "testProtocol"; - } - - public BufferedReader getReader() throws IOException { - return null; - } - - public String getRealPath(String arg0) { - return null; - } - - public String getRemoteAddr() { - return "testRemoteAddress"; - } - - public String getRemoteHost() { - return "testHost"; - } - - public int getRemotePort() { - return 0; - } - - public RequestDispatcher getRequestDispatcher(String arg0) { - return null; - } - - public String getScheme() { - return null; - } - - public String getServerName() { - return "testServerName"; - } - - public int getServerPort() { - return 0; - } - - public boolean isSecure() { - return false; - } - - public void removeAttribute(String arg0) { - } - - public void setAttribute(String name, Object value) { - attributes.put(name, value); - } - - public void setCharacterEncoding(String arg0) throws UnsupportedEncodingException { - } - - public void setRequestUri(String uri) { - this.uri = uri; - } - - @Override - public long getContentLengthLong() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public String changeSessionId() { - return null; - } - - @Override - public T upgrade(Class httpUpgradeHandlerClass) throws IOException, ServletException { - return null; - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyResponse.java b/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyResponse.java deleted file mode 100644 index e8950c1412..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyResponse.java +++ /dev/null @@ -1,188 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.dummy; - -import java.io.IOException; -import java.io.PrintWriter; -import java.util.*; - -import javax.servlet.ServletOutputStream; -import javax.servlet.http.Cookie; -import javax.servlet.http.HttpServletResponse; - -public class DummyResponse implements HttpServletResponse { - - public static final int DUMMY_DEFAULT_STATUS = 200; - public static final int DUMMY_DEFAULT_CONTENT_COUNT = 1000; - public static final Map DUMMY_DEFAULT_HDEADER_MAP = new HashMap();; - - static { - DUMMY_DEFAULT_HDEADER_MAP.put("headerName1", "headerValue1"); - DUMMY_DEFAULT_HDEADER_MAP.put("headerName2", "headerValue2"); - } - - int status = DUMMY_DEFAULT_STATUS; - public Map headerMap; - - String characterEncoding = null; - ServletOutputStream outputStream = null; - - public DummyResponse() { - headerMap = DUMMY_DEFAULT_HDEADER_MAP; - } - - public void addCookie(Cookie arg0) { - } - - public void addDateHeader(String arg0, long arg1) { - } - - public void addHeader(String arg0, String arg1) { - } - - public void addIntHeader(String arg0, int arg1) { - } - - public boolean containsHeader(String arg0) { - return false; - } - - public String encodeRedirectURL(String arg0) { - return null; - } - - public String encodeRedirectUrl(String arg0) { - return null; - } - - public String encodeURL(String arg0) { - return null; - } - - public String encodeUrl(String arg0) { - return null; - } - - public void sendError(int arg0) throws IOException { - } - - public void sendError(int arg0, String arg1) throws IOException { - } - - public void sendRedirect(String arg0) throws IOException { - } - - public void setDateHeader(String arg0, long arg1) { - } - - public void setHeader(String arg0, String arg1) { - } - - public void setIntHeader(String arg0, int arg1) { - } - - public void setStatus(int arg0, String arg1) { - } - - public void flushBuffer() throws IOException { - } - - public int getBufferSize() { - return 0; - } - - public String getCharacterEncoding() { - return characterEncoding; - } - - public String getContentType() { - return null; - } - - public Locale getLocale() { - return null; - } - - public ServletOutputStream getOutputStream() throws IOException { - return outputStream; - } - - public void setOutputStream(ServletOutputStream outputStream) { - this.outputStream = outputStream; - } - - public PrintWriter getWriter() throws IOException { - return null; - } - - public boolean isCommitted() { - return false; - } - - public void reset() { - } - - public void resetBuffer() { - } - - public void setBufferSize(int arg0) { - } - - public void setCharacterEncoding(String characterEncoding) { - this.characterEncoding = characterEncoding; - } - - public void setContentLength(int arg0) { - } - - public void setContentType(String arg0) { - } - - public void setLocale(Locale arg0) { - } - - public String getHeader(String key) { - return headerMap.get(key); - } - - public Collection getHeaders(String name) { - String val = headerMap.get(name); - List list = new ArrayList(); - if (val != null) - list.add(val); - return list; - } - - public Collection getHeaderNames() { - return headerMap.keySet(); - } - - public long getContentCount() { - return DUMMY_DEFAULT_CONTENT_COUNT; - } - - public int getStatus() { - return status; - } - - public void setStatus(int status) { - this.status = status; - } - - @Override - public void setContentLengthLong(long length) { - // TODO Auto-generated method stub - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServerAdapter.java b/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServerAdapter.java deleted file mode 100644 index 7af921da30..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServerAdapter.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.dummy; - -import ch.qos.logback.access.spi.ServerAdapter; - -import java.util.Map; - -public class DummyServerAdapter implements ServerAdapter { - - DummyRequest request; - DummyResponse response; - - public DummyServerAdapter(DummyRequest dummyRequest, DummyResponse dummyResponse) { - this.request = dummyRequest; - this.response = dummyResponse; - } - - public long getContentLength() { - return response.getContentCount(); - } - - public int getStatusCode() { - return response.getStatus(); - } - - public long getRequestTimestamp() { - return -1; - } - - public Map buildResponseHeaderMap() { - return response.headerMap; - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServletOutputStream.java b/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServletOutputStream.java deleted file mode 100644 index 6937a4fef8..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/dummy/DummyServletOutputStream.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.dummy; - -import javax.servlet.ServletOutputStream; -import javax.servlet.WriteListener; - -import java.io.IOException; -import java.io.OutputStream; - -public class DummyServletOutputStream extends ServletOutputStream { - - private final OutputStream targetStream; - - public DummyServletOutputStream(OutputStream targetStream) { - this.targetStream = targetStream; - } - - @Override - public void write(int b) throws IOException { - this.targetStream.write(b); - } - - public void flush() throws IOException { - super.flush(); - this.targetStream.flush(); - } - - public void close() throws IOException { - super.close(); - this.targetStream.close(); - } - - @Override - public boolean isReady() { - // TODO Auto-generated method stub - return false; - } - - @Override - public void setWriteListener(WriteListener listener) { - // TODO Auto-generated method stub - - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/filter/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/filter/PackageTest.java deleted file mode 100644 index 9498170d1a..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/filter/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import junit.framework.TestCase; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ StatsByDayTest.class }) -public class PackageTest extends TestCase { -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/filter/StatsByDayTest.java b/logback-access/src/test/java/ch/qos/logback/access/filter/StatsByDayTest.java deleted file mode 100644 index a503e8f65b..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/filter/StatsByDayTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.filter; - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - -import ch.qos.logback.core.util.TimeUtil; - -public class StatsByDayTest { - - @Test - public void testBasic() { - // Tue Nov 21 18:05:36 CET 2006 - long now = 1164128736369L; - StatsByDay statsByDay = new StatsByDay(now); - - int total = 0; - // test fresh start - statsByDay.update(now, 0); - assertEquals(0, statsByDay.getLastCount()); - assertEquals(0, statsByDay.getAverage(), 0.01); - - total++; - statsByDay.update(now, total); - assertEquals(0, statsByDay.getLastCount()); - assertEquals(0.0, statsByDay.getAverage(), 0.01); - - long nextDay0 = TimeUtil.computeStartOfNextDay(now); - nextDay0 += 99; - - // there should be one event the next day, avg should also be 1 - statsByDay.update(nextDay0, total); - assertEquals(1.0, statsByDay.getLastCount(), 0.01); - assertEquals(1.0, statsByDay.getAverage(), 0.01); - - total += 2; - - statsByDay.update(nextDay0, total); - assertEquals(1, statsByDay.getLastCount()); - assertEquals(1.0, statsByDay.getAverage(), 0.01); - - long nextDay1 = TimeUtil.computeStartOfNextDay(nextDay0) + 6747; - statsByDay.update(nextDay1, total); - assertEquals(2, statsByDay.getLastCount()); - assertEquals(1.5, statsByDay.getAverage(), 0.01); - - nextDay1 += 4444; - total += 4; - - statsByDay.update(nextDay1, total); - // values should remain unchanged - assertEquals(2, statsByDay.getLastCount()); - assertEquals(1.5, statsByDay.getAverage(), 0.01); - - long nextDay2 = TimeUtil.computeStartOfNextDay(nextDay1) + 11177; - - statsByDay.update(nextDay2, total); - // values should remain unchanged - assertEquals(4, statsByDay.getLastCount()); - assertEquals(7.0 / 3, statsByDay.getAverage(), 0.01); - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java b/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java deleted file mode 100644 index eb4b559635..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyBasicTest.java +++ /dev/null @@ -1,119 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.concurrent.TimeUnit; - -import org.junit.AfterClass; -import org.junit.BeforeClass; -import org.junit.Test; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.access.spi.Util; -import ch.qos.logback.access.testUtil.NotifyingListAppender; -import ch.qos.logback.core.testUtil.RandomUtil; - -public class JettyBasicTest { - - static RequestLogImpl REQUEST_LOG_IMPL; - static JettyFixtureWithListAndConsoleAppenders JETTY_FIXTURE; - - private static final int TIMEOUT = 5; - static int RANDOM_SERVER_PORT = RandomUtil.getRandomServerPort(); - - @BeforeClass - static public void startServer() throws Exception { - REQUEST_LOG_IMPL = new RequestLogImpl(); - JETTY_FIXTURE = new JettyFixtureWithListAndConsoleAppenders(REQUEST_LOG_IMPL, RANDOM_SERVER_PORT); - JETTY_FIXTURE.start(); - } - - @AfterClass - static public void stopServer() throws Exception { - if (JETTY_FIXTURE != null) { - JETTY_FIXTURE.stop(); - } - } - - @Test - public void getRequest() throws Exception { - URL url = new URL("http://localhost:" + RANDOM_SERVER_PORT + "/"); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - - String result = Util.readToString(connection.getInputStream()); - - assertEquals("hello world", result); - - NotifyingListAppender listAppender = (NotifyingListAppender) REQUEST_LOG_IMPL.getAppender("list"); - listAppender.list.clear(); - } - - @Test - public void eventGoesToAppenders() throws Exception { - URL url = new URL(JETTY_FIXTURE.getUrl()); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - - String result = Util.readToString(connection.getInputStream()); - - assertEquals("hello world", result); - - NotifyingListAppender listAppender = (NotifyingListAppender) REQUEST_LOG_IMPL.getAppender("list"); - IAccessEvent event = listAppender.list.poll(TIMEOUT, TimeUnit.SECONDS); - assertNotNull("No events received", event); - - assertEquals("127.0.0.1", event.getRemoteHost()); - assertEquals("localhost", event.getServerName()); - listAppender.list.clear(); - } - - @Test - public void postContentConverter() throws Exception { - URL url = new URL(JETTY_FIXTURE.getUrl()); - String msg = "test message"; - - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - // this line is necessary to make the stream aware of when the message is - // over. - connection.setFixedLengthStreamingMode(msg.getBytes().length); - ((HttpURLConnection) connection).setRequestMethod("POST"); - connection.setDoOutput(true); - connection.setDoInput(true); - connection.setUseCaches(false); - connection.setRequestProperty("Content-Type", "text/plain"); - - PrintWriter output = new PrintWriter(new OutputStreamWriter(connection.getOutputStream())); - output.print(msg); - output.flush(); - output.close(); - - // StatusPrinter.print(requestLogImpl.getStatusManager()); - - NotifyingListAppender listAppender = (NotifyingListAppender) REQUEST_LOG_IMPL.getAppender("list"); - - IAccessEvent event = listAppender.list.poll(TIMEOUT, TimeUnit.SECONDS); - assertNotNull("No events received", event); - - // we should test the contents of the requests - // assertEquals(msg, event.getRequestContent()); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureBase.java b/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureBase.java deleted file mode 100644 index 733776116b..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureBase.java +++ /dev/null @@ -1,99 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import org.eclipse.jetty.server.Connector; -import org.eclipse.jetty.server.Request; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.server.handler.RequestLogHandler; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.util.ByteArrayISO8859Writer; - -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.io.OutputStream; - -public class JettyFixtureBase { - final protected RequestLogImpl requestLogImpl; - protected Handler handler = new BasicHandler(); - private final int port; - Server server; - protected String url; - - public JettyFixtureBase(RequestLogImpl impl, int port) { - requestLogImpl = impl; - this.port = port; - url = "http://localhost:" + port + "/"; - } - - public String getName() { - return "Jetty Test Setup"; - } - - public String getUrl() { - return url; - } - - public void start() throws Exception { - server = new Server(); - Connector connector = new SelectChannelConnector(); - connector.setPort(port); - server.setConnectors(new Connector[] { connector }); - - RequestLogHandler requestLogHandler = new RequestLogHandler(); - configureRequestLogImpl(); - requestLogHandler.setRequestLog(requestLogImpl); - - HandlerList handlers = new HandlerList(); - handlers.addHandler(requestLogHandler); - handlers.addHandler(getRequestHandler()); - - server.setHandler(handlers); - server.start(); - } - - public void stop() throws Exception { - server.stop(); - server = null; - } - - protected void configureRequestLogImpl() { - requestLogImpl.start(); - } - - protected Handler getRequestHandler() { - return handler; - } - - class BasicHandler extends AbstractHandler { - @SuppressWarnings("resource") - public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - OutputStream out = response.getOutputStream(); - ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(); - writer.write("hello world"); - writer.flush(); - response.setContentLength(writer.size()); - writer.writeTo(out); - out.flush(); - - baseRequest.setHandled(true); - - } - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureWithListAndConsoleAppenders.java b/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureWithListAndConsoleAppenders.java deleted file mode 100644 index 741f5f75ce..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/jetty/JettyFixtureWithListAndConsoleAppenders.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.access.PatternLayoutEncoder; -import ch.qos.logback.access.testUtil.NotifyingListAppender; -import ch.qos.logback.core.ConsoleAppender; - -public class JettyFixtureWithListAndConsoleAppenders extends JettyFixtureBase { - - public JettyFixtureWithListAndConsoleAppenders(RequestLogImpl impl, int port) { - super(impl, port); - url = "http://localhost:" + port + "/"; - } - - public void start() throws Exception { - super.start(); - Thread.yield(); - } - - public void stop() throws Exception { - super.stop(); - Thread.sleep(500); - } - - @Override - protected void configureRequestLogImpl() { - NotifyingListAppender appender = new NotifyingListAppender(); - appender.setContext(requestLogImpl); - appender.setName("list"); - appender.start(); - - ConsoleAppender console = new ConsoleAppender(); - console.setContext(requestLogImpl); - console.setName("console"); - PatternLayoutEncoder layout = new PatternLayoutEncoder(); - layout.setContext(requestLogImpl); - layout.setPattern("%date %server %clientHost"); - console.setEncoder(layout); - layout.start(); - console.start(); - - requestLogImpl.addAppender(appender); - requestLogImpl.addAppender(console); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/jetty/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/jetty/PackageTest.java deleted file mode 100644 index 45731f4796..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/jetty/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.jetty; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ JettyBasicTest.class }) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/joran/ConditionalTest.java b/logback-access/src/test/java/ch/qos/logback/access/joran/ConditionalTest.java deleted file mode 100644 index f03fddbf07..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/joran/ConditionalTest.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran; - -import ch.qos.logback.access.AccessTestConstants; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.ConsoleAppender; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.read.ListAppender; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; - -import org.junit.Before; -import org.junit.Test; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -/** - * @author Ceki Gülcü - */ -public class ConditionalTest { - - AccessContext context = new AccessContext(); - StatusChecker checker = new StatusChecker(context); - - int diff = RandomUtil.getPositiveInt(); - String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; - - @Before - public void setUp() { - InetAddress localhost = null; - try { - localhost = InetAddress.getLocalHost(); - context.putProperty("aHost", localhost.getHostName()); - } catch (UnknownHostException e) { - e.printStackTrace(); - } - } - - void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(context); - jc.doConfigure(file); - } - - @Test - public void conditionalConsoleApp_IF_THEN_True() throws JoranException, UnknownHostException { - configure(AccessTestConstants.TEST_DIR_PREFIX + "input/joran/conditional/conditionalConsole.xml"); - ConsoleAppender consoleAppender = (ConsoleAppender) context.getAppender("CON"); - assertNotNull(consoleAppender); - assertTrue(checker.isErrorFree(0)); - } - - @Test - public void conditionalConsoleApp_IF_THEN_False() throws JoranException, IOException, InterruptedException { - context.putProperty("aHost", null); - configure(AccessTestConstants.TEST_DIR_PREFIX + "input/joran/conditional/conditionalConsole.xml"); - - ConsoleAppender consoleAppender = (ConsoleAppender) context.getAppender("CON"); - assertNull(consoleAppender); - - StatusChecker checker = new StatusChecker(context); - assertTrue(checker.isErrorFree(0)); - } - - @Test - public void conditionalConsoleApp_ELSE() throws JoranException, IOException, InterruptedException { - configure(AccessTestConstants.TEST_DIR_PREFIX + "input/joran/conditional/conditionalConsole_ELSE.xml"); - ConsoleAppender consoleAppender = (ConsoleAppender) context.getAppender("CON"); - assertNull(consoleAppender); - - ListAppender listAppender = (ListAppender) context.getAppender("LIST"); - assertNotNull(listAppender); - assertTrue(checker.isErrorFree(0)); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/joran/JoranConfiguratorTest.java b/logback-access/src/test/java/ch/qos/logback/access/joran/JoranConfiguratorTest.java deleted file mode 100644 index c4d91058ee..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/joran/JoranConfiguratorTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import ch.qos.logback.access.spi.IAccessEvent; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.AccessTestConstants; -import ch.qos.logback.access.dummy.DummyAccessEventBuilder; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.read.ListAppender; -import ch.qos.logback.core.testUtil.StringListAppender; - -public class JoranConfiguratorTest { - - AccessContext context = new AccessContext(); - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(context); - jc.doConfigure(file); - } - - @Test - public void smoke() throws Exception { - configure(AccessTestConstants.TEST_DIR_PREFIX + "input/joran/smoke.xml"); - - ListAppender listAppender = (ListAppender) context.getAppender("LIST"); - IAccessEvent event = DummyAccessEventBuilder.buildNewAccessEvent(); - listAppender.doAppend(event); - - assertEquals(1, listAppender.list.size()); - - assertEquals(1, listAppender.list.size()); - IAccessEvent ae = listAppender.list.get(0); - assertNotNull(ae); - } - - @Test - public void defaultLayout() throws Exception { - configure(AccessTestConstants.TEST_DIR_PREFIX + "input/joran/defaultLayout.xml"); - StringListAppender listAppender = (StringListAppender) context.getAppender("STR_LIST"); - IAccessEvent event = DummyAccessEventBuilder.buildNewAccessEvent(); - listAppender.doAppend(event); - assertEquals(1, listAppender.strList.size()); - // the result contains a line separator at the end - assertTrue(listAppender.strList.get(0).startsWith("testMethod")); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/joran/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/joran/PackageTest.java deleted file mode 100644 index 079a5ba368..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/joran/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.joran; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -/** - * @author Ceki Gülcü - */ -@RunWith(Suite.class) -@Suite.SuiteClasses({ JoranConfiguratorTest.class, ConditionalTest.class }) -public class PackageTest { -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/net/NOPOutputStream.java b/logback-access/src/test/java/ch/qos/logback/access/net/NOPOutputStream.java deleted file mode 100644 index bcb24588ea..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/net/NOPOutputStream.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.io.IOException; -import java.io.OutputStream; - -public class NOPOutputStream extends OutputStream { - - @Override - public void write(int b) throws IOException { - // do nothing - - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/net/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/net/PackageTest.java deleted file mode 100644 index c18da6b241..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/net/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import junit.framework.TestCase; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ URLEvaluatorTest.class }) -public class PackageTest extends TestCase { -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/net/SerializationPerfTest.java b/logback-access/src/test/java/ch/qos/logback/access/net/SerializationPerfTest.java deleted file mode 100644 index df39c1fe97..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/net/SerializationPerfTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import java.io.IOException; -import java.io.ObjectOutputStream; - -import ch.qos.logback.access.spi.IAccessEvent; -import junit.framework.TestCase; -import ch.qos.logback.access.dummy.DummyAccessEventBuilder; - -public class SerializationPerfTest extends TestCase { - - ObjectOutputStream oos; - - int loopNumber = 10000; - int resetFrequency = 100; - int pauseFrequency = 10; - long pauseLengthInMillis = 20; - - public void setUp() throws Exception { - super.setUp(); - oos = new ObjectOutputStream(new NOPOutputStream()); - - } - - public void tearDown() throws Exception { - super.tearDown(); - oos.close(); - oos = null; - } - - public void test1() throws Exception { - // first run for just in time compiler - int resetCounter = 0; - int pauseCounter = 0; - for (int i = 0; i < loopNumber; i++) { - try { - IAccessEvent ae = DummyAccessEventBuilder.buildNewAccessEvent(); - // average time for the next method: 5000 nanos - ae.prepareForDeferredProcessing(); - oos.writeObject(ae); - oos.flush(); - if (++resetCounter >= resetFrequency) { - oos.reset(); - resetCounter = 0; - } - if (++pauseCounter >= pauseFrequency) { - Thread.sleep(pauseLengthInMillis); - pauseCounter = 0; - } - - } catch (IOException ex) { - fail(ex.getMessage()); - } - } - - // second run - Long t1; - Long t2; - Long total = 0L; - resetCounter = 0; - pauseCounter = 0; - // System.out.println("Beginning mesured run"); - for (int i = 0; i < loopNumber; i++) { - try { - IAccessEvent ae = DummyAccessEventBuilder.buildNewAccessEvent(); - t1 = System.nanoTime(); - // average length of the next method: 4000 nanos - ae.prepareForDeferredProcessing(); - oos.writeObject(ae); - oos.flush(); - t2 = System.nanoTime(); - total += (t2 - t1); - if (++resetCounter >= resetFrequency) { - oos.reset(); - resetCounter = 0; - } - if (++pauseCounter >= pauseFrequency) { - Thread.sleep(pauseLengthInMillis); - pauseCounter = 0; - } - } catch (IOException ex) { - fail(ex.getMessage()); - } - } - - total /= (1000);// nanos -> micros - System.out.println("Loop done : average time = " + total / loopNumber + " microsecs after " + loopNumber + " writes."); - // average time: 26-30 microsec = 0.030 millis - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/net/URLEvaluatorTest.java b/logback-access/src/test/java/ch/qos/logback/access/net/URLEvaluatorTest.java deleted file mode 100644 index 45cf530d54..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/net/URLEvaluatorTest.java +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.net; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServerAdapter; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.AccessEvent; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.boolex.EvaluationException; - -public class URLEvaluatorTest { - - final String expectedURL1 = "testUrl1"; - final String expectedURL2 = "testUrl2"; - AccessContext accessContext = new AccessContext(); - URLEvaluator evaluator; - DummyRequest request; - DummyResponse response; - DummyServerAdapter serverAdapter; - - @Before - public void setUp() throws Exception { - evaluator = new URLEvaluator(); - evaluator.setContext(accessContext); - evaluator.addURL(expectedURL1); - evaluator.start(); - request = new DummyRequest(); - response = new DummyResponse(); - serverAdapter = new DummyServerAdapter(request, response); - } - - @After - public void tearDown() throws Exception { - evaluator.stop(); - evaluator = null; - request = null; - response = null; - serverAdapter = null; - accessContext = null; - } - - @Test - public void testExpectFalse() throws EvaluationException { - request.setRequestUri("test"); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - assertFalse(evaluator.evaluate(ae)); - } - - @Test - public void testExpectTrue() throws EvaluationException { - request.setRequestUri(expectedURL1); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - assertTrue(evaluator.evaluate(ae)); - } - - @Test - public void testExpectTrueMultiple() throws EvaluationException { - evaluator.addURL(expectedURL2); - request.setRequestUri(expectedURL2); - IAccessEvent ae = new AccessEvent(accessContext, request, response, serverAdapter); - assertTrue(evaluator.evaluate(ae)); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/pattern/ConverterTest.java b/logback-access/src/test/java/ch/qos/logback/access/pattern/ConverterTest.java deleted file mode 100644 index 17cf71df8f..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/pattern/ConverterTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import static org.junit.Assert.assertEquals; - -import java.util.ArrayList; -import java.util.List; - -import javax.servlet.http.Cookie; - -import ch.qos.logback.access.spi.IAccessEvent; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServerAdapter; -import ch.qos.logback.access.spi.AccessContext; -import ch.qos.logback.access.spi.AccessEvent; - -public class ConverterTest { - - IAccessEvent event; - DummyRequest request = new DummyRequest(); - DummyResponse response = new DummyResponse(); - AccessContext accessContext = new AccessContext(); - - @Before - public void setUp() throws Exception { - event = createEvent(); - } - - @After - public void tearDown() throws Exception { - event = null; - request = null; - response = null; - } - - @Test - public void testContentLengthConverter() { - ContentLengthConverter converter = new ContentLengthConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(Long.toString(event.getServerAdapter().getContentLength()), result); - } - - @Test - public void testDateConverter() { - DateConverter converter = new DateConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(converter.cachingDateFormatter.format(event.getTimeStamp()), result); - } - - public void testLineLocalPortConverter() { - LocalPortConverter converter = new LocalPortConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(Integer.toString(request.getLocalPort()), result); - } - - @Test - public void testRemoteHostConverter() { - RemoteHostConverter converter = new RemoteHostConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getRemoteHost(), result); - } - - @Test - public void testRemoteIPAddressConverter() { - RemoteIPAddressConverter converter = new RemoteIPAddressConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getRemoteAddr(), result); - } - - @Test - public void testRemoteUserConverter() { - RemoteUserConverter converter = new RemoteUserConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getRemoteUser(), result); - } - - @Test - public void testRequestAttributeConverter() { - RequestAttributeConverter converter = new RequestAttributeConverter(); - List optionList = new ArrayList(); - optionList.add("testKey"); - converter.setOptionList(optionList); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getAttribute("testKey"), result); - } - - @Test - public void testRequestCookieConverter() { - RequestCookieConverter converter = new RequestCookieConverter(); - List optionList = new ArrayList(); - optionList.add("testName"); - converter.setOptionList(optionList); - converter.start(); - String result = converter.convert(event); - Cookie cookie = request.getCookies()[0]; - assertEquals(cookie.getValue(), result); - } - - @Test - public void testRequestHeaderConverter() { - RequestHeaderConverter converter = new RequestHeaderConverter(); - List optionList = new ArrayList(); - optionList.add("headerName1"); - converter.setOptionList(optionList); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getHeader("headerName1"), result); - } - - @Test - public void testRequestMethodConverter() { - RequestMethodConverter converter = new RequestMethodConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getMethod(), result); - } - - @Test - public void testRequestProtocolConverter() { - RequestProtocolConverter converter = new RequestProtocolConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getProtocol(), result); - } - - @Test - public void testRequestURIConverter() { - RequestURIConverter converter = new RequestURIConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getRequestURI(), result); - } - - @Test - public void testRequestURLConverter() { - RequestURLConverter converter = new RequestURLConverter(); - converter.start(); - String result = converter.convert(event); - String expected = request.getMethod() + " " + request.getRequestURI() + " " + request.getProtocol(); - assertEquals(expected, result); - } - - @Test - public void testResponseHeaderConverter() { - ResponseHeaderConverter converter = new ResponseHeaderConverter(); - List optionList = new ArrayList(); - optionList.add("headerName1"); - converter.setOptionList(optionList); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getHeader("headerName1"), result); - } - - @Test - public void testServerNameConverter() { - ServerNameConverter converter = new ServerNameConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(request.getServerName(), result); - } - - @Test - public void testStatusCodeConverter() { - StatusCodeConverter converter = new StatusCodeConverter(); - converter.start(); - String result = converter.convert(event); - assertEquals(Integer.toString(event.getServerAdapter().getStatusCode()), result); - } - - private IAccessEvent createEvent() { - DummyServerAdapter dummyAdapter = new DummyServerAdapter(request, response); - return new AccessEvent(accessContext, request, response, dummyAdapter); - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/pattern/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/pattern/PackageTest.java deleted file mode 100644 index f019c3e41d..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/pattern/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.pattern; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ConverterTest.class }) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/servlet/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/servlet/PackageTest.java deleted file mode 100644 index 8520fcda4f..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/servlet/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import junit.framework.TestCase; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ TeeFilterTest.class }) -public class PackageTest extends TestCase { -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeFilterTest.java b/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeFilterTest.java deleted file mode 100644 index 163a153ae1..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeFilterTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import org.junit.Test; - -import java.util.Arrays; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; - -public class TeeFilterTest { - - @Test - public void extractNameList() { - assertEquals(Arrays.asList(new String[] { "a" }), TeeFilter.extractNameList("a")); - assertEquals(Arrays.asList(new String[] { "a", "b" }), TeeFilter.extractNameList("a, b")); - assertEquals(Arrays.asList(new String[] { "a", "b" }), TeeFilter.extractNameList("a; b")); - assertEquals(Arrays.asList(new String[] { "a", "b", "c" }), TeeFilter.extractNameList("a; b, c")); - } - - @Test - public void defaultCase() { - assertTrue(TeeFilter.computeActivation("somehost", "", "")); - assertTrue(TeeFilter.computeActivation("somehost", null, null)); - } - - @Test - public void withIncludesOnly() { - assertTrue(TeeFilter.computeActivation("a", "a", null)); - assertTrue(TeeFilter.computeActivation("a", "a, b", null)); - assertFalse(TeeFilter.computeActivation("a", "b", null)); - assertFalse(TeeFilter.computeActivation("a", "b, c", null)); - } - - @Test - public void withExcludesOnly() { - assertFalse(TeeFilter.computeActivation("a", null, "a")); - assertFalse(TeeFilter.computeActivation("a", null, "a, b")); - assertTrue(TeeFilter.computeActivation("a", null, "b")); - assertTrue(TeeFilter.computeActivation("a", null, "b, c")); - } - - @Test - public void withIncludesAndExcludes() { - assertFalse(TeeFilter.computeActivation("a", "a", "a")); - assertTrue(TeeFilter.computeActivation("a", "a", "b")); - assertFalse(TeeFilter.computeActivation("a", "b", "a")); - assertFalse(TeeFilter.computeActivation("a", "b", "b")); - - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeHttpServletResponseTest.java b/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeHttpServletResponseTest.java deleted file mode 100644 index 798d3668c6..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/servlet/TeeHttpServletResponseTest.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.servlet; - -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServletOutputStream; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.Parameterized; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.PrintWriter; -import java.util.Arrays; -import java.util.Collection; - -import static org.junit.Assert.assertArrayEquals; - -@RunWith(Parameterized.class) -public class TeeHttpServletResponseTest { - - String characterEncoding; - String testString; - byte[] expectedBytes; - - public TeeHttpServletResponseTest(String characterEncoding, String testString, byte[] expectedBytes) { - this.characterEncoding = characterEncoding; - this.testString = testString; - this.expectedBytes = expectedBytes; - } - - @Parameterized.Parameters - public static Collection inputValues() { - return Arrays.asList(new Object[][] { - { "utf-8", "Gülcü", new byte[] { (byte) 0x47, (byte) 0xC3, (byte) 0xBC, (byte) 0x6C, (byte) 0x63, (byte) 0xC3, (byte) 0xBC } }, - { "iso-8859-1", "Gülcü", new byte[] { (byte) 0x47, (byte) 0xFC, (byte) 0x6C, (byte) 0x63, (byte) 0xFC } } }); - } - - @Test - public void testWriterEncoding() throws IOException { - ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); - - DummyResponse dummyResponse = new DummyResponse(); - dummyResponse.setCharacterEncoding(characterEncoding); - dummyResponse.setOutputStream(new DummyServletOutputStream(byteArrayOutputStream)); - - TeeHttpServletResponse teeServletResponse = new TeeHttpServletResponse(dummyResponse); - - PrintWriter writer = teeServletResponse.getWriter(); - writer.write(testString); - writer.flush(); - - assertArrayEquals(expectedBytes, byteArrayOutputStream.toByteArray()); - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/sift/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/sift/PackageTest.java deleted file mode 100644 index 977c6ef425..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/sift/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ SiftingAppenderTest.class }) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/sift/SiftingAppenderTest.java b/logback-access/src/test/java/ch/qos/logback/access/sift/SiftingAppenderTest.java deleted file mode 100644 index 2c20edebcb..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/sift/SiftingAppenderTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.sift; - -import static org.junit.Assert.assertEquals; - -import java.net.HttpURLConnection; -import java.net.URL; -import java.util.LinkedHashSet; -import java.util.Set; - -import ch.qos.logback.access.jetty.JettyFixtureBase; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.jetty.RequestLogImpl; -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.access.spi.Util; -import ch.qos.logback.core.read.ListAppender; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.util.StatusPrinter; - -public class SiftingAppenderTest { - static final String PREFIX = "src/test/input/jetty/"; - static int RANDOM_SERVER_PORT = RandomUtil.getRandomServerPort(); - - JettyFixtureBase jettyFixture; - RequestLogImpl rli = new RequestLogImpl(); - - @Before - public void startServer() throws Exception { - jettyFixture = new JettyFixtureBase(rli, RANDOM_SERVER_PORT); - } - - @After - public void stopServer() throws Exception { - if (jettyFixture != null) { - jettyFixture.stop(); - } - } - - @Test - public void invokingDifferentPathShouldBeSiftedAccordingly() throws Exception { - rli.setFileName(PREFIX + "sifting.xml"); - jettyFixture.start(); - invokeServer("/"); - invokeServer("/x"); - invokeServer("/x"); - invokeServer("/y"); - - StatusPrinter.print(rli); - SiftingAppender siftingAppender = (SiftingAppender) rli.getAppender("SIFTING"); - Set keySet = siftingAppender.getAppenderTracker().allKeys(); - assertEquals(3, keySet.size()); - - Set witnessSet = new LinkedHashSet(); - witnessSet.add("NA"); - witnessSet.add("x"); - witnessSet.add("y"); - assertEquals(witnessSet, keySet); - - check(siftingAppender, "NA", 1); - check(siftingAppender, "x", 2); - check(siftingAppender, "y", 1); - } - - private void check(SiftingAppender siftingAppender, String key, int expectedCount) { - ListAppender listAppender = (ListAppender) siftingAppender.getAppenderTracker().find(key); - assertEquals(expectedCount, listAppender.list.size()); - } - - void invokeServer(String uri) throws Exception { - URL url = new URL("http://localhost:" + RANDOM_SERVER_PORT + uri); - HttpURLConnection connection = (HttpURLConnection) url.openConnection(); - connection.setDoInput(true); - Util.readToString(connection.getInputStream()); - Thread.sleep(10); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventSerializationTest.java b/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventSerializationTest.java deleted file mode 100644 index 97b7993cc4..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventSerializationTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.ObjectOutputStream; - -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyAccessEventBuilder; -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.access.dummy.DummyResponse; -import ch.qos.logback.access.dummy.DummyServerAdapter; -import ch.qos.logback.access.net.HardenedAccessEventInputStream; - -public class AccessEventSerializationTest { - - private Object buildSerializedAccessEvent() throws IOException, ClassNotFoundException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(baos); - IAccessEvent ae = DummyAccessEventBuilder.buildNewAccessEvent(); - // average time for the next method: 5000 nanos - ae.prepareForDeferredProcessing(); - oos.writeObject(ae); - oos.flush(); - - ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); - HardenedAccessEventInputStream hardenedOIS = new HardenedAccessEventInputStream(bais); - - Object sae = hardenedOIS.readObject(); - hardenedOIS.close(); - return sae; - } - - @Test - public void testSerialization() throws IOException, ClassNotFoundException { - Object o = buildSerializedAccessEvent(); - assertNotNull(o); - IAccessEvent aeBack = (IAccessEvent) o; - - assertEquals(DummyResponse.DUMMY_DEFAULT_HDEADER_MAP, aeBack.getResponseHeaderMap()); - assertEquals(DummyResponse.DUMMY_DEFAULT_HDEADER_MAP.get("x"), aeBack.getResponseHeader("x")); - assertEquals(DummyResponse.DUMMY_DEFAULT_HDEADER_MAP.get("headerName1"), aeBack.getResponseHeader("headerName1")); - assertEquals(DummyResponse.DUMMY_DEFAULT_HDEADER_MAP.size(), aeBack.getResponseHeaderNameList().size()); - assertEquals(DummyResponse.DUMMY_DEFAULT_CONTENT_COUNT, aeBack.getContentLength()); - assertEquals(DummyResponse.DUMMY_DEFAULT_STATUS, aeBack.getStatusCode()); - - assertEquals(DummyRequest.DUMMY_CONTENT_STRING, aeBack.getRequestContent()); - - assertEquals(DummyRequest.DUMMY_RESPONSE_CONTENT_STRING, aeBack.getResponseContent()); - - assertEquals(DummyRequest.DUMMY_DEFAULT_ATTR_MAP.get("testKey"), aeBack.getAttribute("testKey")); - } - - // Web containers may (and will) recycle requests objects. So we must make sure that after - // we prepared an event for deferred processing it won't be using data from the original - // HttpRequest object which may at that time represent another request - @Test - public void testAttributesAreNotTakenFromRecycledRequestWhenProcessingDeferred() { - - DummyRequest request = new DummyRequest(); - DummyResponse response = new DummyResponse(); - DummyServerAdapter adapter = new DummyServerAdapter(request, response); - AccessContext accessContext = new AccessContext(); - - IAccessEvent event = new AccessEvent(accessContext, request, response, adapter); - - request.setAttribute("testKey", "ORIGINAL"); - - event.prepareForDeferredProcessing(); - - request.setAttribute("testKey", "NEW"); - - // Event should capture the original value - assertEquals("ORIGINAL", event.getAttribute("testKey")); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventTest.java b/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventTest.java deleted file mode 100755 index d0ec14e9b7..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/spi/AccessEventTest.java +++ /dev/null @@ -1,57 +0,0 @@ -package ch.qos.logback.access.spi; - -import static org.junit.Assert.*; - -import java.util.Map; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.access.dummy.DummyAccessEventBuilder; -import ch.qos.logback.access.dummy.DummyRequest; -import ch.qos.logback.core.testUtil.RandomUtil; - -public class AccessEventTest { - - int diff = RandomUtil.getPositiveInt(); - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - // See LOGBACK-1189 - @Test - public void callingPrepareForDeferredProcessingShouldBeIdempotent() { - String key = "key-"+diff; - String val = "val-"+diff; - - IAccessEvent ae = DummyAccessEventBuilder.buildNewAccessEvent(); - DummyRequest request = (DummyRequest) ae.getRequest(); - Map headersMap = request.getHeaders(); - Map parametersMap = request.getParameterMap(); - - headersMap.put(key, val); - request.setAttribute(key, val); - parametersMap.put(key, new String[] {val}); - ae.prepareForDeferredProcessing(); - assertEquals(val, ae.getAttribute(key)); - assertEquals(val, ae.getRequestHeader(key)); - assertEquals(val, ae.getRequestParameter(key)[0]); - - - request.setAttribute(key, "change"); - headersMap.put(key, "change"); - parametersMap.put(key, new String[] {"change"}); - ae.prepareForDeferredProcessing(); - assertEquals(val, ae.getAttribute(key)); - assertEquals(val, ae.getRequestHeader(key)); - assertEquals(val, ae.getRequestParameter(key)[0]); - - } - -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/spi/PackageTest.java b/logback-access/src/test/java/ch/qos/logback/access/spi/PackageTest.java deleted file mode 100644 index a40373256e..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/spi/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.spi; - -import junit.framework.TestCase; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ AccessEventSerializationTest.class }) -public class PackageTest extends TestCase { -} \ No newline at end of file diff --git a/logback-access/src/test/java/ch/qos/logback/access/testUtil/NotifyingListAppender.java b/logback-access/src/test/java/ch/qos/logback/access/testUtil/NotifyingListAppender.java deleted file mode 100644 index 5061c9fc41..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/testUtil/NotifyingListAppender.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.testUtil; - -import java.util.concurrent.LinkedBlockingQueue; - -import ch.qos.logback.access.spi.IAccessEvent; -import ch.qos.logback.core.AppenderBase; - -public class NotifyingListAppender extends AppenderBase { - - public final LinkedBlockingQueue list = new LinkedBlockingQueue(); - - protected void append(IAccessEvent e) { - list.add(e); - } -} diff --git a/logback-access/src/test/java/ch/qos/logback/access/tomcat/LogbackValveTest.java b/logback-access/src/test/java/ch/qos/logback/access/tomcat/LogbackValveTest.java deleted file mode 100755 index 86cafe5843..0000000000 --- a/logback-access/src/test/java/ch/qos/logback/access/tomcat/LogbackValveTest.java +++ /dev/null @@ -1,97 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.access.tomcat; - -import static org.junit.Assert.*; - -import org.apache.catalina.LifecycleException; -import org.apache.catalina.core.ContainerBase; -import org.junit.After; -import org.junit.Test; - -import ch.qos.logback.access.AccessTestConstants; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.StatusChecker; - -public class LogbackValveTest { - - LogbackValve valve = new LogbackValve(); - StatusChecker checker = new StatusChecker(valve); - - @After - public void tearDown() { - System.clearProperty(LogbackValve.CATALINA_BASE_KEY); - System.clearProperty(LogbackValve.CATALINA_HOME_KEY); - } - - @Test - public void nonExistingConfigFileShouldResultInWarning() throws LifecycleException { - final String resourceName = "logback-test2-config.xml"; - setupValve(resourceName); - valve.start(); - checker.assertContainsMatch(Status.WARN, "Failed to find valid"); - } - - @Test - public void fileUnderCatalinaBaseShouldBeFound() throws LifecycleException { - System.setProperty(LogbackValve.CATALINA_BASE_KEY, AccessTestConstants.JORAN_INPUT_PREFIX + "tomcat/"); - final String fileName = "logback-access.xml"; - setupValve(fileName); - valve.start(); - checker.assertContainsMatch("Found configuration file"); - checker.assertContainsMatch("Done configuring"); - checker.assertIsErrorFree(); - } - - @Test - public void fileUnderCatalinaHomeShouldBeFound() throws LifecycleException { - System.setProperty(LogbackValve.CATALINA_HOME_KEY, AccessTestConstants.JORAN_INPUT_PREFIX + "tomcat/"); - final String fileName = "logback-access.xml"; - setupValve(fileName); - valve.start(); - checker.assertContainsMatch("Found configuration file"); - checker.assertContainsMatch("Done configuring"); - checker.assertIsErrorFree(); - } - - @Test - public void resourceShouldBeFound() throws LifecycleException { - final String fileName = "logback-asResource.xml"; - setupValve(fileName); - valve.start(); - checker.assertContainsMatch("Found ." + fileName + ". as a resource."); - checker.assertContainsMatch("Done configuring"); - checker.assertIsErrorFree(); - } - - @Test - public void executorServiceShouldBeNotNull() throws LifecycleException { - final String fileName = "logback-asResource.xml"; - setupValve(fileName); - valve.start(); - assertNotNull(valve.getScheduledExecutorService()); - - } - - private void setupValve(final String resourceName) { - valve.setFilename(resourceName); - valve.setName("test"); - valve.setContainer(new ContainerBase() { - @Override - protected String getObjectNameKeyProperties() { - return "getObjectNameKeyProperties-test"; - } - }); - } -} \ No newline at end of file diff --git a/logback-access/src/test/resources/logback-asResource.xml b/logback-access/src/test/resources/logback-asResource.xml deleted file mode 100755 index b4d2a9f8ee..0000000000 --- a/logback-access/src/test/resources/logback-asResource.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - %h %l %u %user %date "%r" %s %b - - - - - \ No newline at end of file diff --git a/logback-classic-blackbox/LICENSE.txt b/logback-classic-blackbox/LICENSE.txt new file mode 100644 index 0000000000..0d92689462 --- /dev/null +++ b/logback-classic-blackbox/LICENSE.txt @@ -0,0 +1,15 @@ +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2015, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v2.0 as published by +the Eclipse Foundation + + or (per the licensee's choosing) + +under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation. + diff --git a/logback-classic-blackbox/pom.xml b/logback-classic-blackbox/pom.xml new file mode 100644 index 0000000000..aefca3cb3b --- /dev/null +++ b/logback-classic-blackbox/pom.xml @@ -0,0 +1,178 @@ + + + + 4.0.0 + + + ch.qos.logback + logback-parent + 1.5.28-SNAPSHOT + + + logback-classic-blackbox + jar + Logback Classic Blackbox Testing + Logback Classic Blackbox Testing Module + + + + ch.qos.logback + logback-core + + + ch.qos.logback + logback-classic + + + + org.eclipse.jetty + jetty-server + ${jetty.version} + test + + + + org.eclipse.jetty.ee10 + jetty-ee10-servlet + ${jetty.version} + + + + + jakarta.servlet + jakarta.servlet-api + 6.0.0 + + + + jakarta.mail + jakarta.mail-api + compile + + + + org.codehaus.janino + janino + compile + + + + org.dom4j + dom4j + compile + + + + org.eclipse.angus + angus-mail + test + + + + com.icegreen + greenmail + compile + + + junit + junit + + + com.sun.mail + jakarta.mail + + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + + + + classes + 8 + + 1C + true + plain + false + + + true + + + ch.qos.logback.classic.blackbox.VersionCheckTest + + + + + + + + + + + + + older-core + + 1.5.25 + + + + + ch.qos.logback + logback-core + ${older-logback-core.version} + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + + ${older-logback-core.version} + + once + + plain + true + + ch.qos.logback.classic.blackbox.VersionCheckTest + + + **/*Xest.java + + + + + + + + + + + + + + diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/callerData.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/callerData.xml new file mode 100644 index 0000000000..81a93042d4 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/callerData.xml @@ -0,0 +1,25 @@ + + + + + + + + + hello + + + + + %caller{4, helloEval}%d %level - %m%n + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/conditional/conditionalConsoleApp.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalConsoleApp.xml similarity index 100% rename from logback-classic/src/test/input/joran/conditional/conditionalConsoleApp.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalConsoleApp.xml diff --git a/logback-classic/src/test/input/joran/conditional/conditionalConsoleApp_ELSE.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalConsoleApp_ELSE.xml similarity index 100% rename from logback-classic/src/test/input/joran/conditional/conditionalConsoleApp_ELSE.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalConsoleApp_ELSE.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalIncludeExistingFile.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalIncludeExistingFile.xml new file mode 100644 index 0000000000..195574c924 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalIncludeExistingFile.xml @@ -0,0 +1,32 @@ + + + + + + + + + + src/test/blackboxInput/joran/conditional/includedFile.xml + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/conditional/conditionalIncludeInexistentFile.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalIncludeInexistentFile.xml similarity index 100% rename from logback-classic/src/test/input/joran/conditional/conditionalIncludeInexistentFile.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/conditional/conditionalIncludeInexistentFile.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithInnerConditional.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithInnerConditional.xml new file mode 100644 index 0000000000..40bfb7185e --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithInnerConditional.xml @@ -0,0 +1,23 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithVariableAndConditional.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithVariableAndConditional.xml new file mode 100644 index 0000000000..22d43a0ba9 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includeWithVariableAndConditional.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/conditional/includedFile.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includedFile.xml similarity index 100% rename from logback-classic/src/test/input/joran/conditional/includedFile.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includedFile.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includedWithInnerConditional.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includedWithInnerConditional.xml new file mode 100644 index 0000000000..c7901a3d53 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/includedWithInnerConditional.xml @@ -0,0 +1,28 @@ + + + + + + + + %d %-5level %logger{35} - %msg %n + + + + + + + + \ No newline at end of file diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/siftNestedWithinIfThen.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/siftNestedWithinIfThen.xml new file mode 100644 index 0000000000..92f09133d2 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/conditional/siftNestedWithinIfThen.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + userid + ifThenDefault + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/evaluatorFilter.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/evaluatorFilter.xml similarity index 100% rename from logback-classic/src/test/input/joran/evaluatorFilter.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/evaluatorFilter.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/evaluatorFilterWithImports.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/evaluatorFilterWithImports.xml new file mode 100644 index 0000000000..f532f9bf07 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/evaluatorFilterWithImports.xml @@ -0,0 +1,21 @@ + + + + + + + + + myFilter + DENY + + mdcEvaluator + "to be ignored".equals(message) + + + + + + + + \ No newline at end of file diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673.xml new file mode 100644 index 0000000000..118a40eb96 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673.xml @@ -0,0 +1,23 @@ + + + + + + + + + + ... + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673bis.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673bis.xml new file mode 100644 index 0000000000..cda9bc0d8f --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1673bis.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1678.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1678.xml new file mode 100644 index 0000000000..3401eb0119 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/issues/logback_1678.xml @@ -0,0 +1,21 @@ + + + + + + ... + + + + ... + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/jul/levelChangePropagator0.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/jul/levelChangePropagator0.xml similarity index 100% rename from logback-classic/src/test/input/joran/jul/levelChangePropagator0.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/jul/levelChangePropagator0.xml diff --git a/logback-classic/src/test/input/joran/jul/levelChangePropagator1.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/jul/levelChangePropagator1.xml similarity index 100% rename from logback-classic/src/test/input/joran/jul/levelChangePropagator1.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/jul/levelChangePropagator1.xml diff --git a/logback-classic/src/test/input/joran/roct/inclusion/inner0.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/inner0.xml similarity index 100% rename from logback-classic/src/test/input/joran/roct/inclusion/inner0.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/inner0.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topByResource.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topByResource.xml new file mode 100644 index 0000000000..683f17d436 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topByResource.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topLevel0.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topLevel0.xml new file mode 100644 index 0000000000..ab691e2248 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/inclusion/topLevel0.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/roct/scan 1.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan 1.xml similarity index 100% rename from logback-classic/src/test/input/joran/roct/scan 1.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan 1.xml diff --git a/logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan_logback_474.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan_logback_474.xml new file mode 100755 index 0000000000..29ecc5d289 --- /dev/null +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan_logback_474.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/roct/scan_period_default.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan_period_default.xml similarity index 100% rename from logback-classic/src/test/input/joran/roct/scan_period_default.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/roct/scan_period_default.xml diff --git a/logback-classic/src/test/input/joran/smtp/customBufferSize.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/smtp/customBufferSize.xml similarity index 100% rename from logback-classic/src/test/input/joran/smtp/customBufferSize.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/smtp/customBufferSize.xml diff --git a/logback-classic/src/test/input/joran/smtp/customEvaluator.xml b/logback-classic-blackbox/src/test/blackboxInput/joran/smtp/customEvaluator.xml similarity index 87% rename from logback-classic/src/test/input/joran/smtp/customEvaluator.xml rename to logback-classic-blackbox/src/test/blackboxInput/joran/smtp/customEvaluator.xml index b2e39a76df..a28c7eca67 100644 --- a/logback-classic/src/test/input/joran/smtp/customEvaluator.xml +++ b/logback-classic-blackbox/src/test/blackboxInput/joran/smtp/customEvaluator.xml @@ -6,7 +6,7 @@ nospam@qos.ch user@host.dom testCustomEvaluator %logger - %m - + 2 diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/BlackboxClassicTestConstants.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/BlackboxClassicTestConstants.java new file mode 100644 index 0000000000..70b65af5ec --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/BlackboxClassicTestConstants.java @@ -0,0 +1,22 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox; + +public class BlackboxClassicTestConstants { + + public static final String TEST_SRC_PREFIX = "src/test/"; + public static final String TEST_INPUT_PREFIX = TEST_SRC_PREFIX + "blackboxInput/"; + public static final String JORAN_INPUT_PREFIX = TEST_INPUT_PREFIX + "joran/"; +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/VersionCheckTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/VersionCheckTest.java new file mode 100644 index 0000000000..4e98908d81 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/VersionCheckTest.java @@ -0,0 +1,97 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.CoreVersionUtil; +import ch.qos.logback.core.util.VersionUtil; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + +/** + * The VersionCheckTest class is designed to perform a validation test + * on the compatibility of version dependencies, specifically focusing + * on the interaction between "logback-classic" and "logback-core" libraries. + * + *

In particular, it checks that when "logback-core" is older than version 1.5.25, + * a NoClassDefFoundError is caught. + *

+ * + *

Use the following command to run this test + *

+ * + *
  cd logback-classic-blackbox;
+ * mvn test -P older-core -Dtest=ch.qos.logback.classic.blackbox.VersionCheckTest
+ * 
+ * + * @since 1.5.25 + */ + +public class VersionCheckTest { + + + // WARNING: do not add other tests to this file + + LoggerContext loggerContext = new LoggerContext(); + + /** + * + * Assertions: + * 1. Verifies that the "olderCore" property matches the expected value "1.5.20". + * 2. Ensures that a {@link NoClassDefFoundError} is thrown in presence of logback-core version + * 1.5.25 or older. + */ + @Test + @Disabled + public void versionTest() { + String olderCoreVersion = System.getProperty("olderCore", "none"); + //assertEquals("1.5.20", olderCoreVersion); + assertEquals("1.5.25", olderCoreVersion); + try { + VersionUtil.checkForVersionEquality(loggerContext, this.getClass(), CoreConstants.class, "logback-classic", "logback-core"); + fail("Expected NoClassDefFoundError"); + } catch (NoClassDefFoundError e) { + // logback-core version is older than 1.5.25 + System.out.println("Got expected NoClassDefFoundError."); + } + } + + @Test + public void otherVersionTest() { + String olderCoreVersion = System.getProperty("olderCore", "none"); + //assertEquals("1.5.20", olderCoreVersion); + assertEquals("1.5.25", olderCoreVersion); + try { + CoreVersionUtil.getCoreVersionBySelfDeclaredProperties(); + fail("Expected Error"); + } catch (NoClassDefFoundError e) { + // logback-core version is 1.5.24 or older + System.out.println("Got expected NoClassDefFoundError."); + } catch (NoSuchMethodError e) { + // logback-core version is 1.5.25 or older + System.out.println("Got expected NoSuchFieldError."); + } + } + + + + + // WARNING: do not add other tests to this file + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/evaluator/MatchHelloEvaluator.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/evaluator/MatchHelloEvaluator.java new file mode 100644 index 0000000000..6b082f06dc --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/evaluator/MatchHelloEvaluator.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.evaluator; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.boolex.EvaluationException; +import ch.qos.logback.core.boolex.EventEvaluatorBase; + +public class MatchHelloEvaluator extends EventEvaluatorBase { + + String checkForInclusion; + + public void start() { + if (checkForInclusion != null) { + start(); + } + } + + public boolean evaluate(ILoggingEvent event) throws NullPointerException, EvaluationException { + if (!isStarted()) { + return false; + } + + String message = event.getMessage(); + boolean result = message.contains(checkForInclusion); + return result; + } + + public String getCheckForInclusion() { + return checkForInclusion; + } + + public void setCheckForInclusion(String checkForInclusion) { + this.checkForInclusion = checkForInclusion; + } + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/html/XHTMLEntityResolver.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/html/XHTMLEntityResolver.java new file mode 100644 index 0000000000..6d481485b5 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/html/XHTMLEntityResolver.java @@ -0,0 +1,52 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.html; + +import java.io.InputStream; +import java.util.HashMap; +import java.util.Map; + +import org.xml.sax.EntityResolver; +import org.xml.sax.InputSource; + +public class XHTMLEntityResolver implements EntityResolver { + + // key: public id, value: relative path to DTD file + static Map entityMap = new HashMap(); + + static { + entityMap.put("-//W3C//DTD XHTML 1.0 Strict//EN", "/dtd/xhtml1-strict.dtd"); + entityMap.put("-//W3C//ENTITIES Latin 1 for XHTML//EN", "/dtd/xhtml-lat1.ent"); + entityMap.put("-//W3C//ENTITIES Symbols for XHTML//EN", "/dtd/xhtml-symbol.ent"); + entityMap.put("-//W3C//ENTITIES Special for XHTML//EN", "/dtd/xhtml-special.ent"); + } + + public InputSource resolveEntity(String publicId, String systemId) { + // System.out.println(publicId); + final String relativePath = (String) entityMap.get(publicId); + + if (relativePath != null) { + Class clazz = getClass(); + InputStream in = clazz.getResourceAsStream(relativePath); + if (in == null) { + return null; + } else { + return new InputSource(in); + } + } else { + return null; + } + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingRunnable.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingRunnable.java similarity index 81% rename from logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingRunnable.java rename to logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingRunnable.java index 2b8f6f4226..96d5085ada 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingRunnable.java +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingRunnable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,10 +11,10 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.issue.lbclassic135; +package ch.qos.logback.classic.blackbox.issue.lbclassic135; import org.slf4j.Logger; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; public class LoggingRunnable extends RunnableWithCounterAndDone { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingToFileThroughput.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingToFileThroughput.java old mode 100755 new mode 100644 similarity index 79% rename from logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingToFileThroughput.java rename to logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingToFileThroughput.java index d9f61c71c4..82717cf0fe --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/LoggingToFileThroughput.java +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/issue/lbclassic135/LoggingToFileThroughput.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,14 +11,15 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.issue.lbclassic135; +package ch.qos.logback.classic.blackbox.issue.lbclassic135; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.contention.ThreadedThroughputCalculator; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; +//import ch.qos.logback.core.contention.ThreadedThroughputCalculator; /** * Short sample code testing the throughput of a fair lock. @@ -32,19 +33,20 @@ public class LoggingToFileThroughput { public static void main(String args[]) throws InterruptedException { - ThreadedThroughputCalculator tp = new ThreadedThroughputCalculator(OVERALL_DURATION_IN_MILLIS); - tp.printEnvironmentInfo("lbclassic135 LoggingToFileThrouhput"); - - LoggerContext lc = new LoggerContext(); - Logger logger = buildLoggerContext(lc); - - for (int i = 0; i < 2; i++) { - tp.execute(buildArray(logger)); - } - - tp.execute(buildArray(logger)); - tp.printThroughput("File: "); - lc.stop(); +// ThreadedThroughputCalculator tp = new ThreadedThroughputCalculator(OVERALL_DURATION_IN_MILLIS); +// tp.printEnvironmentInfo("lbclassic135 LoggingToFileThrouhput"); +// +// LoggerContext lc = new LoggerContext(); +// Logger logger = buildLoggerContext(lc); +// +// for (int i = 0; i < 2; i++) { +// tp.execute(buildArray(logger)); +// } +// +// RunnableWithCounterAndDone[] runnnableArray = buildArray(logger); +// tp.execute(runnnableArray); +// tp.printThroughput(runnnableArray, "File: "); +// lc.stop(); } static Logger buildLoggerContext(LoggerContext lc) { diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/BlackboxJoranConfiguratorTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/BlackboxJoranConfiguratorTest.java new file mode 100644 index 0000000000..14c9b36a59 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/BlackboxJoranConfiguratorTest.java @@ -0,0 +1,229 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.jul.JULHelper; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.DefaultJoranConfigurator; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.testUtil.StringListAppender; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import static org.junit.jupiter.api.Assertions.*; + +public class BlackboxJoranConfiguratorTest { + + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + StatusChecker checker = new StatusChecker(loggerContext); + int diff = RandomUtil.getPositiveInt(); + + void configure(String file) throws JoranException { + loggerContext.setMDCAdapter(logbackMDCAdapter); + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + loggerContext.putProperty("diff", "" + diff); + jc.doConfigure(file); + } + + + @Test + public void eval() throws JoranException { + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "callerData.xml"); + String msg = "hello world"; + logger.debug("toto"); + logger.debug(msg); + + StringListAppender slAppender = (StringListAppender) loggerContext + .getLogger("root").getAppender("STR_LIST"); + assertNotNull(slAppender); + assertEquals(2, slAppender.strList.size()); + assertTrue(slAppender.strList.get(0).contains(" DEBUG - toto")); + + String str1 = slAppender.strList.get(1); + assertTrue(str1.contains("Caller+0")); + assertTrue(str1.contains(" DEBUG - hello world")); + } + + @Disabled + @Test + public void testEvaluatorFilter() throws JoranException { + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilter.xml"); + + // StatusPrinter.print(loggerContext); + + logger.warn("hello"); + logger.error("to be ignored"); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + + assertNotNull(listAppender); + assertEquals(1, listAppender.list.size()); + ILoggingEvent back = listAppender.list.get(0); + assertEquals(Level.WARN, back.getLevel()); + assertEquals("hello", back.getMessage()); + } + + @Disabled + @Test + public void testEvaluatorFilterWithImports() throws JoranException { + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilterWithImports.xml"); + + // StatusPrinter.print(loggerContext); + + logger.warn("hello"); + logger.error("to be ignored"); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + + assertNotNull(listAppender); + assertEquals(1, listAppender.list.size()); + ILoggingEvent back = listAppender.list.get(0); + assertEquals(Level.WARN, back.getLevel()); + assertEquals("hello", back.getMessage()); + } + + @Test + public void conditional1673() throws JoranException { + loggerContext.putProperty("EXTRA", "true"); + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673.xml"; + configure(configFileAsStr); + } + + @Test + public void conditional1673bisWithActiveThen() throws JoranException { + loggerContext.putProperty("EXTRA", "true"); + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673bis.xml"; + configure(configFileAsStr); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ListAppender listThen = (ListAppender) root.getAppender("LIST_THEN"); + assertNotNull(listThen); + + ListAppender listElse = (ListAppender) root.getAppender("LIST_ELSE"); + assertNull(listElse); + } + + @Test + public void conditional1673bisWithActiveElse() throws JoranException { + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1673bis.xml"; + configure(configFileAsStr); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ListAppender listThen = (ListAppender) root.getAppender("LIST_THEN"); + assertNull(listThen); + + ListAppender listElse = (ListAppender) root.getAppender("LIST_ELSE"); + assertNotNull(listElse); + } + + @Test + public void nestedIf() throws JoranException { + loggerContext.putProperty("EXTRA", "true"); + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1678.xml"; + configure(configFileAsStr); + StatusPrinter.print(loggerContext); + + } + + @Test + public void levelChangePropagator0() throws JoranException, IOException, InterruptedException { + String loggerName = "changePropagator0" + diff; + java.util.logging.Logger.getLogger(loggerName).setLevel(java.util.logging.Level.INFO); + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator0.xml"; + configure(configFileAsStr); + + checker.assertIsErrorFree(); + verifyJULLevel(loggerName, null); + verifyJULLevel("a.b.c." + diff, Level.WARN); + verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE); + } + + @Test + public void levelChangePropagator1() throws JoranException, IOException, InterruptedException { + String loggerName = "changePropagator1" + diff; + java.util.logging.Logger logger1 = java.util.logging.Logger.getLogger(loggerName); + logger1.setLevel(java.util.logging.Level.INFO); + verifyJULLevel(loggerName, Level.INFO); + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator1.xml"; + configure(configFileAsStr); + + checker.assertIsErrorFree(); + verifyJULLevel(loggerName, Level.INFO); // + verifyJULLevel("a.b.c." + diff, Level.WARN); + verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE); + } + + void verifyJULLevel(String loggerName, Level expectedLevel) { + java.util.logging.Logger julLogger = JULHelper.asJULLogger(loggerName); + java.util.logging.Level julLevel = julLogger.getLevel(); + + if (expectedLevel == null) { + assertNull(julLevel); + } else { + assertEquals(JULHelper.asJULLevel(expectedLevel), julLevel); + } + } + + // See https://github.com/qos-ch/logback/issues/1001 + // See https://github.com/qos-ch/logback/issues/997 + @Test + public void fileAsResource() throws JoranException, IOException, InterruptedException { + JoranConfigurator joranConfigurator = new JoranConfigurator(); + joranConfigurator.setContext(loggerContext); + ClassLoader classLoader = Loader.getClassLoaderOfObject(joranConfigurator); + + + String logbackConfigFile = "asResource/topFile.xml"; + // asResource/topFile.xml + // + // + // + + // inner1.xml + // + // + // + + URL aURL = Loader.getResource(logbackConfigFile, classLoader); + InputStream inputStream = aURL.openStream(); + assertNotNull(inputStream); + joranConfigurator.doConfigure(inputStream); + + assertEquals(Level.ERROR, root.getLevel()); + + //StatusPrinter.print(loggerContext); + inputStream.close(); + checker.assertContainsMatch("Scan attribute not set or set to unrecognized value."); + checker.assertIsWarningOrErrorFree(); + } + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ChangeDetectedListener.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ChangeDetectedListener.java new file mode 100644 index 0000000000..81ddf601e1 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ChangeDetectedListener.java @@ -0,0 +1,48 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; + +import java.util.concurrent.CountDownLatch; + +class ChangeDetectedListener implements ConfigurationEventListener { + + CountDownLatch countDownLatch; + + ReconfigureOnChangeTask reconfigureOnChangeTask; + + ChangeDetectedListener(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch (configurationEvent.getEventType()) { + case CHANGE_DETECTED: + //System.out.println(this.toString() + "#listen Change detected " + configurationEvent +" count="+countDownLatch.getCount()); + + countDownLatch.countDown(); + Object data = configurationEvent.getData(); + if (data instanceof ReconfigureOnChangeTask) { + reconfigureOnChangeTask = (ReconfigureOnChangeTask) data; + } + break; + default: + } + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/PartialConfigurationEndedSuccessfullyEventListener.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/PartialConfigurationEndedSuccessfullyEventListener.java new file mode 100644 index 0000000000..6bad4b0e55 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/PartialConfigurationEndedSuccessfullyEventListener.java @@ -0,0 +1,41 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; + +import java.util.concurrent.CountDownLatch; + +class PartialConfigurationEndedSuccessfullyEventListener implements ConfigurationEventListener { + + CountDownLatch countDownLatch; + + PartialConfigurationEndedSuccessfullyEventListener(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch (configurationEvent.getEventType()) { + case PARTIAL_CONFIGURATION_ENDED_SUCCESSFULLY: + //System.out.println(this.toString() + "#listen PARTIAL_CONFIGURATION_ENDED_SUCCESSFULLY detected " + configurationEvent +" count="+countDownLatch.getCount()); + + countDownLatch.countDown(); + break; + default: + } + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationDoneListener.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationDoneListener.java new file mode 100644 index 0000000000..b7c8de6c9c --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationDoneListener.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; + +import java.util.concurrent.CountDownLatch; + +class ReconfigurationDoneListener implements ConfigurationEventListener { + CountDownLatch countDownLatch; + + ReconfigureOnChangeTask roct; + + ReconfigurationDoneListener(CountDownLatch countDownLatch, ReconfigureOnChangeTask aRoct) { + this.countDownLatch = countDownLatch; + this.roct = aRoct; + } + + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch (configurationEvent.getEventType()) { + case CONFIGURATION_ENDED_SUCCESSFULLY: + if(roct == null) { + countDownLatch.countDown(); + } else { + Object data = configurationEvent.getData(); + if(data instanceof ReconfigureOnChangeTask && roct == data) { + countDownLatch.countDown(); + } + } + break; + default: + } + + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationTaskRegisteredConfigEventListener.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationTaskRegisteredConfigEventListener.java new file mode 100644 index 0000000000..877bc1a8a1 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigurationTaskRegisteredConfigEventListener.java @@ -0,0 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; + +public class ReconfigurationTaskRegisteredConfigEventListener implements ConfigurationEventListener { + + boolean changeDetectorRegisteredEventOccurred = false; + ReconfigureOnChangeTask reconfigureOnChangeTask; + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch (configurationEvent.getEventType()) { + case CHANGE_DETECTOR_REGISTERED: + changeDetectorRegisteredEventOccurred = true; + Object data = configurationEvent.getData(); + if(data instanceof ReconfigureOnChangeTask) + reconfigureOnChangeTask = (ReconfigureOnChangeTask) data; + break; + default: + } + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskHarness.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskHarness.java new file mode 100644 index 0000000000..fb901d7077 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskHarness.java @@ -0,0 +1,47 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness; +import ch.qos.logback.core.status.InfoStatus; + +import java.util.concurrent.CountDownLatch; + +class ReconfigureOnChangeTaskHarness extends AbstractMultiThreadedHarness { + + private final LoggerContext loggerContext; + + private final CountDownLatch countDownLatch; + + ReconfigureOnChangeTaskHarness(LoggerContext loggerContext, int aChangeCountLimit) { + this.loggerContext = loggerContext; + this.countDownLatch = new CountDownLatch(aChangeCountLimit); + ChangeDetectedListener cdl = new ChangeDetectedListener(countDownLatch); + loggerContext.addConfigurationEventListener(cdl); + } + + public void waitUntilEndCondition() throws InterruptedException { + + String classname = this.getClass().getSimpleName(); + + loggerContext.getStatusManager() + .add(new InfoStatus("Entering " + classname + ".waitUntilEndCondition()", this)); + countDownLatch.await(); + loggerContext.getStatusManager() + .add(new InfoStatus("*****Exiting " + classname + ".waitUntilEndCondition()", this)); + } + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskTest.java new file mode 100644 index 0000000000..2392988838 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureOnChangeTaskTest.java @@ -0,0 +1,443 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.blackbox.issue.lbclassic135.LoggingRunnable; +import ch.qos.logback.classic.joran.*; +import ch.qos.logback.classic.model.processor.ConfigurationModelHandlerFull; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; +import ch.qos.logback.core.joran.spi.ConfigurationWatchList; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; +import ch.qos.logback.core.status.InfoStatus; +import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.WarnStatus; +import ch.qos.logback.core.testUtil.CoreTestConstants; +import ch.qos.logback.core.testUtil.FileTestUtil; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.*; + +import java.io.*; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +import static ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants.JORAN_INPUT_PREFIX; +import static ch.qos.logback.classic.joran.ReconfigureOnChangeTask.*; +import static org.junit.jupiter.api.Assertions.*; + +public class ReconfigureOnChangeTaskTest extends ReconfigureTaskTestSupport { + final static int THREAD_COUNT = 5; + + final static int TIMEOUT = 4; + final static int TIMEOUT_LONG = 10; + + // the space in the file name mandated by + // http://jira.qos.ch/browse/LOGBACK-67 + final static String SCAN1_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan 1.xml"; + + final static String SCAN_LOGBACK_474_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan_logback_474.xml"; + + final static String INCLUSION_SCAN_TOPLEVEL0_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/topLevel0.xml"; + + final static String INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/topByResource.xml"; + + final static String INCLUSION_SCAN_INNER0_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/inner0.xml"; + + final static String INCLUSION_SCAN_INNER1_AS_STR = "target/test-classes/asResource/inner1.xml"; + + private static final String SCAN_PERIOD_DEFAULT_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan_period_default.xml"; + + Logger logger = loggerContext.getLogger(this.getClass()); + StatusChecker statusChecker = new StatusChecker(loggerContext); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + + + @BeforeAll + static public void classSetup() { + FileTestUtil.makeTestOutputDir(); + } + + @BeforeEach + public void before() { + loggerContext.setName("ROCTTest-context" + diff); + } + + void configure(File file) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + jc.doConfigure(file); + } + + @Test + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + public void checkBasicLifecyle() throws JoranException, IOException, InterruptedException { + File file = new File(SCAN1_FILE_AS_STR); + configure(file); + List fileList = getConfigurationWatchList(loggerContext); + assertThatListContainsFile(fileList, file); + checkThatTaskHasRan(); + checkThatTaskCanBeStopped(); + } + + private void checkThatTaskCanBeStopped() { + ScheduledFuture future = loggerContext.getCopyOfScheduledFutures().get(0); + loggerContext.stop(); + assertTrue(future.isCancelled()); + } + + private void checkThatTaskHasRan() throws InterruptedException { + waitForReconfigureOnChangeTaskToRun(); + } + + List getConfigurationWatchList(LoggerContext lc) { + ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(lc); + return configurationWatchList.getCopyOfFileWatchList(); + } + + @Test + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + public void scanWithFileInclusion() throws JoranException, IOException, InterruptedException { + File topLevelFile = new File(INCLUSION_SCAN_TOPLEVEL0_AS_STR); + File innerFile = new File(INCLUSION_SCAN_INNER0_AS_STR); + configure(topLevelFile); + List fileList = getConfigurationWatchList(loggerContext); + assertThatListContainsFile(fileList, topLevelFile); + assertThatListContainsFile(fileList, innerFile); + checkThatTaskHasRan(); + checkThatTaskCanBeStopped(); + } + + @Test + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + public void scanWithResourceInclusion() throws JoranException, IOException, InterruptedException { + File topLevelFile = new File(INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR); + File innerFile = new File(INCLUSION_SCAN_INNER1_AS_STR); + configure(topLevelFile); + List fileList = getConfigurationWatchList(loggerContext); + assertThatListContainsFile(fileList, topLevelFile); + assertThatListContainsFile(fileList, innerFile); + } + + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + @Test + public void propertiesConfigurationTest() throws IOException, JoranException, InterruptedException { + String loggerName = "abc"; + String propertiesFileStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "roct-" + diff + ".properties"; + File propertiesFile = new File(propertiesFileStr); + String configurationStr = ""; + writeToFile(propertiesFile, PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + loggerName+"=INFO"); + configure(asBAIS(configurationStr)); + Logger abcLogger = loggerContext.getLogger(loggerName); + assertEquals(Level.INFO, abcLogger.getLevel()); + + CountDownLatch changeDetectedLatch0 = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch0 = registerPartialConfigurationEndedSuccessfullyEventListener(); + + writeToFile(propertiesFile, PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + loggerName+"=WARN"); + changeDetectedLatch0.await(); + configurationDoneLatch0.await(); + assertEquals(Level.WARN, abcLogger.getLevel()); + + CountDownLatch changeDetectedLatch1 = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch1 = registerPartialConfigurationEndedSuccessfullyEventListener(); + writeToFile(propertiesFile, PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + loggerName+"=ERROR"); + changeDetectedLatch1.await(); + configurationDoneLatch1.await(); + + assertEquals(Level.ERROR, abcLogger.getLevel()); + + } + + @Disabled + @Test + void propertiesFromHTTPS() throws InterruptedException, UnsupportedEncodingException, JoranException { + String loggerName = "com.bazinga"; + String propertiesURLStr = "https://www.qos.ch/foo.properties"; + Logger aLogger = loggerContext.getLogger(loggerName); + String configurationStr = ""; + + configure(asBAIS(configurationStr)); + assertEquals(Level.WARN, aLogger.getLevel()); + System.out.println("first phase OK"); + CountDownLatch changeDetectedLatch0 = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch0 = registerPartialConfigurationEndedSuccessfullyEventListener(); + + changeDetectedLatch0.await(); + System.out.println("after changeDetectedLatch0.await();"); + configurationDoneLatch0.await(); + assertEquals(Level.ERROR, aLogger.getLevel()); + } + + // See also http://jira.qos.ch/browse/LOGBACK-338 + @Test + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + public void reconfigurationIsNotPossibleInTheAbsenceOfATopFile() throws IOException, JoranException, InterruptedException { + + ReconfigurationTaskRegisteredConfigEventListener listener = new ReconfigurationTaskRegisteredConfigEventListener(); + loggerContext.addConfigurationEventListener(listener); + String configurationStr = ""; + configure(asBAIS(configurationStr)); + + ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); + + assertNotNull(configurationWatchList); + assertFalse(ConfigurationWatchListUtil.watchPredicateFulfilled(loggerContext)); + statusChecker.containsMatch(Status.WARN, ConfigurationModelHandlerFull.FAILED_WATCH_PREDICATE_MESSAGE_1); + + assertFalse(listener.changeDetectorRegisteredEventOccurred); + assertEquals(0, loggerContext.getCopyOfScheduledFutures().size()); + } + + @Test + @Timeout(value = TIMEOUT, unit = TimeUnit.SECONDS) + public void fallbackToSafe_FollowedByRecovery() throws IOException, JoranException, InterruptedException { + addInfo("Start fallbackToSafe_FollowedByRecovery", this); + String path = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_fallbackToSafe-" + diff + ".xml"; + File topLevelFile = new File(path); + writeToFile(topLevelFile, " "); + + addResetResistantOnConsoleStatusListener(); + configure(topLevelFile); + //statusPrinter2.print(loggerContext); + CountDownLatch changeDetectedLatch = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch = registerNewReconfigurationDoneSuccessfullyListener(); + + String badXML = "\n" + " "; + writeToFile(topLevelFile, badXML); + changeDetectedLatch.await(); + configurationDoneLatch.await(); + + statusChecker.assertContainsMatch(Status.ERROR, CoreConstants.XML_PARSING); + statusChecker.assertContainsMatch(Status.WARN, FALLING_BACK_TO_SAFE_CONFIGURATION); + statusChecker.assertContainsMatch(Status.INFO, RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION); + + // clear required for checks down below + loggerContext.getStatusManager().clear(); + + addInfo("RECOVERY - Writing error-free config file ", this); + CountDownLatch secondConfigEndedLatch = registerNewReconfigurationDoneSuccessfullyListener(); + + // recovery + writeToFile(topLevelFile, " "); + + try { + addInfo("Awaiting secondConfigEndedLatch ", this); + secondConfigEndedLatch.await(5, TimeUnit.SECONDS); + addInfo("after secondConfigEndedLatch.await ", this); + statusChecker.assertIsErrorFree(); + statusChecker.containsMatch(DETECTED_CHANGE_IN_CONFIGURATION_FILES); + } finally { + //StatusPrinter.print(loggerContext); + } + } + + private void addResetResistantOnConsoleStatusListener() { + // enable when debugging + if(1==1) + return; + OnConsoleStatusListener ocs = new OnConsoleStatusListener(); + ocs.setContext(loggerContext); + ocs.setResetResistant(true); + ocs.start(); + loggerContext.getStatusManager().add(ocs); + } + + @Test + @Timeout(value = TIMEOUT_LONG, unit = TimeUnit.SECONDS) + public void fallbackToSafeWithIncludedFile_FollowedByRecovery() throws IOException, JoranException, InterruptedException, ExecutionException { + String topLevelFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_top-" + diff + ".xml"; + String innerFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_inner-" + diff + ".xml"; + File topLevelFile = new File(topLevelFileAsStr); + writeToFile(topLevelFile, + " "); + + File innerFile = new File(innerFileAsStr); + writeToFile(innerFile, " "); + addResetResistantOnConsoleStatusListener(); + + ReconfigurationTaskRegisteredConfigEventListener roctRegisteredListener = new ReconfigurationTaskRegisteredConfigEventListener(); + loggerContext.addConfigurationEventListener(roctRegisteredListener); + + configure(topLevelFile); + + ReconfigureOnChangeTask roct = roctRegisteredListener.reconfigureOnChangeTask; + + + CountDownLatch changeDetectedLatch = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch = registerNewReconfigurationDoneSuccessfullyListener(roct); + + writeToFile(innerFile, "\n\n"); + changeDetectedLatch.await(); + configurationDoneLatch.await(); + addInfo("Woke from configurationDoneLatch.await()", this); + + statusChecker.assertContainsMatch(Status.ERROR, CoreConstants.XML_PARSING); + statusChecker.assertContainsMatch(Status.WARN, FALLING_BACK_TO_SAFE_CONFIGURATION); + statusChecker.assertContainsMatch(Status.INFO, RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION); + + //statusPrinter2.print(loggerContext); + + loggerContext.getStatusManager().clear(); + + CountDownLatch secondDoneLatch = registerNewReconfigurationDoneSuccessfullyListener(); + writeToFile(innerFile, " "); + secondDoneLatch.await(); + + statusChecker.assertIsErrorFree(); + statusChecker.containsMatch(DETECTED_CHANGE_IN_CONFIGURATION_FILES); + + } + + CountDownLatch registerNewReconfigurationDoneSuccessfullyListener() { + return registerNewReconfigurationDoneSuccessfullyListener(null); + } + + CountDownLatch registerNewReconfigurationDoneSuccessfullyListener(ReconfigureOnChangeTask roct) { + CountDownLatch latch = new CountDownLatch(1); + ReconfigurationDoneListener reconfigurationDoneListener = new ReconfigurationDoneListener(latch, roct); + loggerContext.addConfigurationEventListener(reconfigurationDoneListener); + return latch; + } + + class RunMethodInvokedListener implements ConfigurationEventListener { + CountDownLatch countDownLatch; + ReconfigureOnChangeTask reconfigureOnChangeTask; + + RunMethodInvokedListener(CountDownLatch countDownLatch) { + this.countDownLatch = countDownLatch; + } + + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch (configurationEvent.getEventType()) { + case CHANGE_DETECTOR_RUNNING: + countDownLatch.countDown(); + Object data = configurationEvent.getData(); + if (data instanceof ReconfigureOnChangeTask) { + reconfigureOnChangeTask = (ReconfigureOnChangeTask) data; + } + break; + default: + } + } + } + + private ReconfigureOnChangeTask waitForReconfigureOnChangeTaskToRun() throws InterruptedException { + addInfo("entering waitForReconfigureOnChangeTaskToRun", this); + + CountDownLatch countDownLatch = new CountDownLatch(1); + RunMethodInvokedListener runMethodInvokedListener = new RunMethodInvokedListener(countDownLatch); + + loggerContext.addConfigurationEventListener(runMethodInvokedListener); + countDownLatch.await(); + return runMethodInvokedListener.reconfigureOnChangeTask; + } + + private RunnableWithCounterAndDone[] buildRunnableArray(File configFile, UpdateType updateType) { + RunnableWithCounterAndDone[] rArray = new RunnableWithCounterAndDone[THREAD_COUNT]; + rArray[0] = new UpdaterRunnable(this, configFile, updateType); + for (int i = 1; i < THREAD_COUNT; i++) { + rArray[i] = new LoggingRunnable(logger); + } + return rArray; + } + + @Test + public void checkReconfigureTaskScheduledWhenDefaultScanPeriodUsed() throws JoranException { + File file = new File(SCAN_PERIOD_DEFAULT_FILE_AS_STR); + configure(file); + + final List> scheduledFutures = loggerContext.getCopyOfScheduledFutures(); + //StatusPrinter.print(loggerContext); + assertFalse(scheduledFutures.isEmpty()); + statusChecker.containsMatch("No 'scanPeriod' specified. Defaulting to"); + + } + + // check for deadlocks + @Test + @Timeout(value = 4, unit = TimeUnit.SECONDS) + public void scan_LOGBACK_474() throws JoranException, IOException, InterruptedException { + File file = new File(SCAN_LOGBACK_474_FILE_AS_STR); + addResetResistantOnConsoleStatusListener(); + configure(file); + + int expectedResets = 2; + ReconfigureOnChangeTaskHarness harness = new ReconfigureOnChangeTaskHarness(loggerContext, expectedResets); + + RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(file, UpdateType.TOUCH); + harness.execute(runnableArray); + + addInfo("scan_LOGBACK_474 end of execution ", this); + checkResetCount(expectedResets); + } + + private void assertThatListContainsFile(List fileList, File file) { + // conversion to absolute file seems to work nicely + assertTrue(fileList.contains(file.getAbsoluteFile())); + } + + private void checkResetCount(int expected) { + StatusChecker checker = new StatusChecker(loggerContext); + checker.assertIsErrorFree(); + + int effectiveResets = checker.matchCount(CoreConstants.RESET_MSG_PREFIX); + assertEquals(expected, effectiveResets); + + // String failMsg = "effective=" + effectiveResets + ", expected=" + expected; + // + // there might be more effective resets than the expected amount + // since the harness may be sleeping while a reset occurs + // assertTrue(failMsg, expected <= effectiveResets && (expected + 2) >= + // effectiveResets); + + } + + void addInfo(String msg, Object o) { + loggerContext.getStatusManager().add(new InfoStatus(msg, o)); + } + + void addWarn(String msg, Object o) { + loggerContext.getStatusManager().add(new WarnStatus(msg, o)); + } + + enum UpdateType { + TOUCH, MALFORMED, MALFORMED_INNER + } + + void writeToFile(File file, String contents) throws IOException { + FileWriter fw = new FileWriter(file); + fw.write(contents); + fw.close(); + // on linux changes to last modified are not propagated if the + // time stamp is near the previous time stamp hence the random delta + boolean success = file.setLastModified(System.currentTimeMillis() + RandomUtil.getPositiveInt()); + if (!success) { + addWarn("failed to setLastModified on file " + file, this); + } + } + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureTaskTestSupport.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureTaskTestSupport.java new file mode 100644 index 0000000000..a0a212d288 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/ReconfigureTaskTestSupport.java @@ -0,0 +1,75 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.blackbox.joran.spi.ConfigFileServlet; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.spi.HttpUtil; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.testUtil.RandomUtil; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.util.concurrent.CountDownLatch; + +public class ReconfigureTaskTestSupport { + + protected int diff = RandomUtil.getPositiveInt(); + protected LoggerContext loggerContext = new LoggerContext(); + + protected void configure(InputStream is) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + jc.doConfigure(is); + } + + protected CountDownLatch registerChangeDetectedListener() { + CountDownLatch latch = new CountDownLatch(1); + ChangeDetectedListener changeDetectedListener = new ChangeDetectedListener(latch); + loggerContext.addConfigurationEventListener(changeDetectedListener); + return latch; + } + + protected static ByteArrayInputStream asBAIS(String configurationStr) throws UnsupportedEncodingException { + return new ByteArrayInputStream(configurationStr.getBytes("UTF-8")); + } + + protected String get(String urlString) throws MalformedURLException { + HttpUtil httpGetUtil = new HttpUtil(HttpUtil.RequestMethod.GET, urlString); + HttpURLConnection getConnection = httpGetUtil.connectTextTxt(); + String response = httpGetUtil.readResponse(getConnection); + return response; + } + + protected String post(String urlString, String val) throws MalformedURLException { + HttpUtil httpPostUtil1 = new HttpUtil(HttpUtil.RequestMethod.POST, urlString); + HttpURLConnection postConnection1 = httpPostUtil1.connectTextTxt(); + httpPostUtil1.post(postConnection1, ConfigFileServlet.CONTENT_KEY+ CoreConstants.EQUALS_CHAR+val); + String response = httpPostUtil1.readResponse(postConnection1); + return response; + } + + protected CountDownLatch registerPartialConfigurationEndedSuccessfullyEventListener() { + CountDownLatch latch = new CountDownLatch(1); + PartialConfigurationEndedSuccessfullyEventListener listener = new PartialConfigurationEndedSuccessfullyEventListener(latch); + loggerContext.addConfigurationEventListener(listener); + return latch; + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/RecursivelyLoggingAppender474.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/RecursivelyLoggingAppender474.java new file mode 100644 index 0000000000..75200d14a0 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/RecursivelyLoggingAppender474.java @@ -0,0 +1,40 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.Logger; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +/** + * An appender which calls logback recursively + * + * @author Ralph Goers + */ + +public class RecursivelyLoggingAppender474 extends AppenderBase { + + Logger logger; + + public void start() { + super.start(); + logger = ((LoggerContext) getContext()).getLogger("Ignore"); + } + + protected void append(ILoggingEvent eventObject) { + logger.debug("Ignore this"); + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/StatusChecker.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/StatusChecker.java new file mode 100644 index 0000000000..38477e03d6 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/StatusChecker.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.status.StatusUtil; +import org.junit.jupiter.api.Assertions; + +public class StatusChecker extends StatusUtil { + + public StatusChecker(StatusManager sm) { + super(sm); + } + + public StatusChecker(Context context) { + super(context); + } + + public void assertContainsMatch(int level, String regex) { + Assertions.assertTrue(containsMatch(level, regex)); + } + + public void assertNoMatch(String regex) { + Assertions.assertFalse(containsMatch(regex)); + } + + public void assertContainsMatch(String regex) { + Assertions.assertTrue(containsMatch(regex)); + } + + public void assertContainsException(Class scanExceptionClass) { + Assertions.assertTrue(containsException(scanExceptionClass)); + } + + public void assertContainsException(Class scanExceptionClass, String msg) { + Assertions.assertTrue(containsException(scanExceptionClass, msg)); + } + + public void assertIsErrorFree() { + Assertions.assertTrue(isErrorFree(0)); + } + + public void assertIsErrorFree(long treshhold) { + Assertions.assertTrue(isErrorFree(treshhold)); + } + + public void assertIsWarningOrErrorFree() { + Assertions.assertTrue(isWarningOrErrorFree(0)); + } + + public void assertErrorCount(int i) { + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/UpdaterRunnable.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/UpdaterRunnable.java new file mode 100644 index 0000000000..03fa98010f --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/UpdaterRunnable.java @@ -0,0 +1,97 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran; + +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; + +import java.io.File; +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.fail; + +class UpdaterRunnable extends RunnableWithCounterAndDone { + private final ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest; + File configFile; + ReconfigureOnChangeTaskTest.UpdateType updateType; + + // it actually takes time for Windows to propagate file modification changes + // values below 100 milliseconds can be problematic the same propagation + // latency occurs in Linux but is even larger (>600 ms) + // final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 60; + + final int sleepBetweenUpdates = 100; + + UpdaterRunnable(ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest, File configFile, ReconfigureOnChangeTaskTest.UpdateType updateType) { + this.reconfigureOnChangeTaskTest = reconfigureOnChangeTaskTest; + this.configFile = configFile; + this.updateType = updateType; + } + + UpdaterRunnable(ReconfigureOnChangeTaskTest reconfigureOnChangeTaskTest, File configFile) { + this(reconfigureOnChangeTaskTest, configFile, ReconfigureOnChangeTaskTest.UpdateType.TOUCH); + } + + public void run() { + while (!isDone()) { + try { + Thread.sleep(sleepBetweenUpdates); + } catch (InterruptedException e) { + } + if (isDone()) { + reconfigureOnChangeTaskTest.addInfo("Exiting Updater.run()", this); + return; + } + counter++; + reconfigureOnChangeTaskTest.addInfo("Touching [" + configFile + "]", this); + switch (updateType) { + case TOUCH: + touchFile(); + break; + case MALFORMED: + try { + malformedUpdate(); + } catch (IOException e) { + e.printStackTrace(); + fail("malformedUpdate failed"); + } + break; + case MALFORMED_INNER: + try { + malformedInnerUpdate(); + } catch (IOException e) { + e.printStackTrace(); + fail("malformedInnerUpdate failed"); + } + } + } + reconfigureOnChangeTaskTest.addInfo("Exiting Updater.run()", this); + } + + private void malformedUpdate() throws IOException { + reconfigureOnChangeTaskTest.writeToFile(configFile, + "\n" + " \n" + ""); + } + + private void malformedInnerUpdate() throws IOException { + reconfigureOnChangeTaskTest.writeToFile(configFile, "\n" + " \n" + ""); + } + + void touchFile() { + + boolean result = configFile.setLastModified(System.currentTimeMillis()); + if (!result) + reconfigureOnChangeTaskTest.addWarn(this.getClass().getName() + ".touchFile on " + configFile.toString() + " FAILED", this); + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/conditional/ConditionalTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/conditional/ConditionalTest.java new file mode 100644 index 0000000000..6d857b8f43 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/conditional/ConditionalTest.java @@ -0,0 +1,210 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.blackbox.joran.conditional; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.sift.SiftingAppender; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.sift.AppenderTracker; +import ch.qos.logback.core.status.StatusUtil; +import ch.qos.logback.core.testUtil.CoreTestConstants; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ConditionalTest { + + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + + Logger logger = loggerContext.getLogger(this.getClass().getName()); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + + StatusUtil checker = new StatusUtil(loggerContext); + int diff = RandomUtil.getPositiveInt(); + String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; + + @BeforeEach + public void setUp() throws UnknownHostException { + loggerContext.setMDCAdapter(logbackMDCAdapter); + loggerContext.setName("c" + diff); + loggerContext.putProperty("randomOutputDir", randomOutputDir); + } + + @AfterEach + public void tearDown() { + StatusPrinter.printIfErrorsOccured(loggerContext); + } + + void configure(String file) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + jc.doConfigure(file); + } + + @SuppressWarnings("rawtypes") + @Test + public void conditionalConsoleApp_IF_THEN_True() throws JoranException, IOException, InterruptedException { + InetAddress localhost = InetAddress.getLocalHost(); + System.out.println("In conditionalConsoleApp_IF_THEN_True, canonicalHostName=\"" + + localhost.getCanonicalHostName() + "] and hostNmae=\"" + localhost.getHostName() + "\""); + loggerContext.putProperty("aHost", localhost.getHostName()); + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp.xml"; + configure(configFileAsStr); + FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); + assertNotNull(fileAppender); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNotNull(consoleAppender); + assertTrue(checker.isErrorFree(0)); + } + + @SuppressWarnings("rawtypes") + @Test + public void conditionalConsoleApp_IF_THEN_False() throws JoranException, IOException, InterruptedException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp.xml"; + configure(configFileAsStr); + FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); + assertNotNull(fileAppender); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNull(consoleAppender); + assertTrue(checker.isErrorFree(0)); + } + + @SuppressWarnings("rawtypes") + @Test + public void conditionalConsoleApp_IF_THEN_ELSE() throws JoranException, IOException, InterruptedException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp_ELSE.xml"; + configure(configFileAsStr); + + FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); + assertNotNull(fileAppender); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNull(consoleAppender); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + + // StatusPrinter.printIfErrorsOccured(context); + assertTrue(checker.isErrorFree(0)); + } + + @Test + public void conditionalInclusionWithExistingFile() throws JoranException, IOException, InterruptedException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + + "conditional/conditionalIncludeExistingFile.xml"; + configure(configFileAsStr); + //statusPrinter2.print(loggerContext); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNotNull(consoleAppender); + + assertTrue(checker.isErrorFree(0)); + } + + @Test + public void conditionalInclusionWithInexistentFile() throws JoranException, IOException, InterruptedException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + + "conditional/conditionalIncludeInexistentFile.xml"; + configure(configFileAsStr); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNull(consoleAppender); + assertTrue(checker.isErrorFree(0)); + } + + // https://jira.qos.ch/browse/LOGBACK-1732 + @Test + public void conditionalInclusionWithVariableDefinition() throws JoranException, IOException, InterruptedException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + + "conditional/includeWithVariableAndConditional.xml"; + configure(configFileAsStr); + + //statusPrinter2.print(loggerContext); + + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNotNull(consoleAppender); + assertTrue(checker.isErrorFree(0)); + } + + + // https://github.com/qos-ch/logback/issues/805 + @Test + public void includedWithNestedConditional() throws JoranException { + + String configFileAsStr = BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + + "conditional/includeWithInnerConditional.xml"; + + configure(configFileAsStr); + //statusPrinter2.print(loggerContext); + ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); + assertNotNull(consoleAppender); + assertTrue(checker.isErrorFree(0)); + } + + private AppenderTracker getAppenderTracker() { + SiftingAppender ha = (SiftingAppender) root.getAppender("SIFT"); + return ha.getAppenderTracker(); + } + + // see also https://jira.qos.ch/browse/LOGBACK-1713 + @Test + public void nestedWithinIfThen() throws JoranException { + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/siftNestedWithinIfThen.xml"); + //statusPrinter2.print(loggerContext); + String msg = "nestedWithinIfThen"; + logger.debug(msg); + Appender appender = getAppenderTracker().find("ifThenDefault"); + assertNotNull(appender); + ListAppender listAppender = (ListAppender) appender; + List eventList = listAppender.list; + assertEquals(1, listAppender.list.size()); + assertEquals(msg, eventList.get(0).getMessage()); + checker.isWarningOrErrorFree(0); + } + + + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigEmbeddedJetty.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigEmbeddedJetty.java new file mode 100644 index 0000000000..13a9378b23 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigEmbeddedJetty.java @@ -0,0 +1,68 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran.spi; + +import jakarta.servlet.http.HttpServlet; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.ee10.servlet.ServletContextHandler; +import org.eclipse.jetty.ee10.servlet.ServletHolder; + +import java.util.HashMap; +import java.util.Map; + +public class ConfigEmbeddedJetty { + + int port; + Server server = new Server(port); + Map servletPathMap = new HashMap<>(); + + public ConfigEmbeddedJetty(int port) { + this.port = port; + } + + public Map getServletMap() { + return servletPathMap; + } + + // new ConfigFileServlet() + public void init() throws Exception { + Server server = new Server(port); + + // Create a handler for the root context + ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS); + context.setContextPath("/"); + + + servletPathMap.forEach( (path, servlet) -> context.addServlet(new ServletHolder(servlet), path)); + + // Set the handler for the server + server.setHandler(context); + + System.out.println("Starting jetty server on port: " + port); + // Start the server + server.start(); + + System.out.println("After Jetty server start(). Joining"); + + while(!server.isStarted()) { + Thread.sleep(10); + } + System.out.println("Jetty server started"); + } + + public void stop() throws Exception { + server.stop(); + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigFileServlet.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigFileServlet.java new file mode 100644 index 0000000000..61aeee42c7 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigFileServlet.java @@ -0,0 +1,91 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran.spi; + +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; + +public class ConfigFileServlet extends HttpServlet { + + Map headers = new HashMap(); + static final String DEFAULT_CONTENT = "That was all"; + String contents; + static final String LAST_MODIFIED = "last-modified"; + public final static String CONTENT_KEY = "content"; + + public ConfigFileServlet(String contents) { + this.contents = contents; + } + + public ConfigFileServlet() { + this(DEFAULT_CONTENT); + } + + /** + * Returns data set when {@link #doPost(HttpServletRequest, HttpServletResponse)} was called + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/txt;charset=utf-8"); + + String lastModifiedHeaderValue = headers.get(LAST_MODIFIED); + if(lastModifiedHeaderValue != null) { + response.setHeader(LAST_MODIFIED, lastModifiedHeaderValue); + } + + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println(contents); + } + + /** + * Remembers posted values to be returned when {@link #doGet(HttpServletRequest, HttpServletResponse)} is called. + * @param request + * @param response + * @throws ServletException + * @throws IOException + */ + @Override + protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + Enumeration paramNames = request.getParameterNames(); + while (paramNames.hasMoreElements()) { + String paramName = paramNames.nextElement(); + String[] paramValues = request.getParameterValues(paramName); + // Handle single or multiple values for the parameter + if (paramValues.length >= 1) { + if(CONTENT_KEY.equals(paramName)) { + contents = paramValues[0]; + } else { + headers.put(paramName, paramValues[0]); + } + } + response.setStatus(HttpServletResponse.SC_OK); + response.getWriter().println(contents); + } + + + } + +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigurationWatchListTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigurationWatchListTest.java new file mode 100644 index 0000000000..44903ef393 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/joran/spi/ConfigurationWatchListTest.java @@ -0,0 +1,141 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.joran.spi; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.blackbox.joran.ReconfigureOnChangeTaskTest; +import ch.qos.logback.classic.blackbox.joran.ReconfigureTaskTestSupport; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.spi.ConfigurationWatchList; +import ch.qos.logback.core.joran.spi.HttpUtil; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.testUtil.RandomUtil; +import jakarta.servlet.http.HttpServlet; +import org.junit.jupiter.api.*; +import org.slf4j.LoggerFactory; + +import java.io.UnsupportedEncodingException; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.*; + +public class ConfigurationWatchListTest extends ReconfigureTaskTestSupport { + + static String BAZINGA_LOGGER_NAME = "com.bazinga"; + static String BAZINGA_LOGGER_SETUP_0 = "logback.logger."+BAZINGA_LOGGER_NAME+"=WARN"; + static String BAZINGA_LOGGER_SETUP_1 = "logback.logger."+BAZINGA_LOGGER_NAME+"=ERROR"; + + int randomPort = RandomUtil.getRandomServerPort(); + ConfigEmbeddedJetty configEmbeddedJetty; + ConfigurationWatchList cwl = new ConfigurationWatchList(); + static String FOO_PROPERTIES = "/foo.properties"; + String urlString = "http://127.0.0.1:"+randomPort+FOO_PROPERTIES; + + @BeforeEach + public void setUp() throws Exception { + Logger rootLogger = (Logger) LoggerFactory.getLogger( Logger.ROOT_LOGGER_NAME ); + rootLogger.setLevel(Level.INFO); + + configEmbeddedJetty = new ConfigEmbeddedJetty(randomPort); + + cwl.setContext(loggerContext); + + HttpServlet configServlet = new ConfigFileServlet(BAZINGA_LOGGER_SETUP_0); + configEmbeddedJetty.getServletMap().put(FOO_PROPERTIES, configServlet); + + configEmbeddedJetty.init(); + + } + + @AfterEach + public void tearDown() throws Exception { + configEmbeddedJetty.stop(); + } + + + + @Test + public void testInfrastructure() throws MalformedURLException { + String response = get(urlString); + assertNotNull(response); + Assertions.assertEquals(BAZINGA_LOGGER_SETUP_0, response); + + String setResponse1 = "bla bla"; + String response1 = post(urlString, setResponse1); + assertEquals(response1, setResponse1); + + String response2 = get(urlString); + assertEquals(response1, response2); + } + + @Test + public void smoke() throws MalformedURLException { + URL url = new URL(urlString); + cwl.addToWatchList(url); + URL changedURL0 = cwl.changeDetectedInURL(); + assertNull(changedURL0); + + String setResponse1 = "bla bla"; + String response1 = post(urlString, setResponse1); + assertEquals(response1, setResponse1); + + URL changedURL1 = cwl.changeDetectedInURL(); + assertEquals(urlString, changedURL1.toString()); + + URL changedURL2 = cwl.changeDetectedInURL(); + assertNull(changedURL2); + + URL changedURL3 = cwl.changeDetectedInURL(); + assertNull(changedURL3); + } + + @Disabled + @Test + public void propertiesFromHTTP() throws UnsupportedEncodingException, JoranException, InterruptedException, MalformedURLException { + + + + String propertiesURLStr = urlString; + Logger bazingaLogger = loggerContext.getLogger(BAZINGA_LOGGER_NAME); + + assertEquals(BAZINGA_LOGGER_SETUP_0, get(urlString)); + + String configurationStr = ""; + + configure(asBAIS(configurationStr)); + + // allow for the first update + Thread.sleep(50); + assertEquals(Level.WARN, bazingaLogger.getLevel()); + System.out.println("first test passed with success"); + + CountDownLatch changeDetectedLatch0 = registerChangeDetectedListener(); + CountDownLatch configurationDoneLatch0 = registerPartialConfigurationEndedSuccessfullyEventListener(); + + String response1 = post(urlString, BAZINGA_LOGGER_SETUP_1); + assertEquals(BAZINGA_LOGGER_SETUP_1, get(urlString)); + + changeDetectedLatch0.await(100, TimeUnit.MICROSECONDS); + configurationDoneLatch0.await(100, TimeUnit.MICROSECONDS); + assertEquals(Level.ERROR, bazingaLogger.getLevel()); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/CounterBasedEvaluator.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/CounterBasedEvaluator.java similarity index 88% rename from logback-classic/src/test/java/ch/qos/logback/classic/net/CounterBasedEvaluator.java rename to logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/CounterBasedEvaluator.java index 54eeef111e..dad0516dbd 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/CounterBasedEvaluator.java +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/CounterBasedEvaluator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.net; +package ch.qos.logback.classic.blackbox.net; import ch.qos.logback.core.boolex.EvaluationException; import ch.qos.logback.core.boolex.EventEvaluator; @@ -21,7 +21,8 @@ * A simple EventEvaluator implementation that triggers email transmission after * a given number of events occur, regardless of event level. * - *

By default, the limit is 1024. + *

+ * By default, the limit is 1024. */ public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator { diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/SMTPAppender_GreenTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/SMTPAppender_GreenTest.java new file mode 100644 index 0000000000..8daefb4d84 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/net/SMTPAppender_GreenTest.java @@ -0,0 +1,553 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.blackbox.net; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.blackbox.BlackboxClassicTestConstants; +import ch.qos.logback.classic.net.SMTPAppender; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.util.EnvUtil; +import com.icegreen.greenmail.util.*; +import org.dom4j.DocumentException; +import org.dom4j.io.SAXReader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; + +//import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.html.HTMLLayout; +import ch.qos.logback.classic.blackbox.html.XHTMLEntityResolver; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusListenerConfigHelper; +import ch.qos.logback.core.util.StatusPrinter; +import jakarta.mail.MessagingException; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; + +import static org.junit.jupiter.api.Assertions.*; + +public class SMTPAppender_GreenTest { + + static boolean NO_SSL = false; + static boolean WITH_SSL = true; + + static final String HEADER = "HEADER\n"; + static final String FOOTER = "FOOTER\n"; + static final String DEFAULT_PATTERN = "%-4relative %mdc [%thread] %-5level %class - %msg%n"; + + static final String JAVAX_NET_DEBUG_KEY = "javax.net.debug"; + static final String SSH_HANDSHAKE_DEBUG_VAL = "ssl:handshake"; + // for use in authenticated SSL tests only (to avoid SSL handshake failures) + static final String CHECK_SERVER_IDENTITY_KEY = "mail.smtp.ssl.checkserveridentity"; + + static final boolean SYNCHRONOUS = false; + static final boolean ASYNCHRONOUS = true; + static int TIMEOUT = 3000; + + int port = RandomUtil.getRandomServerPort(); + // GreenMail cannot be static. As a shared server induces race conditions + GreenMail greenMailServer; + + SMTPAppender smtpAppender; + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger logger = loggerContext.getLogger(this.getClass()); + + static String REQUIRED_USERNAME = "alice"; + static String REQUIRED_PASSWORD = "alicepass"; + + + @BeforeEach + public void setUp() throws Exception { + loggerContext.setMDCAdapter(logbackMDCAdapter); + StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener()); + setGlobalLogbackLoggerForGreenmail(Level.INFO); + } + + + @AfterEach + public void tearDown() throws Exception { + greenMailServer.stop(); + setGlobalLogbackLoggerForGreenmail(null); + } + + private static void setGlobalLogbackLoggerForGreenmail(Level level) { + Logger logbackLogger = (Logger) LoggerFactory.getLogger("com.icegreen.greenmail"); + logbackLogger.setLevel(level); + } + + + void startSMTPServer(boolean withSSL) { + ServerSetup serverSetup; + + if (withSSL) { + serverSetup = new ServerSetup(port, null, ServerSetup.PROTOCOL_SMTPS); + } else { + serverSetup = new ServerSetup(port, null, ServerSetup.PROTOCOL_SMTP); + } + greenMailServer = new GreenMail(serverSetup); + // user password is checked for the specified user ONLY + greenMailServer.setUser(REQUIRED_USERNAME, REQUIRED_PASSWORD); + greenMailServer.start(); + // give the server a head start + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } + } + + + void buildSMTPAppender(String subject, boolean synchronicity) throws Exception { + smtpAppender = new SMTPAppender(); + smtpAppender.setContext(loggerContext); + smtpAppender.setName("smtp"); + smtpAppender.setFrom("user@host.dom"); + smtpAppender.setSMTPHost("localhost"); + smtpAppender.setSMTPPort(port); + smtpAppender.setSubject(subject); + smtpAppender.addTo("nospam@qos.ch"); + smtpAppender.setAsynchronousSending(synchronicity); + } + + private Layout buildPatternLayout(String pattern) { + PatternLayout layout = new PatternLayout(); + layout.setContext(loggerContext); + layout.setFileHeader(HEADER); + layout.setOutputPatternAsHeader(false); + layout.setPattern(pattern); + layout.setFileFooter(FOOTER); + layout.start(); + return layout; + } + + private Layout buildHTMLLayout() { + HTMLLayout layout = new HTMLLayout(); + layout.setContext(loggerContext); + layout.setPattern("%level%class%msg"); + layout.start(); + return layout; + } + + private void waitForServerToReceiveEmails(int emailCount) throws InterruptedException { + greenMailServer.waitForIncomingEmail(5000, emailCount); + } + + private MimeMultipart verifyAndExtractMimeMultipart(String subject) + throws MessagingException, IOException, InterruptedException { + int oldCount = 0; + int expectedEmailCount = 1; + // wait for the server to receive the messages + waitForServerToReceiveEmails(expectedEmailCount); + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertNotNull(mma); + assertEquals(expectedEmailCount, mma.length); + MimeMessage mm = mma[oldCount]; + // http://jira.qos.ch/browse/LBCLASSIC-67 + assertEquals(subject, mm.getSubject()); + return (MimeMultipart) mm.getContent(); + } + + + + void waitUntilEmailIsSent() throws InterruptedException { + ExecutorService es = loggerContext.getExecutorService(); + es.shutdown(); + boolean terminated = es.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS); + + // this assertion may be needlessly strict, skipped on MacOS + if(!terminated && !EnvUtil.isMacOs()) { + fail("executor elapsed before accorded delay " + System.getProperty("os.name")); + } + + } + + @Test + public void synchronousSmoke() throws Exception { + startSMTPServer(NO_SSL); + String subject = "synchronousSmoke"; + buildSMTPAppender(subject, SYNCHRONOUS); + + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + + smtpAppender.start(); + + logger.addAppender(smtpAppender); + logger.debug("hello"); + logger.error("en error", new Exception("an exception")); + + MimeMultipart mp = verifyAndExtractMimeMultipart(subject); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertTrue(body.startsWith(HEADER.trim())); + assertTrue(body.endsWith(FOOTER.trim())); + } + + @Test + public void asynchronousSmoke() throws Exception { + startSMTPServer(NO_SSL); + + String subject = "asynchronousSmoke"; + buildSMTPAppender(subject, ASYNCHRONOUS); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + + logger.addAppender(smtpAppender); + logger.debug("hello"); + logger.error("en error", new Exception("an exception")); + + waitUntilEmailIsSent(); + MimeMultipart mp = verifyAndExtractMimeMultipart(subject); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertTrue(body.startsWith(HEADER.trim())); + assertTrue(body.endsWith(FOOTER.trim())); + } + + // See also http://jira.qos.ch/browse/LOGBACK-734 + @Test + public void callerDataShouldBeCorrectlySetWithAsynchronousSending() throws Exception { + startSMTPServer(NO_SSL); + String subject = "LOGBACK-734"; + buildSMTPAppender("LOGBACK-734", ASYNCHRONOUS); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.setIncludeCallerData(true); + smtpAppender.start(); + logger.addAppender(smtpAppender); + logger.debug("LOGBACK-734"); + logger.error("callerData", new Exception("ShouldBeCorrectlySetWithAsynchronousSending")); + + waitUntilEmailIsSent(); + MimeMultipart mp = verifyAndExtractMimeMultipart(subject); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertTrue(body.contains("DEBUG " + this.getClass().getName() + " - LOGBACK-734"), "actual [" + body + "]"); + } + + // lost MDC + @Test + public void LOGBACK_352() throws Exception { + startSMTPServer(NO_SSL); + String subject = "LOGBACK_352"; + buildSMTPAppender(subject, SYNCHRONOUS); + smtpAppender.setAsynchronousSending(false); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + logger.addAppender(smtpAppender); + logbackMDCAdapter.put("key", "val"); + logger.debug("LBCLASSIC_104"); + logbackMDCAdapter.clear(); + logger.error("en error", new Exception("test")); + + MimeMultipart mp = verifyAndExtractMimeMultipart(subject); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertTrue(body.startsWith(HEADER.trim())); + System.out.println(body); + assertTrue(body.contains("key=val")); + assertTrue(body.endsWith(FOOTER.trim())); + } + + @Test + public void html() throws Exception { + startSMTPServer(NO_SSL); + String subject = "html"; + buildSMTPAppender(subject, SYNCHRONOUS); + smtpAppender.setAsynchronousSending(false); + smtpAppender.setLayout(buildHTMLLayout()); + smtpAppender.start(); + logger.addAppender(smtpAppender); + logger.debug("html"); + logger.error("en error", new Exception("an exception")); + + MimeMultipart mp = verifyAndExtractMimeMultipart(subject); + + // verifyAndExtractMimeMultipart strict adherence to xhtml1-strict.dtd + SAXReader reader = new SAXReader(); + reader.setValidation(true); + reader.setEntityResolver(new XHTMLEntityResolver()); + byte[] messageBytes = getAsByteArray(mp.getBodyPart(0).getInputStream()); + ByteArrayInputStream bais = new ByteArrayInputStream(messageBytes); + try { + reader.read(bais); + } catch (DocumentException de) { + System.out.println("incoming message:"); + System.out.println(new String(messageBytes)); + throw de; + } + System.out.println("incoming message:"); + System.out.println(new String(messageBytes)); + } + + private byte[] getAsByteArray(InputStream inputStream) throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + byte[] buffer = new byte[1024]; + int n = -1; + while ((n = inputStream.read(buffer)) != -1) { + baos.write(buffer, 0, n); + } + return baos.toByteArray(); + } + + private void configure(String file) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + loggerContext.putProperty("port", "" + port); + jc.doConfigure(file); + } + + @Test + public void testCustomEvaluator() throws Exception { + startSMTPServer(NO_SSL); + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customEvaluator.xml"); + + logger.debug("test"); + String msg2 = "CustomEvaluator"; + logger.debug(msg2); + logger.debug("invisible"); + waitUntilEmailIsSent(); + MimeMultipart mp = verifyAndExtractMimeMultipart( + "testCustomEvaluator " + this.getClass().getName() + " - " + msg2); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertEquals("testCustomEvaluator", body); + } + + @Test + public void testCustomBufferSize() throws Exception { + startSMTPServer(NO_SSL); + configure(BlackboxClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customBufferSize.xml"); + + logger.debug("invisible1"); + logger.debug("invisible2"); + String msg = "hello"; + logger.error(msg); + waitUntilEmailIsSent(); + MimeMultipart mp = verifyAndExtractMimeMultipart( + "testCustomBufferSize " + this.getClass().getName() + " - " + msg); + String body = GreenMailUtil.getBody(mp.getBodyPart(0)); + assertEquals(msg, body); + } + + // this test fails intermittently on Jenkins. + @Test + public void testMultipleTo() throws Exception { + startSMTPServer(NO_SSL); + buildSMTPAppender("testMultipleTo", SYNCHRONOUS); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + // buildSMTPAppender() already added one destination address + smtpAppender.addTo("Test , other-test@example.com"); + smtpAppender.start(); + logger.addAppender(smtpAppender); + logger.debug("testMultipleTo hello"); + logger.error("testMultipleTo en error", new Exception("an exception")); + Thread.yield(); + int expectedEmailCount = 3; + waitForServerToReceiveEmails(expectedEmailCount); + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertNotNull(mma); + assertEquals(expectedEmailCount, mma.length); + } + + // http://jira.qos.ch/browse/LBCLASSIC-221 + @Test + public void bufferShouldBeResetBetweenMessages() throws Exception { + startSMTPServer(NO_SSL); + buildSMTPAppender("bufferShouldBeResetBetweenMessages", SYNCHRONOUS); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + logger.addAppender(smtpAppender); + String msg0 = "hello zero"; + logger.debug(msg0); + logger.error("error zero"); + + String msg1 = "hello one"; + logger.debug(msg1); + logger.error("error one"); + + Thread.yield(); + int oldCount = 0; + int expectedEmailCount = oldCount + 2; + waitForServerToReceiveEmails(expectedEmailCount); + + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertNotNull(mma); + assertEquals(expectedEmailCount, mma.length); + + MimeMessage mm0 = mma[oldCount]; + MimeMultipart content0 = (MimeMultipart) mm0.getContent(); + @SuppressWarnings("unused") + String body0 = GreenMailUtil.getBody(content0.getBodyPart(0)); + + MimeMessage mm1 = mma[oldCount + 1]; + MimeMultipart content1 = (MimeMultipart) mm1.getContent(); + String body1 = GreenMailUtil.getBody(content1.getBodyPart(0)); + // second body should not contain content from first message + assertFalse(body1.contains(msg0)); + } + + @Test + public void multiLineSubjectTruncatedAtFirstNewLine() throws Exception { + startSMTPServer(NO_SSL); + String line1 = "line 1 of subject"; + String subject = line1 + "\nline 2 of subject\n"; + buildSMTPAppender(subject, ASYNCHRONOUS); + + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + logger.addAppender(smtpAppender); + logger.debug("hello"); + logger.error("en error", new Exception("an exception")); + + Thread.yield(); + waitUntilEmailIsSent(); + waitForServerToReceiveEmails(1); + + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertEquals(1, mma.length); + assertEquals(line1, mma[0].getSubject()); + } + + @Test + public void authenticated() throws Exception { + startSMTPServer(NO_SSL); + buildSMTPAppender("testMultipleTo", SYNCHRONOUS); + smtpAppender.setUsername(REQUIRED_USERNAME); + smtpAppender.setPassword(REQUIRED_PASSWORD); + + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + + logger.addAppender(smtpAppender); + logger.debug("authenticated"); + logger.error("authenticated en error", new Exception("an exception")); + + waitUntilEmailIsSent(); + waitForServerToReceiveEmails(1); + + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertNotNull(mma); + assertTrue(mma.length == 1, "body should not be empty"); + } + + void setSystemPropertiesForStartTLS() { + String PREFIX = "mail.smtp."; + System.setProperty(PREFIX + "starttls.enable", "true"); + System.setProperty(PREFIX + "socketFactory.class", DummySSLSocketFactory.class.getName()); + System.setProperty(PREFIX + "socketFactory.fallback", "false"); + } + + void unsetSystemPropertiesForStartTLS() { + String PREFIX = "mail.smtp."; + System.clearProperty(PREFIX + "starttls.enable"); + System.clearProperty(PREFIX + "socketFactory.class"); + System.clearProperty(PREFIX + "socketFactory.fallback"); + } + + @Test + public void authenticatedSSL() throws Exception { + + try { + // without setting this property, the SSL handshake fails with newer icegreen, angus versions + System.setProperty(CHECK_SERVER_IDENTITY_KEY, "false"); + setSystemPropertiesForStartTLS(); + //System.setProperty("greenmail.smtps.host", "127.0.0.1"); + startSMTPServer(WITH_SSL); + buildSMTPAppender("testMultipleTo", SYNCHRONOUS); + smtpAppender.setUsername(REQUIRED_USERNAME); + smtpAppender.setPassword(REQUIRED_PASSWORD); + smtpAppender.setSTARTTLS(true); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + + logger.addAppender(smtpAppender); + logger.debug("authenticated"); + logger.error("authenticated en error", new Exception("an exception")); + + waitUntilEmailIsSent(); + waitForServerToReceiveEmails(1); + + MimeMessage[] mma = greenMailServer.getReceivedMessages(); + assertNotNull(mma); + assertTrue(mma.length == 1, "body should not be empty"); + } finally { + System.clearProperty(CHECK_SERVER_IDENTITY_KEY); + unsetSystemPropertiesForStartTLS(); + } + } + + // ============================================================================== + // IGNORED + // ============================================================================== + static String GMAIL_USER_NAME = "xx@gmail.com"; + static String GMAIL_PASSWORD = "xxx"; + + @Disabled + @Test + public void authenticatedGmailStartTLS() throws Exception { + smtpAppender.setSMTPHost("smtp.gmail.com"); + smtpAppender.setSMTPPort(587); + smtpAppender.setAsynchronousSending(false); + smtpAppender.addTo(GMAIL_USER_NAME); + + smtpAppender.setSTARTTLS(true); + smtpAppender.setUsername(GMAIL_USER_NAME); + smtpAppender.setPassword(GMAIL_PASSWORD); + + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.setSubject("authenticatedGmailStartTLS - %level %logger{20} - %m"); + smtpAppender.start(); + Logger logger = loggerContext.getLogger("authenticatedGmailSTARTTLS"); + logger.addAppender(smtpAppender); + logger.debug("authenticatedGmailStartTLS =- hello"); + logger.error("en error", new Exception("an exception")); + + StatusPrinter.print(loggerContext); + } + + @Disabled + @Test + public void authenticatedGmail_SSL() throws Exception { + smtpAppender.setSMTPHost("smtp.gmail.com"); + smtpAppender.setSMTPPort(465); + smtpAppender.setSubject("authenticatedGmail_SSL - %level %logger{20} - %m"); + smtpAppender.addTo(GMAIL_USER_NAME); + smtpAppender.setSSL(true); + smtpAppender.setUsername(GMAIL_USER_NAME); + smtpAppender.setPassword(GMAIL_PASSWORD); + smtpAppender.setAsynchronousSending(false); + smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); + smtpAppender.start(); + Logger logger = loggerContext.getLogger("authenticatedGmail_SSL"); + logger.addAppender(smtpAppender); + logger.debug("hello" + new java.util.Date()); + logger.error("en error", new Exception("an exception")); + + StatusPrinter.print(loggerContext); + + } +} diff --git a/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/util/ClassicVersionUtilTest.java b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/util/ClassicVersionUtilTest.java new file mode 100644 index 0000000000..9b03baf164 --- /dev/null +++ b/logback-classic-blackbox/src/test/java/ch/qos/logback/classic/blackbox/util/ClassicVersionUtilTest.java @@ -0,0 +1,32 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.util; + +import ch.qos.logback.classic.util.ClassicVersionUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ClassicVersionUtilTest { + + @Test + public void bySelfDeclaredProperties() { + String version = ClassicVersionUtil.getVersionBySelfDeclaredProperties(); + assertNotNull(version); + assertTrue(version.startsWith("1.5")); + } + +} diff --git a/logback-classic-blackbox/src/test/java/module-info.java b/logback-classic-blackbox/src/test/java/module-info.java new file mode 100644 index 0000000000..1579b25f1f --- /dev/null +++ b/logback-classic-blackbox/src/test/java/module-info.java @@ -0,0 +1,29 @@ +module logback.classic.blackbox { + requires java.xml; + requires ch.qos.logback.core; + requires ch.qos.logback.classic; + requires jakarta.mail; + requires janino; + + requires dom4j; + requires greenmail; + + requires org.junit.jupiter.api; + requires org.junit.jupiter.engine; + requires org.slf4j; + + requires org.eclipse.jetty.ee10.servlet; + + requires java.logging; + + exports ch.qos.logback.classic.blackbox; + exports ch.qos.logback.classic.blackbox.joran; + exports ch.qos.logback.classic.blackbox.joran.conditional; + exports ch.qos.logback.classic.blackbox.joran.spi; + exports ch.qos.logback.classic.blackbox.html; + exports ch.qos.logback.classic.blackbox.net; + + // resources in named modules are accessible only if opened + opens asResource; + exports ch.qos.logback.classic.blackbox.util; +} \ No newline at end of file diff --git a/logback-classic/src/test/resources/asResource/inner1.xml b/logback-classic-blackbox/src/test/resources/asResource/inner1.xml similarity index 100% rename from logback-classic/src/test/resources/asResource/inner1.xml rename to logback-classic-blackbox/src/test/resources/asResource/inner1.xml diff --git a/logback-classic-blackbox/src/test/resources/asResource/topFile.xml b/logback-classic-blackbox/src/test/resources/asResource/topFile.xml new file mode 100644 index 0000000000..a9fe297954 --- /dev/null +++ b/logback-classic-blackbox/src/test/resources/asResource/topFile.xml @@ -0,0 +1,18 @@ + + + + + + \ No newline at end of file diff --git a/logback-classic/.gitignore b/logback-classic/.gitignore index 00cf4f4a88..63a7430722 100644 --- a/logback-classic/.gitignore +++ b/logback-classic/.gitignore @@ -1,3 +1,4 @@ bundle/ lib/ -felix-cache/ \ No newline at end of file +felix-cache/ +bin/ diff --git a/logback-classic/LICENSE.txt b/logback-classic/LICENSE.txt new file mode 100644 index 0000000000..b87c4d056e --- /dev/null +++ b/logback-classic/LICENSE.txt @@ -0,0 +1,15 @@ +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2026, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v2.0 as published by +the Eclipse Foundation + + or (per the licensee's choosing) + +under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation. + diff --git a/logback-classic/integration.xml b/logback-classic/integration.xml index eef2199e9d..869a81b0e7 100644 --- a/logback-classic/integration.xml +++ b/logback-classic/integration.xml @@ -1,73 +1,69 @@ - - - - - - - - - - - - - - - + - - - - + + + + - - Making lib/ folder in case it does not already exist. - - Copying ${org.slf4j:slf4j-api:jar} to lib/ - - - - - - + + - - - - - - - + + + + + + + + + + - - - - - - - + + + + + + + + + + Making lib/ folder in case it does not already exist. + + Copying ${org.slf4j:slf4j-api:jar} to lib/ + + + + + + + + + + + + + + + + + diff --git a/logback-classic/osgi-build.xml b/logback-classic/osgi-build.xml index 849c93d460..4ba1068ad2 100755 --- a/logback-classic/osgi-build.xml +++ b/logback-classic/osgi-build.xml @@ -55,7 +55,7 @@ Making lib/ folder in case it does not already exist. Copying ${org.slf4j:slf4j-api:jar} to lib/ - + @@ -97,7 +97,7 @@ diff --git a/logback-classic/performance/records/ArrayListLogger/results.txt b/logback-classic/performance/records/ArrayListLogger/results.txt index b96a2e020c..6b2d04f277 100644 --- a/logback-classic/performance/records/ArrayListLogger/results.txt +++ b/logback-classic/performance/records/ArrayListLogger/results.txt @@ -4,11 +4,11 @@ children in an ArrayList. - LoggerContext has a cache of existing loggers in a map. This - significantly improves the performance of retrival of existing + significantly improves the performance of retrieval of existing loggers. -Memomy footprint for 1000 loggers: +Memory footprint for 1000 loggers: ================================== The memory consumption of various logger implementations are shown @@ -27,7 +27,7 @@ LOG4J implementation. The logger cache adds a memory overhead but very significantly improves the efficiency of retreiving an existing logger. -The mem foorprint without the logger cache: +The mem footprint without the logger cache: 73 kB - 2,046 alloc. com.logback.LoggerCreation.testListLoggers @@ -47,7 +47,7 @@ CategoryKey for every call to the Hierarchy.getLogger method. Speed of creating of a logger: ============================== -Emtpy logger creation: 1229 nanoseconds per creation. +Empty logger creation: 1229 nanoseconds per creation. List logger creation: 13139 nanoseconds per creation. JUL logger creation: 61990 nanoseconds per creation. LOG4J logger creation: 23503 nanoseconds per creation. diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerCreation.java b/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerCreation.java index bdd4ba14ba..4f4db4a58e 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerCreation.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerCreation.java @@ -1,11 +1,15 @@ /** - * LOGBack: the reliable, fast and flexible logging library for Java. + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2024, QOS.ch. All rights reserved. * - * Copyright (C) 1999-2005, QOS.ch, LOGBack.com + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation. + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. */ package ch.qos.logback.classic; diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerEventCreationTest.java b/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerEventCreationTest.java index 43e3c216cf..3310e18568 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerEventCreationTest.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/LoggerEventCreationTest.java @@ -1,3 +1,16 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.classic; import org.slf4j.LoggerFactory; diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/RetreivalOfExistingLoggerSpeed.java b/logback-classic/performance/src/java/ch/qos/logback/classic/RetreivalOfExistingLoggerSpeed.java index 01dbab7228..8cd52e5fc8 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/RetreivalOfExistingLoggerSpeed.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/RetreivalOfExistingLoggerSpeed.java @@ -1,11 +1,15 @@ /** - * LOGBack: the reliable, fast and flexible logging library for Java. + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. * - * Copyright (C) 1999-2005, QOS.ch, LOGBack.com + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation. + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. */ package ch.qos.logback.classic; diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/SpeedOfDisabledDebug.java b/logback-classic/performance/src/java/ch/qos/logback/classic/SpeedOfDisabledDebug.java index 2ed5912afd..70c40a3fbf 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/SpeedOfDisabledDebug.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/SpeedOfDisabledDebug.java @@ -1,11 +1,15 @@ /** - * LOGBack: the reliable, fast and flexible logging library for Java. + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. * - * Copyright (C) 1999-2005, QOS.ch, LOGBack.com + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation. + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. */ package ch.qos.logback.classic; diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorSpeed.java b/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorSpeed.java index de11a38b4a..7e27f892cf 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorSpeed.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/ClassNameAbbreviatorSpeed.java @@ -1,11 +1,15 @@ /** - * LOGBack: the reliable, fast and flexible logging library for Java. + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. * - * Copyright (C) 1999-2006, QOS.ch + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation. + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. */ package ch.qos.logback.classic.pattern; diff --git a/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/WriteSpeed.java b/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/WriteSpeed.java index fbede5e6f9..6fb594bbe3 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/WriteSpeed.java +++ b/logback-classic/performance/src/java/ch/qos/logback/classic/pattern/WriteSpeed.java @@ -1,11 +1,15 @@ /** - * LOGBack: the reliable, fast and flexible logging library for Java. + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. * - * Copyright (C) 1999-2005, QOS.ch, LOGBack.com + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation * - * This library is free software, you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation. + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. */ package ch.qos.logback.classic.pattern; diff --git a/logback-classic/performance/src/java/ch/qos/logback/reflect/Fruit.java b/logback-classic/performance/src/java/ch/qos/logback/reflect/Fruit.java index 13ccca01e7..cc13ec40b0 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/reflect/Fruit.java +++ b/logback-classic/performance/src/java/ch/qos/logback/reflect/Fruit.java @@ -1,3 +1,16 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.reflect; public class Fruit { diff --git a/logback-classic/performance/src/java/ch/qos/logback/reflect/JEXLTest.java b/logback-classic/performance/src/java/ch/qos/logback/reflect/JEXLTest.java index 53102e4cfe..d6d7f0d798 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/reflect/JEXLTest.java +++ b/logback-classic/performance/src/java/ch/qos/logback/reflect/JEXLTest.java @@ -1,3 +1,16 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.reflect; import org.apache.commons.jexl.Expression; diff --git a/logback-classic/performance/src/java/ch/qos/logback/reflect/JaninoTest.java b/logback-classic/performance/src/java/ch/qos/logback/reflect/JaninoTest.java index 63ed41451a..510e3d7714 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/reflect/JaninoTest.java +++ b/logback-classic/performance/src/java/ch/qos/logback/reflect/JaninoTest.java @@ -1,3 +1,16 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.reflect; import org.codehaus.janino.ExpressionEvaluator; diff --git a/logback-classic/performance/src/java/ch/qos/logback/reflect/ReflectionSpeed.java b/logback-classic/performance/src/java/ch/qos/logback/reflect/ReflectionSpeed.java index 81f6c6af8f..adc3cfc166 100644 --- a/logback-classic/performance/src/java/ch/qos/logback/reflect/ReflectionSpeed.java +++ b/logback-classic/performance/src/java/ch/qos/logback/reflect/ReflectionSpeed.java @@ -1,3 +1,16 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2022, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v1.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.reflect; import java.lang.reflect.InvocationTargetException; diff --git a/logback-classic/pom.xml b/logback-classic/pom.xml index d50c799ad7..42e7fdb3c5 100755 --- a/logback-classic/pom.xml +++ b/logback-classic/pom.xml @@ -3,395 +3,358 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - 4.0.0 - - - ch.qos.logback - logback-parent - 1.3.0-alpha5-SNAPSHOT - - - logback-classic - jar - Logback Classic Module - logback-classic module - - - - ch.qos.logback - logback-core - compile - - - org.slf4j - slf4j-api - compile - - - org.slf4j - slf4j-ext - ${slf4j.version} - test - - - - ch.qos.cal10n.plugins - maven-cal10n-plugin - ${cal10n.version} - test - - - - org.slf4j - slf4j-api - test-jar - ${slf4j.version} - test - - - org.slf4j - log4j-over-slf4j - ${slf4j.version} - test - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - test - - - - log4j - log4j - 1.2.17 - test - - - org.dom4j - dom4j - test - - - org.hsqldb - hsqldb - test - - - com.h2database - h2 - test - - - postgresql - postgresql - test - - - mysql - mysql-connector-java - test - - - javax.mail - javax.mail-api - compile - true - - - com.sun.mail - javax.mail - runtime - - - org.codehaus.janino - janino - compile - true - - - ch.qos.logback - logback-core - test-jar - test - - - org.slf4j - integration - ${slf4j.version} - test - - - javax.servlet - javax.servlet-api - provided - - - com.icegreen - greenmail - 1.3 - test - - - org.subethamail - subethasmtp - 3.1.7 - test - - - org.slf4j - slf4j-api - - - - - org.apache.felix - org.apache.felix.main - 5.6.10 - test - - - - org.mockito - mockito-core - test - - - - - - - - src/main/resources - - - - - - - org.apache.maven.plugins - maven-jar-plugin - - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - bundle-test-jar - package - - test-jar - - - - - - - org.apache.maven.plugins - maven-antrun-plugin - 1.8 - - - org.apache.ant - ant-junit - 1.9.0 - - - junit - junit - ${junit.version} - - - org.hamcrest - hamcrest-core - ${hamcrest.version} - - - - - - - - - ant-integration-test - package - - - - - - - - run - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - none - false - 2 - true - plain - false - - true - - **/AllClassicTest.java - **/PackageTest.java - **/TestConstants.java - **/test_osgi/BundleTest.java - **/ch/qos/logback/classic/util/InitializationIntegrationTest.java - **/ContextDetachingSCLTest.java - **/ContextJNDISelectorTest.java - - - **/*PerfTest.java - - - - - - org.apache.felix - maven-bundle-plugin - true - - - bundle-manifest - process-classes - - manifest - - - - - - <_noee>true - <_failok>true - ch.qos.logback.classic*, org.slf4j.impl;version=${slf4j.version} - - - sun.reflect;resolution:=optional, - javax.*;resolution:=optional, - org.xml.*;resolution:=optional, - org.slf4j, - org.slf4j.spi, - org.slf4j.event, - ch.qos.logback.core.rolling, - ch.qos.logback.core.rolling.helper, - ch.qos.logback.core.util, - ch.qos.logback.core.read, - * - - JavaSE-1.6 - - osgi.extender; filter:="(osgi.extender=osgi.serviceloader.registrar)" - osgi.serviceloader;osgi.serviceloader=org.slf4j.spi.SLF4JServiceProvider - - - - - - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.codehaus.gmavenplus - gmavenplus-plugin - [1.5,) - - testGenerateStubs - generateStubs - testCompile - compile - - - - - - - - - - - - - - - - - - - host-orion - - + 4.0.0 + + + ch.qos.logback + logback-parent + 1.5.28-SNAPSHOT + + + logback-classic + jar + Logback Classic Module + logback-classic module + + + ch.qos.logback.classic + + + + + ch.qos.logback + logback-core + + + org.slf4j + slf4j-api + + + + + + + org.slf4j + slf4j-api + test-jar + ${slf4j.version} + test + + + org.slf4j + log4j-over-slf4j + ${slf4j.version} + test + + + org.slf4j + jul-to-slf4j + ${slf4j.version} + test + + + + ch.qos.reload4j + reload4j + 1.2.18.4 + test + + + + org.dom4j + dom4j + test + + + + jakarta.mail + jakarta.mail-api + compile + true + + + + jakarta.activation + jakarta.activation-api + compile + true + + + + org.eclipse.angus + angus-mail + test + + + + org.codehaus.janino + janino + true + + - com.microsoft.sqlserver - sqljdbc4 - 2.0 - test + ch.qos.logback + logback-core + test-jar + test - - com.oracle - ojdbc14 - 10.2.0.1 - test + jakarta.servlet + jakarta.servlet-api + provided + true - - - - - - host-hora - - + - com.oracle - ojdbc14 - 10.2.0.1 - test + org.apache.felix + org.apache.felix.main + 5.6.10 + test - - - + + + org.mockito + mockito-core + test + + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.version} + test + + + + + + + + src/main/resources + + + + src/main/java/ + true + + ch/qos/logback/classic/logback-classic-version.properties + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + bundle-test-jar + package + + test-jar + + + + + + + org.apache.maven.plugins + maven-antrun-plugin + ${maven-antrun-plugin.version} + + + org.apache.ant + ant-junit + ${ant.version} + + + org.apache.ant + ant-junitlauncher + ${ant.version} + + + + org.junit.jupiter + junit-jupiter-api + ${junit-jupiter-api.version} + + + + + org.junit.vintage + junit-vintage-engine + ${junit-vintage-engine.version} + + + + org.hamcrest + hamcrest-core + ${hamcrest.version} + + + + + + + ant-integration-test + package + + + + + + + + run + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + default-test + + + + --add-modules jakarta.mail + --add-modules jakarta.servlet + --add-opens ch.qos.logback.core/ch.qos.logback.core.testUtil=java.naming + --add-opens ch.qos.logback.classic/ch.qos.logback.classic.issue.github450=ch.qos.logback.core + --add-opens ch.qos.logback.classic/ch.qos.logback.classic.testUtil=ch.qos.logback.core + --add-opens ch.qos.logback.classic/ch.qos.logback.classic.jsonTest=ALL-UNNAMED + + classes + 8 + 1C + true + plain + false + + + true + + + **/test_osgi/BundleTest.java + org.slf4j.implTest.MultithreadedInitializationTest.java + org.slf4j.implTest.InitializationOutputTest.java + ch.qos.logback.classic.util.ContextInitializerTest.java + ch.qos.logback.classic.spi.InvocationTest.java + ch.qos.logback.classic.issue.github450.SLF4JIssue450Test + + + + + + singleJVM + + test + + + 4 + false + + --add-opens ch.qos.logback.classic/ch.qos.logback.classic.issue.github450=ch.qos.logback.core + + + org.slf4j.implTest.MultithreadedInitializationTest.java + org.slf4j.implTest.InitializationOutputTest.java + ch.qos.logback.classic.util.ContextInitializerTest.java + ch.qos.logback.classic.spi.InvocationTest.java + ch.qos.logback.classic.issue.github450.SLF4JIssue450Test + + + + + + + + org.apache.felix + maven-bundle-plugin + + + bundle-manifest + process-classes + + manifest + + + + + + ch.qos.logback.classic* + + + ch.qos.logback.classic*;version="${range;[==,+);${version_cleanup;${project.version}}}", + sun.reflect;resolution:=optional, + jakarta.*;resolution:=optional, + org.xml.*;resolution:=optional, + ch.qos.logback.core.rolling, + ch.qos.logback.core.rolling.helper, + ch.qos.logback.core.read, + * + + + =1.0.0)(!(version>=2.0.0)))";resolution:=optional, + osgi.extender;filter:="(&(osgi.extender=osgi.serviceloader.registrar)(version>=1.0.0)(!(version>=2.0.0)))", + osgi.serviceloader;filter:="(osgi.serviceloader=ch.qos.logback.classic.spi.Configurator)";osgi.serviceloader="ch.qos.logback.classic.spi.Configurator";resolution:=optional;cardinality:=multiple + ]]> + ="jakarta.servlet.ServletContainerInitializer";effective:=active, + osgi.service;objectClass:List="org.slf4j.spi.SLF4JServiceProvider";effective:=active, + osgi.serviceloader;osgi.serviceloader="jakarta.servlet.ServletContainerInitializer";register:="ch.qos.logback.classic.servlet.LogbackServletContainerInitializer", + osgi.serviceloader;osgi.serviceloader="org.slf4j.spi.SLF4JServiceProvider";register:="ch.qos.logback.classic.spi.LogbackServiceProvider" + ]]> + + + + + + + + + diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/boolex/EvaluatorTemplate.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/boolex/EvaluatorTemplate.groovy deleted file mode 100644 index 2dcfc081c8..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/boolex/EvaluatorTemplate.groovy +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.boolex - -import ch.qos.logback.classic.spi.ILoggingEvent - -import static ch.qos.logback.classic.Level.TRACE; -import static ch.qos.logback.classic.Level.DEBUG; -import static ch.qos.logback.classic.Level.INFO; -import static ch.qos.logback.classic.Level.WARN; -import static ch.qos.logback.classic.Level.ERROR; - -// WARNING -// If this file is renamed, this should be reflected in -// logback-classic/pom.xml resources section. - -/** - * @author Ceki Gücü - */ -public class EvaluatorTemplate implements IEvaluator { - - boolean doEvaluate(ILoggingEvent event) { - ILoggingEvent e = event; - //EXPRESSION - } - - -} diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/AppenderDelegate.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/AppenderDelegate.groovy deleted file mode 100644 index a104bce2fa..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/AppenderDelegate.groovy +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer -import java.util.List; -import java.util.Map; - -import ch.qos.logback.core.Appender -import ch.qos.logback.core.spi.AppenderAttachable; - -/** - * @author Ceki Gücü - */ -class AppenderDelegate extends ComponentDelegate { - - Map> appendersByName = [:] - - AppenderDelegate(Appender appender) { - super(appender) - } - - AppenderDelegate(Appender appender, List> appenders) { - super(appender) - appendersByName = appenders.collectEntries { [(it.name) : it]} - } - - String getLabel() { - "appender" - } - - void appenderRef(String name){ - if (!AppenderAttachable.class.isAssignableFrom(component.class)) { - def errorMessage= component.class.name + ' does not implement ' + AppenderAttachable.class.name + '.' - throw new IllegalArgumentException(errorMessage) - } - component.addAppender(appendersByName[name]) - } -} diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ComponentDelegate.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ComponentDelegate.groovy deleted file mode 100644 index 533ef6c5bb..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ComponentDelegate.groovy +++ /dev/null @@ -1,153 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import ch.qos.logback.core.spi.ContextAwareBase -import ch.qos.logback.core.spi.LifeCycle -import ch.qos.logback.core.spi.ContextAware -import ch.qos.logback.core.joran.spi.NoAutoStartUtil - -/** - * @author Ceki Gücü - */ -class ComponentDelegate extends ContextAwareBase { - - final Object component; - - final List fieldsToCascade = []; - - ComponentDelegate(Object component) { - this.component = component; - } - - String getLabel() { "component" } - - String getLabelFistLetterInUpperCase() { getLabel()[0].toUpperCase() + getLabel().substring(1) } - - void methodMissing(String name, def args) { - NestingType nestingType = PropertyUtil.nestingType(component, name, null); - if (nestingType == NestingType.NA) { - addError("${getLabelFistLetterInUpperCase()} ${getComponentName()} of type [${component.getClass().canonicalName}] has no appplicable [${name}] property.") - return; - } - - String subComponentName - Class clazz - Closure closure - - (subComponentName, clazz, closure) = analyzeArgs(args) - if (clazz != null) { - Object subComponent = clazz.newInstance() - if (subComponentName && subComponent.hasProperty(name)) { - subComponent.name = subComponentName; - } - if (subComponent instanceof ContextAware) { - subComponent.context = context; - } - if (closure) { - ComponentDelegate subDelegate = new ComponentDelegate(subComponent) - - cascadeFields(subDelegate) - subDelegate.context = context - injectParent(subComponent) - closure.delegate = subDelegate - closure.resolveStrategy = Closure.DELEGATE_FIRST - closure() - } - if (subComponent instanceof LifeCycle && NoAutoStartUtil.notMarkedWithNoAutoStart(subComponent)) { - subComponent.start(); - } - PropertyUtil.attach(nestingType, component, subComponent, name) - } else { - addError("No 'class' argument specified for [${name}] in ${getLabel()} ${getComponentName()} of type [${component.getClass().canonicalName}]"); - } - } - - void cascadeFields(ComponentDelegate subDelegate) { - for (String k: fieldsToCascade) { - subDelegate.metaClass."${k}" = this."${k}" - } - } - - void injectParent(Object subComponent) { - if(subComponent.hasProperty("parent")) { - subComponent.parent = component; - } - } - - void propertyMissing(String name, def value) { - NestingType nestingType = PropertyUtil.nestingType(component, name, value); - if (nestingType == NestingType.NA) { - addError("${getLabelFistLetterInUpperCase()} ${getComponentName()} of type [${component.getClass().canonicalName}] has no appplicable [${name}] property ") - return; - } - PropertyUtil.attach(nestingType, component, value, name) - } - - - def analyzeArgs(Object[] args) { - String name; - Class clazz; - Closure closure; - - if (args.size() > 3) { - addError("At most 3 arguments allowed but you passed $args") - return [name, clazz, closure] - } - - if (args[-1] instanceof Closure) { - closure = args[-1] - args -= args[-1] - } - - if (args.size() == 1) { - clazz = parseClassArgument(args[0]) - } - - if (args.size() == 2) { - name = parseNameArgument(args[0]) - clazz = parseClassArgument(args[1]) - } - - return [name, clazz, closure] - } - - Class parseClassArgument(arg) { - if (arg instanceof Class) { - return arg - } else if (arg instanceof String) { - return Class.forName(arg) - } else { - addError("Unexpected argument type ${arg.getClass().canonicalName}") - return null; - } - } - - String parseNameArgument(arg) { - if (arg instanceof String) { - return arg - } else { - addError("With 2 or 3 arguments, the first argument must be the component name, i.e of type string") - return null; - } - } - - String getComponentName() { - if (component.hasProperty("name")) - return "[${component.name}]" - else - return "" - - } -} \ No newline at end of file diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationContributor.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationContributor.groovy deleted file mode 100644 index 17739f0fdd..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationContributor.groovy +++ /dev/null @@ -1,31 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -/** - * @author Ceki Gücü - */ -public interface ConfigurationContributor { - - /** - * The list of method mapping from the contributor into the configuration mechanism, - * e.g. the ConfiguratorDelegate - * - *

The key in the map is the method being contributed and the value is the name of - * the method in the target class. - * @return - */ - public Map getMappings() - -} diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegate.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegate.groovy deleted file mode 100644 index 67515affa6..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegate.groovy +++ /dev/null @@ -1,248 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer; - - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.LoggerContext -import ch.qos.logback.classic.jmx.JMXConfigurator -import ch.qos.logback.classic.jmx.MBeanUtil -import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; -import ch.qos.logback.classic.net.ReceiverBase -import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter -import ch.qos.logback.classic.turbo.TurboFilter -import ch.qos.logback.core.Appender -import ch.qos.logback.core.CoreConstants -import ch.qos.logback.core.spi.ContextAwareBase -import ch.qos.logback.core.status.StatusListener -import ch.qos.logback.core.util.CachingDateFormatter -import ch.qos.logback.core.util.Duration -import ch.qos.logback.core.spi.LifeCycle -import ch.qos.logback.core.spi.ContextAware - -import javax.management.MalformedObjectNameException -import javax.management.ObjectName - -import java.lang.management.ManagementFactory -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -/** - * @author Ceki Gücü - */ - -public class ConfigurationDelegate extends ContextAwareBase { - - List appenderList = []; - - Object getDeclaredOrigin() { - return this; - } - - void scan(String scanPeriodStr = null) { - if (scanPeriodStr) { - ReconfigureOnChangeTask rocTask = new ReconfigureOnChangeTask(); - rocTask.setContext(context); - context.putObject(CoreConstants.RECONFIGURE_ON_CHANGE_TASK, rocTask); - try { - Duration duration = Duration.valueOf(scanPeriodStr); - ScheduledExecutorService scheduledExecutorService = context.getScheduledExecutorService(); - - ScheduledFuture scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(rocTask, duration.getMilliseconds(), duration.getMilliseconds(), TimeUnit.MILLISECONDS); - context.addScheduledFuture(scheduledFuture); - addInfo("Setting ReconfigureOnChangeTask scanning period to " + duration); - } catch (NumberFormatException nfe) { - addError("Error while converting [" + scanPeriodStr + "] to long", nfe); - } - } - } - - void statusListener(Class listenerClass) { - StatusListener statusListener = listenerClass.newInstance() - context.statusManager.add(statusListener) - if(statusListener instanceof ContextAware) { - ((ContextAware) statusListener).setContext(context); - } - if(statusListener instanceof LifeCycle) { - ((LifeCycle) statusListener).start(); - } - addInfo("Added status listener of type [${listenerClass.canonicalName}]"); - } - - void conversionRule(String conversionWord, Class converterClass) { - String converterClassName = converterClass.getName(); - - Map ruleRegistry = (Map) context.getObject(CoreConstants.PATTERN_RULE_REGISTRY); - if (ruleRegistry == null) { - ruleRegistry = new HashMap(); - context.putObject(CoreConstants.PATTERN_RULE_REGISTRY, ruleRegistry); - } - // put the new rule into the rule registry - addInfo("registering conversion word " + conversionWord + " with class [" + converterClassName + "]"); - ruleRegistry.put(conversionWord, converterClassName); - } - - void root(Level level, List appenderNames = []) { - if (level == null) { - addError("Root logger cannot be set to level null"); - } else { - logger(org.slf4j.Logger.ROOT_LOGGER_NAME, level, appenderNames); - } - } - - void logger(String name, Level level, List appenderNames = [], Boolean additivity = null) { - if (name) { - Logger logger = ((LoggerContext) context).getLogger(name); - addInfo("Setting level of logger [${name}] to " + level); - logger.level = level; - - for (aName in appenderNames) { - Appender appender = appenderList.find { it -> it.name == aName }; - if (appender != null) { - addInfo("Attaching appender named [${aName}] to " + logger); - logger.addAppender(appender); - } else { - addError("Failed to find appender named [${aName}]"); - } - } - - if (additivity != null) { - logger.additive = additivity; - } - } else { - addInfo("No name attribute for logger"); - } - } - - void appender(String name, Class clazz, Closure closure = null) { - addInfo("About to instantiate appender of type [" + clazz.name + "]"); - Appender appender = clazz.newInstance(); - addInfo("Naming appender as [" + name + "]"); - appender.name = name - appender.context = context - appenderList.add(appender) - if (closure != null) { - AppenderDelegate ad = new AppenderDelegate(appender, appenderList) - copyContributions(ad, appender) - ad.context = context; - closure.delegate = ad; - closure.resolveStrategy = Closure.DELEGATE_FIRST - closure(); - } - try { - appender.start() - } catch (RuntimeException e) { - addError("Failed to start apppender named [" + name + "]", e) - } - } - - void receiver(String name, Class aClass, Closure closure = null) { - addInfo("About to instantiate receiver of type [" + clazz.name + "]"); - ReceiverBase receiver = aClass.newInstance(); - receiver.context = context; - if(closure != null) { - ComponentDelegate componentDelegate = new ComponentDelegate(receiver); - componentDelegate.context = context; - closure.delegate = componentDelegate; - closure.resolveStrategy = Closure.DELEGATE_FIRST - closure(); - } - try { - receiver.start() - } catch (RuntimeException e) { - addError("Failed to start receiver of type [" + aClass.getName() + "]", e) - } - } - - private void copyContributions(AppenderDelegate appenderDelegate, Appender appender) { - if (appender instanceof ConfigurationContributor) { - ConfigurationContributor cc = (ConfigurationContributor) appender; - cc.getMappings().each() { oldName, newName -> - appenderDelegate.metaClass."${newName}" = appender.&"$oldName" - } - } - } - - void turboFilter(Class clazz, Closure closure = null) { - addInfo("About to instantiate turboFilter of type [" + clazz.name + "]"); - TurboFilter turboFilter = clazz.newInstance(); - turboFilter.context = context - - if (closure != null) { - ComponentDelegate componentDelegate = new ComponentDelegate(turboFilter); - componentDelegate.context = context; - closure.delegate = componentDelegate; - closure.resolveStrategy = Closure.DELEGATE_FIRST - closure(); - } - turboFilter.start(); - addInfo("Adding aforementioned turbo filter to context"); - context.addTurboFilter(turboFilter) - } - - String timestamp(String datePattern, long timeReference = -1) { - long now = -1; - - if (timeReference == -1) { - addInfo("Using current interpretation time, i.e. now, as time reference."); - now = System.currentTimeMillis() - } else { - now = timeReference - addInfo("Using " + now + " as time reference."); - } - CachingDateFormatter sdf = new CachingDateFormatter(datePattern); - sdf.format(now) - } - - /** - * Creates and registers a {@link JMXConfigurator} with the platform MBean Server. - * Allows specifying a custom context name to derive the used ObjectName, or a complete - * ObjectName string representation to determine your own (the syntax automatically determines the intent). - * - * @param name custom context name or full ObjectName string representation (defaults to null) - */ - void jmxConfigurator(String name = null) { - def objectName = null - def contextName = context.name - if (name != null) { - // check if this is a valid ObjectName - try { - objectName = new ObjectName(name) - } catch (MalformedObjectNameException e) { - contextName = name - } - } - if (objectName == null) { - def objectNameAsStr = MBeanUtil.getObjectNameFor(contextName, JMXConfigurator.class) - objectName = MBeanUtil.string2ObjectName(context, this, objectNameAsStr) - if (objectName == null) { - addError("Failed to construct ObjectName for [${objectNameAsStr}]") - return - } - } - - def platformMBeanServer = ManagementFactory.platformMBeanServer - if (!MBeanUtil.isRegistered(platformMBeanServer, objectName)) { - JMXConfigurator jmxConfigurator = new JMXConfigurator((LoggerContext) context, platformMBeanServer, objectName) - try { - platformMBeanServer.registerMBean(jmxConfigurator, objectName) - } catch (all) { - addError("Failed to create mbean", all) - } - } - } - -} diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/GafferConfigurator.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/GafferConfigurator.groovy deleted file mode 100644 index 2b28514842..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/GafferConfigurator.groovy +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import ch.qos.logback.classic.Level -import ch.qos.logback.classic.LoggerContext -import ch.qos.logback.classic.encoder.PatternLayoutEncoder -import ch.qos.logback.classic.sift.SiftingAppender -import ch.qos.logback.core.status.OnConsoleStatusListener -import ch.qos.logback.core.util.ContextUtil -import ch.qos.logback.core.util.OptionHelper - -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil -import org.codehaus.groovy.control.CompilerConfiguration -import org.codehaus.groovy.control.customizers.ImportCustomizer - -class GafferConfigurator { - - LoggerContext context - - static final String DEBUG_SYSTEM_PROPERTY_KEY = "logback.debug"; - - GafferConfigurator(LoggerContext context) { - this.context = context - } - - protected void informContextOfURLUsedForConfiguration(URL url) { - ConfigurationWatchListUtil.setMainWatchURL(context, url); - } - - void run(URL url) { - informContextOfURLUsedForConfiguration(url); - run(url.text); - } - - void run(File file) { - informContextOfURLUsedForConfiguration(file.toURI().toURL()); - run(file.text); - } - - void run(String dslText) { - Binding binding = new Binding(); - binding.setProperty("hostname", ContextUtil.localHostName); - - def configuration = new CompilerConfiguration() - configuration.addCompilationCustomizers(importCustomizer()) - - String debugAttrib = System.getProperty(DEBUG_SYSTEM_PROPERTY_KEY); - if (OptionHelper.isEmpty(debugAttrib) || debugAttrib.equalsIgnoreCase("false") - || debugAttrib.equalsIgnoreCase("null")) { - // For now, Groovy/Gaffer configuration DSL does not support "debug" attribute. But in order to keep - // the conditional logic identical to that in XML/Joran, we have this empty block. - } else { - OnConsoleStatusListener.addNewInstanceToContext(context); - } - - // caller data should take into account groovy frames - new ContextUtil(context).addGroovyPackages(context.getFrameworkPackages()); - - Script dslScript = new GroovyShell(binding, configuration).parse(dslText) - - dslScript.metaClass.mixin(ConfigurationDelegate) - dslScript.setContext(context) - dslScript.metaClass.getDeclaredOrigin = { dslScript } - - dslScript.run() - } - - protected ImportCustomizer importCustomizer() { - def customizer = new ImportCustomizer() - - - def core = 'ch.qos.logback.core' - customizer.addStarImports(core, "${core}.encoder", "${core}.read", "${core}.rolling", "${core}.status", - "ch.qos.logback.classic.net") - - customizer.addImports(PatternLayoutEncoder.class.name) - - customizer.addStaticStars(Level.class.name) - - customizer.addStaticImport('off', Level.class.name, 'OFF') - customizer.addStaticImport('error', Level.class.name, 'ERROR') - customizer.addStaticImport('warn', Level.class.name, 'WARN') - customizer.addStaticImport('info', Level.class.name, 'INFO') - customizer.addStaticImport('debug', Level.class.name, 'DEBUG') - customizer.addStaticImport('trace', Level.class.name, 'TRACE') - customizer.addStaticImport('all', Level.class.name, 'ALL') - - customizer - } - -} \ No newline at end of file diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/NestedType.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/NestedType.groovy deleted file mode 100644 index 6924e50b15..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/NestedType.groovy +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -/** - * @author Ceki Gücü - */ - -enum NestingType { - NA, SINGLE, SINGLE_WITH_VALUE_OF_CONVENTION, AS_COLLECTION; -} - diff --git a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/PropertyUtil.groovy b/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/PropertyUtil.groovy deleted file mode 100644 index 1bdb9d184b..0000000000 --- a/logback-classic/src/main/groovy/ch/qos/logback/classic/gaffer/PropertyUtil.groovy +++ /dev/null @@ -1,96 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import java.lang.reflect.Method - -import com.sun.org.apache.xpath.internal.axes.SubContextList; - -import ch.qos.logback.core.joran.util.StringToObjectConverter; -import ch.qos.logback.core.joran.util.beans.BeanUtil - -/** - * @author Ceki Gücü - */ -class PropertyUtil { - - static boolean hasAdderMethod(Object obj, String name) { - String addMethod = "add${upperCaseFirstLetter(name)}"; - return obj.metaClass.respondsTo(obj, addMethod); - } - - - static NestingType nestingType(Object obj, String name, Object value) { - def decapitalizedName = BeanUtil.toLowerCamelCase(name); - MetaProperty metaProperty = obj.hasProperty(decapitalizedName); - - if(metaProperty != null) { - boolean VALUE_IS_A_STRING = value instanceof String; - - if(VALUE_IS_A_STRING && StringToObjectConverter.followsTheValueOfConvention(metaProperty.getType())) { - return NestingType.SINGLE_WITH_VALUE_OF_CONVENTION; - } else { - return NestingType.SINGLE; - } - } - if (hasAdderMethod(obj, name)) { - return NestingType.AS_COLLECTION; - } - return NestingType.NA; - } - - static Object convertByValueMethod(Object component, String name, String value) { - def decapitalizedName = BeanUtil.toLowerCamelCase(name); - MetaProperty metaProperty = component.hasProperty(decapitalizedName); - Method valueOfMethod = StringToObjectConverter.getValueOfMethod(metaProperty.getType()); - return valueOfMethod.invoke(null, value); - } - - static void attach(NestingType nestingType, Object component, Object subComponent, String name) { - switch (nestingType) { - case NestingType.SINGLE_WITH_VALUE_OF_CONVENTION: - name = BeanUtil.toLowerCamelCase(name); - Object value = convertByValueMethod(component, name, subComponent); - component."${name}" = value; - break; - case NestingType.SINGLE: - name = BeanUtil.toLowerCamelCase(name); - component."${name}" = subComponent; - break; - - case NestingType.AS_COLLECTION: - String firstUpperName = PropertyUtil.upperCaseFirstLetter(name) - component."add${firstUpperName}"(subComponent); - break; - } - } - - static String transformFirstLetter(String s, Closure closure) { - if (s == null || s.length() == 0) - return s; - - String firstLetter = new String(s.getAt(0)); - - String modifiedFistLetter = closure(firstLetter); - - if (s.length() == 1) - return modifiedFistLetter - else - return modifiedFistLetter + s.substring(1); - } - - static String upperCaseFirstLetter(String s) { - return transformFirstLetter(s, {String it -> it.toUpperCase()}) - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/AsyncAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/AsyncAppender.java index b514b8221d..62bbbffbd9 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/AsyncAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/AsyncAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,9 +17,10 @@ import ch.qos.logback.core.AsyncAppenderBase; /** - * In order to optimize performance this appender deems events of level TRACE, DEBUG and INFO as discardable. See the - * chapter on appenders in the manual for - * further information. + * In order to optimize performance this appender deems events of level TRACE, + * DEBUG and INFO as discardable. See the + * chapter + * on appenders in the manual for further information. * * * @author Ceki Gülcü @@ -31,6 +32,7 @@ public class AsyncAppender extends AsyncAppenderBase { /** * Events of level TRACE, DEBUG and INFO are deemed to be discardable. + * * @param event * @return true if the event is of level TRACE, DEBUG or INFO false otherwise. */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/BasicConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/BasicConfigurator.java index 9e9488b28f..c2e2b514cf 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/BasicConfigurator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/BasicConfigurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,46 +15,52 @@ import ch.qos.logback.classic.layout.TTLLLayout; import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.classic.spi.ConfiguratorRank; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.Context; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import ch.qos.logback.core.spi.ContextAwareBase; /** - * BasicConfigurator configures logback-classic by attaching a - * {@link ConsoleAppender} to the root logger. The console appender's layout - * is set to a {@link ch.qos.logback.classic.layout.TTLLLayout TTLLLayout}. + * BasicConfigurator configures logback-classic by attaching a + * {@link ConsoleAppender} to the root logger. The console appender's layout is + * set to a {@link ch.qos.logback.classic.layout.TTLLLayout TTLLLayout}. * * @author Ceki Gülcü */ +@ConfiguratorRank(value = ConfiguratorRank.FALLBACK) public class BasicConfigurator extends ContextAwareBase implements Configurator { public BasicConfigurator() { } - public void configure(LoggerContext lc) { + public ExecutionStatus configure(LoggerContext loggerContext) { addInfo("Setting up default configuration."); - + ConsoleAppender ca = new ConsoleAppender(); - ca.setContext(lc); + ca.setContext(context); ca.setName("console"); LayoutWrappingEncoder encoder = new LayoutWrappingEncoder(); - encoder.setContext(lc); - - - // same as + encoder.setContext(context); + + // same as // PatternLayout layout = new PatternLayout(); - // layout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"); + // layout.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - + // %msg%n"); TTLLLayout layout = new TTLLLayout(); - - layout.setContext(lc); + + layout.setContext(context); layout.start(); encoder.setLayout(layout); - + ca.setEncoder(encoder); ca.start(); - - Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + + Logger rootLogger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); rootLogger.addAppender(ca); + + // let the caller decide + return ExecutionStatus.NEUTRAL; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/ClassicConstants.java b/logback-classic/src/main/java/ch/qos/logback/classic/ClassicConstants.java index 9e79d26cec..404ecfb64c 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/ClassicConstants.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/ClassicConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,8 @@ */ package ch.qos.logback.classic; +import static ch.qos.logback.core.CoreConstants.JNDI_JAVA_NAMESPACE; + import org.slf4j.Marker; import org.slf4j.MarkerFactory; @@ -22,14 +24,22 @@ public class ClassicConstants { public static final String LOGBACK_CONTEXT_SELECTOR = "logback.ContextSelector"; public static final String CONFIG_FILE_PROPERTY = "logback.configurationFile"; - public static final String JNDI_CONFIGURATION_RESOURCE = "java:comp/env/logback/configuration-resource"; - public static final String JNDI_CONTEXT_NAME = "java:comp/env/logback/context-name"; + /** + * property name designating the path for the serialized configuration model file + * @since 1.3.9/1.4.9 + */ + public static final String MODEL_CONFIG_FILE_PROPERTY = "logback.scmoFile"; + + public static final String JNDI_CONFIGURATION_RESOURCE = JNDI_JAVA_NAMESPACE + + "comp/env/logback/configuration-resource"; + public static final String JNDI_CONTEXT_NAME = JNDI_JAVA_NAMESPACE + "comp/env/logback/context-name"; /** - * The maximum number of package separators (dots) that abbreviation - * algorithms can handle. Class or logger names with more separators will have - * their first MAX_DOTS parts shortened. + * The maximum number of package separators (dots) that abbreviation algorithms + * can handle. Class or logger names with more separators will have their first + * MAX_DOTS parts shortened. * + * Since 1.3.0, no longer unused */ public static final int MAX_DOTS = 16; @@ -50,4 +60,10 @@ public class ClassicConstants { public static final String FINALIZE_SESSION = "FINALIZE_SESSION"; public static final Marker FINALIZE_SESSION_MARKER = MarkerFactory.getMarker(FINALIZE_SESSION); + final public static String AUTOCONFIG_FILE = "logback.xml"; + final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml"; + + public static final String LOGBACK_CLASSIC_VERSION_MESSAGE = "This is logback-classic version "; + public static final String LOGBACK_VERSIONS_MISMATCH = "Versions of logback-core and logback-classic are different!"; + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/Level.java b/logback-classic/src/main/java/ch/qos/logback/classic/Level.java index bcb56695b4..02321782a0 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/Level.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/Level.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,15 @@ */ package ch.qos.logback.classic; +import org.slf4j.event.EventConstants; import org.slf4j.spi.LocationAwareLogger; /** - * Defines the set of levels recognized by logback-classic, that is {@link #OFF}, - * {@link #ERROR}, {@link #WARN}, {@link #INFO}, {@link #DEBUG}, - * {@link #TRACE} and {@link #ALL}.

The Level class is - * final and cannot be sub-classed. + * Defines the set of levels recognized by logback-classic, that is + * {@link #OFF}, {@link #ERROR}, {@link #WARN}, {@link #INFO}, {@link #DEBUG}, + * {@link #TRACE} and {@link #ALL}. + *

+ * The Level class is final and cannot be sub-classed. *

*/ public final class Level implements java.io.Serializable { @@ -43,13 +45,17 @@ public final class Level implements java.io.Serializable { public static final Integer ALL_INTEGER = ALL_INT; /** - * The OFF is used to turn off logging. + * The OFF is used to turn off logging. It is intended to be used + * for logging system configuration. + * + * Warning: it should never be passed as an argument to logger methods + * in a regular log statement. */ public static final Level OFF = new Level(OFF_INT, "OFF"); /** - * The ERROR level designates error events which may or not - * be fatal to the application. + * The ERROR level designates error events which may or not be + * fatal to the application. */ public static final Level ERROR = new Level(ERROR_INT, "ERROR"); @@ -59,8 +65,8 @@ public final class Level implements java.io.Serializable { public static final Level WARN = new Level(WARN_INT, "WARN"); /** - * The INFO level designates informational messages - * highlighting overall progress of the application. + * The INFO level designates informational messages highlighting + * overall progress of the application. */ public static final Level INFO = new Level(INFO_INT, "INFO"); @@ -77,7 +83,18 @@ public final class Level implements java.io.Serializable { public static final Level TRACE = new Level(TRACE_INT, "TRACE"); /** - * The ALL is used to turn on all logging. + *

The ALL is used to turn on all logging. The ALL level is vestigial from + * log4j 1.x. + *

+ * + *

In logback, where the Level class is final, logging can be turned on for all levels by setting + * a logger's level to TRACE. + *

+ * + *

Thus, the ALL level is marked as deprecated. + *

+ * + * @deprecated with no replacement */ public static final Level ALL = new Level(ALL_INT, "ALL"); @@ -106,6 +123,11 @@ public int toInt() { return levelInt; } + static public Level convertAnSLF4JLevel(org.slf4j.event.Level slf4jLevel) { + final int levelInt = slf4jLevel.toInt(); + return fromLocationAwareLoggerInteger(levelInt); + } + /** * Convert a Level to an Integer object. * @@ -133,8 +155,8 @@ public Integer toInteger() { } /** - * Returns true if this Level has a higher or equal Level than - * the Level passed as argument, false otherwise. + * Returns true if this Level has a higher or equal Level than the + * Level passed as argument, false otherwise. */ public boolean isGreaterOrEqual(Level r) { return levelInt >= r.levelInt; @@ -202,7 +224,7 @@ public static Level toLevel(final String sArg, Level defaultLevel) { // see LOGBACK-1288 final String in = sArg.trim(); - + if (in.equalsIgnoreCase("ALL")) { return Level.ALL; } @@ -228,7 +250,7 @@ public static Level toLevel(final String sArg, Level defaultLevel) { } /** - * Return the flyweight instance of the level received through serizalization, + * Return the flyweight instance of the level received through serialization, * i.e. 'this'. * * @return The appropriate flyweight instance @@ -241,26 +263,27 @@ private Object readResolve() { * Convert one of the integer values defined in {@link LocationAwareLogger} * interface to an instance of this class, i.e. a Level. * - * @param levelInt An integer value representing a level as defined in LocationAwareLogger + * @param levelInt An integer value representing a level as defined in + * LocationAwareLogger * @return an instance of this class, i.e. a Level. * @since 1.0.1 */ public static Level fromLocationAwareLoggerInteger(int levelInt) { Level level; switch (levelInt) { - case LocationAwareLogger.TRACE_INT: + case EventConstants.TRACE_INT: level = TRACE; break; - case LocationAwareLogger.DEBUG_INT: + case EventConstants.DEBUG_INT: level = DEBUG; break; - case LocationAwareLogger.INFO_INT: + case EventConstants.INFO_INT: level = INFO; break; - case LocationAwareLogger.WARN_INT: + case EventConstants.WARN_INT: level = WARN; break; - case LocationAwareLogger.ERROR_INT: + case EventConstants.ERROR_INT: level = ERROR; break; default: @@ -270,11 +293,12 @@ public static Level fromLocationAwareLoggerInteger(int levelInt) { } /** - * Convert this level instance to an integer value defined in the + * Convert this level instance to an integer value defined in the * {@link LocationAwareLogger} interface. * * @param level The level to convert to LocationAwareLogger integer - * @return int An integer corresponding to this level as defined in LocationAwareLogger + * @return int An integer corresponding to this level as defined in + * LocationAwareLogger * @since 1.0.1 */ public static int toLocationAwareLoggerInteger(Level level) { @@ -282,15 +306,15 @@ public static int toLocationAwareLoggerInteger(Level level) { throw new IllegalArgumentException("null level parameter is not admitted"); switch (level.toInt()) { case Level.TRACE_INT: - return LocationAwareLogger.TRACE_INT; + return EventConstants.TRACE_INT; case Level.DEBUG_INT: - return LocationAwareLogger.DEBUG_INT; + return EventConstants.DEBUG_INT; case Level.INFO_INT: - return LocationAwareLogger.INFO_INT; + return EventConstants.INFO_INT; case Level.WARN_INT: - return LocationAwareLogger.WARN_INT; + return EventConstants.WARN_INT; case Level.ERROR_INT: - return LocationAwareLogger.ERROR_INT; + return EventConstants.ERROR_INT; default: throw new IllegalArgumentException(level + " not a valid level value"); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/Logger.java b/logback-classic/src/main/java/ch/qos/logback/classic/Logger.java index 70048c7c31..82f5024f4c 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/Logger.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/Logger.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,7 +22,10 @@ import org.slf4j.LoggerFactory; import org.slf4j.Marker; +import org.slf4j.spi.DefaultLoggingEventBuilder; import org.slf4j.spi.LocationAwareLogger; +import org.slf4j.spi.LoggingEventAware; +import org.slf4j.spi.LoggingEventBuilder; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; @@ -33,13 +36,13 @@ import ch.qos.logback.core.spi.AppenderAttachableImpl; import ch.qos.logback.core.spi.FilterReply; -public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable, Serializable { +public final class Logger + implements org.slf4j.Logger, LocationAwareLogger, LoggingEventAware, AppenderAttachable, Serializable { private static final long serialVersionUID = 5454405123156820674L; // 8745934908040027998L; /** - * The fully qualified name of this class. Used in gathering caller - * information. + * The fully qualified name of this class. Used in gathering caller information. */ public static final String FQCN = ch.qos.logback.classic.Logger.class.getName(); @@ -56,8 +59,8 @@ public final class Logger implements org.slf4j.Logger, LocationAwareLogger, Appe transient private int effectiveLevelInt; /** - * The parent of this category. All categories have at least one ancestor - * which is the root category. + * The parent of this category. All categories have at least one ancestor which + * is the root category. */ transient private Logger parent; @@ -68,15 +71,15 @@ public final class Logger implements org.slf4j.Logger, LocationAwareLogger, Appe /** * It is assumed that once the 'aai' variable is set to a non-null value, it - * will never be reset to null. it is further assumed that only place where - * the 'aai'ariable is set is within the addAppender method. This method is + * will never be reset to null. it is further assumed that only place where the + * 'aai variable is set is within the addAppender method. This method is * synchronized on 'this' (Logger) protecting against simultaneous * re-configuration of this logger (a very unlikely scenario). * *

- * It is further assumed that the AppenderAttachableImpl is responsible for - * its internal synchronization and thread safety. Thus, we can get away with - * *not* synchronizing on the 'aai' (check null/ read) because + * It is further assumed that the AppenderAttachableImpl is responsible for its + * internal synchronization and thread safety. Thus, we can get away with *not* + * synchronizing on the 'aai' (check null/ read) because *

* 1) the 'aai' variable is immutable once set to non-null *

@@ -88,12 +91,12 @@ public final class Logger implements org.slf4j.Logger, LocationAwareLogger, Appe */ transient private AppenderAttachableImpl aai; /** - * Additivity is set to true by default, that is children inherit the - * appenders of their ancestors by default. If this variable is set to - * false then the appenders located in the ancestors of this - * logger will not be used. However, the children of this logger will inherit - * its appenders, unless the children have their additivity flag set to - * false too. See the user manual for more details. + * Additivity is set to true by default, that is children inherit the appenders + * of their ancestors by default. If this variable is set to false + * then the appenders located in the ancestors of this logger will not be used. + * However, the children of this logger will inherit its appenders, unless the + * children have their additivity flag set to false too. See the + * user manual for more details. */ transient private boolean additive = true; @@ -248,8 +251,7 @@ public Appender getAppender(String name) { /** * Invoke all the appenders of this logger. * - * @param event - * The event to log + * @param event The event to log */ public void callAppenders(ILoggingEvent event) { int writes = 0; @@ -292,16 +294,16 @@ public boolean detachAppender(Appender appender) { * IMPORTANT: Calls to this method must be within a synchronized block on this * logger. * - * @param lastPart - * the suffix (i.e. last part) of the child logger name. This - * parameter may not include dots, i.e. the logger separator - * character. + * @param lastPart the suffix (i.e. last part) of the child logger name. This + * parameter may not include dots, i.e. the logger separator + * character. * @return */ Logger createChildByLastNamePart(final String lastPart) { int i_index = LoggerNameUtil.getFirstSeparatorIndexOf(lastPart); if (i_index != -1) { - throw new IllegalArgumentException("Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]"); + throw new IllegalArgumentException( + "Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]"); } if (childrenList == null) { @@ -348,7 +350,7 @@ Logger createChildByName(final String childName) { int i_index = LoggerNameUtil.getSeparatorIndexOf(childName, this.name.length() + 1); if (i_index != -1) { throw new IllegalArgumentException("For logger [" + this.name + "] child name [" + childName - + " passed as parameter, may not include '.' after index" + (this.name.length() + 1)); + + " passed as parameter, may not include '.' after index" + (this.name.length() + 1)); } if (childrenList == null) { @@ -363,15 +365,18 @@ Logger createChildByName(final String childName) { /** * The next methods are not merged into one because of the time we gain by not - * creating a new Object[] with the params. This reduces the cost of not - * logging by about 20 nanoseconds. + * creating a new Object[] with the params. This reduces the cost of not logging + * by about 20 nanoseconds. */ - private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params, - final Throwable t) { + // for 0 or 3 or more parameters + private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, final Level level, + final String msg, final Object[] params, final Throwable t) { - final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, params, t); + final FilterReply decision = loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, msg, + params, t); + // the ACCEPT case falls through if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; @@ -383,10 +388,13 @@ private void filterAndLog_0_Or3Plus(final String localFQCN, final Marker marker, buildLoggingEventAndAppend(localFQCN, marker, level, msg, params, t); } - private void filterAndLog_1(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param, final Throwable t) { + + private void filterAndLog_1(final String localFQCN, final Marker marker, final Level level, final String msg, + final Object param, final Throwable t) { final FilterReply decision = loggerContext.getTurboFilterChainDecision_1(marker, this, level, msg, param, t); + // the ACCEPT case falls through if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; @@ -398,11 +406,13 @@ private void filterAndLog_1(final String localFQCN, final Marker marker, final L buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param }, t); } - private void filterAndLog_2(final String localFQCN, final Marker marker, final Level level, final String msg, final Object param1, final Object param2, - final Throwable t) { + private void filterAndLog_2(final String localFQCN, final Marker marker, final Level level, final String msg, + final Object param1, final Object param2, final Throwable t) { - final FilterReply decision = loggerContext.getTurboFilterChainDecision_2(marker, this, level, msg, param1, param2, t); + final FilterReply decision = loggerContext.getTurboFilterChainDecision_2(marker, this, level, msg, param1, + param2, t); + // the ACCEPT case falls through if (decision == FilterReply.NEUTRAL) { if (effectiveLevelInt > level.levelInt) { return; @@ -414,10 +424,10 @@ private void filterAndLog_2(final String localFQCN, final Marker marker, final L buildLoggingEventAndAppend(localFQCN, marker, level, msg, new Object[] { param1, param2 }, t); } - private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, final String msg, final Object[] params, - final Throwable t) { + private void buildLoggingEventAndAppend(final String localFQCN, final Marker marker, final Level level, + final String msg, final Object[] params, final Throwable t) { LoggingEvent le = new LoggingEvent(localFQCN, this, level, msg, t, params); - le.setMarker(marker); + le.addMarker(marker); callAppenders(le); } @@ -740,10 +750,12 @@ public String toString() { * Method that calls the attached TurboFilter objects based on the logger and * the level. * - * It is used by isYYYEnabled() methods. - * - * It returns the typical FilterReply values: ACCEPT, NEUTRAL or DENY. - * + *

It is used by isXYZEnabled() methods such as {@link #isDebugEnabled()}, + * {@link #isInfoEnabled()} etc. + *

+ * + *

It returns the typical FilterReply values: ACCEPT, NEUTRAL or DENY. + *

* @param level * @return the reply given by the TurboFilters */ @@ -751,6 +763,24 @@ private FilterReply callTurboFilters(Marker marker, Level level) { return loggerContext.getTurboFilterChainDecision_0_3OrMore(marker, this, level, null, null, null); } + /** + * Method that calls the attached TurboFilter objects based on this logger and + * {@link org.slf4j.event.LoggingEvent LoggingEvent}. + * + *

This method is typically called by + * {@link #log(org.slf4j.event.LoggingEvent) log(LoggingEvent)} method.

+ * + *

It returns {@link FilterReply} values: ACCEPT, NEUTRAL or DENY. + *

+ * + * @param slf4jEvent the SLF4J LoggingEvent + * @return the reply given by the TurboFilters + */ + private FilterReply callTurboFilters(org.slf4j.event.LoggingEvent slf4jEvent) { + return loggerContext.getTurboFilterChainDecision(this, slf4jEvent); + } + + /** * Return the context for this logger. * @@ -760,25 +790,70 @@ public LoggerContext getLoggerContext() { return loggerContext; } + /** + * Creates a {@link LoggingEventBuilder} of type {@link DefaultLoggingEventBuilder}. + * + * @since 1.3 + */ + @Override + public LoggingEventBuilder makeLoggingEventBuilder(org.slf4j.event.Level level) { + return new DefaultLoggingEventBuilder(this, level); + } + public void log(Marker marker, String fqcn, int levelInt, String message, Object[] argArray, Throwable t) { Level level = Level.fromLocationAwareLoggerInteger(levelInt); filterAndLog_0_Or3Plus(fqcn, marker, level, message, argArray, t); } /** - * Support SLF4J interception during initialization as introduced in SLF4J version 1.7.15 - * @since 1.1.4 + * Support SLF4J interception during initialization as introduced in SLF4J + * version 1.7.15. Alternatively, this method can be called by SLF4J's fluent API, i.e. by + * {@link LoggingEventBuilder}. + * + * + * @since 1.1.4 * @param slf4jEvent */ public void log(org.slf4j.event.LoggingEvent slf4jEvent) { - Level level = Level.fromLocationAwareLoggerInteger(slf4jEvent.getLevel().toInt()); - filterAndLog_0_Or3Plus(FQCN, slf4jEvent.getMarker(), level, slf4jEvent.getMessage(), slf4jEvent.getArgumentArray(), slf4jEvent.getThrowable()); + org.slf4j.event.Level slf4jLevel = slf4jEvent.getLevel(); + Level logbackLevel = Level.convertAnSLF4JLevel(slf4jLevel); + + // invoke turbo filters. See also https://github.com/qos-ch/logback/issues/871 + final FilterReply decision = loggerContext.getTurboFilterChainDecision(this, slf4jEvent); + // the ACCEPT and NEUTRAL cases falls through as there are no further level checks to be done + if (decision == FilterReply.DENY) { + return; + } + + // By default, assume this class was the caller. In some cases, {@link SubstituteLogger} can also be a caller. + // + // It is possible that the caller is some other library, e.g. slf4j-jdk-platform-logging + + String callerBoundary = slf4jEvent.getCallerBoundary(); + if (callerBoundary == null) { + callerBoundary = FQCN; + } + + LoggingEvent lle = new LoggingEvent(callerBoundary, this, logbackLevel, slf4jEvent.getMessage(), + slf4jEvent.getThrowable(), slf4jEvent.getArgumentArray()); + List markers = slf4jEvent.getMarkers(); + + if (markers != null) { + markers.forEach(m -> lle.addMarker(m)); + } + + lle.setKeyValuePairs(slf4jEvent.getKeyValuePairs()); + + // Note that at this point, any calls made with a logger disabled + // for a given level, will be already filtered out/in. TurboFilters cannot + // act at this point in the process. + this.callAppenders(lle); } /** - * After serialization, the logger instance does not know its LoggerContext. - * The best we can do here, is to return a logger with the same name - * returned by org.slf4j.LoggerFactory. + * After serialization, the logger instance does not know its LoggerContext. The + * best we can do here, is to return a logger with the same name returned by + * org.slf4j.LoggerFactory. * * @return Logger instance with the same name * @throws ObjectStreamException diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java b/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java index e99a22e7d5..dcbaf9a73d 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,20 +13,6 @@ */ package ch.qos.logback.classic; -import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ScheduledFuture; - -import org.slf4j.ILoggerFactory; -import org.slf4j.Marker; - import ch.qos.logback.classic.spi.LoggerComparator; import ch.qos.logback.classic.spi.LoggerContextListener; import ch.qos.logback.classic.spi.LoggerContextVO; @@ -41,6 +27,15 @@ import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.status.WarnStatus; +import org.slf4j.ILoggerFactory; +import org.slf4j.Marker; +import org.slf4j.spi.MDCAdapter; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ScheduledFuture; + +import static ch.qos.logback.core.CoreConstants.EVALUATOR_MAP; /** * LoggerContext glues many of the logback-classic components together. In @@ -53,7 +48,9 @@ */ public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCycle { - /** Default setting of packaging data in stack traces */ + /** + * Default setting of packaging data in stack traces + */ public static final boolean DEFAULT_PACKAGING_DATA = false; final Logger root; @@ -67,7 +64,9 @@ public class LoggerContext extends ContextBase implements ILoggerFactory, LifeCy private final TurboFilterList turboFilterList = new TurboFilterList(); private boolean packagingDataEnabled = DEFAULT_PACKAGING_DATA; SequenceNumberGenerator sequenceNumberGenerator = null; // by default there is no SequenceNumberGenerator - + + MDCAdapter mdcAdapter; + private int maxCallerDataDepth = ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH; int resetCount = 0; @@ -84,6 +83,10 @@ public LoggerContext() { initEvaluatorMap(); size = 1; this.frameworkPackages = new ArrayList(); + // In 1.5.7, the stop() method assumes that at some point the context has been started + // since earlier versions of logback did not mandate calling the start method + // we need to call in the constructor + this.start(); } void initEvaluatorMap() { @@ -208,39 +211,26 @@ public boolean isPackagingDataEnabled() { return packagingDataEnabled; } - /** - * This method clears all internal properties, except internal status messages, - * closes all appenders, removes any turboFilters, fires an OnReset event, - * removes all status listeners, removes all context listeners - * (except those which are reset resistant). - *

- * As mentioned above, internal status messages survive resets. - */ - @Override - public void reset() { - resetCount++; - super.reset(); - initEvaluatorMap(); - initCollisionMaps(); - root.recursiveReset(); - resetTurboFilterList(); - cancelScheduledTasks(); - fireOnReset(); - resetListenersExceptResetResistant(); - resetStatusListeners(); - } + void cancelScheduledTasks() { + + try { + configurationLock.lock(); - private void cancelScheduledTasks() { - for(ScheduledFuture sf: scheduledFutures) { - sf.cancel(false); + for (ScheduledFuture sf : scheduledFutures) { + sf.cancel(false); + } + scheduledFutures.clear(); + } finally { + configurationLock.unlock(); } - scheduledFutures.clear(); } - private void resetStatusListeners() { + private void resetStatusListenersExceptResetResistant() { StatusManager sm = getStatusManager(); for (StatusListener sl : sm.getCopyOfStatusListenerList()) { - sm.remove(sl); + if (!sl.isResetResistant()) { + sm.remove(sl); + } } } @@ -253,8 +243,8 @@ public void addTurboFilter(TurboFilter newFilter) { } /** - * First processPriorToRemoval all registered turbo filters and then clear the registration - * list. + * First processPriorToRemoval all registered turbo filters and then clear the + * registration list. */ public void resetTurboFilterList() { for (TurboFilter tf : turboFilterList) { @@ -281,12 +271,20 @@ final FilterReply getTurboFilterChainDecision_1(final Marker marker, final Logge final FilterReply getTurboFilterChainDecision_2(final Marker marker, final Logger logger, final Level level, final String format, final Object param1, final Object param2, final Throwable t) { - if (turboFilterList.size() == 0) { + if (turboFilterList.isEmpty()) { return FilterReply.NEUTRAL; } return turboFilterList.getTurboFilterChainDecision(marker, logger, level, format, new Object[] { param1, param2 }, t); } + final FilterReply getTurboFilterChainDecision(final Logger logger, final org.slf4j.event.LoggingEvent slf4jEvent) { + if (turboFilterList.isEmpty()) { + return FilterReply.NEUTRAL; + } + return turboFilterList.getTurboFilterChainDecision(logger, slf4jEvent); + } + + // === start listeners ============================================== public void addListener(LoggerContextListener listener) { loggerContextListenerList.add(listener); @@ -341,16 +339,49 @@ private void fireOnStop() { // === end listeners ============================================== + @Override public void start() { super.start(); fireOnStart(); } public void stop() { - reset(); - fireOnStop(); - resetAllListeners(); - super.stop(); + if (!isStarted()) + return; + + try { + configurationLock.lock(); + if (!isStarted()) + return; + + reset(); + fireOnStop(); + resetAllListeners(); + super.stop(); + } finally { + configurationLock.unlock(); + } + } + + /** + * This method clears all internal properties, except internal status messages, + * closes all appenders, removes any turboFilters, fires an OnReset event, + * removes all status listeners, removes all context listeners (except those + * which are reset resistant). + *

+ * As mentioned above, internal status messages survive resets. + */ + @Override + public void reset() { + resetCount++; + super.reset(); + initEvaluatorMap(); + root.recursiveReset(); + resetTurboFilterList(); + cancelScheduledTasks(); + fireOnReset(); + resetListenersExceptResetResistant(); + resetStatusListenersExceptResetResistant(); } @Override @@ -367,11 +398,12 @@ public void setMaxCallerDataDepth(int maxCallerDataDepth) { } /** - * List of packages considered part of the logging framework such that they are never considered - * as callers of the logging framework. This list used to compute the caller for logging events. + * List of packages considered part of the logging framework such that they are + * never considered as callers of the logging framework. This list used to + * compute the caller for logging events. *

- * To designate package "com.foo" as well as all its subpackages as being part of the logging framework, simply add - * "com.foo" to this list. + * To designate package "com.foo" as well as all its subpackages as being part + * of the logging framework, simply add "com.foo" to this list. * * @return list of framework packages */ @@ -379,7 +411,25 @@ public List getFrameworkPackages() { return frameworkPackages; } + @Override + public void setSequenceNumberGenerator(SequenceNumberGenerator sng) { + this.sequenceNumberGenerator = sng; + } + + @Override public SequenceNumberGenerator getSequenceNumberGenerator() { - return null; + return sequenceNumberGenerator; + } + + public MDCAdapter getMDCAdapter() { + return mdcAdapter; + } + + public void setMDCAdapter(MDCAdapter anAdapter) { + if (this.mdcAdapter != null) { + StatusManager sm = getStatusManager(); + sm.add(new WarnStatus("mdcAdapter being reset a second time", this)); + } + this.mdcAdapter = anAdapter; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java b/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java index 186c316acd..c00235019b 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/PatternLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,21 +15,23 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.classic.pattern.*; import ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.pattern.DynamicConverter; import ch.qos.logback.core.pattern.PatternLayoutBase; import ch.qos.logback.core.pattern.color.*; import ch.qos.logback.core.pattern.parser.Parser; /** *

- * A flexible layout configurable with pattern string. The goal of this class is - * to {@link #format format} a {@link ILoggingEvent} and return the results in a - * {#link String}. The format of the result depends on the - * conversion pattern. + * A flexible layout configurable with pattern string. The main method in this class is + * to {@link #doLayout(ILoggingEvent)}. It returns the results as a + * {#link String}. The format and contents of the result depends on the conversion + * pattern. *

* For more information about this layout, please refer to the online manual at * http://logback.qos.ch/manual/layouts.html#PatternLayout @@ -38,91 +40,141 @@ public class PatternLayout extends PatternLayoutBase { - public static final Map defaultConverterMap = new HashMap(); - public static final String HEADER_PREFIX = "#logback.classic pattern: "; - - static { - defaultConverterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); - - defaultConverterMap.put("d", DateConverter.class.getName()); - defaultConverterMap.put("date", DateConverter.class.getName()); - - defaultConverterMap.put("r", RelativeTimeConverter.class.getName()); - defaultConverterMap.put("relative", RelativeTimeConverter.class.getName()); - - defaultConverterMap.put("level", LevelConverter.class.getName()); - defaultConverterMap.put("le", LevelConverter.class.getName()); - defaultConverterMap.put("p", LevelConverter.class.getName()); - - defaultConverterMap.put("t", ThreadConverter.class.getName()); - defaultConverterMap.put("thread", ThreadConverter.class.getName()); - - defaultConverterMap.put("lo", LoggerConverter.class.getName()); - defaultConverterMap.put("logger", LoggerConverter.class.getName()); - defaultConverterMap.put("c", LoggerConverter.class.getName()); - - defaultConverterMap.put("m", MessageConverter.class.getName()); - defaultConverterMap.put("msg", MessageConverter.class.getName()); - defaultConverterMap.put("message", MessageConverter.class.getName()); - - defaultConverterMap.put("C", ClassOfCallerConverter.class.getName()); - defaultConverterMap.put("class", ClassOfCallerConverter.class.getName()); - - defaultConverterMap.put("M", MethodOfCallerConverter.class.getName()); - defaultConverterMap.put("method", MethodOfCallerConverter.class.getName()); + public static final Map> DEFAULT_CONVERTER_SUPPLIER_MAP = new HashMap<>(); - defaultConverterMap.put("L", LineOfCallerConverter.class.getName()); - defaultConverterMap.put("line", LineOfCallerConverter.class.getName()); + /** + * @deprecated replaced by {@link #DEFAULT_CONVERTER_SUPPLIER_MAP} + */ + @Deprecated + public static final Map DEFAULT_CONVERTER_MAP = new HashMap<>(); + public static final Map CONVERTER_CLASS_TO_KEY_MAP = new HashMap(); - defaultConverterMap.put("F", FileOfCallerConverter.class.getName()); - defaultConverterMap.put("file", FileOfCallerConverter.class.getName()); + /** + * @deprecated replaced by {@link #DEFAULT_CONVERTER_MAP} in turn itself replaced by + * {@link #DEFAULT_CONVERTER_SUPPLIER_MAP} + */ + @Deprecated + public static final Map defaultConverterMap = DEFAULT_CONVERTER_MAP; - defaultConverterMap.put("X", MDCConverter.class.getName()); - defaultConverterMap.put("mdc", MDCConverter.class.getName()); - - defaultConverterMap.put("ex", ThrowableProxyConverter.class.getName()); - defaultConverterMap.put("exception", ThrowableProxyConverter.class.getName()); - defaultConverterMap.put("rEx", RootCauseFirstThrowableProxyConverter.class.getName()); - defaultConverterMap.put("rootException", RootCauseFirstThrowableProxyConverter.class.getName()); - defaultConverterMap.put("throwable", ThrowableProxyConverter.class.getName()); - - defaultConverterMap.put("xEx", ExtendedThrowableProxyConverter.class.getName()); - defaultConverterMap.put("xException", ExtendedThrowableProxyConverter.class.getName()); - defaultConverterMap.put("xThrowable", ExtendedThrowableProxyConverter.class.getName()); - - defaultConverterMap.put("nopex", NopThrowableInformationConverter.class.getName()); - defaultConverterMap.put("nopexception", NopThrowableInformationConverter.class.getName()); - - defaultConverterMap.put("cn", ContextNameConverter.class.getName()); - defaultConverterMap.put("contextName", ContextNameConverter.class.getName()); - - defaultConverterMap.put("caller", CallerDataConverter.class.getName()); - - defaultConverterMap.put("marker", MarkerConverter.class.getName()); - - defaultConverterMap.put("property", PropertyConverter.class.getName()); + public static final String HEADER_PREFIX = "#logback.classic pattern: "; - defaultConverterMap.put("n", LineSeparatorConverter.class.getName()); + static { + DEFAULT_CONVERTER_SUPPLIER_MAP.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("d", DateConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("date", DateConverter::new); + // used by PrefixComposite converter + CONVERTER_CLASS_TO_KEY_MAP.put(DateConverter.class.getName(), "date"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("ms", MicrosecondConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("micros", MicrosecondConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(MicrosecondConverter.class.getName(), "micros"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("ep", EpochConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("epoch", EpochConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(EpochConverter.class.getName(), "epoch"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("r", RelativeTimeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("relative", RelativeTimeConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(RelativeTimeConverter.class.getName(), "relative"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("level", LevelConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("le", LevelConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("p", LevelConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(LevelConverter.class.getName(), "level"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("t", ThreadConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("thread", ThreadConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(ThreadConverter.class.getName(), "thread"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("lo", LoggerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("logger", LoggerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("c", LoggerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(LoggerConverter.class.getName(), "logger"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("m", MessageConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("msg", MessageConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("message", MessageConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(MessageConverter.class.getName(), "message"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("C", ClassOfCallerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("class", ClassOfCallerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(ClassOfCallerConverter.class.getName(), "class"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("M", MethodOfCallerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("method", MethodOfCallerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(MethodOfCallerConverter.class.getName(), "method"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("L", LineOfCallerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("line", LineOfCallerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(LineOfCallerConverter.class.getName(), "line"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("F", FileOfCallerConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("file", FileOfCallerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(FileOfCallerConverter.class.getName(), "file"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("X", MDCConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("mdc", MDCConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("ex", ThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("exception", ThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("rEx", RootCauseFirstThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("rootException", RootCauseFirstThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("throwable", ThrowableProxyConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("xEx", ExtendedThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("xException", ExtendedThrowableProxyConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("xThrowable", ExtendedThrowableProxyConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("nopex", NopThrowableInformationConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("nopexception", NopThrowableInformationConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("cn", ContextNameConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("contextName", ContextNameConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(ContextNameConverter.class.getName(), "contextName"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("caller", CallerDataConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(CallerDataConverter.class.getName(), "caller"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("marker", MarkerConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(MarkerConverter.class.getName(), "marker"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("kvp", KeyValuePairConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(KeyValuePairConverter.class.getName(), "kvp"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("maskedKvp", MaskedKeyValuePairConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(MaskedKeyValuePairConverter.class.getName(), "maskedKvp"); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("property", PropertyConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("n", LineSeparatorConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("black", BlackCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("red", RedCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("green", GreenCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("yellow", YellowCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("blue", BlueCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("magenta", MagentaCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("cyan", CyanCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("white", WhiteCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("gray", GrayCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldRed", BoldRedCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldGreen", BoldGreenCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldYellow", BoldYellowCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldBlue", BoldBlueCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldMagenta", BoldMagentaCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldCyan", BoldCyanCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("boldWhite", BoldWhiteCompositeConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("highlight", HighlightingCompositeConverter::new); + + DEFAULT_CONVERTER_SUPPLIER_MAP.put("lsn", LocalSequenceNumberConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(LocalSequenceNumberConverter.class.getName(), "lsn"); - defaultConverterMap.put("black", BlackCompositeConverter.class.getName()); - defaultConverterMap.put("red", RedCompositeConverter.class.getName()); - defaultConverterMap.put("green", GreenCompositeConverter.class.getName()); - defaultConverterMap.put("yellow", YellowCompositeConverter.class.getName()); - defaultConverterMap.put("blue", BlueCompositeConverter.class.getName()); - defaultConverterMap.put("magenta", MagentaCompositeConverter.class.getName()); - defaultConverterMap.put("cyan", CyanCompositeConverter.class.getName()); - defaultConverterMap.put("white", WhiteCompositeConverter.class.getName()); - defaultConverterMap.put("gray", GrayCompositeConverter.class.getName()); - defaultConverterMap.put("boldRed", BoldRedCompositeConverter.class.getName()); - defaultConverterMap.put("boldGreen", BoldGreenCompositeConverter.class.getName()); - defaultConverterMap.put("boldYellow", BoldYellowCompositeConverter.class.getName()); - defaultConverterMap.put("boldBlue", BoldBlueCompositeConverter.class.getName()); - defaultConverterMap.put("boldMagenta", BoldMagentaCompositeConverter.class.getName()); - defaultConverterMap.put("boldCyan", BoldCyanCompositeConverter.class.getName()); - defaultConverterMap.put("boldWhite", BoldWhiteCompositeConverter.class.getName()); - defaultConverterMap.put("highlight", HighlightingCompositeConverter.class.getName()); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("sn", SequenceNumberConverter::new); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("sequenceNumber", SequenceNumberConverter::new); + CONVERTER_CLASS_TO_KEY_MAP.put(SequenceNumberConverter.class.getName(), "sequenceNumber"); - defaultConverterMap.put("lsn", LocalSequenceNumberConverter.class.getName()); + DEFAULT_CONVERTER_SUPPLIER_MAP.put("prefix", PrefixCompositeConverter::new); } @@ -130,8 +182,24 @@ public PatternLayout() { this.postCompileProcessor = new EnsureExceptionHandling(); } + public Map> getDefaultConverterSupplierMap() { + return DEFAULT_CONVERTER_SUPPLIER_MAP; + } + + /** + *

BEWARE: The map of type String,String for mapping conversion words is deprecated. + * Use {@link #getDefaultConverterSupplierMap()} instead.

+ * + *

Existing code such as getDefaultMap().put("k", X.class.getName()) should be replaced by + * getDefaultConverterSupplierMap().put("k", X::new)

+ * + *

Note that values in the map will still be taken into account and processed correctly.

+ * + * @return a map of keys and class names + */ + @Deprecated public Map getDefaultConverterMap() { - return defaultConverterMap; + return DEFAULT_CONVERTER_MAP; } public String doLayout(ILoggingEvent event) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/ViewStatusMessagesServlet.java b/logback-classic/src/main/java/ch/qos/logback/classic/ViewStatusMessagesServlet.java index 113aacabb3..ed851de2b7 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/ViewStatusMessagesServlet.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/ViewStatusMessagesServlet.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,8 @@ */ package ch.qos.logback.classic; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import org.slf4j.LoggerFactory; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/ExceptionMatchEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/ExceptionMatchEvaluator.java new file mode 100644 index 0000000000..57e27524a7 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/ExceptionMatchEvaluator.java @@ -0,0 +1,98 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.boolex; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.core.boolex.EvaluationException; +import ch.qos.logback.core.boolex.EventEvaluatorBase; + +/** + * A simple {@link ch.qos.logback.core.boolex.EventEvaluator} that checks whether the + * logging event being evaluated has a throwable of the same class as specified by the + * {@link #exceptionClass} parameter. + * + *

Here is a

+ *
+ *  <configuration>
+ *     <import class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"/>
+ *     <import class="ch.qos.logback.core.filter.EvaluatorFilter"/>
+ *     <import class="ch.qos.logback.classic.boolex.ExceptionMatchEvaluator"/>
+ *     <import class="ch.qos.logback.core.ConsoleAppender"/>
+ *
+ *     <appender name="CONSOLE" class="ConsoleAppender">
+ *         <filter class="EvaluatorFilter">
+ *             <evaluator class="ExceptionMatchEvaluator">
+ *                 <exceptionClass>java.lang.RuntimeException</exceptionClass>
+ *             </evaluator>
+ *             <OnMismatch>DENY</OnMismatch>
+ *             <OnMatch>NEUTRAL</OnMatch>
+ *         </filter>
+ *
+ *         <encoder class="PatternLayoutEncoder">
+ *             <pattern>%-4relative [%thread] %-5level %logger -%kvp -%msg%n</pattern>
+ *         </encoder>
+ *     </appender>
+ *
+ *     <root level="INFO">
+ *         <appender-ref ref="CONSOLE"/>
+ *     </root>
+ * </configuration>
+ *
+ *
+ * 
+ * + * @since 1.5.15 + */ +public class ExceptionMatchEvaluator extends EventEvaluatorBase { + + String exceptionClass; + private boolean start = false; + + public void start() { + if (exceptionClass == null) { + addError("The exceptionClass must be set"); + return; + } + start = true; + } + + public void stop() { + start = false; + } + + public boolean isStarted() { + return start; + } + + @Override + public boolean evaluate(ILoggingEvent event) throws NullPointerException, EvaluationException { + + IThrowableProxy throwableProxy = event.getThrowableProxy(); + if (throwableProxy == null) { + return false; + } + return throwableProxy.getClassName().equalsIgnoreCase(exceptionClass); + } + + public String getExceptionClass() { + return exceptionClass; + } + + public void setExceptionClass(String exceptionClass) { + this.exceptionClass = exceptionClass; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/IEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/IEvaluator.java index 673a26bf0e..04b6c754cb 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/IEvaluator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/IEvaluator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/JaninoEventEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/JaninoEventEvaluator.java deleted file mode 100644 index 47080eae73..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/JaninoEventEvaluator.java +++ /dev/null @@ -1,152 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.boolex; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.slf4j.Marker; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.IThrowableProxy; -import ch.qos.logback.classic.spi.LoggerContextVO; -import ch.qos.logback.classic.spi.ThrowableProxy; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.boolex.JaninoEventEvaluatorBase; -import ch.qos.logback.core.boolex.Matcher; - -public class JaninoEventEvaluator extends JaninoEventEvaluatorBase { - - public final static String IMPORT_LEVEL = "import ch.qos.logback.classic.Level;\r\n"; - - public final static List DEFAULT_PARAM_NAME_LIST = new ArrayList<>(); - public final static List> DEFAULT_PARAM_TYPE_LIST = new ArrayList<>(); - - static { - DEFAULT_PARAM_NAME_LIST.add("DEBUG"); - DEFAULT_PARAM_NAME_LIST.add("INFO"); - DEFAULT_PARAM_NAME_LIST.add("WARN"); - DEFAULT_PARAM_NAME_LIST.add("ERROR"); - - DEFAULT_PARAM_NAME_LIST.add("event"); - DEFAULT_PARAM_NAME_LIST.add("message"); - - DEFAULT_PARAM_NAME_LIST.add("formattedMessage"); - DEFAULT_PARAM_NAME_LIST.add("logger"); - DEFAULT_PARAM_NAME_LIST.add("loggerContext"); - DEFAULT_PARAM_NAME_LIST.add("level"); - DEFAULT_PARAM_NAME_LIST.add("timeStamp"); - DEFAULT_PARAM_NAME_LIST.add("marker"); - DEFAULT_PARAM_NAME_LIST.add("mdc"); - DEFAULT_PARAM_NAME_LIST.add("throwableProxy"); - DEFAULT_PARAM_NAME_LIST.add("throwable"); - - DEFAULT_PARAM_TYPE_LIST.add(int.class); - DEFAULT_PARAM_TYPE_LIST.add(int.class); - DEFAULT_PARAM_TYPE_LIST.add(int.class); - DEFAULT_PARAM_TYPE_LIST.add(int.class); - - DEFAULT_PARAM_TYPE_LIST.add(ILoggingEvent.class); - DEFAULT_PARAM_TYPE_LIST.add(String.class); - DEFAULT_PARAM_TYPE_LIST.add(String.class); - DEFAULT_PARAM_TYPE_LIST.add(String.class); - DEFAULT_PARAM_TYPE_LIST.add(LoggerContextVO.class); - DEFAULT_PARAM_TYPE_LIST.add(int.class); - DEFAULT_PARAM_TYPE_LIST.add(long.class); - DEFAULT_PARAM_TYPE_LIST.add(Marker.class); - DEFAULT_PARAM_TYPE_LIST.add(Map.class); - DEFAULT_PARAM_TYPE_LIST.add(IThrowableProxy.class); - DEFAULT_PARAM_TYPE_LIST.add(Throwable.class); - } - - protected String getDecoratedExpression() { - String expression = getExpression(); - if (!expression.contains("return")) { - expression = "return " + expression + ";"; - addInfo("Adding [return] prefix and a semicolon suffix. Expression becomes [" + expression + "]"); - addInfo("See also " + CoreConstants.CODES_URL + "#block"); - - } - return IMPORT_LEVEL + expression; - } - - protected String[] getParameterNames() { - List fullNameList = new ArrayList(); - fullNameList.addAll(DEFAULT_PARAM_NAME_LIST); - - for (int i = 0; i < matcherList.size(); i++) { - Matcher m = (Matcher) matcherList.get(i); - fullNameList.add(m.getName()); - } - - return (String[]) fullNameList.toArray(CoreConstants.EMPTY_STRING_ARRAY); - } - - protected Class[] getParameterTypes() { - List> fullTypeList = new ArrayList<>(); - fullTypeList.addAll(DEFAULT_PARAM_TYPE_LIST); - for (int i = 0; i < matcherList.size(); i++) { - fullTypeList.add(Matcher.class); - } - return (Class[]) fullTypeList.toArray(CoreConstants.EMPTY_CLASS_ARRAY); - } - - protected Object[] getParameterValues(ILoggingEvent loggingEvent) { - final int matcherListSize = matcherList.size(); - - int i = 0; - Object[] values = new Object[DEFAULT_PARAM_NAME_LIST.size() + matcherListSize]; - - values[i++] = Level.DEBUG_INTEGER; - values[i++] = Level.INFO_INTEGER; - values[i++] = Level.WARN_INTEGER; - values[i++] = Level.ERROR_INTEGER; - - values[i++] = loggingEvent; - values[i++] = loggingEvent.getMessage(); - values[i++] = loggingEvent.getFormattedMessage(); - values[i++] = loggingEvent.getLoggerName(); - values[i++] = loggingEvent.getLoggerContextVO(); - values[i++] = loggingEvent.getLevel().toInteger(); - values[i++] = loggingEvent.getTimeStamp(); - // In order to avoid NullPointerException, we could push a dummy marker if - // the event's marker is null. However, this would surprise user who - // expect to see a null marker instead of a dummy one. - values[i++] = loggingEvent.getMarker(); - values[i++] = loggingEvent.getMDCPropertyMap(); - - IThrowableProxy iThrowableProxy = loggingEvent.getThrowableProxy(); - - if (iThrowableProxy != null) { - values[i++] = iThrowableProxy; - if (iThrowableProxy instanceof ThrowableProxy) { - values[i++] = ((ThrowableProxy) iThrowableProxy).getThrowable(); - } else { - values[i++] = null; - } - } else { - values[i++] = null; - values[i++] = null; - } - - for (int j = 0; j < matcherListSize; j++) { - values[i++] = (Matcher) matcherList.get(j); - } - - return values; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/MarkerList.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/MarkerList.java new file mode 100644 index 0000000000..25b1e7f5bc --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/MarkerList.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.boolex; + +import org.slf4j.Marker; + +import java.util.List; + +/** + * A helper class to be used in conjunction with JaninoEventEvaluator (removed in 1.5.13). + * + * @since 1.5.4 + */ +public class MarkerList { + + List markers; + + public MarkerList(List markers) { + this.markers = markers; + } + + /** + * Check whether this list contains a given marker. + * + * @param markerName + * @return + */ + public boolean contains(String markerName) { + if(markerName == null || markerName.trim().length() == 0) + return false; + + if(markers == null || markers.isEmpty()) + return false; + + final boolean result = markers.stream().anyMatch( m -> m.contains(markerName)); + return result; + } + + /** + * Return the first marker on the list, can be null. + * + * + * @return the first marker on the list, can be null + */ + public Marker getFirstMarker() { + if(markers == null || markers.isEmpty()) { + return null; + } else { + return markers.get(0); + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnErrorEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnErrorEvaluator.java index e25917a2db..a1e784cc2e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnErrorEvaluator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnErrorEvaluator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnMarkerEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnMarkerEvaluator.java index bca84143e4..8450c2fde6 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnMarkerEvaluator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/OnMarkerEvaluator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -42,14 +42,16 @@ public void addMarker(String markerStr) { */ public boolean evaluate(ILoggingEvent event) throws NullPointerException, EvaluationException { - Marker eventsMarker = event.getMarker(); - if (eventsMarker == null) { + List markerListInEvent = event.getMarkerList(); + if (markerListInEvent == null || markerListInEvent.isEmpty()) { return false; } for (String markerStr : markerList) { - if (eventsMarker.contains(markerStr)) { - return true; + for (Marker markerInEvent : markerListInEvent) { + if (markerInEvent.contains(markerStr)) { + return true; + } } } return false; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/boolex/StubEventEvaluator.java b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/StubEventEvaluator.java new file mode 100644 index 0000000000..35d6d4cdd6 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/boolex/StubEventEvaluator.java @@ -0,0 +1,59 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.boolex; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.boolex.EvaluationException; +import ch.qos.logback.core.boolex.EventEvaluatorBase; +import ch.qos.logback.core.boolex.Matcher; + +import java.util.ArrayList; +import java.util.List; + +public class StubEventEvaluator extends EventEvaluatorBase { + + static public final String MSG_0 = "This class is a stub for JaninoEventEvaluator which was removed in logback version 1.5.13"; + static public final String MSG_1 = "You can migrate existing configurations to Java-only equivalents with the \"Janino Expression migrator\" tool at:"; + static public final String MSG_2 ="https://logback.qos.ch/translator/services/janinoExpressionMigrator.html"; + + protected List matcherList = new ArrayList<>(); + String expression; + + @Override + public void start() { + stop(); + addWarn(MSG_0); + addWarn(MSG_1); + addWarn(MSG_2); + } + + @Override + public boolean evaluate(ILoggingEvent event) throws NullPointerException, EvaluationException { + return false; + } + + public String getExpression() { + return expression; + } + + public void setExpression(String expression) { + this.expression = expression; + } + + public void addMatcher(Matcher matcher) { + matcherList.add(matcher); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java deleted file mode 100644 index defd2b98c3..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/DBAppender.java +++ /dev/null @@ -1,295 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static ch.qos.logback.core.db.DBHelper.closeStatement; - -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import ch.qos.logback.classic.db.names.DBNameResolver; -import ch.qos.logback.classic.db.names.DefaultDBNameResolver; -import ch.qos.logback.classic.spi.*; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.db.DBAppenderBase; - -/** - * The DBAppender inserts logging events into three database tables in a format - * independent of the Java programming language. - * - * For more information about this appender, please refer to the online manual - * at http://logback.qos.ch/manual/appenders.html#DBAppender - * - * @author Ceki Gülcü - * @author Ray DeCampo - * @author Sébastien Pennec - */ -public class DBAppender extends DBAppenderBase { - protected String insertPropertiesSQL; - protected String insertExceptionSQL; - protected String insertSQL; - protected static final Method GET_GENERATED_KEYS_METHOD; - - private DBNameResolver dbNameResolver; - - static final int TIMESTMP_INDEX = 1; - static final int FORMATTED_MESSAGE_INDEX = 2; - static final int LOGGER_NAME_INDEX = 3; - static final int LEVEL_STRING_INDEX = 4; - static final int THREAD_NAME_INDEX = 5; - static final int REFERENCE_FLAG_INDEX = 6; - static final int ARG0_INDEX = 7; - static final int ARG1_INDEX = 8; - static final int ARG2_INDEX = 9; - static final int ARG3_INDEX = 10; - static final int CALLER_FILENAME_INDEX = 11; - static final int CALLER_CLASS_INDEX = 12; - static final int CALLER_METHOD_INDEX = 13; - static final int CALLER_LINE_INDEX = 14; - static final int EVENT_ID_INDEX = 15; - - static final StackTraceElement EMPTY_CALLER_DATA = CallerData.naInstance(); - - static { - // PreparedStatement.getGeneratedKeys() method was added in JDK 1.4 - Method getGeneratedKeysMethod; - try { - // the - getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", (Class[]) null); - } catch (Exception ex) { - getGeneratedKeysMethod = null; - } - GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod; - } - - public void setDbNameResolver(DBNameResolver dbNameResolver) { - this.dbNameResolver = dbNameResolver; - } - - @Override - public void start() { - if (dbNameResolver == null) - dbNameResolver = new DefaultDBNameResolver(); - insertExceptionSQL = SQLBuilder.buildInsertExceptionSQL(dbNameResolver); - insertPropertiesSQL = SQLBuilder.buildInsertPropertiesSQL(dbNameResolver); - insertSQL = SQLBuilder.buildInsertSQL(dbNameResolver); - super.start(); - } - - @Override - protected void subAppend(ILoggingEvent event, Connection connection, PreparedStatement insertStatement) throws Throwable { - - bindLoggingEventWithInsertStatement(insertStatement, event); - bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray()); - - // This is expensive... should we do it every time? - bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData()); - - int updateCount = insertStatement.executeUpdate(); - if (updateCount != 1) { - addWarn("Failed to insert loggingEvent"); - } - } - - protected void secondarySubAppend(ILoggingEvent event, Connection connection, long eventId) throws Throwable { - Map mergedMap = mergePropertyMaps(event); - insertProperties(mergedMap, connection, eventId); - - if (event.getThrowableProxy() != null) { - insertThrowable(event.getThrowableProxy(), connection, eventId); - } - } - - void bindLoggingEventWithInsertStatement(PreparedStatement stmt, ILoggingEvent event) throws SQLException { - stmt.setLong(TIMESTMP_INDEX, event.getTimeStamp()); - stmt.setString(FORMATTED_MESSAGE_INDEX, event.getFormattedMessage()); - stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName()); - stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString()); - stmt.setString(THREAD_NAME_INDEX, event.getThreadName()); - stmt.setShort(REFERENCE_FLAG_INDEX, DBHelper.computeReferenceMask(event)); - } - - void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt, Object[] argArray) throws SQLException { - - int arrayLen = argArray != null ? argArray.length : 0; - - for (int i = 0; i < arrayLen && i < 4; i++) { - stmt.setString(ARG0_INDEX + i, asStringTruncatedTo254(argArray[i])); - } - if (arrayLen < 4) { - for (int i = arrayLen; i < 4; i++) { - stmt.setString(ARG0_INDEX + i, null); - } - } - } - - String asStringTruncatedTo254(Object o) { - String s = null; - if (o != null) { - s = o.toString(); - } - - if (s == null) { - return null; - } - if (s.length() <= 254) { - return s; - } else { - return s.substring(0, 254); - } - } - - void bindCallerDataWithPreparedStatement(PreparedStatement stmt, StackTraceElement[] callerDataArray) throws SQLException { - - StackTraceElement caller = extractFirstCaller(callerDataArray); - - stmt.setString(CALLER_FILENAME_INDEX, caller.getFileName()); - stmt.setString(CALLER_CLASS_INDEX, caller.getClassName()); - stmt.setString(CALLER_METHOD_INDEX, caller.getMethodName()); - stmt.setString(CALLER_LINE_INDEX, Integer.toString(caller.getLineNumber())); - } - - private StackTraceElement extractFirstCaller(StackTraceElement[] callerDataArray) { - StackTraceElement caller = EMPTY_CALLER_DATA; - if (hasAtLeastOneNonNullElement(callerDataArray)) - caller = callerDataArray[0]; - return caller; - } - - private boolean hasAtLeastOneNonNullElement(StackTraceElement[] callerDataArray) { - return callerDataArray != null && callerDataArray.length > 0 && callerDataArray[0] != null; - } - - Map mergePropertyMaps(ILoggingEvent event) { - Map mergedMap = new HashMap(); - // we add the context properties first, then the event properties, since - // we consider that event-specific properties should have priority over - // context-wide properties. - Map loggerContextMap = event.getLoggerContextVO().getPropertyMap(); - Map mdcMap = event.getMDCPropertyMap(); - if (loggerContextMap != null) { - mergedMap.putAll(loggerContextMap); - } - if (mdcMap != null) { - mergedMap.putAll(mdcMap); - } - - return mergedMap; - } - - @Override - protected Method getGeneratedKeysMethod() { - return GET_GENERATED_KEYS_METHOD; - } - - @Override - protected String getInsertSQL() { - return insertSQL; - } - - protected void insertProperties(Map mergedMap, Connection connection, long eventId) throws SQLException { - Set propertiesKeys = mergedMap.keySet(); - if (propertiesKeys.size() > 0) { - PreparedStatement insertPropertiesStatement = null; - try { - insertPropertiesStatement = connection.prepareStatement(insertPropertiesSQL); - - for (String key : propertiesKeys) { - String value = mergedMap.get(key); - - insertPropertiesStatement.setLong(1, eventId); - insertPropertiesStatement.setString(2, key); - insertPropertiesStatement.setString(3, value); - - if (cnxSupportsBatchUpdates) { - insertPropertiesStatement.addBatch(); - } else { - insertPropertiesStatement.execute(); - } - } - - if (cnxSupportsBatchUpdates) { - insertPropertiesStatement.executeBatch(); - } - } finally { - closeStatement(insertPropertiesStatement); - } - } - } - - /** - * Add an exception statement either as a batch or execute immediately if - * batch updates are not supported. - */ - void updateExceptionStatement(PreparedStatement exceptionStatement, String txt, short i, long eventId) throws SQLException { - exceptionStatement.setLong(1, eventId); - exceptionStatement.setShort(2, i); - exceptionStatement.setString(3, txt); - if (cnxSupportsBatchUpdates) { - exceptionStatement.addBatch(); - } else { - exceptionStatement.execute(); - } - } - - short buildExceptionStatement(IThrowableProxy tp, short baseIndex, PreparedStatement insertExceptionStatement, long eventId) throws SQLException { - - StringBuilder buf = new StringBuilder(); - ThrowableProxyUtil.subjoinFirstLine(buf, tp); - updateExceptionStatement(insertExceptionStatement, buf.toString(), baseIndex++, eventId); - - int commonFrames = tp.getCommonFrames(); - StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); - for (int i = 0; i < stepArray.length - commonFrames; i++) { - StringBuilder sb = new StringBuilder(); - sb.append(CoreConstants.TAB); - ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]); - updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex++, eventId); - } - - if (commonFrames > 0) { - StringBuilder sb = new StringBuilder(); - sb.append(CoreConstants.TAB).append("... ").append(commonFrames).append(" common frames omitted"); - updateExceptionStatement(insertExceptionStatement, sb.toString(), baseIndex++, eventId); - } - - return baseIndex; - } - - protected void insertThrowable(IThrowableProxy tp, Connection connection, long eventId) throws SQLException { - - PreparedStatement exceptionStatement = null; - try { - exceptionStatement = connection.prepareStatement(insertExceptionSQL); - - short baseIndex = 0; - while (tp != null) { - baseIndex = buildExceptionStatement(tp, baseIndex, exceptionStatement, eventId); - tp = tp.getCause(); - } - - if (cnxSupportsBatchUpdates) { - exceptionStatement.executeBatch(); - } - } finally { - closeStatement(exceptionStatement); - } - - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java deleted file mode 100644 index 852a44a2a0..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/DBHelper.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import ch.qos.logback.classic.spi.ILoggingEvent; - -/** - * @author Ceki Gülcü - * - */ -public class DBHelper { - - public static final short PROPERTIES_EXIST = 0x01; - public static final short EXCEPTION_EXISTS = 0x02; - - public static short computeReferenceMask(ILoggingEvent event) { - short mask = 0; - - int mdcPropSize = 0; - if (event.getMDCPropertyMap() != null) { - mdcPropSize = event.getMDCPropertyMap().keySet().size(); - } - int contextPropSize = 0; - if (event.getLoggerContextVO().getPropertyMap() != null) { - contextPropSize = event.getLoggerContextVO().getPropertyMap().size(); - } - - if (mdcPropSize > 0 || contextPropSize > 0) { - mask = PROPERTIES_EXIST; - } - if (event.getThrowableProxy() != null) { - mask |= EXCEPTION_EXISTS; - } - return mask; - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/SQLBuilder.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/SQLBuilder.java deleted file mode 100644 index 7c950784f2..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/SQLBuilder.java +++ /dev/null @@ -1,64 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import ch.qos.logback.classic.db.names.*; - -/** - * @author Tomasz Nurkiewicz - * @since 2010-03-16 - */ -public class SQLBuilder { - - static String buildInsertPropertiesSQL(DBNameResolver dbNameResolver) { - StringBuilder sqlBuilder = new StringBuilder("INSERT INTO "); - sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_PROPERTY)).append(" ("); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_KEY)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.MAPPED_VALUE)).append(") "); - sqlBuilder.append("VALUES (?, ?, ?)"); - return sqlBuilder.toString(); - } - - static String buildInsertExceptionSQL(DBNameResolver dbNameResolver) { - StringBuilder sqlBuilder = new StringBuilder("INSERT INTO "); - sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT_EXCEPTION)).append(" ("); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.EVENT_ID)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.I)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TRACE_LINE)).append(") "); - sqlBuilder.append("VALUES (?, ?, ?)"); - return sqlBuilder.toString(); - } - - static String buildInsertSQL(DBNameResolver dbNameResolver) { - StringBuilder sqlBuilder = new StringBuilder("INSERT INTO "); - sqlBuilder.append(dbNameResolver.getTableName(TableName.LOGGING_EVENT)).append(" ("); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.TIMESTMP)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.FORMATTED_MESSAGE)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LOGGER_NAME)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.LEVEL_STRING)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.THREAD_NAME)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.REFERENCE_FLAG)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG0)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG1)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG2)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.ARG3)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_FILENAME)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_CLASS)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_METHOD)).append(", "); - sqlBuilder.append(dbNameResolver.getColumnName(ColumnName.CALLER_LINE)).append(") "); - sqlBuilder.append("VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); - return sqlBuilder.toString(); - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/ColumnName.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/names/ColumnName.java deleted file mode 100644 index 2ab9f419ad..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/ColumnName.java +++ /dev/null @@ -1,26 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -public enum ColumnName { - - EVENT_ID, - - TIMESTMP, FORMATTED_MESSAGE, LOGGER_NAME, LEVEL_STRING, THREAD_NAME, REFERENCE_FLAG, ARG0, ARG1, ARG2, ARG3, CALLER_FILENAME, CALLER_CLASS, CALLER_METHOD, CALLER_LINE, - - // MDC - MAPPED_KEY, MAPPED_VALUE, - - I, TRACE_LINE; -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DBNameResolver.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DBNameResolver.java deleted file mode 100644 index af4e75d244..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DBNameResolver.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -/** - * Source of table and column names used in SQL queries generated by {@link ch.qos.logback.classic.db.DBAppender} - * - * Implement this interface to override default table and/or column names used by {@link ch.qos.logback.classic.db.DBAppender}. - * - * @author Tomasz Nurkiewicz - * @author Ceki Gulcu - * @since 0.9.19 - * @see ch.qos.logback.classic.db.names.DefaultDBNameResolver - * @see ch.qos.logback.classic.db.names.SimpleDBNameResolver - */ -public interface DBNameResolver { - - > String getTableName(N tableName); - - > String getColumnName(N columnName); -} \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DefaultDBNameResolver.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DefaultDBNameResolver.java deleted file mode 100644 index d604a4461a..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/DefaultDBNameResolver.java +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -/** - * The default name resolver simply returns the enum passes as parameter - * as a lower case string. - * - * @author Tomasz Nurkiewicz - * @author Ceki Gulcu - * @since 0.9.19 - */ -public class DefaultDBNameResolver implements DBNameResolver { - - public > String getTableName(N tableName) { - return tableName.toString().toLowerCase(); - } - - public > String getColumnName(N columnName) { - return columnName.toString().toLowerCase(); - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/SimpleDBNameResolver.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/names/SimpleDBNameResolver.java deleted file mode 100644 index 5bb9609c1e..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/SimpleDBNameResolver.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -/** - * Adds custom prefix/suffix to table and column names. - * - * @author Tomasz Nurkiewicz - * @since 0.9.20 - */ -public class SimpleDBNameResolver implements DBNameResolver { - - private String tableNamePrefix = ""; - - private String tableNameSuffix = ""; - - private String columnNamePrefix = ""; - - private String columnNameSuffix = ""; - - public > String getTableName(N tableName) { - return tableNamePrefix + tableName.name().toLowerCase() + tableNameSuffix; - } - - public > String getColumnName(N columnName) { - return columnNamePrefix + columnName.name().toLowerCase() + columnNameSuffix; - } - - public void setTableNamePrefix(String tableNamePrefix) { - this.tableNamePrefix = tableNamePrefix != null ? tableNamePrefix : ""; - } - - public void setTableNameSuffix(String tableNameSuffix) { - this.tableNameSuffix = tableNameSuffix != null ? tableNameSuffix : ""; - } - - public void setColumnNamePrefix(String columnNamePrefix) { - this.columnNamePrefix = columnNamePrefix != null ? columnNamePrefix : ""; - } - - public void setColumnNameSuffix(String columnNameSuffix) { - this.columnNameSuffix = columnNameSuffix != null ? columnNameSuffix : ""; - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/TableName.java b/logback-classic/src/main/java/ch/qos/logback/classic/db/names/TableName.java deleted file mode 100644 index b2c33cfdf1..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/names/TableName.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -/** - * @author Tomasz Nurkiewicz - * @since 0.9.19 - */ -public enum TableName { - - LOGGING_EVENT, LOGGING_EVENT_PROPERTY, LOGGING_EVENT_EXCEPTION - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/db/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/db/package.html deleted file mode 100644 index 7bc56106c3..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/db/package.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - -

The ch.qos.logback.classic.db package provides means to append logging events -into various databases. -

- -

Most popular database systems, such as PostgreSQL, MySQL, Oracle, DB2 and MsSQL -are supported. -

- -

Just as importantly, the way for obtaining JDBC connections is pluggable. Connections can -be obtained through the traditional way of DriverManager, or alternatively as a DataSource. -A DataSource can be instantiated directly or it can obtained through JNDI. -

- - \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java new file mode 100644 index 0000000000..00ae19d754 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/JsonEncoder.java @@ -0,0 +1,678 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.encoder; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.encoder.EncoderBase; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static ch.qos.logback.core.CoreConstants.COLON_CHAR; +import static ch.qos.logback.core.CoreConstants.COMMA_CHAR; +import static ch.qos.logback.core.CoreConstants.DOUBLE_QUOTE_CHAR; +import static ch.qos.logback.core.CoreConstants.UTF_8_CHARSET; +import static ch.qos.logback.core.encoder.JsonEscapeUtil.jsonEscapeString; +import static ch.qos.logback.core.model.ModelConstants.NULL_STR; + +/** + * JSON encoder that produces one JSON object per line in JSON Lines format, suitable for structured logging. + * Each {@link ILoggingEvent} is encoded into a JSON object containing fields such as timestamp, level, message, + * and optional elements like MDC properties, markers, and stack traces. + * + *

This encoder supports extensive configuration through boolean flags to include or exclude specific fields + * in the output, allowing customization for different logging needs. For example, you can enable/disable + * sequence numbers, nanoseconds, thread names, logger context, markers, MDC, key-value pairs, arguments, + * and throwable information.

+ * + *

The encoder is designed for extensibility: subclasses can override protected methods (e.g., + * {@link #appendLoggerContext}, {@link #appendThrowableProxy}, {@link #appendMarkers}) to customize + * how specific parts of the JSON are generated. Additionally, the {@link #appendCustomFields} hook + * allows appending custom top-level fields to the JSON object.

+ * + *

Configuration

+ *

Use the setter methods (e.g., {@link #setWithSequenceNumber}, {@link #setWithTimestamp}) to control + * which fields are included. By default, most fields are enabled except {@code withFormattedMessage}.

+ * + *

Example Usage

+ *
{@code
+ * 
+ *   
+ *     false
+ *     false
+ *     false
+ *   
+ * 
+ * }
+ * + *

This produces output similar to the following (on a single line): + *

+ * + *
{"timestamp":1640995200000,"level":"INFO","loggerName":"com.example.MyClass","context":{"name":"default","birthdate":1640995200000,"properties":{}},"message":"Hello World"}
+ * + * @see JSON Lines + * @see RFC 8259 (The JavaScript Object Notation (JSON) Data Interchange Format) + * @see ch.qos.logback.core.encoder.EncoderBase + * @since 1.3.8/1.4.8 + * @author Ceki Gülcü + */ +public class JsonEncoder extends EncoderBase { + static final boolean DO_NOT_ADD_QUOTE_KEY = false; + static final boolean ADD_QUOTE_KEY = true; + static int DEFAULT_SIZE = 1024; + static int DEFAULT_SIZE_WITH_THROWABLE = DEFAULT_SIZE * 8; + + static byte[] EMPTY_BYTES = new byte[0]; + + public static final String CONTEXT_ATTR_NAME = "context"; + public static final String NAME_ATTR_NAME = "name"; + public static final String BIRTHDATE_ATTR_NAME = "birthdate"; + public static final String CONTEXT_PROPERTIES_ATTR_NAME = "properties"; + + public static final String TIMESTAMP_ATTR_NAME = "timestamp"; + + public static final String NANOSECONDS_ATTR_NAME = "nanoseconds"; + + public static final String SEQUENCE_NUMBER_ATTR_NAME = "sequenceNumber"; + + public static final String LEVEL_ATTR_NAME = "level"; + public static final String MARKERS_ATTR_NAME = "markers"; + public static final String THREAD_NAME_ATTR_NAME = "threadName"; + public static final String MDC_ATTR_NAME = "mdc"; + public static final String LOGGER_ATTR_NAME = "loggerName"; + + public static final String MESSAGE_ATTR_NAME = "message"; + + public static final String FORMATTED_MESSAGE_ATTR_NAME = "formattedMessage"; + + public static final String ARGUMENT_ARRAY_ATTR_NAME = "arguments"; + public static final String KEY_VALUE_PAIRS_ATTR_NAME = "kvpList"; + + public static final String THROWABLE_ATTR_NAME = "throwable"; + + private static final String CYCLIC_THROWABLE_ATTR_NAME = "cyclic"; + + public static final String CAUSE_ATTR_NAME = "cause"; + + public static final String SUPPRESSED_ATTR_NAME = "suppressed"; + + public static final String COMMON_FRAMES_COUNT_ATTR_NAME = "commonFramesCount"; + + public static final String CLASS_NAME_ATTR_NAME = "className"; + public static final String METHOD_NAME_ATTR_NAME = "methodName"; + private static final String FILE_NAME_ATTR_NAME = "fileName"; + private static final String LINE_NUMBER_ATTR_NAME = "lineNumber"; + + public static final String STEP_ARRAY_NAME_ATTRIBUTE = "stepArray"; + + protected static final char OPEN_OBJ = '{'; + protected static final char CLOSE_OBJ = '}'; + protected static final char OPEN_ARRAY = '['; + protected static final char CLOSE_ARRAY = ']'; + + protected static final char QUOTE = DOUBLE_QUOTE_CHAR; + protected static final char SP = ' '; + protected static final char ENTRY_SEPARATOR = COLON_CHAR; + + protected static final String COL_SP = ": "; + + protected static final String QUOTE_COL = "\":"; + + protected static final char VALUE_SEPARATOR = COMMA_CHAR; + + private boolean withSequenceNumber = true; + + private boolean withTimestamp = true; + private boolean withNanoseconds = true; + + private boolean withLevel = true; + private boolean withThreadName = true; + private boolean withLoggerName = true; + private boolean withContext = true; + private boolean withMarkers = true; + private boolean withMDC = true; + private boolean withKVPList = true; + private boolean withMessage = true; + private boolean withArguments = true; + private boolean withThrowable = true; + private boolean withFormattedMessage = false; + + @Override + public byte[] headerBytes() { + return EMPTY_BYTES; + } + + @Override + public byte[] encode(ILoggingEvent event) { + final int initialCapacity = event.getThrowableProxy() == null ? DEFAULT_SIZE : DEFAULT_SIZE_WITH_THROWABLE; + StringBuilder sb = new StringBuilder(initialCapacity); + sb.append(OPEN_OBJ); + + if (withSequenceNumber) { + appenderMemberWithLongValue(sb, SEQUENCE_NUMBER_ATTR_NAME, event.getSequenceNumber()); + } + + if (withTimestamp) { + appendValueSeparator(sb, withSequenceNumber); + appenderMemberWithLongValue(sb, TIMESTAMP_ATTR_NAME, event.getTimeStamp()); + } + + if (withNanoseconds) { + appendValueSeparator(sb, withSequenceNumber, withTimestamp); + appenderMemberWithLongValue(sb, NANOSECONDS_ATTR_NAME, event.getNanoseconds()); + } + + if (withLevel) { + appendValueSeparator(sb, withNanoseconds, withSequenceNumber, withTimestamp); + String levelStr = event.getLevel() != null ? event.getLevel().levelStr : NULL_STR; + appenderMember(sb, LEVEL_ATTR_NAME, levelStr); + } + + if (withThreadName) { + appendValueSeparator(sb, withLevel, withNanoseconds, withSequenceNumber, withTimestamp); + appenderMember(sb, THREAD_NAME_ATTR_NAME, jsonEscape(event.getThreadName())); + } + + if (withLoggerName) { + appendValueSeparator(sb, withThreadName, withLevel, withNanoseconds, withSequenceNumber, withTimestamp); + appenderMember(sb, LOGGER_ATTR_NAME, event.getLoggerName()); + } + + if (withContext) { + // at this stage we assume that at least one field was written + sb.append(VALUE_SEPARATOR); + appendLoggerContext(sb, event.getLoggerContextVO()); + } + + if (withMarkers) + appendMarkers(sb, event); + + if (withMDC) + appendMDC(sb, event); + + if (withKVPList) + appendKeyValuePairs(sb, event); + + if (withMessage) { + sb.append(VALUE_SEPARATOR); + appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(event.getMessage())); + } + + if (withFormattedMessage) { + sb.append(VALUE_SEPARATOR); + appenderMember(sb, FORMATTED_MESSAGE_ATTR_NAME, jsonEscape(event.getFormattedMessage())); + } + + if (withArguments) { + appendArgumentArray(sb, event); + } + + if (withThrowable) + appendThrowableProxy(sb, THROWABLE_ATTR_NAME, event.getThrowableProxy()); + + // allow subclasses to append custom top-level fields; default implementation is a no-op + appendCustomFields(sb, event); + + sb.append(CLOSE_OBJ); + sb.append(CoreConstants.JSON_LINE_SEPARATOR); + return sb.toString().getBytes(UTF_8_CHARSET); + } + + /** + * Append a JSON value separator (a comma) to the provided {@link StringBuilder} + * when any of the supplied boolean flags indicate that a subsequent element + * is present. + * + *

Callers pass a sequence of booleans that represent whether subsequent + * JSON members will be written. If at least one of those booleans is + * {@code true}, this method appends a single comma (',') to separate JSON + * fields.

+ * + *

This method is protected so subclasses that extend the encoder can + * reuse or override the logic for inserting separators between generated + * JSON members.

+ * + * @param sb the {@link StringBuilder} to append the separator to; must not be {@code null} + * @param subsequentConditionals one or more booleans indicating whether + * subsequent JSON elements will be written. + * If any value is {@code true}, a comma is appended. + */ + protected void appendValueSeparator(StringBuilder sb, boolean... subsequentConditionals) { + boolean enabled = false; + for (boolean subsequent : subsequentConditionals) { + if (subsequent) { + enabled = true; + break; + } + } + + if (enabled) + sb.append(VALUE_SEPARATOR); + } + + protected void appendLoggerContext(StringBuilder sb, LoggerContextVO loggerContextVO) { + + sb.append(QUOTE).append(CONTEXT_ATTR_NAME).append(QUOTE_COL); + if (loggerContextVO == null) { + sb.append(NULL_STR); + return; + } + + sb.append(OPEN_OBJ); + appenderMember(sb, NAME_ATTR_NAME, nullSafeStr(loggerContextVO.getName())); + sb.append(VALUE_SEPARATOR); + appenderMemberWithLongValue(sb, BIRTHDATE_ATTR_NAME, loggerContextVO.getBirthTime()); + sb.append(VALUE_SEPARATOR); + + appendMap(sb, CONTEXT_PROPERTIES_ATTR_NAME, loggerContextVO.getPropertyMap()); + sb.append(CLOSE_OBJ); + + } + + protected void appendMap(StringBuilder sb, String attrName, Map map) { + sb.append(QUOTE).append(attrName).append(QUOTE_COL); + if (map == null) { + sb.append(NULL_STR); + return; + } + + sb.append(OPEN_OBJ); + + boolean addComma = false; + Set> entries = map.entrySet(); + for (Map.Entry entry : entries) { + if (addComma) { + sb.append(VALUE_SEPARATOR); + } + addComma = true; + appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue())); + } + + sb.append(CLOSE_OBJ); + } + + protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp) { + appendThrowableProxy(sb, attributeName, itp, true); + } + + protected void appendThrowableProxy(StringBuilder sb, String attributeName, IThrowableProxy itp, boolean appendValueSeparator) { + + if (appendValueSeparator) + sb.append(VALUE_SEPARATOR); + + // in the nominal case, attributeName != null. However, attributeName will be null for suppressed + // IThrowableProxy array, in which case no attribute name is needed + if (attributeName != null) { + sb.append(QUOTE).append(attributeName).append(QUOTE_COL); + if (itp == null) { + sb.append(NULL_STR); + return; + } + } + + sb.append(OPEN_OBJ); + + appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(itp.getClassName())); + + sb.append(VALUE_SEPARATOR); + appenderMember(sb, MESSAGE_ATTR_NAME, jsonEscape(itp.getMessage())); + + if (itp.isCyclic()) { + sb.append(VALUE_SEPARATOR); + appenderMember(sb, CYCLIC_THROWABLE_ATTR_NAME, jsonEscape("true")); + } + + sb.append(VALUE_SEPARATOR); + appendSTEPArray(sb, itp.getStackTraceElementProxyArray(), itp.getCommonFrames()); + + if (itp.getCommonFrames() != 0) { + sb.append(VALUE_SEPARATOR); + appenderMemberWithIntValue(sb, COMMON_FRAMES_COUNT_ATTR_NAME, itp.getCommonFrames()); + } + + IThrowableProxy cause = itp.getCause(); + if (cause != null) { + appendThrowableProxy(sb, CAUSE_ATTR_NAME, cause); + } + + IThrowableProxy[] suppressedArray = itp.getSuppressed(); + if (suppressedArray != null && suppressedArray.length != 0) { + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(SUPPRESSED_ATTR_NAME).append(QUOTE_COL); + sb.append(OPEN_ARRAY); + + boolean first = true; + for (IThrowableProxy suppressedITP : suppressedArray) { + appendThrowableProxy(sb, null, suppressedITP, !first); + if (first) + first = false; + } + sb.append(CLOSE_ARRAY); + } + + sb.append(CLOSE_OBJ); + + } + + protected void appendSTEPArray(StringBuilder sb, StackTraceElementProxy[] stepArray, int commonFrames) { + sb.append(QUOTE).append(STEP_ARRAY_NAME_ATTRIBUTE).append(QUOTE_COL).append(OPEN_ARRAY); + + // If there are no stack trace elements, write an empty array and return early. + if (stepArray == null || stepArray.length == 0) { + sb.append(CLOSE_ARRAY); + return; + } + + int len = stepArray.length; + + if (commonFrames >= len) { + commonFrames = 0; + } + + for (int i = 0; i < len - commonFrames; i++) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + + StackTraceElementProxy step = stepArray[i]; + + sb.append(OPEN_OBJ); + StackTraceElement ste = step.getStackTraceElement(); + + appenderMember(sb, CLASS_NAME_ATTR_NAME, nullSafeStr(ste.getClassName())); + sb.append(VALUE_SEPARATOR); + + appenderMember(sb, METHOD_NAME_ATTR_NAME, nullSafeStr(ste.getMethodName())); + sb.append(VALUE_SEPARATOR); + + appenderMember(sb, FILE_NAME_ATTR_NAME, nullSafeStr(ste.getFileName())); + sb.append(VALUE_SEPARATOR); + + appenderMemberWithIntValue(sb, LINE_NUMBER_ATTR_NAME, ste.getLineNumber()); + sb.append(CLOSE_OBJ); + + } + + sb.append(CLOSE_ARRAY); + } + + /** + * Hook allowing subclasses to append additional fields into the root JSON object. + * Default implementation is a no-op. + * + *

Subclasses may append additional top-level JSON members here. If a + * subclass writes additional members it should prepend them with + * {@link #VALUE_SEPARATOR} (a comma) if necessary to keep the JSON valid. + * Implementations must not close the root JSON object or write the final + * line separator; {@link JsonEncoder} handles those.

+ * + * @param sb the StringBuilder that accumulates the JSON output; never null + * @param event the logging event being encoded; never null + */ + protected void appendCustomFields(StringBuilder sb, ILoggingEvent event) { + // no-op by default; subclasses may append VALUE_SEPARATOR then their fields + } + + protected void appenderMember(StringBuilder sb, String key, String value) { + sb.append(QUOTE).append(key).append(QUOTE_COL).append(QUOTE).append(value).append(QUOTE); + } + + protected void appenderMemberWithIntValue(StringBuilder sb, String key, int value) { + sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); + } + + protected void appenderMemberWithLongValue(StringBuilder sb, String key, long value) { + sb.append(QUOTE).append(key).append(QUOTE_COL).append(value); + } + + protected void appendKeyValuePairs(StringBuilder sb, ILoggingEvent event) { + List kvpList = event.getKeyValuePairs(); + if (kvpList == null || kvpList.isEmpty()) + return; + + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(KEY_VALUE_PAIRS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); + final int len = kvpList.size(); + for (int i = 0; i < len; i++) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + KeyValuePair kvp = kvpList.get(i); + sb.append(OPEN_OBJ); + appenderMember(sb, jsonEscapedToString(kvp.key), jsonEscapedToString(kvp.value)); + sb.append(CLOSE_OBJ); + } + sb.append(CLOSE_ARRAY); + } + + protected void appendArgumentArray(StringBuilder sb, ILoggingEvent event) { + Object[] argumentArray = event.getArgumentArray(); + if (argumentArray == null) + return; + + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(ARGUMENT_ARRAY_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); + final int len = argumentArray.length; + for (int i = 0; i < len; i++) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(jsonEscapedToString(argumentArray[i])).append(QUOTE); + + } + sb.append(CLOSE_ARRAY); + } + + protected void appendMarkers(StringBuilder sb, ILoggingEvent event) { + List markerList = event.getMarkerList(); + if (markerList == null) + return; + + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(MARKERS_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_ARRAY); + final int len = markerList.size(); + for (int i = 0; i < len; i++) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(jsonEscapedToString(markerList.get(i))).append(QUOTE); + + } + sb.append(CLOSE_ARRAY); + } + + private String jsonEscapedToString(Object o) { + if (o == null) + return NULL_STR; + return jsonEscapeString(o.toString()); + } + + private String nullSafeStr(String s) { + if (s == null) + return NULL_STR; + return s; + } + + private String jsonEscape(String s) { + if (s == null) + return NULL_STR; + return jsonEscapeString(s); + } + + protected void appendMDC(StringBuilder sb, ILoggingEvent event) { + Map map = event.getMDCPropertyMap(); + sb.append(VALUE_SEPARATOR); + sb.append(QUOTE).append(MDC_ATTR_NAME).append(QUOTE_COL).append(SP).append(OPEN_OBJ); + if (isNotEmptyMap(map)) { + Set> entrySet = map.entrySet(); + int i = 0; + for (Map.Entry entry : entrySet) { + if (i != 0) + sb.append(VALUE_SEPARATOR); + appenderMember(sb, jsonEscapedToString(entry.getKey()), jsonEscapedToString(entry.getValue())); + i++; + } + + } + sb.append(CLOSE_OBJ); + } + + /** + * Return {@code true} when the provided map is non-null and non-empty. + * + * @param map the map to check; may be null + * @return {@code true} if the map contains at least one entry + */ + boolean isNotEmptyMap(Map map) { + if (map == null) + return false; + return !map.isEmpty(); + } + + @Override + public byte[] footerBytes() { + return EMPTY_BYTES; + } + + /** + * Set whether the sequence number is included in each encoded event. + * @param withSequenceNumber {@code true} to include the sequence number in the output + * @since 1.5.0 + */ + public void setWithSequenceNumber(boolean withSequenceNumber) { + this.withSequenceNumber = withSequenceNumber; + } + + /** + * Set whether the event timestamp is included in each encoded event. + * @param withTimestamp {@code true} to include the event timestamp in the output + * @since 1.5.0 + */ + public void setWithTimestamp(boolean withTimestamp) { + this.withTimestamp = withTimestamp; + } + + /** + * Set whether nanoseconds will be included in the timestamp output. + * @param withNanoseconds {@code true} to include nanoseconds in the timestamp output + * @since 1.5.0 + */ + public void setWithNanoseconds(boolean withNanoseconds) { + this.withNanoseconds = withNanoseconds; + } + + /** + * Enable or disable the inclusion of the log level in the encoded output. + * + * @param withLevel {@code true} to include the log level. Default is {@code true}. + */ + public void setWithLevel(boolean withLevel) { + this.withLevel = withLevel; + } + + /** + * Enable or disable the inclusion of the thread name in the encoded output. + * + * @param withThreadName {@code true} to include the thread name. Default is {@code true}. + */ + public void setWithThreadName(boolean withThreadName) { + this.withThreadName = withThreadName; + } + + /** + * Enable or disable the inclusion of the logger name in the encoded output. + * + * @param withLoggerName {@code true} to include the logger name. Default is {@code true}. + */ + public void setWithLoggerName(boolean withLoggerName) { + this.withLoggerName = withLoggerName; + } + + /** + * Enable or disable the inclusion of the logger context information. + * + * @param withContext {@code true} to include the logger context. Default is {@code true}. + */ + public void setWithContext(boolean withContext) { + this.withContext = withContext; + } + + /** + * Enable or disable the inclusion of markers in the encoded output. + * + * @param withMarkers {@code true} to include markers. Default is {@code true}. + */ + public void setWithMarkers(boolean withMarkers) { + this.withMarkers = withMarkers; + } + + /** + * Enable or disable the inclusion of MDC properties in the encoded output. + * + * @param withMDC {@code true} to include MDC properties. Default is {@code true}. + */ + public void setWithMDC(boolean withMDC) { + this.withMDC = withMDC; + } + + /** + * Enable or disable the inclusion of key-value pairs attached to the logging event. + * + * @param withKVPList {@code true} to include the key/value pairs list. Default is {@code true}. + */ + public void setWithKVPList(boolean withKVPList) { + this.withKVPList = withKVPList; + } + + /** + * Enable or disable the inclusion of the raw message text in the encoded output. + * + * @param withMessage {@code true} to include the message. Default is {@code true}. + */ + public void setWithMessage(boolean withMessage) { + this.withMessage = withMessage; + } + + /** + * Enable or disable the inclusion of the event argument array in the encoded output. + * + * @param withArguments {@code true} to include the argument array. Default is {@code true}. + */ + public void setWithArguments(boolean withArguments) { + this.withArguments = withArguments; + } + + /** + * Enable or disable the inclusion of throwable information in the encoded output. + * + * @param withThrowable {@code true} to include throwable/stacktrace information. Default is {@code true}. + */ + public void setWithThrowable(boolean withThrowable) { + this.withThrowable = withThrowable; + } + + /** + * Enable or disable the inclusion of the formatted message in the encoded output. + * + * @param withFormattedMessage {@code true} to include the formatted message. Default is {@code false}. + */ + public void setWithFormattedMessage(boolean withFormattedMessage) { + this.withFormattedMessage = withFormattedMessage; + } + + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/PatternLayoutEncoder.java b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/PatternLayoutEncoder.java index 2f08202897..e3dbdb3a12 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/encoder/PatternLayoutEncoder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/encoder/PatternLayoutEncoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/filter/LevelFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/filter/LevelFilter.java index ae2f84a1ae..32a53481dc 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/filter/LevelFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/filter/LevelFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,7 +20,7 @@ /** * A class that filters events by the level equality. - + * *

* For more information about this filter, please refer to the online manual at * http://logback.qos.ch/manual/filters.html#levelFilter diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/filter/ThresholdFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/filter/ThresholdFilter.java index 6c38ad96b8..95d5f29d16 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/filter/ThresholdFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/filter/ThresholdFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,11 +21,10 @@ /** * Filters events below the threshold level. * - * Events with a level below the specified - * level will be denied, while events with a level - * equal or above the specified level will trigger a - * FilterReply.NEUTRAL result, to allow the rest of the - * filter chain process the event. + * Events with a level below the specified level will be denied, while events + * with a level equal or above the specified level will trigger a + * FilterReply.NEUTRAL result, to allow the rest of the filter chain process the + * event. * * For more information about filters, please refer to the online manual at * http://logback.qos.ch/manual/filters.html#thresholdFilter diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.java index 4b1e7aa8e7..90232a5913 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,13 +15,13 @@ import java.io.IOException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; -import javax.servlet.http.HttpServletRequest; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; import org.slf4j.MDC; @@ -42,7 +42,8 @@ public void destroy() { // do nothing } - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { insertIntoMDC(request); try { @@ -75,7 +76,7 @@ void clearMDC() { MDC.remove(ClassicConstants.REQUEST_REMOTE_HOST_MDC_KEY); MDC.remove(ClassicConstants.REQUEST_REQUEST_URI); MDC.remove(ClassicConstants.REQUEST_QUERY_STRING); - // removing possibly inexistent item is OK + // removing possibly nonexistent item is OK MDC.remove(ClassicConstants.REQUEST_REQUEST_URL); MDC.remove(ClassicConstants.REQUEST_METHOD); MDC.remove(ClassicConstants.REQUEST_USER_AGENT_MDC_KEY); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/helpers/WithLayoutListAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/WithLayoutListAppender.java new file mode 100644 index 0000000000..f315e10c21 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/WithLayoutListAppender.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.helpers; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; + +/** + * An appender used for testing. + * + * @author ceki + * @since 1.3.0 + */ +public class WithLayoutListAppender extends AppenderBase { + + public List list = new ArrayList<>(); + + String pattern; + + PatternLayout patternLayout; + + @Override + public void start() { + if (pattern == null) { + addError("null pattern disallowed"); + return; + } + patternLayout = new PatternLayout(); + patternLayout.setContext(context); + patternLayout.setPattern(pattern); + patternLayout.start(); + if (patternLayout.isStarted()) + super.start(); + } + + protected void append(ILoggingEvent e) { + String result = patternLayout.doLayout(e); + list.add(result); + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/helpers/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/package.html index 4caced844c..c89646b9f9 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/helpers/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/helpers/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java b/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java index 7cadbe823c..d16997f57e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultCssBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java b/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java index 05520b3a84..f698a05bfa 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/html/DefaultThrowableRenderer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -49,7 +49,8 @@ void render(StringBuilder sbuf, IThrowableProxy tp) { if (commonFrames > 0) { sbuf.append(TRACE_PREFIX); - sbuf.append("\t... ").append(commonFrames).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR); + sbuf.append("\t... ").append(commonFrames).append(" common frames omitted") + .append(CoreConstants.LINE_SEPARATOR); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java b/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java index 6f79477812..1512a32773 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/html/HTMLLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,24 +14,28 @@ package ch.qos.logback.classic.html; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.pattern.MDCConverter; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.html.DefaultCssBuilder; import ch.qos.logback.core.helpers.Transform; import ch.qos.logback.core.html.HTMLLayoutBase; import ch.qos.logback.core.html.IThrowableRenderer; import ch.qos.logback.core.pattern.Converter; +import ch.qos.logback.core.pattern.DynamicConverter; + import static ch.qos.logback.core.CoreConstants.LINE_SEPARATOR; /** * - * HTMLLayout outputs events in an HTML table.

The content of the table - * columns are specified using a conversion pattern. See - * {@link ch.qos.logback.classic.PatternLayout} for documentation on the - * available patterns.

For more information about this layout, please refer - * to the online manual at + * HTMLLayout outputs events in an HTML table. + *

+ * The content of the table columns are specified using a conversion pattern. + * See {@link ch.qos.logback.classic.PatternLayout} for documentation on the + * available patterns. + *

+ * For more information about this layout, please refer to the online manual at * http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout * * @author Ceki Gülcü @@ -69,8 +73,9 @@ public void start() { } } - protected Map getDefaultConverterMap() { - return PatternLayout.defaultConverterMap; + @Override + protected Map> getDefaultConverterSupplierMap() { + return PatternLayout.DEFAULT_CONVERTER_SUPPLIER_MAP; } public String doLayout(ILoggingEvent event) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java b/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java index d4216f42f0..967a92fd72 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/html/UrlCssBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,9 +16,9 @@ import ch.qos.logback.core.html.CssBuilder; /** - * This class helps the HTMLLayout build the CSS link. - * It either provides the HTMLLayout with a default css file, - * or builds the link to an external, user-specified, file. + * This class helps the HTMLLayout build the CSS link. It either provides the + * HTMLLayout with a default css file, or builds the link to an external, + * user-specified, file. * * @author Sébastien Pennec */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/html/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/html/package.html index c988c3f19e..823e0ff809 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/html/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/html/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java deleted file mode 100644 index 6f7b135e48..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfigurator.java +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2016, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jmx; - -import java.io.File; -import java.io.FileNotFoundException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import javax.management.InstanceNotFoundException; -import javax.management.MBeanRegistrationException; -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.LoggerContextListener; -import ch.qos.logback.classic.util.ContextInitializer; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.status.StatusListener; -import ch.qos.logback.core.status.StatusListenerAsList; -import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.util.StatusPrinter; - -/** - * A class that provides access to logback components via JMX. - * - *

Since this class implements {@link JMXConfiguratorMBean} it has to be - * named as JMXConfigurator}. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - * - * Contributor: Sebastian Davids See http://bugzilla.qos.ch/show_bug.cgi?id=35 - */ -public class JMXConfigurator extends ContextAwareBase implements JMXConfiguratorMBean, LoggerContextListener { - - private static String EMPTY = ""; - - LoggerContext loggerContext; - MBeanServer mbs; - ObjectName objectName; - String objectNameAsString; - - // whether to output status events on the console when reloading the - // configuration - boolean debug = true; - - boolean started; - - public JMXConfigurator(LoggerContext loggerContext, MBeanServer mbs, ObjectName objectName) { - started = true; - this.context = loggerContext; - this.loggerContext = loggerContext; - this.mbs = mbs; - this.objectName = objectName; - this.objectNameAsString = objectName.toString(); - if (previouslyRegisteredListenerWithSameObjectName()) { - addError("Previously registered JMXConfigurator named [" + objectNameAsString + "] in the logger context named [" + loggerContext.getName() + "]"); - } else { - // register as a listener only if there are no homonyms - loggerContext.addListener(this); - } - } - - private boolean previouslyRegisteredListenerWithSameObjectName() { - List lcll = loggerContext.getCopyOfListenerList(); - for (LoggerContextListener lcl : lcll) { - if (lcl instanceof JMXConfigurator) { - JMXConfigurator jmxConfigurator = (JMXConfigurator) lcl; - if (objectName.equals(jmxConfigurator.objectName)) { - return true; - } - } - } - return false; - } - - public void reloadDefaultConfiguration() throws JoranException { - ContextInitializer ci = new ContextInitializer(loggerContext); - URL url = ci.findURLOfDefaultConfigurationFile(true); - reloadByURL(url); - } - - public void reloadByFileName(String fileName) throws JoranException, FileNotFoundException { - File f = new File(fileName); - if (f.exists() && f.isFile()) { - URL url; - try { - url = f.toURI().toURL(); - reloadByURL(url); - } catch (MalformedURLException e) { - throw new RuntimeException("Unexpected MalformedURLException occured. See nexted cause.", e); - } - - } else { - String errMsg = "Could not find [" + fileName + "]"; - addInfo(errMsg); - throw new FileNotFoundException(errMsg); - } - } - - void addStatusListener(StatusListener statusListener) { - StatusManager sm = loggerContext.getStatusManager(); - sm.add(statusListener); - } - - void removeStatusListener(StatusListener statusListener) { - StatusManager sm = loggerContext.getStatusManager(); - sm.remove(statusListener); - } - - public void reloadByURL(URL url) throws JoranException { - StatusListenerAsList statusListenerAsList = new StatusListenerAsList(); - - addStatusListener(statusListenerAsList); - addInfo("Resetting context: " + loggerContext.getName()); - loggerContext.reset(); - - // after a reset the statusListenerAsList gets removed as a listener - addStatusListener(statusListenerAsList); - - try { - if (url != null) { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(loggerContext); - configurator.doConfigure(url); - addInfo("Context: " + loggerContext.getName() + " reloaded."); - } - } finally { - removeStatusListener(statusListenerAsList); - if (debug) { - StatusPrinter.print(statusListenerAsList.getStatusList()); - } - } - } - - public void setLoggerLevel(String loggerName, String levelStr) { - if (loggerName == null) { - return; - } - if (levelStr == null) { - return; - } - loggerName = loggerName.trim(); - levelStr = levelStr.trim(); - - addInfo("Trying to set level " + levelStr + " to logger " + loggerName); - LoggerContext lc = (LoggerContext) context; - - Logger logger = lc.getLogger(loggerName); - if ("null".equalsIgnoreCase(levelStr)) { - logger.setLevel(null); - } else { - Level level = Level.toLevel(levelStr, null); - if (level != null) { - logger.setLevel(level); - } - } - } - - public String getLoggerLevel(String loggerName) { - if (loggerName == null) { - return EMPTY; - } - - loggerName = loggerName.trim(); - - LoggerContext lc = (LoggerContext) context; - Logger logger = lc.exists(loggerName); - if (logger != null && logger.getLevel() != null) { - return logger.getLevel().toString(); - } else { - return EMPTY; - } - } - - public String getLoggerEffectiveLevel(String loggerName) { - if (loggerName == null) { - return EMPTY; - } - - loggerName = loggerName.trim(); - - LoggerContext lc = (LoggerContext) context; - Logger logger = lc.exists(loggerName); - if (logger != null) { - return logger.getEffectiveLevel().toString(); - } else { - return EMPTY; - } - } - - public List getLoggerList() { - LoggerContext lc = (LoggerContext) context; - List strList = new ArrayList(); - Iterator it = lc.getLoggerList().iterator(); - while (it.hasNext()) { - Logger log = it.next(); - strList.add(log.getName()); - } - return strList; - } - - public List getStatuses() { - List list = new ArrayList(); - Iterator it = context.getStatusManager().getCopyOfStatusList().iterator(); - while (it.hasNext()) { - list.add(it.next().toString()); - } - return list; - } - - /** - * When the associated LoggerContext is stopped, this configurator must be - * unregistered - */ - public void onStop(LoggerContext context) { - if (!started) { - addInfo("onStop() method called on a stopped JMXActivator [" + objectNameAsString + "]"); - return; - } - if (mbs.isRegistered(objectName)) { - try { - addInfo("Unregistering mbean [" + objectNameAsString + "]"); - mbs.unregisterMBean(objectName); - } catch (InstanceNotFoundException e) { - // this is theoretically impossible - addError("Unable to find a verifiably registered mbean [" + objectNameAsString + "]", e); - } catch (MBeanRegistrationException e) { - addError("Failed to unregister [" + objectNameAsString + "]", e); - } - } else { - addInfo("mbean [" + objectNameAsString + "] was not in the mbean registry. This is OK."); - } - stop(); - } - - public void onLevelChange(Logger logger, Level level) { - // nothing to do - } - - public void onReset(LoggerContext context) { - addInfo("onReset() method called JMXActivator [" + objectNameAsString + "]"); - } - - /** - * JMXConfigurator should not be removed subsequent to a LoggerContext reset. - * - * @return - */ - public boolean isResetResistant() { - return true; - } - - private void clearFields() { - mbs = null; - objectName = null; - loggerContext = null; - } - - private void stop() { - started = false; - clearFields(); - } - - public void onStart(LoggerContext context) { - // nop - } - - @Override - public String toString() { - return this.getClass().getName() + "(" + context.getName() + ")"; - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfiguratorMBean.java b/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfiguratorMBean.java deleted file mode 100644 index 8700cd266e..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/JMXConfiguratorMBean.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jmx; - -import java.io.FileNotFoundException; -import java.net.URL; -import java.util.List; - -import ch.qos.logback.core.joran.spi.JoranException; - -public interface JMXConfiguratorMBean { - - void reloadDefaultConfiguration() throws JoranException; - - void reloadByFileName(String fileName) throws JoranException, FileNotFoundException; - - void reloadByURL(URL url) throws JoranException; - - void setLoggerLevel(String loggerName, String levelStr); - - String getLoggerLevel(String loggerName); - - String getLoggerEffectiveLevel(String loggerName); - - List getLoggerList(); - - List getStatuses(); -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java deleted file mode 100755 index 6be947af15..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jmx/MBeanUtil.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jmx; - -import javax.management.InstanceNotFoundException; -import javax.management.MBeanRegistrationException; -import javax.management.MBeanServer; -import javax.management.MalformedObjectNameException; -import javax.management.ObjectName; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.status.StatusUtil; - -public class MBeanUtil { - - static final String DOMAIN = "ch.qos.logback.classic"; - - static public String getObjectNameFor(String contextName, Class type) { - return DOMAIN + ":Name=" + contextName + ",Type=" + type.getName(); - } - - public static ObjectName string2ObjectName(Context context, Object caller, String objectNameAsStr) { - String msg = "Failed to convert [" + objectNameAsStr + "] to ObjectName"; - - StatusUtil statusUtil = new StatusUtil(context); - try { - return new ObjectName(objectNameAsStr); - } catch (MalformedObjectNameException e) { - statusUtil.addError(caller, msg, e); - return null; - } catch (NullPointerException e) { - statusUtil.addError(caller, msg, e); - return null; - } - } - - public static boolean isRegistered(MBeanServer mbs, ObjectName objectName) { - return mbs.isRegistered(objectName); - } - - public static void createAndRegisterJMXConfigurator(MBeanServer mbs, LoggerContext loggerContext, JMXConfigurator jmxConfigurator, ObjectName objectName, - Object caller) { - try { - mbs.registerMBean(jmxConfigurator, objectName); - } catch (Exception e) { - StatusUtil statusUtil = new StatusUtil(loggerContext); - statusUtil.addError(caller, "Failed to create mbean", e); - } - } - - public static void unregister(LoggerContext loggerContext, MBeanServer mbs, ObjectName objectName, Object caller) { - - StatusUtil statusUtil = new StatusUtil(loggerContext); - if (mbs.isRegistered(objectName)) { - try { - statusUtil.addInfo(caller, "Unregistering mbean [" + objectName + "]"); - mbs.unregisterMBean(objectName); - } catch (InstanceNotFoundException e) { - // this is theoretically impossible - statusUtil.addError(caller, "Failed to unregister mbean" + objectName, e); - } catch (MBeanRegistrationException e) { - // this is theoretically impossible - statusUtil.addError(caller, "Failed to unregister mbean" + objectName, e); - } - } else { - statusUtil.addInfo(caller, "mbean [" + objectName + "] does not seem to be registered"); - } - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java index cd642cfb1d..184bcbadd1 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/JoranConfigurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,20 +14,18 @@ package ch.qos.logback.classic.joran; import ch.qos.logback.classic.joran.action.*; -import ch.qos.logback.classic.sift.SiftAction; +import ch.qos.logback.classic.joran.sanity.IfNestedWithinSecondPhaseElementSC; +import ch.qos.logback.classic.model.processor.ConfigurationModelHandlerFull; +import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.PlatformInfo; -import ch.qos.logback.classic.util.DefaultNestedComponentRules; import ch.qos.logback.core.joran.JoranConfiguratorBase; import ch.qos.logback.core.joran.action.AppenderRefAction; import ch.qos.logback.core.joran.action.IncludeAction; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.joran.conditional.ElseAction; -import ch.qos.logback.core.joran.conditional.IfAction; -import ch.qos.logback.core.joran.conditional.ThenAction; import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; import ch.qos.logback.core.joran.spi.ElementSelector; import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.DefaultProcessor; /** * JoranConfigurator class adds rules specific to logback-classic. @@ -36,53 +34,72 @@ */ public class JoranConfigurator extends JoranConfiguratorBase { - @Override - public void addInstanceRules(RuleStore rs) { - // parent rules already added - super.addInstanceRules(rs); - rs.addRule(new ElementSelector("configuration"), new ConfigurationAction()); - rs.addRule(new ElementSelector("configuration/contextName"), new ContextNameAction()); - rs.addRule(new ElementSelector("configuration/contextListener"), new LoggerContextListenerAction()); - rs.addRule(new ElementSelector("configuration/insertFromJNDI"), new InsertFromJNDIAction()); - rs.addRule(new ElementSelector("configuration/evaluator"), new EvaluatorAction()); + @Override + public void addElementSelectorAndActionAssociations(RuleStore rs) { + // add parent rules + super.addElementSelectorAndActionAssociations(rs); + + rs.addRule(new ElementSelector("configuration"), () -> new ConfigurationAction()); - rs.addRule(new ElementSelector("configuration/appender/sift"), new SiftAction()); - rs.addRule(new ElementSelector("configuration/appender/sift/*"), new NOPAction()); + rs.addRule(new ElementSelector("configuration/contextName"), () -> new ContextNameAction()); + rs.addRule(new ElementSelector("configuration/contextListener"), () -> new LoggerContextListenerAction()); + rs.addRule(new ElementSelector("configuration/insertFromJNDI"), () -> new InsertFromJNDIAction()); - rs.addRule(new ElementSelector("configuration/logger"), new LoggerAction()); - rs.addRule(new ElementSelector("configuration/logger/level"), new LevelAction()); + rs.addRule(new ElementSelector("configuration/logger"), () -> new LoggerAction()); + rs.addRule(new ElementSelector("configuration/logger/level"), () -> new LevelAction()); - rs.addRule(new ElementSelector("configuration/root"), new RootLoggerAction()); - rs.addRule(new ElementSelector("configuration/root/level"), new LevelAction()); - rs.addRule(new ElementSelector("configuration/logger/appender-ref"), new AppenderRefAction()); - rs.addRule(new ElementSelector("configuration/root/appender-ref"), new AppenderRefAction()); + rs.addRule(new ElementSelector("configuration/root"), () -> new RootLoggerAction()); + rs.addRule(new ElementSelector("configuration/root/level"), () -> new LevelAction()); + rs.addRule(new ElementSelector("configuration/logger/appender-ref"), () -> new AppenderRefAction()); + rs.addRule(new ElementSelector("configuration/root/appender-ref"), () -> new AppenderRefAction()); - // add if-then-else support - rs.addRule(new ElementSelector("*/if"), new IfAction()); - rs.addRule(new ElementSelector("*/if/then"), new ThenAction()); - rs.addRule(new ElementSelector("*/if/then/*"), new NOPAction()); - rs.addRule(new ElementSelector("*/if/else"), new ElseAction()); - rs.addRule(new ElementSelector("*/if/else/*"), new NOPAction()); + rs.addRule(new ElementSelector("configuration/include"), () -> new IncludeAction()); + rs.addRule(new ElementSelector("configuration/propertiesConfigurator"), () -> new PropertiesConfiguratorAction()); - // add jmxConfigurator only if we have JMX available. - // If running under JDK 1.4 (retrotranslateed logback) then we - // might not have JMX. - if (PlatformInfo.hasJMXObjectName()) { - rs.addRule(new ElementSelector("configuration/jmxConfigurator"), new JMXConfiguratorAction()); - } - rs.addRule(new ElementSelector("configuration/include"), new IncludeAction()); + rs.addRule(new ElementSelector("configuration/consolePlugin"), () -> new ConsolePluginAction()); - rs.addRule(new ElementSelector("configuration/consolePlugin"), new ConsolePluginAction()); + } - rs.addRule(new ElementSelector("configuration/receiver"), new ReceiverAction()); + @Override + protected void sanityCheck(Model topModel) { + super.sanityCheck(topModel); + performCheck(new IfNestedWithinSecondPhaseElementSC(), topModel); } @Override protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { - DefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(registry); + LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(registry); + } + + private JoranConfigurator makeAnotherInstance() { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(context); + return jc; + } + + public void buildModelInterpretationContext() { + super.buildModelInterpretationContext(); + this.modelInterpretationContext.setConfiguratorSupplier( () -> this.makeAnotherInstance() ); + } + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + ModelClassToModelHandlerLinker m = new ModelClassToModelHandlerLinker(context); + m.setConfigurationModelHandlerFactoryMethod(ConfigurationModelHandlerFull::makeInstance2); + m.link(defaultProcessor); + } + + + // The final filters in the two filter chain are rather crucial. + // They ensure that only Models attached to the firstPhaseFilter will + // be handled in the first phase and all models not previously handled + // in the second phase will be handled in a catch-all fallback case. + private void sealModelFilters(DefaultProcessor defaultProcessor) { + defaultProcessor.getPhaseOneFilter().denyAll(); + defaultProcessor.getPhaseTwoFilter().allowAll(); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/ModelClassToModelHandlerLinker.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ModelClassToModelHandlerLinker.java new file mode 100644 index 0000000000..e437c2da09 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ModelClassToModelHandlerLinker.java @@ -0,0 +1,99 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.model.*; +import ch.qos.logback.classic.model.processor.*; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.ModelClassToModelHandlerLinkerBase; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.AppenderRefModel; +import ch.qos.logback.core.model.InsertFromJNDIModel; +import ch.qos.logback.core.model.ModelHandlerFactoryMethod; +import ch.qos.logback.core.model.processor.*; + +/** + * For a given DefaultProcessor instance link a {@link ch.qos.logback.core.model.Model Model} class to a + * {@link ch.qos.logback.core.model.processor.ModelHandlerBase ModelHandler} instance for + * logback-classic. + * + *

Will also use links from super class.

+ * + * @since 1.3.9/1.4.9 + */ +public class ModelClassToModelHandlerLinker extends ModelClassToModelHandlerLinkerBase { + + public ModelClassToModelHandlerLinker(Context context) { + super(context); + } + + ModelHandlerFactoryMethod configurationModelHandlerFactoryMethod; + + @Override + public void link(DefaultProcessor defaultProcessor) { + super.link(defaultProcessor); + defaultProcessor.addHandler(ConfigurationModel.class, getConfigurationModelHandlerFactoryMethod()); + defaultProcessor.addHandler(ContextNameModel.class, ContextNameModelHandler::makeInstance); + defaultProcessor.addHandler(LoggerContextListenerModel.class, LoggerContextListenerModelHandler::makeInstance); + + defaultProcessor.addHandler(PropertiesConfiguratorModel.class, PropertiesConfiguratorModelHandler::makeInstance); + defaultProcessor.addHandler(InsertFromJNDIModel.class, InsertFromJNDIModelHandler::makeInstance); + + defaultProcessor.addHandler(AppenderModel.class, AppenderModelHandler::makeInstance); + defaultProcessor.addHandler(AppenderRefModel.class, AppenderRefModelHandler::makeInstance); + defaultProcessor.addHandler(RootLoggerModel.class, RootLoggerModelHandler::makeInstance); + defaultProcessor.addHandler(LoggerModel.class, LoggerModelHandler::makeInstance); + defaultProcessor.addHandler(LevelModel.class, LevelModelHandler::makeInstance); + + defaultProcessor.addAnalyser(RootLoggerModel.class, + () -> new AppenderRefDependencyAnalyser(context)); + + defaultProcessor.addAnalyser(LoggerModel.class, + () -> new AppenderRefDependencyAnalyser(context)); + + // an appender may contain appender refs, e.g. AsyncAppender + defaultProcessor.addAnalyser(AppenderModel.class, + () -> new AppenderRefDependencyAnalyser(context)); + + defaultProcessor.addAnalyser(AppenderModel.class, () -> new FileCollisionAnalyser(context)); + + + defaultProcessor.addAnalyser(AppenderModel.class, () -> new AppenderDeclarationAnalyser(context)); + + sealModelFilters(defaultProcessor); + + } + + public ModelHandlerFactoryMethod getConfigurationModelHandlerFactoryMethod() { + if (configurationModelHandlerFactoryMethod == null) { + //System.out.println("returning default ConfigurationModelHandler::makeInstance;"); + return ConfigurationModelHandler::makeInstance; + } else { + //System.out.println("returning set "+configurationModelHandlerFactoryMethod); + return configurationModelHandlerFactoryMethod; + } + } + + + /** + * Allow configurators to override the factory method for ConfigurationModelHandler + * + */ + public void setConfigurationModelHandlerFactoryMethod(ModelHandlerFactoryMethod cmhfm) { + //System.out.println("setConfigurationModelHandlerFactoryMethod called with "+cmhfm); + this.configurationModelHandlerFactoryMethod = cmhfm; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertiesConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertiesConfigurator.java new file mode 100644 index 0000000000..1d88331a51 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/PropertiesConfigurator.java @@ -0,0 +1,196 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ErrorCodes; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.*; + +import static ch.qos.logback.core.CoreConstants.DOT; +import static ch.qos.logback.core.joran.JoranConstants.NULL; + +public class PropertiesConfigurator extends ContextAwareBase { + + static Comparator LENGTH_COMPARATOR = new Comparator() { + @Override + public int compare(String o1, String o2) { + int len1 = o1 == null ? 0 : o1.length(); + int len2 = o2 == null ? 0 : o2.length(); + // longer strings first + int diff = len2 - len1; + if (diff != 0) { + return diff; + } else { + return o2.compareTo(o1); + } + } + }; + + static final String LOGBACK_PREFIX = "logback"; + static final String LOGBACK_ROOT_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "root"; + static final int LOGBACK_ROOT_LOGGER_PREFIX_LENGTH = LOGBACK_ROOT_LOGGER_PREFIX.length(); + + public static final String LOGBACK_LOGGER_PREFIX = LOGBACK_PREFIX + DOT + "logger" + DOT; + static final int LOGBACK_LOGGER_PREFIX_LENGTH = LOGBACK_LOGGER_PREFIX.length(); + + VariableSubstitutionsHelper variableSubstitutionsHelper; + + LoggerContext getLoggerContext() { + return (LoggerContext) getContext(); + } + + @Override + public void setContext(Context context) { + super.setContext(context); + } + + public void doConfigure(URL url) throws JoranException { + try { + URLConnection urlConnection = url.openConnection(); + // per http://jira.qos.ch/browse/LOGBACK-117 + // per http://jira.qos.ch/browse/LOGBACK-163 + urlConnection.setUseCaches(false); + InputStream in = urlConnection.getInputStream(); + doConfigure(in); + } catch (IOException ioe) { + String errMsg = "Could not open URL [" + url + "]."; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } + } + + public void doConfigure(File file) throws JoranException { + try (FileInputStream fileInputStream = new FileInputStream(file)) { + doConfigure(fileInputStream); + } catch (IOException e) { + throw new JoranException("Failed to load file " + file, e); + } + } + + public void doConfigure(String filename) throws JoranException { + doConfigure(new File(filename)); + } + + public void doConfigure(InputStream inputStream) throws JoranException { + Properties props = new Properties(); + try { + props.load(inputStream); + } catch (IOException e) { + throw new JoranException("Failed to load from input stream", e); + } finally { + close(inputStream); + } + + doConfigure(props); + } + + private void close(InputStream inputStream) throws JoranException { + if (inputStream != null) { + try { + inputStream.close(); + } catch (IOException e) { + throw new JoranException("failed to close stream", e); + } + } + } + + void doConfigure(Properties properties) { + Map variablesMap = extractVariablesMap(properties); + Map instructionMap = extractLogbackInstructionMap(properties); + + this.variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, variablesMap); + configureLoggers(instructionMap); + configureRootLogger(instructionMap); + } + + void configureRootLogger(Map instructionMap) { + String val = subst(instructionMap.get(LOGBACK_ROOT_LOGGER_PREFIX)); + if (val != null) { + setLevel(org.slf4j.Logger.ROOT_LOGGER_NAME, val); + } + } + + void configureLoggers(Map instructionMap) { + for (String key : instructionMap.keySet()) { + if (key.startsWith(LOGBACK_LOGGER_PREFIX)) { + String loggerName = key.substring(LOGBACK_LOGGER_PREFIX_LENGTH); + String value = subst(instructionMap.get(key)); + setLevel(loggerName, value); + } + } + } + + private void setLevel(String loggerName, String levelStr) { + Logger logger = getLoggerContext().getLogger(loggerName); + + if (JoranConstants.INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { + if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(loggerName)) { + addError(ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + } else { + addInfo("Setting level of logger [" + loggerName + "] to null, i.e. INHERITED"); + logger.setLevel(null); + } + } else { + Level level = Level.toLevel(levelStr); + logger.setLevel(level); + } + } + + private Map extractVariablesMap(Properties properties) { + Map variablesMap = new HashMap<>(); + for (String key : properties.stringPropertyNames()) { + if (key != null && !key.startsWith(LOGBACK_PREFIX)) { + variablesMap.put(key, properties.getProperty(key)); + } + } + + return variablesMap; + } + + private Map extractLogbackInstructionMap(Properties properties) { + Map instructionMap = new TreeMap<>(LENGTH_COMPARATOR); + for (String key : properties.stringPropertyNames()) { + if (key != null && key.startsWith(LOGBACK_PREFIX)) { + instructionMap.put(key, properties.getProperty(key)); + } + } + return instructionMap; + } + + public String subst(String ref) { + + String substituted = variableSubstitutionsHelper.subst(ref); + if (ref != null && !ref.equals(substituted)) { + String sanitized = variableSubstitutionsHelper.sanitizeIfConfidential(ref, substituted); + addInfo("value \"" + sanitized + "\" substituted for \"" + ref + "\""); + } + return substituted; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTask.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTask.java index a4324c3f73..46420b331c 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTask.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTask.java @@ -1,162 +1,180 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.classic.joran; import java.io.File; import java.net.URL; -import java.util.ArrayList; import java.util.List; +import java.util.concurrent.ScheduledFuture; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.util.EnvUtil; import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.event.SaxEvent; import ch.qos.logback.core.joran.spi.ConfigurationWatchList; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelUtil; +import ch.qos.logback.core.spi.ConfigurationEvent; import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.status.StatusUtil; +import static ch.qos.logback.core.CoreConstants.PROPERTIES_FILE_EXTENSION; +import static ch.qos.logback.core.spi.ConfigurationEvent.*; + public class ReconfigureOnChangeTask extends ContextAwareBase implements Runnable { public static final String DETECTED_CHANGE_IN_CONFIGURATION_FILES = "Detected change in configuration files."; - static final String RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION = "Re-registering previous fallback configuration once more as a fallback configuration point"; - static final String FALLING_BACK_TO_SAFE_CONFIGURATION = "Given previous errors, falling back to previously registered safe configuration."; + public static final String RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION = "Re-registering previous fallback configuration once more as a fallback configuration point"; + public static final String FALLING_BACK_TO_SAFE_CONFIGURATION = "Given previous errors, falling back to previously registered safe configuration."; - - long birthdate = System.currentTimeMillis(); - List listeners; - - - void addListener(ReconfigureOnChangeTaskListener listener) { - if(listeners==null) - listeners = new ArrayList(); - listeners.add(listener); - } - + List listeners = null; + + ScheduledFuture scheduledFuture; + @Override public void run() { - fireEnteredRunMethod(); - + context.fireConfigurationEvent(newConfigurationChangeDetectorRunningEvent(this)); + ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context); if (configurationWatchList == null) { addWarn("Empty ConfigurationWatchList in context"); return; } - List filesToWatch = configurationWatchList.getCopyOfFileWatchList(); - if (filesToWatch == null || filesToWatch.isEmpty()) { - addInfo("Empty watch file list. Disabling "); + if (configurationWatchList.emptyWatchLists()) { + addInfo("Both watch lists are empty. Disabling "); return; } - if (!configurationWatchList.changeDetected()) { + File changedFile = configurationWatchList.changeDetectedInFile(); + URL changedURL = configurationWatchList.changeDetectedInURL(); + + if (changedFile == null && changedURL == null) { return; } - fireChangeDetected(); - URL mainConfigurationURL = configurationWatchList.getMainURL(); - + context.fireConfigurationEvent(ConfigurationEvent.newConfigurationChangeDetectedEvent(this)); addInfo(DETECTED_CHANGE_IN_CONFIGURATION_FILES); + if(changedFile != null) { + changeInFile(changedFile, configurationWatchList); + } + + if(changedURL != null) { + changeInURL(changedURL); + } + } + + private void changeInURL(URL url) { + String path = url.getPath(); + if(path.endsWith(PROPERTIES_FILE_EXTENSION)) { + runPropertiesConfigurator(url); + } + } + private void changeInFile(File changedFile, ConfigurationWatchList configurationWatchList) { + + if(changedFile.getName().endsWith(PROPERTIES_FILE_EXTENSION)) { + runPropertiesConfigurator(changedFile); + return; + } + + // ======== fuller processing below addInfo(CoreConstants.RESET_MSG_PREFIX + "named [" + context.getName() + "]"); + cancelFutureInvocationsOfThisTaskInstance(); + URL mainConfigurationURL = configurationWatchList.getTopURL(); LoggerContext lc = (LoggerContext) context; if (mainConfigurationURL.toString().endsWith("xml")) { performXMLConfiguration(lc, mainConfigurationURL); - } else if (mainConfigurationURL.toString().endsWith("groovy")) { - if (EnvUtil.isGroovyAvailable()) { - lc.reset(); - // avoid directly referring to GafferConfigurator so as to avoid - // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214 - // GafferUtil.runGafferConfiguratorOn(lc, this, mainConfigurationURL); - addError("Groovy configuration disabled due to Java 9 compilation issues."); - - } else { - addError("Groovy classes are not available on the class path. ABORTING INITIALIZATION."); - } } - fireDoneReconfiguring(); } - private void fireEnteredRunMethod() { - if(listeners == null) - return; - - for(ReconfigureOnChangeTaskListener listener: listeners) - listener.enteredRunMethod(); + private void runPropertiesConfigurator(Object changedObject) { + addInfo("Will run PropertyConfigurator on "+changedObject); + PropertiesConfigurator propertiesConfigurator = new PropertiesConfigurator(); + propertiesConfigurator.setContext(context); + try { + if(changedObject instanceof File) { + File changedFile = (File) changedObject; + propertiesConfigurator.doConfigure(changedFile); + } else if(changedObject instanceof URL) { + URL changedURL = (URL) changedObject; + propertiesConfigurator.doConfigure(changedURL); + } + context.fireConfigurationEvent(newPartialConfigurationEndedSuccessfullyEvent(this)); + } catch (JoranException e) { + addError("Failed to reload "+ changedObject); + } } - private void fireChangeDetected() { - if(listeners == null) - return; - - for(ReconfigureOnChangeTaskListener listener: listeners) - listener.changeDetected(); + private void cancelFutureInvocationsOfThisTaskInstance() { + boolean result = scheduledFuture.cancel(false); + if(!result) { + addWarn("could not cancel "+ this.toString()); + } } + private void performXMLConfiguration(LoggerContext loggerContext, URL mainConfigurationURL) { - private void fireDoneReconfiguring() { - if(listeners == null) - return; - - for(ReconfigureOnChangeTaskListener listener: listeners) - listener.doneReconfiguring(); - } - - private void performXMLConfiguration(LoggerContext lc, URL mainConfigurationURL) { JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(context); - StatusUtil statusUtil = new StatusUtil(context); - List eventList = jc.recallSafeConfiguration(); - - URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context); - lc.reset(); + jc.setContext(loggerContext); + StatusUtil statusUtil = new StatusUtil(loggerContext); + Model failsafeTop = jc.recallSafeConfiguration(); + URL topURL = ConfigurationWatchListUtil.getMainWatchURL(context); + addInfo("Resetting loggerContext ["+loggerContext.getName()+"]"); + loggerContext.reset(); long threshold = System.currentTimeMillis(); try { jc.doConfigure(mainConfigurationURL); + // e.g. IncludeAction will add a status regarding XML parsing errors but no exception will reach here if (statusUtil.hasXMLParsingErrors(threshold)) { - fallbackConfiguration(lc, eventList, mainURL); + fallbackConfiguration(loggerContext, failsafeTop, topURL); } } catch (JoranException e) { - fallbackConfiguration(lc, eventList, mainURL); + addWarn("Exception occurred during reconfiguration", e); + fallbackConfiguration(loggerContext, failsafeTop, topURL); } } - private List removeIncludeEvents(List unsanitizedEventList) { - List sanitizedEvents = new ArrayList(); - if (unsanitizedEventList == null) - return sanitizedEvents; - - for (SaxEvent e : unsanitizedEventList) { - if (!"include".equalsIgnoreCase(e.getLocalName())) - sanitizedEvents.add(e); - - } - return sanitizedEvents; - } - - private void fallbackConfiguration(LoggerContext lc, List eventList, URL mainURL) { + private void fallbackConfiguration(LoggerContext loggerContext, Model failsafeTopModel, URL topURL) { // failsafe events are used only in case of errors. Therefore, we must *not* // invoke file inclusion since the included files may be the cause of the error. - List failsafeEvents = removeIncludeEvents(eventList); + // List failsafeEvents = removeIncludeEvents(eventList); JoranConfigurator joranConfigurator = new JoranConfigurator(); - joranConfigurator.setContext(context); - ConfigurationWatchList oldCWL = ConfigurationWatchListUtil.getConfigurationWatchList(context); - ConfigurationWatchList newCWL = oldCWL.buildClone(); - - if (failsafeEvents == null || failsafeEvents.isEmpty()) { + joranConfigurator.setContext(loggerContext); + joranConfigurator.setTopURL(topURL); + +// ConfigurationWatchList oldCWL = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); +// System.out.println("--------oldCWL:"+oldCWL); +// ConfigurationWatchList newCWL = oldCWL.buildClone(); + + if (failsafeTopModel == null) { addWarn("No previous configuration to fall back on."); + return; } else { addWarn(FALLING_BACK_TO_SAFE_CONFIGURATION); + addInfo("Safe model "+failsafeTopModel); try { - lc.reset(); - ConfigurationWatchListUtil.registerConfigurationWatchList(context, newCWL); - joranConfigurator.doConfigure(failsafeEvents); + loggerContext.reset(); +// ConfigurationWatchListUtil.registerConfigurationWatchList(context, newCWL); + ModelUtil.resetForReuse(failsafeTopModel); + joranConfigurator.processModel(failsafeTopModel); addInfo(RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION); - joranConfigurator.registerSafeConfiguration(eventList); - - addInfo("after registerSafeConfiguration: " + eventList); - } catch (JoranException e) { + joranConfigurator.registerSafeConfiguration(failsafeTopModel); + context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this)); + } catch (Exception e) { addError("Unexpected exception thrown by a configuration considered safe.", e); } } @@ -166,4 +184,23 @@ private void fallbackConfiguration(LoggerContext lc, List eventList, U public String toString() { return "ReconfigureOnChangeTask(born:" + birthdate + ")"; } + + /** + * Contains typo. Replaced by {@link #setScheduledFuture(ScheduledFuture)}. + * @param aScheduledFuture + * @deprecated + */ + @Deprecated + public void setScheduredFuture(ScheduledFuture aScheduledFuture) { + setScheduledFuture(aScheduledFuture); + } + + /** + * Replaces {@link #setScheduredFuture(ScheduledFuture)} + * @param aScheduledFuture + * @since 1.5.19 + */ + public void setScheduledFuture(ScheduledFuture aScheduledFuture) { + this.scheduledFuture = aScheduledFuture; + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskListener.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskListener.java index c1ab822986..3b83569496 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskListener.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskListener.java @@ -1,6 +1,20 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.classic.joran; -public class ReconfigureOnChangeTaskListener { +class ReconfigureOnChangeTaskListener { + void enteredRunMethod() { } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/SerializedModelConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/SerializedModelConfigurator.java new file mode 100644 index 0000000000..a782f29544 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/SerializedModelConfigurator.java @@ -0,0 +1,187 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream; +import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; +import ch.qos.logback.classic.spi.ConfiguratorRank; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.LogbackException; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelUtil; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.status.InfoStatus; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.concurrent.locks.ReentrantLock; + +import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION; + +/** + * @since 1.3.9/1.4.9 + */ + +// BEWARE: the fqcn is used in SerializedModelModelHandler +@ConfiguratorRank(value = ConfiguratorRank.SERIALIZED_MODEL) +public class SerializedModelConfigurator extends ContextAwareBase implements Configurator { + + final public static String AUTOCONFIG_MODEL_FILE = "logback"+ MODEL_CONFIG_FILE_EXTENSION; + + final public static String TEST_AUTOCONFIG_MODEL_FILE = "logback-test"+ MODEL_CONFIG_FILE_EXTENSION; + protected ModelInterpretationContext modelInterpretationContext; + + @Override + public ExecutionStatus configure(LoggerContext loggerContext) { + + URL url = performMultiStepModelFileSearch(true); + if (url != null) { + addWarn("Replaced by logback-tyler, SerializedModelConfigurator has been deprecated and will be removed on 2025-07-01."); + configureByResource(url); + return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY; + } else { + return ExecutionStatus.INVOKE_NEXT_IF_ANY; + } + } + + private void configureByResource(URL url) { + final String urlString = url.toString(); + if (urlString.endsWith(MODEL_CONFIG_FILE_EXTENSION)) { + Model model = retrieveModel(url); + if(model == null) { + addWarn("Empty model. Abandoning."); + return; + } + ModelUtil.resetForReuse(model); + buildModelInterpretationContext(model); + + DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); + ModelClassToModelHandlerLinker mc2mhl = new ModelClassToModelHandlerLinker(context); + mc2mhl.link(defaultProcessor); + + // disallow simultaneous configurations of the same context + ReentrantLock configurationLock = context.getConfigurationLock(); + try { + configurationLock.lock(); + defaultProcessor.process(model); + } finally { + configurationLock.unlock(); + } + } else { + throw new LogbackException( + "Unexpected filename extension of file [" + url.toString() + "]. Should be " + MODEL_CONFIG_FILE_EXTENSION); + } + } + + private void buildModelInterpretationContext(Model topModel) { + this.modelInterpretationContext = new ModelInterpretationContext(context, this); + this.modelInterpretationContext.setTopModel(topModel); + LogbackClassicDefaultNestedComponentRules.addDefaultNestedComponentRegistryRules( + modelInterpretationContext.getDefaultNestedComponentRegistry()); + this.modelInterpretationContext.createAppenderBags(); + } + + private Model retrieveModel(URL url) { + long start = System.currentTimeMillis(); + try (InputStream is = url.openStream()) { + HardenedModelInputStream hmis = new HardenedModelInputStream(is); + + Model model = (Model) hmis.readObject(); + long diff = System.currentTimeMillis() - start; + addInfo("Model at ["+url+"] read in "+diff + " milliseconds"); + return model; + } catch(IOException e) { + addError("Failed to open "+url, e); + } catch (ClassNotFoundException e) { + addError("Failed read model object in "+ url, e); + } + return null; + } + + private URL performMultiStepModelFileSearch(boolean updateState) { + ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); + URL url = findModelConfigFileURLFromSystemProperties(myClassLoader); + if (url != null) { + return url; + } + + url = getResource(TEST_AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); + if (url != null) { + return url; + } + + url = getResource(AUTOCONFIG_MODEL_FILE, myClassLoader, updateState); + return url; + } + + URL findModelConfigFileURLFromSystemProperties(ClassLoader classLoader) { + String logbackModelFile = OptionHelper.getSystemProperty(ClassicConstants.MODEL_CONFIG_FILE_PROPERTY); + + if (logbackModelFile != null) { + URL result = null; + try { + result = new URL(logbackModelFile); + return result; + } catch (MalformedURLException e) { + // so, resource is not a URL: + // attempt to get the resource from the class path + result = Loader.getResource(logbackModelFile, classLoader); + if (result != null) { + return result; + } + File f = new File(logbackModelFile); + if (f.exists() && f.isFile()) { + try { + result = f.toURI().toURL(); + return result; + } catch (MalformedURLException e1) { + } + } + } finally { + statusOnResourceSearch(logbackModelFile, result); + } + } + return null; + } + + + private URL getResource(String filename, ClassLoader classLoader, boolean updateStatus) { + URL url = Loader.getResource(filename, classLoader); + if (updateStatus) { + statusOnResourceSearch(filename, url); + } + return url; + } + + private void statusOnResourceSearch(String resourceName, URL url) { + StatusManager sm = context.getStatusManager(); + if (url == null) { + sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context)); + } else { + sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context)); + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java index 4170127294..dcb8c9f523 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConfigurationAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,133 +13,28 @@ */ package ch.qos.logback.classic.joran.action; -import java.net.URL; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - import org.xml.sax.Attributes; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; -import ch.qos.logback.classic.util.EnvUtil; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.status.OnConsoleStatusListener; -import ch.qos.logback.core.util.ContextUtil; -import ch.qos.logback.core.util.Duration; -import ch.qos.logback.core.util.OptionHelper; -import ch.qos.logback.core.util.StatusListenerConfigHelper; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; -public class ConfigurationAction extends Action { +public class ConfigurationAction extends BaseModelAction { static final String INTERNAL_DEBUG_ATTR = "debug"; - static final String PACKAGING_DATA_ATTR = "packagingData"; static final String SCAN_ATTR = "scan"; static final String SCAN_PERIOD_ATTR = "scanPeriod"; - static final String DEBUG_SYSTEM_PROPERTY_KEY = "logback.debug"; - static final Duration SCAN_PERIOD_DEFAULT = Duration.buildByMinutes(1); - - long threshold = 0; - - public void begin(InterpretationContext ic, String name, Attributes attributes) { - threshold = System.currentTimeMillis(); - - // See LOGBACK-527 (the system property is looked up first. Thus, it overrides - // the equivalent property in the config file. This reversal of scope priority is justified - // by the use case: the admin trying to chase rogue config file - String debugAttrib = getSystemProperty(DEBUG_SYSTEM_PROPERTY_KEY); - if (debugAttrib == null) { - debugAttrib = ic.subst(attributes.getValue(INTERNAL_DEBUG_ATTR)); - } - - if (OptionHelper.isEmpty(debugAttrib) || debugAttrib.equalsIgnoreCase("false") || debugAttrib.equalsIgnoreCase("null")) { - addInfo(INTERNAL_DEBUG_ATTR + " attribute not set"); - } else { - StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); - } - - processScanAttrib(ic, attributes); - - LoggerContext lc = (LoggerContext) context; - boolean packagingData = OptionHelper.toBoolean(ic.subst(attributes.getValue(PACKAGING_DATA_ATTR)), LoggerContext.DEFAULT_PACKAGING_DATA); - lc.setPackagingDataEnabled(packagingData); - - if (EnvUtil.isGroovyAvailable()) { - ContextUtil contextUtil = new ContextUtil(context); - contextUtil.addGroovyPackages(lc.getFrameworkPackages()); - } - - // the context is turbo filter attachable, so it is pushed on top of the - // stack - ic.pushObject(getContext()); - } - - String getSystemProperty(String name) { - /* - * LOGBACK-743: accessing a system property in the presence of a SecurityManager (e.g. applet sandbox) can - * result in a SecurityException. - */ - try { - return System.getProperty(name); - } catch (SecurityException ex) { - return null; - } - } - - void processScanAttrib(InterpretationContext ic, Attributes attributes) { - String scanAttrib = ic.subst(attributes.getValue(SCAN_ATTR)); - if (!OptionHelper.isEmpty(scanAttrib) && !"false".equalsIgnoreCase(scanAttrib)) { - - ScheduledExecutorService scheduledExecutorService = context.getScheduledExecutorService(); - URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context); - if (mainURL == null) { - addWarn("Due to missing top level configuration file, reconfiguration on change (configuration file scanning) cannot be done."); - return; - } - ReconfigureOnChangeTask rocTask = new ReconfigureOnChangeTask(); - rocTask.setContext(context); - - context.putObject(CoreConstants.RECONFIGURE_ON_CHANGE_TASK, rocTask); - - String scanPeriodAttrib = ic.subst(attributes.getValue(SCAN_PERIOD_ATTR)); - Duration duration = getDurationOfScanPeriodAttribute(scanPeriodAttrib, SCAN_PERIOD_DEFAULT); - - addInfo("Will scan for changes in [" + mainURL + "] "); - // Given that included files are encountered at a later phase, the complete list of files - // to scan can only be determined when the configuration is loaded in full. - // However, scan can be active if mainURL is set. Otherwise, when changes are detected - // the top level config file cannot be accessed. - addInfo("Setting ReconfigureOnChangeTask scanning period to " + duration); - - ScheduledFuture scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(rocTask, duration.getMilliseconds(), duration.getMilliseconds(), - TimeUnit.MILLISECONDS); - context.addScheduledFuture(scheduledFuture); - } - } - - private Duration getDurationOfScanPeriodAttribute(String scanPeriodAttrib, Duration defaultDuration) { - Duration duration = null; + static final String PACKAGING_DATA_ATTR = "packagingData"; - if (!OptionHelper.isEmpty(scanPeriodAttrib)) { - try { - duration = Duration.valueOf(scanPeriodAttrib); - } catch(IllegalStateException|IllegalArgumentException e) { - addWarn("Failed to parse 'scanPeriod' attribute ["+scanPeriodAttrib+"]", e); - // default duration will be set below - } - } - - if(duration == null) { - addInfo("No 'scanPeriod' specified. Defaulting to " + defaultDuration.toString()); - duration = defaultDuration; - } - return duration; + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ConfigurationModel configurationModel = new ConfigurationModel(); + configurationModel.setDebugStr(attributes.getValue(INTERNAL_DEBUG_ATTR)); + configurationModel.setScanStr(attributes.getValue(SCAN_ATTR)); + configurationModel.setScanPeriodStr(attributes.getValue(SCAN_PERIOD_ATTR)); + configurationModel.setPackagingDataStr(attributes.getValue(PACKAGING_DATA_ATTR)); + return configurationModel; } - public void end(InterpretationContext ec, String name) { - addInfo("End of configuration."); - ec.popObject(); - } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConsolePluginAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConsolePluginAction.java index d4df5dd92e..9a9f6ba958 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConsolePluginAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ConsolePluginAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,7 +20,7 @@ import ch.qos.logback.classic.net.SocketAppender; import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class ConsolePluginAction extends Action { @@ -28,7 +28,7 @@ public class ConsolePluginAction extends Action { private static final Integer DEFAULT_PORT = 4321; @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) throws ActionException { String portStr = attributes.getValue(PORT_ATTR); Integer port = null; @@ -39,6 +39,8 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) port = Integer.valueOf(portStr); } catch (NumberFormatException ex) { addError("Port " + portStr + " in ConsolePlugin config is not a correct number"); + addError("Abandoning configuration of ConsolePlugin."); + return; } } @@ -56,7 +58,7 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) } @Override - public void end(InterpretationContext ec, String name) throws ActionException { + public void end(SaxEventInterpretationContext ec, String name) throws ActionException { } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ContextNameAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ContextNameAction.java index 537a706435..68fe35d978 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ContextNameAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ContextNameAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,25 +15,18 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.classic.model.ContextNameModel; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; -public class ContextNameAction extends Action { +public class ContextNameAction extends BaseModelAction { - public void begin(InterpretationContext ec, String name, Attributes attributes) { + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ContextNameModel contextNameModel = new ContextNameModel(); + return contextNameModel; } - public void body(InterpretationContext ec, String body) { - - String finalBody = ec.subst(body); - addInfo("Setting logger context name as [" + finalBody + "]"); - try { - context.setName(finalBody); - } catch (IllegalStateException e) { - addError("Failed to rename context [" + context.getName() + "] as [" + finalBody + "]", e); - } - } - - public void end(InterpretationContext ec, String name) { - } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/EvaluatorAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/EvaluatorAction.java deleted file mode 100644 index 219d235e1f..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/EvaluatorAction.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran.action; - -import ch.qos.logback.classic.boolex.JaninoEventEvaluator; -import ch.qos.logback.core.joran.action.AbstractEventEvaluatorAction; - -public class EvaluatorAction extends AbstractEventEvaluatorAction { - protected String defaultClassName() { - return JaninoEventEvaluator.class.getName(); - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/InsertFromJNDIAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/InsertFromJNDIAction.java index d1fcb79150..df6efcc612 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/InsertFromJNDIAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/InsertFromJNDIAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,71 +13,43 @@ */ package ch.qos.logback.classic.joran.action; -import javax.naming.Context; -import javax.naming.NamingException; - import org.xml.sax.Attributes; -import ch.qos.logback.classic.util.JNDIUtil; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ActionUtil; -import ch.qos.logback.core.joran.action.ActionUtil.Scope; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.InsertFromJNDIModel; +import ch.qos.logback.core.model.Model; /** - * Insert an env-entry found in JNDI as a new context variable - + * Insert an env-entry found in JNDI as a new context variable + * * @author Ceki Gulcu * */ -public class InsertFromJNDIAction extends Action { +public class InsertFromJNDIAction extends BaseModelAction { public static final String ENV_ENTRY_NAME_ATTR = "env-entry-name"; public static final String AS_ATTR = "as"; - public void begin(InterpretationContext ec, String name, Attributes attributes) { - - int errorCount = 0; - String envEntryName = ec.subst(attributes.getValue(ENV_ENTRY_NAME_ATTR)); - String asKey = ec.subst(attributes.getValue(AS_ATTR)); - - String scopeStr = attributes.getValue(SCOPE_ATTRIBUTE); - Scope scope = ActionUtil.stringToScope(scopeStr); + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + InsertFromJNDIModel ifjm = new InsertFromJNDIModel(); + ifjm.setEnvEntryName(attributes.getValue(ENV_ENTRY_NAME_ATTR)); + ifjm.setAs(attributes.getValue(AS_ATTR)); + ifjm.setScopeStr(attributes.getValue(SCOPE_ATTRIBUTE)); - String envEntryValue; - - if (OptionHelper.isEmpty(envEntryName)) { - String lineColStr = getLineColStr(ec); - addError("[" + ENV_ENTRY_NAME_ATTR + "] missing, around " + lineColStr); - errorCount++; - } - - if (OptionHelper.isEmpty(asKey)) { - String lineColStr = getLineColStr(ec); - addError("[" + AS_ATTR + "] missing, around " + lineColStr); - errorCount++; - } - - if (errorCount != 0) { - return; - } + return ifjm; + } - try { - Context ctx = JNDIUtil.getInitialContext(); - envEntryValue = JNDIUtil.lookup(ctx, envEntryName); - if (OptionHelper.isEmpty(envEntryValue)) { - addError("[" + envEntryName + "] has null or empty value"); - } else { - addInfo("Setting variable [" + asKey + "] to [" + envEntryValue + "] in [" + scope + "] scope"); - ActionUtil.setProperty(ec, asKey, envEntryValue, scope); - } - } catch (NamingException e) { - addError("Failed to lookup JNDI env-entry [" + envEntryName + "]"); - } + @Override + protected boolean validPreconditions(SaxEventInterpretationContext seic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, seic, name, attributes); + validator.validateGivenAttribute(ENV_ENTRY_NAME_ATTR); + validator.validateGivenAttribute(AS_ATTR); + return validator.isValid(); } - public void end(InterpretationContext ec, String name) { - } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java deleted file mode 100644 index 700f4ab565..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/JMXConfiguratorAction.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran.action; - -import java.lang.management.ManagementFactory; - -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.xml.sax.Attributes; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.jmx.JMXConfigurator; -import ch.qos.logback.classic.jmx.MBeanUtil; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; - -public class JMXConfiguratorAction extends Action { - - static final String OBJECT_NAME_ATTRIBUTE_NAME = "objectName"; - static final String CONTEXT_NAME_ATTRIBUTE_NAME = "contextName"; - static final char JMX_NAME_SEPARATOR = ','; - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - addInfo("begin"); - - String contextName = context.getName(); - String contextNameAttributeVal = attributes.getValue(CONTEXT_NAME_ATTRIBUTE_NAME); - if (!OptionHelper.isEmpty(contextNameAttributeVal)) { - contextName = contextNameAttributeVal; - } - - String objectNameAsStr; - String objectNameAttributeVal = attributes.getValue(OBJECT_NAME_ATTRIBUTE_NAME); - if (OptionHelper.isEmpty(objectNameAttributeVal)) { - objectNameAsStr = MBeanUtil.getObjectNameFor(contextName, JMXConfigurator.class); - } else { - objectNameAsStr = objectNameAttributeVal; - } - - ObjectName objectName = MBeanUtil.string2ObjectName(context, this, objectNameAsStr); - if (objectName == null) { - addError("Failed construct ObjectName for [" + objectNameAsStr + "]"); - return; - } - - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - if (!MBeanUtil.isRegistered(mbs, objectName)) { - // register only of the named JMXConfigurator has not been previously - // registered. Unregistering an MBean within invocation of itself - // caused jconsole to throw an NPE. (This occurs when the reload* method - // unregisters the - JMXConfigurator jmxConfigurator = new JMXConfigurator((LoggerContext) context, mbs, objectName); - try { - mbs.registerMBean(jmxConfigurator, objectName); - } catch (Exception e) { - addError("Failed to create mbean", e); - } - } - - } - - @Override - public void end(InterpretationContext ec, String name) throws ActionException { - - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LevelAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LevelAction.java index b86f8e38ac..6a02909d9f 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LevelAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LevelAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,53 +15,42 @@ import org.xml.sax.Attributes; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.model.LevelModel; import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; /** - * Action to handle the element nested within element. + * Action to handle the element nested within element. * - *

This action is deprecated. Use the level attribute within the logger + *

+ * This action is deprecated. Use the level attribute within the logger * element. * * @author Ceki Gulcu */ -public class LevelAction extends Action { - - boolean inError = false; - - public void begin(InterpretationContext ec, String name, Attributes attributes) { - Object o = ec.peekObject(); - - if (!(o instanceof Logger)) { - inError = true; - addError("For element , could not find a logger at the top of execution stack."); - return; - } - - Logger l = (Logger) o; - - String loggerName = l.getName(); - - String levelStr = ec.subst(attributes.getValue(ActionConst.VALUE_ATTR)); - // addInfo("Encapsulating logger name is [" + loggerName - // + "], level value is [" + levelStr + "]."); - - if (ActionConst.INHERITED.equalsIgnoreCase(levelStr) || ActionConst.NULL.equalsIgnoreCase(levelStr)) { - l.setLevel(null); - } else { - l.setLevel(Level.toLevel(levelStr, Level.DEBUG)); - } - - addInfo(loggerName + " level set to " + l.getLevel()); +public class LevelAction extends BaseModelAction { + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext interpcont, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, interpcont, name, attributes); + pv.validateValueAttribute(); + addWarn(" element is deprecated. Near [" + name + "] on line " + Action.getLineNumber(interpcont)); + addWarn("Please use \"level\" attribute within or elements instead."); + return pv.isValid(); } - public void finish(InterpretationContext ec) { - } + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + LevelModel lm = new LevelModel(); + String value = attributes.getValue(JoranConstants.VALUE_ATTR); + lm.setValue(value); - public void end(InterpretationContext ec, String e) { + return lm; } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerAction.java index 76873e3803..a251160588 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,79 +15,42 @@ import org.xml.sax.Attributes; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.classic.model.LoggerModel; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; /** * Action which handles elements in configuration files. * - * @author Ceki Gulcu + * @author Ceki Gülcü */ -public class LoggerAction extends Action { - public static final String LEVEL_ATTRIBUTE = "level"; - - boolean inError = false; - Logger logger; - - public void begin(InterpretationContext ec, String name, Attributes attributes) { - // Let us forget about previous errors (in this object) - inError = false; - logger = null; - - LoggerContext loggerContext = (LoggerContext) this.context; +public class LoggerAction extends BaseModelAction { - String loggerName = ec.subst(attributes.getValue(NAME_ATTRIBUTE)); - - if (OptionHelper.isEmpty(loggerName)) { - inError = true; - String aroundLine = getLineColStr(ec); - String errorMsg = "No 'name' attribute in element " + name + ", around " + aroundLine; - addError(errorMsg); - return; - } + @Override + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, ic, name, attributes); + validator.validateNameAttribute(); + return validator.isValid(); + } - logger = loggerContext.getLogger(loggerName); + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { - String levelStr = ec.subst(attributes.getValue(LEVEL_ATTRIBUTE)); + LoggerModel loggerModel = new LoggerModel(); - if (!OptionHelper.isEmpty(levelStr)) { - if (ActionConst.INHERITED.equalsIgnoreCase(levelStr) || ActionConst.NULL.equalsIgnoreCase(levelStr)) { - addInfo("Setting level of logger [" + loggerName + "] to null, i.e. INHERITED"); - logger.setLevel(null); - } else { - Level level = Level.toLevel(levelStr); - addInfo("Setting level of logger [" + loggerName + "] to " + level); - logger.setLevel(level); - } - } + String nameStr = attributes.getValue(NAME_ATTRIBUTE); + loggerModel.setName(nameStr); - String additivityStr = ec.subst(attributes.getValue(ActionConst.ADDITIVITY_ATTRIBUTE)); - if (!OptionHelper.isEmpty(additivityStr)) { - boolean additive = OptionHelper.toBoolean(additivityStr, true); - addInfo("Setting additivity of logger [" + loggerName + "] to " + additive); - logger.setAdditive(additive); - } - ec.pushObject(logger); - } + String levelStr = attributes.getValue(JoranConstants.LEVEL_ATTRIBUTE); + loggerModel.setLevel(levelStr); - public void end(InterpretationContext ec, String e) { - if (inError) { - return; - } - Object o = ec.peekObject(); - if (o != logger) { - addWarn("The object on the top the of the stack is not " + logger + " pushed earlier"); - addWarn("It is: " + o); - } else { - ec.popObject(); - } - } + String additivityStr = attributes.getValue(JoranConstants.ADDITIVITY_ATTRIBUTE); + loggerModel.setAdditivity(additivityStr); - public void finish(InterpretationContext ec) { + return loggerModel; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerContextListenerAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerContextListenerAction.java index 691e8873fc..af419095f2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerContextListenerAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/LoggerContextListenerAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,66 +13,32 @@ */ package ch.qos.logback.classic.joran.action; -import ch.qos.logback.classic.spi.LoggerContextListener; -import ch.qos.logback.core.spi.ContextAware; -import ch.qos.logback.core.spi.LifeCycle; import org.xml.sax.Attributes; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.classic.model.LoggerContextListenerModel; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; -public class LoggerContextListenerAction extends Action { +public class LoggerContextListenerAction extends BaseModelAction { boolean inError = false; LoggerContextListener lcl; @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - - inError = false; - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - addError("Mandatory \"" + CLASS_ATTRIBUTE + "\" attribute not set for element"); - inError = true; - return; - } - - try { - lcl = (LoggerContextListener) OptionHelper.instantiateByClassName(className, LoggerContextListener.class, context); - - if (lcl instanceof ContextAware) { - ((ContextAware) lcl).setContext(context); - } - - ec.pushObject(lcl); - addInfo("Adding LoggerContextListener of type [" + className + "] to the object stack"); - - } catch (Exception oops) { - inError = true; - addError("Could not create LoggerContextListener of type " + className + "].", oops); - } + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, ic, name, attributes); + pv.validateClassAttribute(); + return pv.isValid(); } @Override - public void end(InterpretationContext ec, String name) throws ActionException { - if (inError) { - return; - } - Object o = ec.peekObject(); - - if (o != lcl) { - addWarn("The object on the top the of the stack is not the LoggerContextListener pushed earlier."); - } else { - if (lcl instanceof LifeCycle) { - ((LifeCycle) lcl).start(); - addInfo("Starting LoggerContextListener"); - } - ((LoggerContext) context).addListener(lcl); - ec.popObject(); - } + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + LoggerContextListenerModel loggerContextListenerModel = new LoggerContextListenerModel(); + loggerContextListenerModel.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + return loggerContextListenerModel; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/PropertiesConfiguratorAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/PropertiesConfiguratorAction.java new file mode 100644 index 0000000000..3ffcbdf18a --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/PropertiesConfiguratorAction.java @@ -0,0 +1,32 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.action; + +import ch.qos.logback.classic.model.PropertiesConfiguratorModel; +import ch.qos.logback.core.joran.action.ResourceAction; + +/** + * Build an {@link PropertiesConfiguratorModel} instance from SAX events. + * + * @author Ceki Gülcü + * @since 1.5.8 + */ +public class PropertiesConfiguratorAction extends ResourceAction { + + protected PropertiesConfiguratorModel makeNewResourceModel() { + return new PropertiesConfiguratorModel(); + } + +} \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ReceiverAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ReceiverAction.java deleted file mode 100644 index 3a5dfdec72..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/ReceiverAction.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran.action; - -import org.xml.sax.Attributes; - -import ch.qos.logback.classic.net.ReceiverBase; -import ch.qos.logback.classic.net.SocketReceiver; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; - -/** - * A Joran {@link Action} for a {@link SocketReceiver} configuration. - * - * @author Carl Harris - */ -public class ReceiverAction extends Action { - - private ReceiverBase receiver; - private boolean inError; - - @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - addError("Missing class name for receiver. Near [" + name + "] line " + getLineNumber(ic)); - inError = true; - return; - } - - try { - addInfo("About to instantiate receiver of type [" + className + "]"); - - receiver = (ReceiverBase) OptionHelper.instantiateByClassName(className, ReceiverBase.class, context); - receiver.setContext(context); - - ic.pushObject(receiver); - } catch (Exception ex) { - inError = true; - addError("Could not create a receiver of type [" + className + "].", ex); - throw new ActionException(ex); - } - } - - @Override - public void end(InterpretationContext ic, String name) throws ActionException { - - if (inError) - return; - - ic.getContext().register(receiver); - receiver.start(); - - Object o = ic.peekObject(); - if (o != receiver) { - addWarn("The object at the of the stack is not the remote " + "pushed earlier."); - } else { - ic.popObject(); - } - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/RootLoggerAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/RootLoggerAction.java index 5c136292f0..d0b594efd0 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/RootLoggerAction.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/action/RootLoggerAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,49 +13,42 @@ */ package ch.qos.logback.classic.joran.action; +import ch.qos.logback.core.joran.action.PreconditionValidator; import org.xml.sax.Attributes; -import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.classic.model.RootLoggerModel; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import static ch.qos.logback.core.joran.JoranConstants.NULL; +import static ch.qos.logback.core.joran.JoranConstants.INHERITED; +import static ch.qos.logback.core.spi.ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL; -public class RootLoggerAction extends Action { +public class RootLoggerAction extends BaseModelAction { Logger root; boolean inError = false; - public void begin(InterpretationContext ec, String name, Attributes attributes) { - inError = false; - - LoggerContext loggerContext = (LoggerContext) this.context; - root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - - String levelStr = ec.subst(attributes.getValue(ActionConst.LEVEL_ATTRIBUTE)); - if (!OptionHelper.isEmpty(levelStr)) { - Level level = Level.toLevel(levelStr); - addInfo("Setting level of ROOT logger to " + level); - root.setLevel(level); + @Override + protected boolean validPreconditions(SaxEventInterpretationContext interpcont, String name, Attributes attributes) { + PreconditionValidator pv; + String levelStr = attributes.getValue(JoranConstants.LEVEL_ATTRIBUTE); + if(NULL.equalsIgnoreCase(levelStr) || INHERITED.equalsIgnoreCase(levelStr)) { + addError(ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + return false; } - ec.pushObject(root); + return true; } - - public void end(InterpretationContext ec, String name) { - if (inError) { - return; - } - Object o = ec.peekObject(); - if (o != root) { - addWarn("The object on the top the of the stack is not the root logger"); - addWarn("It is: " + o); - } else { - ec.popObject(); - } + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + RootLoggerModel rootLoggerModel = new RootLoggerModel(); + String levelStr = attributes.getValue(JoranConstants.LEVEL_ATTRIBUTE); + rootLoggerModel.setLevel(levelStr); + + return rootLoggerModel; } - public void finish(InterpretationContext ec) { - } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/joran/package.html index 26491f9d79..6360a37c94 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/joran/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSC.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSC.java new file mode 100644 index 0000000000..fbf435a380 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSC.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.sanity; + +import ch.qos.logback.classic.model.LoggerModel; +import ch.qos.logback.classic.model.RootLoggerModel; +import ch.qos.logback.core.joran.sanity.Pair; +import ch.qos.logback.core.joran.sanity.SanityChecker; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.spi.ContextAwareBase; + +import java.util.ArrayList; +import java.util.List; + +import static ch.qos.logback.core.CoreConstants.CODES_URL; + +public class IfNestedWithinSecondPhaseElementSC extends ContextAwareBase implements SanityChecker { + + static final public String NESTED_IF_WARNING_URL = CODES_URL+ "#nested_if_element"; + + @Override + public void check(Model model) { + if (model == null) + return; + + List secondPhaseModels = new ArrayList<>(); + deepFindAllModelsOfType(AppenderModel.class, secondPhaseModels, model); + deepFindAllModelsOfType(LoggerModel.class, secondPhaseModels, model); + deepFindAllModelsOfType(RootLoggerModel.class, secondPhaseModels, model); + + List> nestedPairs = deepFindNestedSubModelsOfType(IfModel.class, secondPhaseModels); + + if (nestedPairs.isEmpty()) + return; + + addWarn(" elements cannot be nested within an , or element"); + addWarn("See also " + NESTED_IF_WARNING_URL); + for (Pair pair : nestedPairs) { + Model p = pair.first; + int pLine = p.getLineNumber(); + Model s = pair.second; + int sLine = s.getLineNumber(); + addWarn("Element <"+p.getTag()+"> at line " + pLine + " contains a nested <"+s.getTag()+"> element at line " +sLine); + } + } + + @Override + public String toString() { + return "IfNestedWithinSecondPhaseElementSC"; + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/joran/serializedModel/HardenedModelInputStream.java b/logback-classic/src/main/java/ch/qos/logback/classic/joran/serializedModel/HardenedModelInputStream.java new file mode 100644 index 0000000000..34ec6ff70c --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/joran/serializedModel/HardenedModelInputStream.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.serializedModel; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.net.HardenedObjectInputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.List; + +public class HardenedModelInputStream extends HardenedObjectInputStream { + + + static public List getWhilelist() { + List whitelist = new ArrayList(); + whitelist.add(Model.class.getName()); + whitelist.add(ch.qos.logback.core.model.Model.class.getName()); + whitelist.add(ch.qos.logback.core.model.IncludeModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.InsertFromJNDIModel.class.getName()); + whitelist.add(ch.qos.logback.classic.model.RootLoggerModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.ImportModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.AppenderRefModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.ComponentModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.StatusListenerModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.ShutdownHookModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.NamedComponentModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.AppenderModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.EventEvaluatorModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.DefineModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.SequenceNumberGeneratorModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.ImplicitModel.class.getName()); + whitelist.add(ch.qos.logback.classic.model.LoggerContextListenerModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.conditional.ThenModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.conditional.IfModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.NamedModel.class.getName()); + whitelist.add(ch.qos.logback.classic.model.ContextNameModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.ParamModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.TimestampModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.PropertyModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.conditional.ElseModel.class.getName()); + whitelist.add(ch.qos.logback.classic.model.ConfigurationModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.SiftModel.class.getName()); + whitelist.add(ch.qos.logback.classic.model.LoggerModel.class.getName()); + whitelist.add(ch.qos.logback.core.model.SerializeModelModel.class.getName()); + + + return whitelist; + } + public HardenedModelInputStream(InputStream is) throws IOException { + super(is, getWhilelist()); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jul/JULHelper.java b/logback-classic/src/main/java/ch/qos/logback/classic/jul/JULHelper.java index 4b5fb7e52a..67a7b4aff3 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jul/JULHelper.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/jul/JULHelper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jul/LevelChangePropagator.java b/logback-classic/src/main/java/ch/qos/logback/classic/jul/LevelChangePropagator.java index a73894ad78..8490656a29 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jul/LevelChangePropagator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/jul/LevelChangePropagator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,7 +27,8 @@ import java.util.logging.LogManager; /** - * Propagate level changes made to a logback logger into the equivalent logger in j.u.l. + * Propagate level changes made to a logback logger into the equivalent logger + * in j.u.l. */ public class LevelChangePropagator extends ContextAwareBase implements LoggerContextListener, LifeCycle { @@ -60,7 +61,7 @@ private void propagate(Logger logger, Level level) { addInfo("Propagating " + level + " level on " + logger + " onto the JUL framework"); java.util.logging.Logger julLogger = JULHelper.asJULLogger(logger); // prevent garbage collection of jul loggers whose level we set - // see also http://jira.qos.ch/browse/LBCLASSIC-256 + // see also http://jira.qos.ch/browse//LOGBACK-404 julLoggerSet.add(julLogger); java.util.logging.Level julLevel = JULHelper.asJULLevel(level); julLogger.setLevel(julLevel); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/jul/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/jul/package.html index 6c24ec4566..e5e2af7b00 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/jul/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/jul/package.html @@ -1,4 +1,18 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/layout/TTLLLayout.java b/logback-classic/src/main/java/ch/qos/logback/classic/layout/TTLLLayout.java index 2d903a0b69..dc397c3f19 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/layout/TTLLLayout.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/layout/TTLLLayout.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.layout; import ch.qos.logback.classic.pattern.ThrowableProxyConverter; @@ -6,16 +20,31 @@ import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.LayoutBase; import ch.qos.logback.core.util.CachingDateFormatter; +import org.slf4j.event.KeyValuePair; + +import java.util.List; /** - * A layout with a fixed format. The output is equivalent to that produced by {@link ch.qos.logback.classic.PatternLayout PatternLayout} with the pattern:

+ * A layout with a fixed format. The output is equivalent to that produced by + * {@link ch.qos.logback.classic.PatternLayout PatternLayout} with the pattern: + *

* - *
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+ *
+ * %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n
+ * 
* - *

TTLLLayout has the advantage of faster load time whereas {@link ch.qos.logback.classic.PatternLayout PatternLayout} - * requires roughly 40 milliseconds to load its parser classes. Note that the second run of PatternLayout will be much much faster (approx. 10 micro-seconds).

+ *

+ * TTLLLayout has the advantage of faster load time whereas + * {@link ch.qos.logback.classic.PatternLayout PatternLayout} requires roughly + * 40 milliseconds to load its parser classes. Note that the second run of + * PatternLayout will be much much faster (approx. 10 micro-seconds). + *

* - *

Fixed format layouts such as TTLLLayout should be considered as an alternative to PatternLayout only if the extra 40 milliseconds at application start-up is considered significant.

+ *

+ * Fixed format layouts such as TTLLLayout should be considered as an + * alternative to PatternLayout only if the extra 40 milliseconds at application + * start-up is considered significant. + *

* * @author Ceki Gülcü * @since 1.1.6 @@ -47,7 +76,9 @@ public String doLayout(ILoggingEvent event) { sb.append(event.getLevel().toString()); sb.append(" "); sb.append(event.getLoggerName()); - sb.append(" - "); + sb.append(" -"); + kvp(event, sb); + sb.append("- "); sb.append(event.getFormattedMessage()); sb.append(CoreConstants.LINE_SEPARATOR); IThrowableProxy tp = event.getThrowableProxy(); @@ -58,4 +89,25 @@ public String doLayout(ILoggingEvent event) { return sb.toString(); } + static final char DOUBLE_QUOTE_CHAR = '"'; + private void kvp(ILoggingEvent event, StringBuilder sb) { + List kvpList = event.getKeyValuePairs(); + if (kvpList == null || kvpList.isEmpty()) { + return; + } + + int len = kvpList.size(); + + for (int i = 0; i < len; i++) { + KeyValuePair kvp = kvpList.get(i); + if (i != 0) + sb.append(' '); + sb.append(String.valueOf(kvp.key)); + sb.append('='); + sb.append(DOUBLE_QUOTE_CHAR); + sb.append(String.valueOf(kvp.value)); + sb.append(DOUBLE_QUOTE_CHAR); + } + } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/log4j/XMLLayout.java b/logback-classic/src/main/java/ch/qos/logback/classic/log4j/XMLLayout.java index ee640eeb6e..06b57b637f 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/log4j/XMLLayout.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/log4j/XMLLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,6 +17,7 @@ import java.util.Set; import java.util.Map.Entry; +import ch.qos.logback.classic.net.SMTPAppender; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.StackTraceElementProxy; @@ -37,10 +38,8 @@ */ public class XMLLayout extends LayoutBase { - private final int DEFAULT_SIZE = 256; - private final int UPPER_LIMIT = 2048; + private static final int DEFAULT_SIZE = 256; - private StringBuilder buf = new StringBuilder(DEFAULT_SIZE); private boolean locationInfo = false; private boolean properties = false; @@ -50,14 +49,17 @@ public void start() { } /** - * The LocationInfo option takes a boolean value. By default, it is - * set to false which means there will be no location information output by - * this layout. If the the option is set to true, then the file name and line - * number of the statement at the origin of the log statement will be output. + * The LocationInfo option takes a boolean value. By default, it is set + * to false which means there will be no location information output by this + * layout. If the option is set to true, then the file name and line number + * of the statement at the origin of the log statement will be output. * - *

If you are embedding this layout within an {@link - * org.apache.log4j.net.SMTPAppender} then make sure to set the - * LocationInfo option of that appender as well. + *

+ * If you are embedding this layout within an + * {@link SMTPAppender} then make sure to set the + * {@link SMTPAppender#setIncludeCallerData(boolean) includeCallerData} option + * as well. + *

*/ public void setLocationInfo(boolean flag) { locationInfo = flag; @@ -73,8 +75,7 @@ public boolean getLocationInfo() { /** * Sets whether MDC key-value pairs should be output, default false. * - * @param flag - * new value. + * @param flag new value. * @since 1.2.15 */ public void setProperties(final boolean flag) { @@ -96,13 +97,7 @@ public boolean getProperties() { */ public String doLayout(ILoggingEvent event) { - // Reset working buffer. If the buffer is too large, then we need a new - // one in order to avoid the penalty of creating a large array. - if (buf.capacity() > UPPER_LIMIT) { - buf = new StringBuilder(DEFAULT_SIZE); - } else { - buf.setLength(0); - } + StringBuilder buf = new StringBuilder(DEFAULT_SIZE); // We yield to the \r\n heresy. @@ -154,7 +149,8 @@ public String doLayout(ILoggingEvent event) { } /* - * + * + * */ if (this.getProperties()) { Map propertyMap = event.getMDCPropertyMap(); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/log4j/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/log4j/package.html index 3538b1ead4..559b5329c1 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/log4j/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/log4j/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/logback-classic-version.properties b/logback-classic/src/main/java/ch/qos/logback/classic/logback-classic-version.properties new file mode 100644 index 0000000000..536bde6ca4 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/logback-classic-version.properties @@ -0,0 +1,15 @@ +# +# Logback: the reliable, generic, fast and flexible logging framework. +# Copyright (C) 1999-2026, QOS.ch. All rights reserved. +# +# This program and the accompanying materials are dual-licensed under +# either the terms of the Eclipse Public License v2.0 as published by +# the Eclipse Foundation +# +# or (per the licensee's choosing) +# +# under the terms of the GNU Lesser General Public License version 2.1 +# as published by the Free Software Foundation. +# + +logback-classic-version=${project.version} \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/ConfigurationModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/ConfigurationModel.java new file mode 100755 index 0000000000..5195ce9a53 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/ConfigurationModel.java @@ -0,0 +1,98 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model; + +import java.util.Objects; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.util.Duration; + +public class ConfigurationModel extends Model { + + private static final long serialVersionUID = 1286156598561818515L; + + String debugStr; + String scanStr; + String scanPeriodStr; + String packagingDataStr; + + @Override + protected ConfigurationModel makeNewInstance() { + return new ConfigurationModel(); + } + + @Override protected void mirror(Model that) { + ConfigurationModel actual = (ConfigurationModel) that; + super.mirror(that); + this.debugStr = actual.debugStr; + this.scanStr = actual.scanStr; + this.scanPeriodStr = actual.scanPeriodStr; + this.packagingDataStr = actual.packagingDataStr; + } + + public String getDebugStr() { + return debugStr; + } + + public void setDebugStr(String debugStr) { + this.debugStr = debugStr; + } + + public String getScanStr() { + return scanStr; + } + + public void setScanStr(String scanStr) { + this.scanStr = scanStr; + } + + public String getScanPeriodStr() { + return scanPeriodStr; + } + + public void setScanPeriodStr(String scanPeriodStr) { + this.scanPeriodStr = scanPeriodStr; + } + + public String getPackagingDataStr() { + return packagingDataStr; + } + + public void setPackagingDataStr(String packagingDataStr) { + this.packagingDataStr = packagingDataStr; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(debugStr, packagingDataStr, scanPeriodStr, scanStr); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ConfigurationModel other = (ConfigurationModel) obj; + return Objects.equals(debugStr, other.debugStr) && Objects.equals(packagingDataStr, other.packagingDataStr) + && Objects.equals(scanPeriodStr, other.scanPeriodStr) && Objects.equals(scanStr, other.scanStr); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/ContextNameModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/ContextNameModel.java new file mode 100755 index 0000000000..4bf2c0ccef --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/ContextNameModel.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model; + +import ch.qos.logback.core.model.NamedModel; + +public class ContextNameModel extends NamedModel { + + private static final long serialVersionUID = -1635653921915985666L; + + @Override + protected ContextNameModel makeNewInstance() { + return new ContextNameModel(); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/LevelModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/LevelModel.java new file mode 100755 index 0000000000..d309c8e10e --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/LevelModel.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model; + +import java.util.Objects; + +import ch.qos.logback.core.model.Model; + +public class LevelModel extends Model { + + private static final long serialVersionUID = -7287549849308062148L; + String value; + + @Override + protected LevelModel makeNewInstance() { + return new LevelModel(); + } + + @Override + protected void mirror(Model that) { + LevelModel actual = (LevelModel) that; + super.mirror(actual); + this.value = actual.value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(value); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + LevelModel other = (LevelModel) obj; + return Objects.equals(value, other.value); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerContextListenerModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerContextListenerModel.java new file mode 100755 index 0000000000..0267f7eb61 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerContextListenerModel.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model; + +import ch.qos.logback.core.model.ComponentModel; + +public class LoggerContextListenerModel extends ComponentModel { + + private static final long serialVersionUID = 8200534537519733363L; + + @Override + protected LoggerContextListenerModel makeNewInstance() { + return new LoggerContextListenerModel(); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerModel.java new file mode 100755 index 0000000000..fc817f53c9 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/LoggerModel.java @@ -0,0 +1,96 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model; + +import java.util.Objects; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.PhaseIndicator; +import ch.qos.logback.core.model.processor.ProcessingPhase; + +@PhaseIndicator(phase = ProcessingPhase.SECOND) +public class LoggerModel extends Model { + + private static final long serialVersionUID = 5326913660697375316L; + + String name; + String level; + String additivity; + + @Override + protected LoggerModel makeNewInstance() { + return new LoggerModel(); + } + + @Override + protected void mirror(Model that) { + LoggerModel actual = (LoggerModel) that; + super.mirror(actual); + this.name = actual.name; + this.level = actual.level; + this.additivity = actual.additivity; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + public String getAdditivity() { + return additivity; + } + + public void setAdditivity(String additivity) { + this.additivity = additivity; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " name=" + name + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(additivity, level, name); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + LoggerModel other = (LoggerModel) obj; + return Objects.equals(additivity, other.additivity) && Objects.equals(level, other.level) + && Objects.equals(name, other.name); + } + + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/PropertiesConfiguratorModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/PropertiesConfiguratorModel.java new file mode 100644 index 0000000000..2778cc850e --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/PropertiesConfiguratorModel.java @@ -0,0 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model; + +import ch.qos.logback.core.model.ResourceModel; + +public class PropertiesConfiguratorModel extends ResourceModel { + + private static final long serialVersionUID = -2009536798661734346L; + + String scanStr; + + public String getScanStr() { + return scanStr; + } + + public void setScanStr(String scanStr) { + this.scanStr = scanStr; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/ReceiverModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/ReceiverModel.java new file mode 100644 index 0000000000..eb876d650d --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/ReceiverModel.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model; + +import ch.qos.logback.core.model.ComponentModel; + +public class ReceiverModel extends ComponentModel { + + private static final long serialVersionUID = -3161852321892056675L; + + @Override + protected ReceiverModel makeNewInstance() { + return new ReceiverModel(); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/RootLoggerModel.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/RootLoggerModel.java new file mode 100755 index 0000000000..7e3bb9b28f --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/RootLoggerModel.java @@ -0,0 +1,70 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model; + +import java.util.Objects; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.PhaseIndicator; +import ch.qos.logback.core.model.processor.ProcessingPhase; + +@PhaseIndicator(phase = ProcessingPhase.SECOND) +public class RootLoggerModel extends Model { + + private static final long serialVersionUID = -2811453129653502831L; + String level; + + @Override + protected RootLoggerModel makeNewInstance() { + return new RootLoggerModel(); + } + + @Override + protected void mirror(Model that) { + RootLoggerModel actual = (RootLoggerModel) that; + super.mirror(actual); + this.level = actual.level; + } + + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(level); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + RootLoggerModel other = (RootLoggerModel) obj; + return Objects.equals(level, other.level); + } + + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandler.java new file mode 100755 index 0000000000..7ecc881898 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandler.java @@ -0,0 +1,148 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.util.ContextUtil; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.util.StatusListenerConfigHelper; + +import static ch.qos.logback.core.model.ModelConstants.DEBUG_SYSTEM_PROPERTY_KEY; +import static ch.qos.logback.core.model.ModelConstants.NULL_STR; +import static java.lang.Boolean.FALSE; + +/** + * In 1.3.9/1.49, ConfigurationModelHandler has been reduced in functionality and no + * longer initiates a reconfiguration task. This change was justified by the need + * to remove java.xml reachability. See also https://jira.qos.ch/browse/LOGBACK-1717 + * + *

+ * See {@link ConfigurationModelHandlerFull} subclass offering configuration + * reloading support. + *

+ */ +public class ConfigurationModelHandler extends ModelHandlerBase { + + static final Duration SCAN_PERIOD_DEFAULT = Duration.buildByMinutes(1); + + protected Boolean scanning = null; + + public ConfigurationModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new ConfigurationModelHandler(context); + } + + protected Class getSupportedModelClass() { + return ConfigurationModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) { + + ConfigurationModel configurationModel = (ConfigurationModel) model; + + // See LOGBACK-527 (the system property is looked up first). Thus, it overrides + // the equivalent property in the config file. This reversal of scope priority + // is justified by the use case: the admin trying to chase rogue config file + String debugAttrib = OptionHelper.getSystemProperty(DEBUG_SYSTEM_PROPERTY_KEY, null); + if (debugAttrib == null) { + debugAttrib = mic.subst(configurationModel.getDebugStr()); + } + + + if (!(OptionHelper.isNullOrEmptyOrAllSpaces(debugAttrib) || debugAttrib.equalsIgnoreCase(FALSE.toString()) + || debugAttrib.equalsIgnoreCase(NULL_STR))) { + StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); + } + + // It is hard to gauge at this stage which URL ares watchable + // However, we know for sure if the user wants scanning or not + this.scanning = scanAttrToBoolean(configurationModel); + + mic.setTopScanBoolean(scanning); + + printScanMessage(scanning); + + if (scanning == Boolean.TRUE) { + ConfigurationWatchListUtil.registerNewConfigurationWatchListWithContext(getContext()); + ConfigurationWatchListUtil.setMainWatchURL(context, mic.getTopURL()); + } + + LoggerContext lc = (LoggerContext) context; + boolean packagingData = OptionHelper.toBoolean(mic.subst(configurationModel.getPackagingDataStr()), + LoggerContext.DEFAULT_PACKAGING_DATA); + lc.setPackagingDataEnabled(packagingData); + + ContextUtil contextUtil = new ContextUtil(context); + contextUtil.addGroovyPackages(lc.getFrameworkPackages()); + + + } + + void printScanMessage(Boolean scanning) { + if (scanning == null) { + addInfo("Scan attribute not set or set to unrecognized value."); + return; + } + if (scanning) { + addInfo("Scan attribute set to true. Will scan for configuration file changes."); + } else { + addInfo("Scan attribute set to false."); + } + } + + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + //ConfigurationModel configurationModel = (ConfigurationModel) model; + + } + + /** + * Converts the scan string attribute of the given model to a Boolean value. + * + *

If the provided model is an instance of {@code ConfigurationModel}, the scan string is retrieved + * and converted to a {@code Boolean}. If the provided model is not a {@code ConfigurationModel}, + * the method returns {@code null}. + *

+ * + * @param model the model object, which may be an instance of {@code ConfigurationModel} + * @return a {@code Boolean} corresponding to the scan string attribute if the model is + * an instance of {@code ConfigurationModel}, or {@code null} otherwise + * + * @since 1.5.27 + */ + private Boolean scanAttrToBoolean(Model model) { + if(model instanceof ConfigurationModel) { + ConfigurationModel configurationModel = (ConfigurationModel) model; + String scanStr = configurationModel.getScanStr(); + return OptionHelper.toBooleanObject(scanStr); + } else { + return null; + } + + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandlerFull.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandlerFull.java new file mode 100755 index 0000000000..efc00d5920 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ConfigurationModelHandlerFull.java @@ -0,0 +1,149 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.joran.ReconfigureOnChangeTask; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.spi.ConfigurationWatchList; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.OptionHelper; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +/** + * This is a subclass of {@link ConfigurationModelHandler} offering configuration reloading support. + * + *

This class is also called by logback-tyler.

+ * + */ +public class ConfigurationModelHandlerFull extends ConfigurationModelHandler { + + public static String FAILED_WATCH_PREDICATE_MESSAGE_1 = "Missing watchable .xml or .properties files."; + public static String FAILED_WATCH_PREDICATE_MESSAGE_2 = "Watching .xml files requires that the main configuration file is reachable as a URL"; + + public ConfigurationModelHandlerFull(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance2(Context context, ModelInterpretationContext mic) { + return new ConfigurationModelHandlerFull(context); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + ConfigurationModel configurationModel = (ConfigurationModel) model; + + // scanning is disabled + if (!(scanning == Boolean.TRUE)) { + return; + } + + String scanPeriodStr = mic.subst(configurationModel.getScanPeriodStr()); + scheduleReconfigureOnChangeTask(scanPeriodStr); + + ConfigurationWatchList cwl = ConfigurationWatchListUtil.getConfigurationWatchList(getContext()); + if (cwl != null) { + try { + addInfo("Main configuration file URL: " + cwl.getTopURL()); + addInfo("FileWatchList= {" + cwl.getFileWatchListAsStr() + "}"); + addInfo("URLWatchList= {" + cwl.getUrlWatchListAsStr() + "}"); + } catch (NoSuchMethodError e) { + addWarn("It looks like the version of logback-classic is more recent than"); + addWarn("the version of logback-core. Please align the two versions."); + } + } + } + + + /** + * This method is called from this class but also from logback-tyler. + *

+ * This method assumes that the variables scanStr and scanPeriodStr have undergone variable substitution + * as applicable to their current environment + * + * @param scanPeriodStr + * @since 1.5.0 + */ + public void detachedPostProcess(String scanStr, String scanPeriodStr) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(scanStr) || "false".equalsIgnoreCase(scanStr)) { + return; + } + + scheduleReconfigureOnChangeTask(scanPeriodStr); + } + + private void scheduleReconfigureOnChangeTask(String scanPeriodStr) { + + ScheduledExecutorService scheduledExecutorService = context.getScheduledExecutorService(); + boolean watchPredicateFulfilled = ConfigurationWatchListUtil.watchPredicateFulfilled(context); + if (!watchPredicateFulfilled) { + addWarn(FAILED_WATCH_PREDICATE_MESSAGE_1); + addWarn(FAILED_WATCH_PREDICATE_MESSAGE_2); + return; + } + ReconfigureOnChangeTask rocTask = new ReconfigureOnChangeTask(); + rocTask.setContext(context); + + addInfo("Registering a new ReconfigureOnChangeTask " + rocTask); + + context.fireConfigurationEvent(ConfigurationEvent.newConfigurationChangeDetectorRegisteredEvent(rocTask)); + + Duration duration = getDurationOfScanPeriodAttribute(scanPeriodStr, SCAN_PERIOD_DEFAULT); + + ConfigurationWatchList cwl = ConfigurationWatchListUtil.getConfigurationWatchList(context); + + String fileWatchListAsStr = (cwl != null) ? cwl.getFileWatchListAsStr() : ""; + + addInfo("Will scan for changes in [" + fileWatchListAsStr + "] "); + // Given that included files are encountered at a later phase, the complete list + // of files to scan can only be determined when the configuration is loaded in full. + // However, scan can be active if mainURL is set. Otherwise, when changes are + // detected the top level config file cannot be accessed. + addInfo("Setting ReconfigureOnChangeTask scanning period to " + duration); + + ScheduledFuture scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(rocTask, duration.getMilliseconds(), duration.getMilliseconds(), + TimeUnit.MILLISECONDS); + rocTask.setScheduledFuture(scheduledFuture); + context.addScheduledFuture(scheduledFuture); + + } + + private Duration getDurationOfScanPeriodAttribute(String scanPeriodAttrib, Duration defaultDuration) { + Duration duration = null; + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(scanPeriodAttrib)) { + try { + duration = Duration.valueOf(scanPeriodAttrib); + } catch (IllegalStateException | IllegalArgumentException e) { + addWarn("Failed to parse 'scanPeriod' attribute [" + scanPeriodAttrib + "]", e); + // default duration will be set below + } + } + + if (duration == null) { + addInfo("No 'scanPeriod' specified. Defaulting to " + defaultDuration.toString()); + duration = defaultDuration; + } + return duration; + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ContextNameModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ContextNameModelHandler.java new file mode 100755 index 0000000000..d9859b87bb --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/ContextNameModelHandler.java @@ -0,0 +1,53 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.model.ContextNameModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class ContextNameModelHandler extends ModelHandlerBase { + + public ContextNameModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new ContextNameModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return ContextNameModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + ContextNameModel contextNameModel = (ContextNameModel) model; + + String finalBody = mic.subst(contextNameModel.getBodyText()); + addInfo("Setting logger context name as [" + finalBody + "]"); + try { + context.setName(finalBody); + } catch (IllegalStateException e) { + addError("Failed to rename context [" + context.getName() + "] as [" + finalBody + "]", e); + } + + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LevelModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LevelModelHandler.java new file mode 100755 index 0000000000..b4d7aaacbc --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LevelModelHandler.java @@ -0,0 +1,76 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import static ch.qos.logback.core.joran.JoranConstants.INHERITED; +import static ch.qos.logback.core.joran.JoranConstants.NULL; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.model.LevelModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ErrorCodes; + +public class LevelModelHandler extends ModelHandlerBase { + + boolean inError = false; + + public LevelModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new LevelModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return LevelModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + Object o = mic.peekObject(); + + if (!(o instanceof Logger)) { + inError = true; + addError("For element , could not find a logger at the top of execution stack."); + return; + } + + Logger l = (Logger) o; + String loggerName = l.getName(); + + LevelModel levelModel = (LevelModel) model; + String levelStr = mic.subst(levelModel.getValue()); + if (INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { + if(Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(loggerName)) + addError(ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + else + l.setLevel(null); + } else { + l.setLevel(Level.toLevel(levelStr, Level.DEBUG)); + } + + addInfo(loggerName + " level set to " + l.getLevel()); + + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LogbackClassicDefaultNestedComponentRules.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LogbackClassicDefaultNestedComponentRules.java new file mode 100644 index 0000000000..e335c4f1b9 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LogbackClassicDefaultNestedComponentRules.java @@ -0,0 +1,76 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model.processor; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.boolex.StubEventEvaluator; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.UnsynchronizedAppenderBase; +import ch.qos.logback.core.filter.EvaluatorFilter; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.util.ParentTag_Tag_Class_Tuple; +import ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules; +import ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean; +import ch.qos.logback.core.net.ssl.KeyStoreFactoryBean; +import ch.qos.logback.core.net.ssl.SSLConfiguration; +import ch.qos.logback.core.net.ssl.SSLParametersConfiguration; +import ch.qos.logback.core.net.ssl.SecureRandomFactoryBean; +import ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBean; + +/** + * Contains mappings for the default type of nested components in + * logback-classic. + * + * @author Ceki Gulcu + * + */ +public class LogbackClassicDefaultNestedComponentRules { + + static public List TUPLES_LIST = createTuplesList(); + + static public void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { + registry.add(AppenderBase.class, "layout", PatternLayout.class); + registry.add(UnsynchronizedAppenderBase.class, "layout", PatternLayout.class); + + registry.add(AppenderBase.class, "encoder", PatternLayoutEncoder.class); + registry.add(UnsynchronizedAppenderBase.class, "encoder", PatternLayoutEncoder.class); + registry.add(EvaluatorFilter.class, "evaluator", StubEventEvaluator.class); + + SSLNestedComponentRegistryRules.addDefaultNestedComponentRegistryRules(registry); + } + + public static List createTuplesList() { + + List tupleList = new ArrayList<>(); + + tupleList.add(new ParentTag_Tag_Class_Tuple("appender", "encoder", PatternLayoutEncoder.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("appender", "layout", PatternLayout.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("receiver", "ssl", SSLConfiguration.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "parameters", SSLParametersConfiguration.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "keyStore", KeyStoreFactoryBean.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "trustStore", KeyManagerFactoryFactoryBean.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "keyManagerFactory", SSLParametersConfiguration.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "trustManagerFactory", TrustManagerFactoryFactoryBean.class.getName())); + tupleList.add(new ParentTag_Tag_Class_Tuple("ssl", "secureRandom", SecureRandomFactoryBean.class.getName())); + return tupleList; + + } + + + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerContextListenerModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerContextListenerModelHandler.java new file mode 100755 index 0000000000..ee08aeb80c --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerContextListenerModelHandler.java @@ -0,0 +1,93 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.model.LoggerContextListenerModel; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.util.OptionHelper; + +public class LoggerContextListenerModelHandler extends ModelHandlerBase { + boolean inError = false; + LoggerContextListener lcl; + + public LoggerContextListenerModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new LoggerContextListenerModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return LoggerContextListenerModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + LoggerContextListenerModel lclModel = (LoggerContextListenerModel) model; + + String className = lclModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addError("Empty class name for LoggerContextListener"); + inError = true; + } else { + className = mic.getImport(className); + } + + try { + lcl = (LoggerContextListener) OptionHelper.instantiateByClassName(className, LoggerContextListener.class, + context); + + if (lcl instanceof ContextAware) { + ((ContextAware) lcl).setContext(context); + } + + mic.pushObject(lcl); + addInfo("Adding LoggerContextListener of type [" + className + "] to the object stack"); + + } catch (Exception oops) { + inError = true; + addError("Could not create LoggerContextListener of type " + className + "].", oops); + } + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + if (inError) { + return; + } + Object o = mic.peekObject(); + + if (o != lcl) { + addWarn("The object on the top the of the stack is not the LoggerContextListener pushed earlier."); + } else { + if (lcl instanceof LifeCycle) { + ((LifeCycle) lcl).start(); + addInfo("Starting LoggerContextListener"); + } + ((LoggerContext) context).addListener(lcl); + mic.popObject(); + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerModelHandler.java new file mode 100755 index 0000000000..ef0f20df81 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/LoggerModelHandler.java @@ -0,0 +1,103 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import static ch.qos.logback.core.joran.JoranConstants.NULL; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.model.LoggerModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ErrorCodes; +import ch.qos.logback.core.util.OptionHelper; + +public class LoggerModelHandler extends ModelHandlerBase { + + Logger logger; + boolean inError = false; + + public LoggerModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new LoggerModelHandler(context); + } + + protected Class getSupportedModelClass() { + return LoggerModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + inError = false; + + LoggerModel loggerModel = (LoggerModel) model; + + String finalLoggerName = mic.subst(loggerModel.getName()); + + LoggerContext loggerContext = (LoggerContext) this.context; + + logger = loggerContext.getLogger(finalLoggerName); + + String levelStr = mic.subst(loggerModel.getLevel()); + if (!OptionHelper.isNullOrEmptyOrAllSpaces(levelStr)) { + if (JoranConstants.INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { + if(Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(finalLoggerName)) { + addError(ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + } else { + addInfo("Setting level of logger [" + finalLoggerName + "] to null, i.e. INHERITED"); + logger.setLevel(null); + } + } else { + Level level = Level.toLevel(levelStr); + addInfo("Setting level of logger [" + finalLoggerName + "] to " + level); + logger.setLevel(level); + } + } + + String additivityStr = mic.subst(loggerModel.getAdditivity()); + if (!OptionHelper.isNullOrEmptyOrAllSpaces(additivityStr)) { + boolean additive = OptionHelper.toBoolean(additivityStr, true); + addInfo("Setting additivity of logger [" + finalLoggerName + "] to " + additive); + logger.setAdditive(additive); + } + + mic.pushObject(logger); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) { + if (inError) { + return; + } + Object o = mic.peekObject(); + if (o != logger) { + LoggerModel loggerModel = (LoggerModel) model; + addWarn("The object [" + o + "] on the top the of the stack is not the expected logger named " + + loggerModel.getName()); + } else { + mic.popObject(); + } + + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/PropertiesConfiguratorModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/PropertiesConfiguratorModelHandler.java new file mode 100644 index 0000000000..87ad5eaa72 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/PropertiesConfiguratorModelHandler.java @@ -0,0 +1,125 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.joran.PropertiesConfigurator; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.classic.model.PropertiesConfiguratorModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ResourceModel; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.model.processor.ResourceHandlerBase; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.InputStream; +import java.net.URL; + +public class PropertiesConfiguratorModelHandler extends ResourceHandlerBase { + boolean inError = false; + + static final boolean CREATE_CWL_IF_NOT_ALREADY_CREATED = true; + + public PropertiesConfiguratorModelHandler(Context context) { + super(context); + } + + static public PropertiesConfiguratorModelHandler makeInstance(Context context, ModelInterpretationContext mic) { + return new PropertiesConfiguratorModelHandler(context); + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + Boolean topScanBoolean = mic.getTopScanBoolean(); + detachedHandle(mic, model, topScanBoolean); + } + + /** + * + * Used by {@link #handle(ModelInterpretationContext, Model)} as well as logback-tyler. Note the widening of the + * base from {@link ModelInterpretationContext} to {@link ContextAwarePropertyContainer}. + * + * @param capc + * @param model + * @throws ModelHandlerException + * @since 1.5.10 + */ + public void detachedHandle(ContextAwarePropertyContainer capc, Model model, Boolean topScanBoolean) throws ModelHandlerException { + + PropertiesConfiguratorModel propertyConfiguratorModel = (PropertiesConfiguratorModel) model; + + this.optional = OptionHelper.toBoolean(propertyConfiguratorModel.getOptional(), false); + + if (!checkAttributes(propertyConfiguratorModel)) { + inError = true; + return; + } + + URL inputURL = getInputURL(capc, propertyConfiguratorModel); + if (inputURL == null) { + inError = true; + return; + } + + + Boolean localScan = OptionHelper.toBooleanObject(propertyConfiguratorModel.getScanStr()); + + InputStream in = openURL(inputURL); + if (in == null) { + inError = true; + return; + } + + if(localScan == Boolean.TRUE || topScanBoolean == Boolean.TRUE) { + if(topScanBoolean != Boolean.TRUE) { + // if topScanBoolean ia not TRUE, then a ConfigurationWatchList has not been created and registered, yet + // we need to do so now + ConfigurationWatchListUtil.registerNewConfigurationWatchListWithContext(context); + } + ConfigurationWatchListUtil.addToWatchList(context, inputURL, CREATE_CWL_IF_NOT_ALREADY_CREATED); + } + + + + addInfo("Reading configuration from [" + getAttribureInUse() + "]"); + + PropertiesConfigurator propertiesConfigurator = new PropertiesConfigurator(); + propertiesConfigurator.setContext(capc.getContext()); + try { + propertiesConfigurator.doConfigure(in); + } catch (JoranException e) { + addError("Could not configure from " + getAttribureInUse()); + throw new ModelHandlerException(e); + } + + } + + protected InputStream getInputStream(ContextAwarePropertyContainer capc, ResourceModel resourceModel) { + URL inputURL = getInputURL(capc, resourceModel); + if (inputURL == null) + return null; + + + + ConfigurationWatchListUtil.addToWatchList(context, inputURL, CREATE_CWL_IF_NOT_ALREADY_CREATED); + return openURL(inputURL); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/RootLoggerModelHandler.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/RootLoggerModelHandler.java new file mode 100755 index 0000000000..18bc42ed74 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/processor/RootLoggerModelHandler.java @@ -0,0 +1,77 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model.processor; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.model.RootLoggerModel; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.util.OptionHelper; + +public class RootLoggerModelHandler extends ModelHandlerBase { + + Logger root; + boolean inError = false; + + public RootLoggerModelHandler(Context context) { + super(context); + } + + static public RootLoggerModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new RootLoggerModelHandler(context); + } + + protected Class getSupportedModelClass() { + return RootLoggerModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + inError = false; + + RootLoggerModel rootLoggerModel = (RootLoggerModel) model; + + LoggerContext loggerContext = (LoggerContext) this.context; + root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + + String levelStr = mic.subst(rootLoggerModel.getLevel()); + if (!OptionHelper.isNullOrEmptyOrAllSpaces(levelStr)) { + Level level = Level.toLevel(levelStr); + addInfo("Setting level of ROOT logger to " + level); + root.setLevel(level); + } + + mic.pushObject(root); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) { + if (inError) { + return; + } + Object o = mic.peekObject(); + if (o != root) { + addWarn("The object [" + o + "] on the top the of the stack is not the root logger"); + } else { + mic.popObject(); + } + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/model/util/DefaultClassNameHelper.java b/logback-classic/src/main/java/ch/qos/logback/classic/model/util/DefaultClassNameHelper.java new file mode 100644 index 0000000000..a5e1b9c223 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/model/util/DefaultClassNameHelper.java @@ -0,0 +1,73 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.model.util; + +import java.util.List; + +import ch.qos.logback.classic.model.processor.LogbackClassicDefaultNestedComponentRules; +import ch.qos.logback.core.joran.util.ParentTag_Tag_Class_Tuple; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.util.TagUtil; + +/** + * Injects missing class names into ImplicitModel instances missing class name. + * + * @author ceki + * @since 1.3.0-alpha15 + */ +public class DefaultClassNameHelper { + + List tupleList = LogbackClassicDefaultNestedComponentRules.TUPLES_LIST; + + /** + * This method injects default components classes to implicit models missing a + * class name. + * + * @param tupleList + * @param aModel + * @param parent + */ + public void injectDefaultComponentClasses(Model aModel, Model parent) { + + applyInjectionRules(aModel, parent); + + for (Model sub : aModel.getSubModels()) { + injectDefaultComponentClasses(sub, aModel); + } + } + + private void applyInjectionRules(Model aModel, Model parent) { + if (parent == null) + return; + + String parentTag = TagUtil.unifiedTag(parent); + String modelTag = TagUtil.unifiedTag(aModel); + + if (aModel instanceof ImplicitModel) { + ImplicitModel implicitModel = (ImplicitModel) aModel; + String className = implicitModel.getClassName(); + + if (className == null || className.isEmpty()) { + for (ParentTag_Tag_Class_Tuple ruleTuple : tupleList) { + if (ruleTuple.parentTag.equals(parentTag) && ruleTuple.tag.equals(modelTag)) { + implicitModel.setClassName(ruleTuple.className); + break; + } + } + + } + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.java index 5db326d785..24dc1c431f 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/LoggingEventPreSerializationTransformer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -26,6 +26,7 @@ public Serializable transform(ILoggingEvent event) { if (event == null) { return null; } + // Note that preprocessing is presumed to be already done if (event instanceof LoggingEvent) { return LoggingEventVO.build(event); } else if (event instanceof LoggingEventVO) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/ReceiverBase.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/ReceiverBase.java deleted file mode 100644 index 360bd9ce6d..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/ReceiverBase.java +++ /dev/null @@ -1,87 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.spi.LifeCycle; - -/** - * An abstract base for components that receive logging events from a remote - * peer and log according to local policy - * - * @author Carl Harris - */ -public abstract class ReceiverBase extends ContextAwareBase implements LifeCycle { - - private boolean started; - - /** - * {@inheritDoc} - */ - public final void start() { - if (isStarted()) - return; - if (getContext() == null) { - throw new IllegalStateException("context not set"); - } - if (shouldStart()) { - getContext().getScheduledExecutorService().execute(getRunnableTask()); - started = true; - } - } - - /** - * {@inheritDoc} - */ - public final void stop() { - if (!isStarted()) - return; - try { - onStop(); - } catch (RuntimeException ex) { - addError("on stop: " + ex, ex); - } - started = false; - } - - /** - * {@inheritDoc} - */ - public final boolean isStarted() { - return started; - } - - /** - * Determines whether this receiver should start. - *

- * Subclasses will implement this method to do any subclass-specific - * validation. The subclass's {@link #getRunnableTask()} method will be - * invoked (and the task returned will be submitted to the executor) - * if and only if this method returns {@code true} - * @return flag indicating whether this receiver should start - */ - protected abstract boolean shouldStart(); - - /** - * Allows a subclass to participate in receiver shutdown. - */ - protected abstract void onStop(); - - /** - * Provides the runnable task this receiver will execute. - * @return runnable task - */ - protected abstract Runnable getRunnableTask(); - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java index a505a509e9..c2aaafcf32 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SMTPAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,14 +21,18 @@ import ch.qos.logback.core.boolex.EventEvaluator; import ch.qos.logback.core.helpers.CyclicBuffer; import ch.qos.logback.core.net.SMTPAppenderBase; + +import java.util.List; +import java.util.concurrent.Future; + import org.slf4j.Marker; /** * Send an e-mail when a specific logging event occurs, typically on errors or * fatal errors. * - * For more information about this appender, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#SMTPAppender + * For more information about this appender, please refer to the online manual + * at http://logback.qos.ch/manual/appenders.html#SMTPAppender * * @author Ceki Gülcü * @author Sébastien Pennec @@ -43,8 +47,8 @@ public class SMTPAppender extends SMTPAppenderBase { /** * The default constructor will instantiate the appender with a - * {@link EventEvaluator} that will trigger on events with level - * ERROR or higher. + * {@link EventEvaluator} that will trigger on events with level ERROR or + * higher. */ public SMTPAppender() { @@ -62,16 +66,15 @@ public void start() { } /** - * Use the parameter as the {@link - * EventEvaluator} for this SMTPAppender. + * Use the parameter as the {@link EventEvaluator} for this SMTPAppender. */ public SMTPAppender(EventEvaluator eventEvaluator) { this.eventEvaluator = eventEvaluator; } /** - * Perform SMTPAppender specific appending actions, mainly adding the event to - * a cyclic buffer. + * Perform SMTPAppender specific appending actions, mainly adding the event to a + * cyclic buffer. */ protected void subAppend(CyclicBuffer cb, ILoggingEvent event) { if (includeCallerData) { @@ -91,11 +94,16 @@ protected void fillBuffer(CyclicBuffer cb, StringBuffer sbuf) { } protected boolean eventMarksEndOfLife(ILoggingEvent eventObject) { - Marker marker = eventObject.getMarker(); - if (marker == null) + List markers = eventObject.getMarkerList(); + if (markers == null || markers.isEmpty()) return false; - return marker.contains(ClassicConstants.FINALIZE_SESSION_MARKER); + for (Marker marker : markers) { + if (marker.contains(ClassicConstants.FINALIZE_SESSION_MARKER)) { + return true; + } + } + return false; } @Override @@ -127,4 +135,8 @@ public boolean isIncludeCallerData() { public void setIncludeCallerData(boolean includeCallerData) { this.includeCallerData = includeCallerData; } + + Future getAsynchronousSendingFuture() { + return asynchronousSendingFuture; + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketAppender.java index d3d9daafd6..b6b3febf46 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ /** * A {@link SocketAppender} that supports SSL. *

- * For more information on this appender, please refer to the online manual - * at http://logback.qos.ch/manual/appenders.html#SSLSocketAppender + * For more information on this appender, please refer to the online manual at + * http://logback.qos.ch/manual/appenders.html#SSLSocketAppender * * @author Carl Harris */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketReceiver.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketReceiver.java deleted file mode 100644 index 1146fcbf99..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SSLSocketReceiver.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import javax.net.SocketFactory; -import javax.net.ssl.SSLContext; - -import ch.qos.logback.core.net.ssl.ConfigurableSSLSocketFactory; -import ch.qos.logback.core.net.ssl.SSLComponent; -import ch.qos.logback.core.net.ssl.SSLConfiguration; -import ch.qos.logback.core.net.ssl.SSLParametersConfiguration; - -/** - * A {@link SocketReceiver} that supports SSL. - * - * @author Carl Harris - */ -public class SSLSocketReceiver extends SocketReceiver implements SSLComponent { - - private SSLConfiguration ssl; - private SocketFactory socketFactory; - - /** - * Gets an {@link SocketFactory} that produces SSL sockets using an - * {@link SSLContext} that is derived from the receiver's configuration. - * @return socket factory - */ - @Override - protected SocketFactory getSocketFactory() { - return socketFactory; - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean shouldStart() { - try { - SSLContext sslContext = getSsl().createContext(this); - SSLParametersConfiguration parameters = getSsl().getParameters(); - parameters.setContext(getContext()); - socketFactory = new ConfigurableSSLSocketFactory(parameters, sslContext.getSocketFactory()); - return super.shouldStart(); - } catch (Exception ex) { - addError(ex.getMessage(), ex); - return false; - } - } - - /** - * Gets the SSL configuration. - * @return SSL configuration; if no configuration has been set, a - * default configuration is returned - */ - public SSLConfiguration getSsl() { - if (ssl == null) { - ssl = new SSLConfiguration(); - } - return ssl; - } - - /** - * Sets the SSL configuration. - * @param ssl the SSL configuration to set - */ - public void setSsl(SSLConfiguration ssl) { - this.ssl = ssl; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSSLSocketServer.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSSLSocketServer.java index 848ffc50fb..de9cab8507 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSSLSocketServer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSSLSocketServer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,7 +31,7 @@ * * * where port is a port number where the server listens and - * configFile is an xml configuration file fed to + * configFile is an XML configuration file fed to * {@link JoranConfigurator}. * * When running the SimpleSSLServerFactory as shown above, it is necessary to @@ -64,10 +64,10 @@ public static void main(String argv[]) throws Exception { /** * Creates a new server using the default SSL context. - * @param lc logger context for received events + * + * @param lc logger context for received events * @param port port on which the server is to listen - * @throws NoSuchAlgorithmException if the default SSL context cannot be - * created + * @throws NoSuchAlgorithmException if the default SSL context cannot be created */ public SimpleSSLSocketServer(LoggerContext lc, int port) throws NoSuchAlgorithmException { this(lc, port, SSLContext.getDefault()); @@ -75,8 +75,9 @@ public SimpleSSLSocketServer(LoggerContext lc, int port) throws NoSuchAlgorithmE /** * Creates a new server using a custom SSL context. - * @param lc logger context for received events - * @param port port on which the server is to listen + * + * @param lc logger context for received events + * @param port port on which the server is to listen * @param sslContext custom SSL context */ public SimpleSSLSocketServer(LoggerContext lc, int port, SSLContext sslContext) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java index 3083f45ced..c6ff69803e 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -37,7 +37,7 @@ * * * where port is a port number where the server listens and - * configFile is an xml configuration file fed to + * configFile is an XML configuration file fed to * {@link JoranConfigurator}. * * @@ -77,6 +77,8 @@ protected static void doMain(Class serverClass, St configureLC(lc, configFile); SimpleSocketServer sss = new SimpleSocketServer(lc, port); + + // start the server in a separate thread sss.start(); } @@ -146,8 +148,8 @@ protected ServerSocketFactory getServerSocketFactory() { } /** - * Signal another thread that we have established a connection - * This is useful for testing purposes. + * Signal another thread that we have established a connection This is useful + * for testing purposes. */ void signalAlmostReadiness() { if (latch != null && latch.getCount() != 0) { @@ -158,6 +160,7 @@ void signalAlmostReadiness() { /** * Used for testing purposes + * * @param latch */ void setLatch(CountDownLatch latch) { @@ -165,8 +168,8 @@ void setLatch(CountDownLatch latch) { } /** - * Used for testing purposes - */ + * Used for testing purposes + */ public CountDownLatch getLatch() { return latch; } @@ -204,7 +207,7 @@ public void socketNodeClosing(SocketNode sn) { // don't allow simultaneous access to the socketNodeList // (e.g. removal whole iterating on the list causes - // java.util.ConcurrentModificationException + // java.util.ConcurrentModificationException) synchronized (socketNodeList) { socketNodeList.remove(sn); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAcceptor.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAcceptor.java index 93a2b2981d..d3e9d48fc4 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAcceptor.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAcceptor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java index b0da8321e4..e6b056c484 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,8 +22,8 @@ * Sends {@link ILoggingEvent} objects to a remote a log server, usually a * {@link SocketNode}. * - * For more information on this appender, please refer to the online manual - * at http://logback.qos.ch/manual/appenders.html#SocketAppender + * For more information on this appender, please refer to the online manual at + * http://logback.qos.ch/manual/appenders.html#SocketAppender * * @author Ceki Gülcü * @author Sébastien Pennec diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java index 02a4137ab7..5718b254a6 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketNode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -67,7 +67,8 @@ public SocketNode(SimpleSocketServer socketServer, Socket socket, LoggerContext public void run() { try { - hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(new BufferedInputStream(socket.getInputStream())); + hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream( + new BufferedInputStream(socket.getInputStream())); } catch (Exception e) { logger.error("Could not open ObjectInputStream to " + socket, e); closed = true; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketReceiver.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketReceiver.java deleted file mode 100644 index c803907af3..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SocketReceiver.java +++ /dev/null @@ -1,221 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import java.io.EOFException; -import java.io.IOException; -import java.io.ObjectInputStream; -import java.net.ConnectException; -import java.net.InetAddress; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.RejectedExecutionException; - -import javax.net.SocketFactory; - -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.net.DefaultSocketConnector; -import ch.qos.logback.core.net.AbstractSocketAppender; -import ch.qos.logback.core.net.SocketConnector; -import ch.qos.logback.core.util.CloseUtil; - -/** - * A component that receives serialized {@link ILoggingEvent} objects from a - * remote appender over a {@link Socket}. - * - * @author Carl Harris - */ -public class SocketReceiver extends ReceiverBase implements Runnable, SocketConnector.ExceptionHandler { - - private static final int DEFAULT_ACCEPT_CONNECTION_DELAY = 5000; - - private String remoteHost; - private InetAddress address; - private int port; - private int reconnectionDelay; - private int acceptConnectionTimeout = DEFAULT_ACCEPT_CONNECTION_DELAY; - - private String receiverId; - private volatile Socket socket; - private Future connectorTask; - - /** - * {@inheritDoc} - */ - protected boolean shouldStart() { - int errorCount = 0; - if (port == 0) { - errorCount++; - addError("No port was configured for receiver. " + "For more information, please visit http://logback.qos.ch/codes.html#receiver_no_port"); - } - - if (remoteHost == null) { - errorCount++; - addError("No host name or address was configured for receiver. " - + "For more information, please visit http://logback.qos.ch/codes.html#receiver_no_host"); - } - - if (reconnectionDelay == 0) { - reconnectionDelay = AbstractSocketAppender.DEFAULT_RECONNECTION_DELAY; - } - - if (errorCount == 0) { - try { - address = InetAddress.getByName(remoteHost); - } catch (UnknownHostException ex) { - addError("unknown host: " + remoteHost); - errorCount++; - } - } - - if (errorCount == 0) { - receiverId = "receiver " + remoteHost + ":" + port + ": "; - } - - return errorCount == 0; - } - - /** - * {@inheritDoc} - */ - protected void onStop() { - if (socket != null) { - CloseUtil.closeQuietly(socket); - } - } - - @Override - protected Runnable getRunnableTask() { - return this; - } - - /** - * {@inheritDoc} - */ - public void run() { - try { - LoggerContext lc = (LoggerContext) getContext(); - while (!Thread.currentThread().isInterrupted()) { - SocketConnector connector = createConnector(address, port, 0, reconnectionDelay); - connectorTask = activateConnector(connector); - if (connectorTask == null) { - break; - } - socket = waitForConnectorToReturnASocket(); - if (socket == null) - break; - dispatchEvents(lc); - } - } catch (InterruptedException ex) { - assert true; // ok... we'll exit now - } - addInfo("shutting down"); - } - - private SocketConnector createConnector(InetAddress address, int port, int initialDelay, int retryDelay) { - SocketConnector connector = newConnector(address, port, initialDelay, retryDelay); - connector.setExceptionHandler(this); - connector.setSocketFactory(getSocketFactory()); - return connector; - } - - private Future activateConnector(SocketConnector connector) { - try { - return getContext().getScheduledExecutorService().submit(connector); - } catch (RejectedExecutionException ex) { - return null; - } - } - - private Socket waitForConnectorToReturnASocket() throws InterruptedException { - try { - Socket s = connectorTask.get(); - connectorTask = null; - return s; - } catch (ExecutionException e) { - return null; - } - } - - private void dispatchEvents(LoggerContext lc) { - ObjectInputStream ois = null; - try { - socket.setSoTimeout(acceptConnectionTimeout); - ois = new HardenedLoggingEventInputStream(socket.getInputStream()); - socket.setSoTimeout(0); - addInfo(receiverId + "connection established"); - while (true) { - ILoggingEvent event = (ILoggingEvent) ois.readObject(); - Logger remoteLogger = lc.getLogger(event.getLoggerName()); - if (remoteLogger.isEnabledFor(event.getLevel())) { - remoteLogger.callAppenders(event); - } - } - } catch (EOFException ex) { - addInfo(receiverId + "end-of-stream detected"); - } catch (IOException ex) { - addInfo(receiverId + "connection failed: " + ex); - } catch (ClassNotFoundException ex) { - addInfo(receiverId + "unknown event class: " + ex); - } finally { - CloseUtil.closeQuietly(ois); - CloseUtil.closeQuietly(socket); - socket = null; - addInfo(receiverId + "connection closed"); - } - } - - /** - * {@inheritDoc} - */ - public void connectionFailed(SocketConnector connector, Exception ex) { - if (ex instanceof InterruptedException) { - addInfo("connector interrupted"); - } else if (ex instanceof ConnectException) { - addInfo(receiverId + "connection refused"); - } else { - addInfo(receiverId + ex); - } - } - - protected SocketConnector newConnector(InetAddress address, int port, int initialDelay, int retryDelay) { - return new DefaultSocketConnector(address, port, initialDelay, retryDelay); - } - - protected SocketFactory getSocketFactory() { - return SocketFactory.getDefault(); - } - - public void setRemoteHost(String remoteHost) { - this.remoteHost = remoteHost; - } - - public void setPort(int port) { - this.port = port; - } - - public void setReconnectionDelay(int reconnectionDelay) { - this.reconnectionDelay = reconnectionDelay; - } - - public void setAcceptConnectionTimeout(int acceptConnectionTimeout) { - this.acceptConnectionTimeout = acceptConnectionTimeout; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java index 13c7866912..4d1c10cdf6 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/SyslogAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -30,9 +30,10 @@ import ch.qos.logback.core.net.SyslogOutputStream; /** - * This appender can be used to send messages to a remote syslog daemon.

For - * more information about this appender, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#SyslogAppender + * This appender can be used to send messages to a remote syslog daemon. + *

+ * For more information about this appender, please refer to the online manual + * at http://logback.qos.ch/manual/appenders.html#SyslogAppender * * @author Ceki Gülcü */ @@ -62,7 +63,7 @@ public SyslogOutputStream createOutputStream() throws SocketException, UnknownHo /** * Convert a level to equivalent syslog severity. Only levels for printing - * methods i.e DEBUG, WARN, INFO and ERROR are converted. + * methods i.e. DEBUG, WARN, INFO and ERROR are converted. * * @see ch.qos.logback.core.net.SyslogAppenderBase#getSeverityForEvent(java.lang.Object) */ @@ -104,7 +105,8 @@ protected void postProcess(Object eventObject, OutputStream sw) { } // LOGBACK-411 and LOGBACK-750 - private void handleThrowableFirstLine(OutputStream sw, IThrowableProxy tp, String stackTracePrefix, boolean isRootException) throws IOException { + private void handleThrowableFirstLine(OutputStream sw, IThrowableProxy tp, String stackTracePrefix, + boolean isRootException) throws IOException { StringBuilder sb = new StringBuilder().append(stackTracePrefix); if (!isRootException) { @@ -122,7 +124,7 @@ boolean stackTraceHeaderLine(StringBuilder sb, boolean topException) { public Layout buildLayout() { PatternLayout layout = new PatternLayout(); - layout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName()); + layout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter::new); if (suffixPattern == null) { suffixPattern = DEFAULT_SUFFIX_PATTERN; } @@ -133,7 +135,7 @@ public Layout buildLayout() { } private void setupStackTraceLayout() { - stackTraceLayout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter.class.getName()); + stackTraceLayout.getInstanceConverterMap().put("syslogStart", SyslogStartConverter::new); stackTraceLayout.setPattern(getPrefixPattern() + stackTracePattern); stackTraceLayout.setContext(getContext()); @@ -145,8 +147,9 @@ public boolean isThrowableExcluded() { } /** - * Setting throwableExcluded to true causes no Throwable's stack trace data to be sent to - * the syslog daemon. By default, stack trace data is sent to syslog daemon. + * Setting throwableExcluded to true causes no Throwable's stack trace data to + * be sent to the syslog daemon. By default, stack trace data is sent to syslog + * daemon. * * @param throwableExcluded * @since 1.0.4 @@ -156,7 +159,7 @@ public void setThrowableExcluded(boolean throwableExcluded) { } /** - * See {@link #setStackTracePattern(String). + * See {@link #setStackTracePattern(String)}. * * @return the stackTraceSuffixPattern * @since 1.0.4 @@ -166,10 +169,11 @@ public String getStackTracePattern() { } /** - * Stack trace lines are sent to the syslog server separately from the main message - * For stack trace lines, the stackTracePattern is used instead of {@link #suffixPattern}. - * The stackTracePattern option allows specification of a separately format for the - * non-standardized part of stack trace lines. + * Stack trace lines are sent to the syslog server separately from the main + * message For stack trace lines, the stackTracePattern is used instead of + * {@link #suffixPattern}. The stackTracePattern option allows + * specification of a separate format for the non-standardized part of stack + * trace lines. * * @param stackTracePattern * @since 1.0.4 @@ -177,4 +181,4 @@ public String getStackTracePattern() { public void setStackTracePattern(String stackTracePattern) { this.stackTracePattern = stackTracePattern; } -} \ No newline at end of file +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/net/package.html index acf2027244..817ec94325 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/HardenedLoggingEventInputStream.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/HardenedLoggingEventInputStream.java index 522a30f495..9a0b587675 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/HardenedLoggingEventInputStream.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/HardenedLoggingEventInputStream.java @@ -1,3 +1,16 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.classic.net.server; import java.io.IOException; @@ -22,7 +35,7 @@ public class HardenedLoggingEventInputStream extends HardenedObjectInputStream { static final String ARRAY_PREFIX = "[L"; - + static public List getWhilelist() { List whitelist = new ArrayList(); whitelist.add(LoggingEventVO.class.getName()); @@ -44,12 +57,13 @@ static public List getWhilelist() { return whitelist; } - + public HardenedLoggingEventInputStream(InputStream is) throws IOException { super(is, getWhilelist()); } - - public HardenedLoggingEventInputStream(InputStream is, List additionalAuthorizedClasses) throws IOException { + + public HardenedLoggingEventInputStream(InputStream is, List additionalAuthorizedClasses) + throws IOException { this(is); super.addToWhitelist(additionalAuthorizedClasses); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderClient.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderClient.java deleted file mode 100644 index 12aa3a4713..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderClient.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.net.server.Client; -import ch.qos.logback.core.net.server.ServerRunner; - -/** - * A client of a {@link ServerRunner} that receives events from a remote - * appender. - * - * @author Carl Harris - */ -interface RemoteAppenderClient extends Client { - - /** - * Sets the client's logger context. - *

- * This provides the local logging context to the client's service thread, - * and is used as the destination for logging events received from the - * client. - *

- * This method must be invoked before the {@link #run()} method. - * @param lc the logger context to set - */ - void setLoggerContext(LoggerContext lc); - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerListener.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerListener.java deleted file mode 100644 index 3320ee8842..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerListener.java +++ /dev/null @@ -1,47 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; - -import ch.qos.logback.core.net.server.ServerListener; -import ch.qos.logback.core.net.server.ServerSocketListener; - -/** - * A {@link ServerListener} for remote appenders. - * - * @author Carl Harris - */ -class RemoteAppenderServerListener extends ServerSocketListener { - - /** - * Constructs a new listener. - * @param serverSocket the {@link ServerSocket} from which to accept - * new client connections - */ - public RemoteAppenderServerListener(ServerSocket serverSocket) { - super(serverSocket); - } - - /** - * {@inheritDoc} - */ - @Override - protected RemoteAppenderClient createClient(String id, Socket socket) throws IOException { - return new RemoteAppenderStreamClient(id, socket); - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerRunner.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerRunner.java deleted file mode 100644 index 2f78e3b3d9..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderServerRunner.java +++ /dev/null @@ -1,51 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import java.util.concurrent.Executor; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.net.server.ConcurrentServerRunner; -import ch.qos.logback.core.net.server.ServerListener; -import ch.qos.logback.core.net.server.ServerRunner; - -/** - * A {@link ServerRunner} that receives logging events from remote appender - * clients. - * - * @author Carl Harris - */ -class RemoteAppenderServerRunner extends ConcurrentServerRunner { - - /** - * Constructs a new server runner. - * @param listener the listener from which the server will accept new - * clients - * @param executor that will be used to execute asynchronous tasks - * on behalf of the runner. - */ - public RemoteAppenderServerRunner(ServerListener listener, Executor executor) { - super(listener, executor); - } - - /** - * {@inheritDoc} - */ - @Override - protected boolean configureClient(RemoteAppenderClient client) { - client.setLoggerContext((LoggerContext) getContext()); - return true; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java deleted file mode 100644 index 39506d09a7..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClient.java +++ /dev/null @@ -1,138 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import java.io.EOFException; -import java.io.IOException; -import java.io.InputStream; -import java.net.Socket; - -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.net.HardenedObjectInputStream; -import ch.qos.logback.core.util.CloseUtil; - -/** - * A {@link RemoteAppenderClient} that reads serialized {@link ILoggingEvent} - * objects from an {@link InputStream}. - * - * @author Carl Harris - */ -class RemoteAppenderStreamClient implements RemoteAppenderClient { - - private final String id; - private final Socket socket; - private final InputStream inputStream; - - private LoggerContext lc; - private Logger logger; - - /** - * Constructs a new client. - * @param id a display name for the client - * @param inputStream input stream from which events will be read - */ - public RemoteAppenderStreamClient(String id, Socket socket) { - this.id = id; - this.socket = socket; - this.inputStream = null; - } - - /** - * Constructs a new client. - *

- * This constructor is provided primarily to support unit tests for which - * it is inconvenient to create a socket. - * - * @param id a display name for the client - * @param inputStream input stream from which events will be read - */ - public RemoteAppenderStreamClient(String id, InputStream inputStream) { - this.id = id; - this.socket = null; - this.inputStream = inputStream; - } - - /** - * {@inheritDoc} - */ - public void setLoggerContext(LoggerContext lc) { - this.lc = lc; - this.logger = lc.getLogger(getClass().getPackage().getName()); - } - - /** - * {@inheritDoc} - */ - public void close() { - if (socket == null) - return; - CloseUtil.closeQuietly(socket); - } - - /** - * {@inheritDoc} - */ - public void run() { - logger.info(this + ": connected"); - HardenedObjectInputStream ois = null; - try { - ois = createObjectInputStream(); - while (true) { - // read an event from the wire - ILoggingEvent event = (ILoggingEvent) ois.readObject(); - // get a logger from the hierarchy. The name of the logger is taken to - // be the name contained in the event. - Logger remoteLogger = lc.getLogger(event.getLoggerName()); - // apply the logger-level filter - if (remoteLogger.isEnabledFor(event.getLevel())) { - // finally log the event as if was generated locally - remoteLogger.callAppenders(event); - } - } - } catch (EOFException ex) { - // this is normal and expected - assert true; - } catch (IOException ex) { - logger.info(this + ": " + ex); - } catch (ClassNotFoundException ex) { - logger.error(this + ": unknown event class"); - } catch (RuntimeException ex) { - logger.error(this + ": " + ex); - } finally { - if (ois != null) { - CloseUtil.closeQuietly(ois); - } - close(); - logger.info(this + ": connection closed"); - } - } - - private HardenedObjectInputStream createObjectInputStream() throws IOException { - if (inputStream != null) { - return new HardenedLoggingEventInputStream(inputStream); - } - return new HardenedLoggingEventInputStream(socket.getInputStream()); - } - - /** - * {@inheritDoc} - */ - @Override - public String toString() { - return "client " + id; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketAppender.java deleted file mode 100755 index 1baed299eb..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketAppender.java +++ /dev/null @@ -1,52 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.net.server.SSLServerSocketAppenderBase; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * A {@link ServerSocketAppender} that supports SSL. - * - * @author Carl Harris - */ -public class SSLServerSocketAppender extends SSLServerSocketAppenderBase { - - private static final PreSerializationTransformer pst = new LoggingEventPreSerializationTransformer(); - - private boolean includeCallerData; - - @Override - protected void postProcessEvent(ILoggingEvent event) { - if (isIncludeCallerData()) { - event.getCallerData(); - } - } - - @Override - protected PreSerializationTransformer getPST() { - return pst; - } - - public boolean isIncludeCallerData() { - return includeCallerData; - } - - public void setIncludeCallerData(boolean includeCallerData) { - this.includeCallerData = includeCallerData; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiver.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiver.java deleted file mode 100644 index 34fb28e40d..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiver.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import javax.net.ServerSocketFactory; -import javax.net.ssl.SSLContext; - -import ch.qos.logback.core.net.ssl.ConfigurableSSLServerSocketFactory; -import ch.qos.logback.core.net.ssl.SSLComponent; -import ch.qos.logback.core.net.ssl.SSLConfiguration; -import ch.qos.logback.core.net.ssl.SSLParametersConfiguration; - -/** - * A {@link ServerSocketReceiver} that supports SSL. - * - * @author Carl Harris - */ -public class SSLServerSocketReceiver extends ServerSocketReceiver implements SSLComponent { - - private SSLConfiguration ssl; - private ServerSocketFactory socketFactory; - - /** - * {@inheritDoc} - */ - @Override - protected ServerSocketFactory getServerSocketFactory() throws Exception { - if (socketFactory == null) { - SSLContext sslContext = getSsl().createContext(this); - SSLParametersConfiguration parameters = getSsl().getParameters(); - parameters.setContext(getContext()); - socketFactory = new ConfigurableSSLServerSocketFactory(parameters, sslContext.getServerSocketFactory()); - } - return socketFactory; - } - - /** - * Gets the server's SSL configuration. - * @return SSL configuration; if no SSL configuration was provided - * a default configuration is returned - */ - public SSLConfiguration getSsl() { - if (ssl == null) { - ssl = new SSLConfiguration(); - } - return ssl; - } - - /** - * Gets the server's SSL configuration. - * @param ssl the SSL configuration to set. - */ - public void setSsl(SSLConfiguration ssl) { - this.ssl = ssl; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketAppender.java deleted file mode 100644 index ca93d6c5ca..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketAppender.java +++ /dev/null @@ -1,54 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.net.server.AbstractServerSocketAppender; -import ch.qos.logback.core.spi.PreSerializationTransformer; - -/** - * An appender that listens on a TCP port for connections from remote - * loggers. Each event delivered to this appender is delivered to all - * connected remote loggers. - * - * @author Carl Harris - */ -public class ServerSocketAppender extends AbstractServerSocketAppender { - - private static final PreSerializationTransformer pst = new LoggingEventPreSerializationTransformer(); - - private boolean includeCallerData; - - @Override - protected void postProcessEvent(ILoggingEvent event) { - if (isIncludeCallerData()) { - event.getCallerData(); - } - } - - @Override - protected PreSerializationTransformer getPST() { - return pst; - } - - public boolean isIncludeCallerData() { - return includeCallerData; - } - - public void setIncludeCallerData(boolean includeCallerData) { - this.includeCallerData = includeCallerData; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketReceiver.java b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketReceiver.java deleted file mode 100644 index d59b46b79c..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/ServerSocketReceiver.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.UnknownHostException; -import java.util.concurrent.Executor; - -import javax.net.ServerSocketFactory; - -import ch.qos.logback.classic.net.ReceiverBase; -import ch.qos.logback.core.net.AbstractSocketAppender; -import ch.qos.logback.core.net.server.ServerListener; -import ch.qos.logback.core.net.server.ServerRunner; -import ch.qos.logback.core.util.CloseUtil; - -/** - * A logging socket server that is configurable using Joran. - * - * @author Carl Harris - */ -public class ServerSocketReceiver extends ReceiverBase { - - /** - * Default {@link ServerSocket} backlog - */ - public static final int DEFAULT_BACKLOG = 50; - - private int port = AbstractSocketAppender.DEFAULT_PORT; - private int backlog = DEFAULT_BACKLOG; - - private String address; - - private ServerSocket serverSocket; - @SuppressWarnings("rawtypes") - private ServerRunner runner; - - /** - * Starts the server. - */ - protected boolean shouldStart() { - try { - ServerSocket serverSocket = getServerSocketFactory().createServerSocket(getPort(), getBacklog(), getInetAddress()); - - ServerListener listener = createServerListener(serverSocket); - - runner = createServerRunner(listener, getContext().getScheduledExecutorService()); - runner.setContext(getContext()); - return true; - } catch (Exception ex) { - addError("server startup error: " + ex, ex); - CloseUtil.closeQuietly(serverSocket); - return false; - } - } - - protected ServerListener createServerListener(ServerSocket socket) { - return new RemoteAppenderServerListener(socket); - } - - @SuppressWarnings("rawtypes") - protected ServerRunner createServerRunner(ServerListener listener, Executor executor) { - return new RemoteAppenderServerRunner(listener, executor); - } - - @Override - protected Runnable getRunnableTask() { - return runner; - } - - /** - * {@inheritDoc} - */ - protected void onStop() { - try { - if (runner == null) - return; - runner.stop(); - } catch (IOException ex) { - addError("server shutdown error: " + ex, ex); - } - } - - /** - * Gets the server socket factory. - *

- * Subclasses may override to provide a custom factory. - * @return server socket factory - * @throws Exception - */ - protected ServerSocketFactory getServerSocketFactory() throws Exception { - return ServerSocketFactory.getDefault(); - } - - /** - * Gets the local address for the listener. - * @return an {@link InetAddress} representation of the local address. - * @throws UnknownHostException - */ - protected InetAddress getInetAddress() throws UnknownHostException { - if (getAddress() == null) - return null; - return InetAddress.getByName(getAddress()); - } - - /** - * Gets the local port for the listener. - * @return local port - */ - public int getPort() { - return port; - } - - /** - * Sets the local port for the listener. - * @param port the local port to set - */ - public void setPort(int port) { - this.port = port; - } - - /** - * Gets the listener queue depth. - *

- * This represents the number of connected clients whose connections - * have not yet been accepted. - * @return queue depth - * @see java.net.ServerSocket - */ - public int getBacklog() { - return backlog; - } - - /** - * Sets the listener queue depth. - *

- * This represents the number of connected clients whose connections - * have not yet been accepted. - * @param backlog the queue depth to set - * @see java.net.ServerSocket - */ - public void setBacklog(int backlog) { - this.backlog = backlog; - } - - /** - * Gets the local address for the listener. - * @return a string representation of the local address - */ - public String getAddress() { - return address; - } - - /** - * Sets the local address for the listener. - * @param address a host name or a string representation of an IP address - */ - public void setAddress(String address) { - this.address = address; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/package.html index f40f7ee074..114f0c8cf2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/net/server/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/net/server/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/package.html index 814f44c7ec..a933b2d1f8 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Abbreviator.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Abbreviator.java index 177cbea077..ccececde40 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Abbreviator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Abbreviator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/CallerDataConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/CallerDataConverter.java index a91cf38d61..9e4cf3a6dc 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/CallerDataConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/CallerDataConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,7 +27,8 @@ import ch.qos.logback.core.status.ErrorStatus; /** - * This converter outputs caller data depending on depth or depth range and marker data. + * This converter outputs caller data depending on depth or depth range and + * marker data. * * @author Ceki Gulcu */ @@ -76,7 +77,8 @@ public void start() { String evaluatorStr = optionList.get(i); Context context = getContext(); if (context != null) { - Map> evaluatorMap = (Map>) context.getObject(CoreConstants.EVALUATOR_MAP); + Map> evaluatorMap = (Map>) context + .getObject(CoreConstants.EVALUATOR_MAP); EventEvaluator ee = (EventEvaluator) evaluatorMap.get(evaluatorStr); if (ee != null) { addEvaluator(ee); @@ -96,7 +98,8 @@ private String[] splitRange(String depthStr) { private void checkRange() { if (depthStart < 0 || depthEnd < 0) { - addError("Invalid depthStart/depthEnd range [" + depthStart + ", " + depthEnd + "] (negative values are not allowed)"); + addError("Invalid depthStart/depthEnd range [" + depthStart + ", " + depthEnd + + "] (negative values are not allowed)"); } else if (depthStart >= depthEnd) { addError("Invalid depthEnd range [" + depthStart + ", " + depthEnd + "] (start greater or equal to end)"); } @@ -126,9 +129,10 @@ public String convert(ILoggingEvent le) { if (errorCount < MAX_ERROR_COUNT) { addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex); } else if (errorCount == MAX_ERROR_COUNT) { - ErrorStatus errorStatus = new ErrorStatus("Exception thrown for evaluator named [" + ee.getName() + "].", this, eex); + ErrorStatus errorStatus = new ErrorStatus( + "Exception thrown for evaluator named [" + ee.getName() + "].", this, eex); errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors." - + "We don't want the StatusManager to get flooded.", this)); + + "We don't want the StatusManager to get flooded.", this)); addStatus(errorStatus); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java index 040e2aa8a9..1c0545e16d 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassNameOnlyAbbreviator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassOfCallerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassOfCallerConverter.java index 6d49147512..c253294635 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassOfCallerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassOfCallerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.classic.spi.CallerData; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; public class ClassOfCallerConverter extends NamedConverter { @@ -24,7 +25,7 @@ protected String getFullyQualifiedName(ILoggingEvent event) { if (cda != null && cda.length > 0) { return cda[0].getClassName(); } else { - return CallerData.NA; + return CoreConstants.NA; } } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassicConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassicConverter.java index b0103cba9e..4b1ad823a8 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassicConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ClassicConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ import ch.qos.logback.core.pattern.DynamicConverter; /** - * This class serves the super-class of all converters in logback. It extends + * This class serves the super-class of almost all converters in logback-classic. It extends * {@link DynamicConverter}. * * @author Ceki Gulcu diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ContextNameConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ContextNameConverter.java index 5a5b69230c..5cc4f37457 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ContextNameConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ContextNameConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/DateConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/DateConverter.java index 1b6b340e42..db4dc7f2bb 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/DateConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/DateConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,9 @@ */ package ch.qos.logback.classic.pattern; +import java.time.ZoneId; import java.util.List; -import java.util.TimeZone; +import java.util.Locale; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.CoreConstants; @@ -22,38 +23,46 @@ public class DateConverter extends ClassicConverter { - long lastTimestamp = -1; - String timestampStrCache = null; CachingDateFormatter cachingDateFormatter = null; public void start() { String datePattern = getFirstOption(); + if (datePattern == null) { datePattern = CoreConstants.ISO8601_PATTERN; + } else if (datePattern.equals(CoreConstants.ISO8601_STR)) { + datePattern = CoreConstants.ISO8601_PATTERN; + } else if (datePattern.equals(CoreConstants.STRICT_STR)) { + datePattern = CoreConstants.STRICT_ISO8601_PATTERN; } - if (datePattern.equals(CoreConstants.ISO8601_STR)) { - datePattern = CoreConstants.ISO8601_PATTERN; + List optionList = getOptionList(); + ZoneId zoneId = null; + // if the option list contains a TZ option, then set it. + if (optionList != null && optionList.size() > 1) { + String zoneIdString = (String) optionList.get(1); + zoneId = ZoneId.of(zoneIdString); + addInfo("Setting zoneId to \""+zoneId+"\""); } + Locale locale = null; + if (optionList != null && optionList.size() > 2) { + String localeIdStr = (String) optionList.get(2); + locale = Locale.forLanguageTag(localeIdStr); + addInfo("Setting locale to \""+locale+"\""); + } try { - cachingDateFormatter = new CachingDateFormatter(datePattern); - // maximumCacheValidity = - // CachedDateFormat.getMaximumCacheValidity(pattern); + // if zoneId is null, the CachingDateFormatter will use the ZoneId.systemDefault() + // if locale is null, the CachingDateFormatter will use the Locale.getDefault() + cachingDateFormatter = new CachingDateFormatter(datePattern, zoneId, locale); } catch (IllegalArgumentException e) { addWarn("Could not instantiate SimpleDateFormat with pattern " + datePattern, e); // default to the ISO8601 format - cachingDateFormatter = new CachingDateFormatter(CoreConstants.ISO8601_PATTERN); + cachingDateFormatter = new CachingDateFormatter(CoreConstants.ISO8601_PATTERN, zoneId); } - List optionList = getOptionList(); - - // if the option list contains a TZ option, then set it. - if (optionList != null && optionList.size() > 1) { - TimeZone tz = TimeZone.getTimeZone((String) optionList.get(1)); - cachingDateFormatter.setTimeZone(tz); - } + super.start(); } public String convert(ILoggingEvent le) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java index e3f23022a4..b8574d1ef2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EnsureExceptionHandling.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,6 +16,7 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.Context; +import ch.qos.logback.core.pattern.CompositeConverter; import ch.qos.logback.core.pattern.Converter; import ch.qos.logback.core.pattern.ConverterUtil; import ch.qos.logback.core.pattern.PostCompileProcessor; @@ -27,13 +28,12 @@ public class EnsureExceptionHandling implements PostCompileProcessor - * This allows appenders using this layout to output exception information - * event if the user forgets to add %ex to the pattern. Note that the - * appenders defined in the Core package are not aware of exceptions nor - * LoggingEvents. + * This allows appenders using this layout to output exception information event + * if the user forgets to add %ex to the pattern. Note that the appenders + * defined in the Core package are not aware of exceptions nor LoggingEvents. *

- * If for some reason the user wishes to NOT print exceptions, then she can - * add %nopex to the pattern. + * If for some reason the user wishes to NOT print exceptions, then she can add + * %nopex to the pattern. * * */ @@ -56,21 +56,45 @@ public void process(Context context, Converter head) { } /** - * This method computes whether a chain of converters handles exceptions or - * not. + * This method computes whether a chain of converters handles exceptions or not. * - * @param head - * The first element of the chain - * @return true if can handle throwables contained in logging events + * @param head The first element of the chain + * @return true if it can handle throwables contained in logging events */ public boolean chainHandlesThrowable(Converter head) { Converter c = head; while (c != null) { if (c instanceof ThrowableHandlingConverter) { return true; + } else if (c instanceof CompositeConverter) { + if (compositeHandlesThrowable((CompositeConverter) c)) { + return true; + } } c = c.getNext(); } return false; } + + /** + * This method computes whether a composite converter handles exceptions or not. + * + * @param compositeConverter The composite converter + * @return true if it can handle throwables contained in logging events + */ + public boolean compositeHandlesThrowable(CompositeConverter compositeConverter) { + Converter childConverter = compositeConverter.getChildConverter(); + + for (Converter c = childConverter; c != null; c = c.getNext()) { + if (c instanceof ThrowableHandlingConverter) { + return true; + } else if (c instanceof CompositeConverter) { + boolean r = compositeHandlesThrowable((CompositeConverter) c); + if (r) + return true; + } + + } + return false; + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EpochConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EpochConverter.java new file mode 100644 index 0000000000..615155a5b5 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/EpochConverter.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +/** + * The EpochConverter class extends the ClassicConverter to handle the conversion of logging event + * timestamps into epoch time. This class allows control over whether the output epoch time is + * represented in milliseconds (default) or seconds. + * + * @since 1.5.25 + */ +public class EpochConverter extends ClassicConverter { + + // assume output in milliseconds by default + boolean inUnitsOfSeconds = false; + + @Override + public void start() { + String millisOrSecondsStr = getFirstOption(); + if ("seconds".equalsIgnoreCase(millisOrSecondsStr)) { + inUnitsOfSeconds = true; + } + + super.start(); + + } + + @Override + public String convert(ILoggingEvent event) { + + if(inUnitsOfSeconds) { + return Long.toString(event.getTimeStamp() / 1000); + } else { + return Long.toString(event.getTimeStamp()); + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java index ea7387d7e4..2cb74a8d42 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/FileOfCallerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/FileOfCallerConverter.java index 8783f746be..833c5bd8ef 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/FileOfCallerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/FileOfCallerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.classic.spi.CallerData; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; public class FileOfCallerConverter extends ClassicConverter { @@ -23,7 +24,7 @@ public String convert(ILoggingEvent le) { if (cda != null && cda.length > 0) { return cda[0].getFileName(); } else { - return CallerData.NA; + return CoreConstants.NA; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/KeyValuePairConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/KeyValuePairConverter.java new file mode 100644 index 0000000000..43573f99d4 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/KeyValuePairConverter.java @@ -0,0 +1,104 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import java.util.List; + +import org.slf4j.event.KeyValuePair; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; + +/** + * Convert the contents of {@link KeyValuePair} list to a String. + * + * Assuming the list contains the list {k1, v1}, {k2, v2}, the String output + * will be "k1=v1 k2=v2", without the quotes. + * + * + * @since 1.3.0 + * @author Ceki Gülcü + * + */ +public class KeyValuePairConverter extends ClassicConverter { + + static final String DOUBLE_OPTION_STR = "DOUBLE"; + static final String SINGLE_OPTION_STR = "SINGLE"; + static final String NONE_OPTION_STR = "NONE"; + + enum ValueQuoteSpecification { + NONE, SINGLE, DOUBLE; + + Character asChar() { + switch (this) { + case NONE: + return null; + case DOUBLE: + return '"'; + case SINGLE: + return '\''; + default: + throw new IllegalStateException(); + } + } + } + + ValueQuoteSpecification valueQuoteSpec = ValueQuoteSpecification.DOUBLE; + + public void start() { + String optStr = getFirstOption(); + valueQuoteSpec = optionStrToSpec(optStr); + super.start(); + } + + private ValueQuoteSpecification optionStrToSpec(String optStr) { + if (optStr == null) + return ValueQuoteSpecification.DOUBLE; + if (DOUBLE_OPTION_STR.equalsIgnoreCase(optStr)) + return ValueQuoteSpecification.DOUBLE; + if (SINGLE_OPTION_STR.equalsIgnoreCase(optStr)) + return ValueQuoteSpecification.SINGLE; + if (NONE_OPTION_STR.equalsIgnoreCase(optStr)) + return ValueQuoteSpecification.NONE; + return ValueQuoteSpecification.DOUBLE; + } + + @Override + public String convert(ILoggingEvent event) { + + List kvpList = event.getKeyValuePairs(); + if (kvpList == null || kvpList.isEmpty()) { + return CoreConstants.EMPTY_STRING; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < kvpList.size(); i++) { + KeyValuePair kvp = kvpList.get(i); + if (i != 0) + sb.append(' '); + sb.append(String.valueOf(kvp.key)); + sb.append('='); + Character quoteChar = valueQuoteSpec.asChar(); + if (quoteChar != null) + sb.append(quoteChar); + sb.append(String.valueOf(kvp.value)); + if (quoteChar != null) + sb.append(quoteChar); + } + + return sb.toString(); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LevelConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LevelConverter.java index f772254105..9a43cbe339 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LevelConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LevelConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineOfCallerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineOfCallerConverter.java index 385a784feb..bed8428079 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineOfCallerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineOfCallerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.classic.spi.CallerData; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; public class LineOfCallerConverter extends ClassicConverter { @@ -23,7 +24,7 @@ public String convert(ILoggingEvent le) { if (cda != null && cda.length > 0) { return Integer.toString(cda[0].getLineNumber()); } else { - return CallerData.NA; + return CoreConstants.NA; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineSeparatorConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineSeparatorConverter.java index e0bb1dcbfd..9ac618cc2d 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineSeparatorConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LineSeparatorConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LocalSequenceNumberConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LocalSequenceNumberConverter.java index db72b8261a..1b29eddf34 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LocalSequenceNumberConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LocalSequenceNumberConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,12 +17,13 @@ import java.util.concurrent.atomic.AtomicLong; /** - * A converters based on a a locally incremented sequence number. The sequence number is - * initialized to the number of milliseconds elapsed since 1970-01-01 until this instance - * is initialized. + * A converters based on a locally incremented sequence number. The sequence + * number is initialized to the number of milliseconds elapsed since 1970-01-01 + * until this instance is initialized. * *

- * EXPERIMENTAL This class is experimental and may be removed in the future. + * EXPERIMENTAL This class is experimental and may be removed in the + * future. * */ public class LocalSequenceNumberConverter extends ClassicConverter { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LoggerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LoggerConverter.java index 011fc8e259..b06efc6c06 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LoggerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/LoggerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,4 +20,5 @@ public class LoggerConverter extends NamedConverter { protected String getFullyQualifiedName(ILoggingEvent event) { return event.getLoggerName(); } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MDCConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MDCConverter.java index 4f6142497b..0a4c3ebf1a 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MDCConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MDCConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -62,7 +62,8 @@ public String convert(ILoggingEvent event) { } /** - * if no key is specified, return all the values present in the MDC, in the format "k1=v1, k2=v2, ..." + * if no key is specified, return all the values present in the MDC, in the + * format "k1=v1, k2=v2, ..." */ private String outputMDCForAllKeys(Map mdcPropertyMap) { StringBuilder buf = new StringBuilder(); @@ -78,4 +79,14 @@ private String outputMDCForAllKeys(Map mdcPropertyMap) { } return buf.toString(); } + + /** + * PrefixCompositeConverter needs the key + * + * @return + */ + public String getKey() { + return key; + } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MarkerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MarkerConverter.java index 61cc97a4ea..159a5f7401 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MarkerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MarkerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,8 @@ */ package ch.qos.logback.classic.pattern; +import java.util.List; + import org.slf4j.Marker; import ch.qos.logback.classic.spi.ILoggingEvent; @@ -27,12 +29,24 @@ public class MarkerConverter extends ClassicConverter { private static String EMPTY = ""; public String convert(ILoggingEvent le) { - Marker marker = le.getMarker(); - if (marker == null) { + List markers = le.getMarkerList(); + if (markers == null || markers.isEmpty()) { return EMPTY; - } else { - return marker.toString(); } + int size = markers.size(); + + if (size == 1) + return markers.get(0).toString(); + + StringBuffer buf = new StringBuffer(32); + for (int i = 0; i < size; i++) { + if (i != 0) + buf.append(' '); + Marker m = markers.get(i); + buf.append(m.toString()); + } + return buf.toString(); + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverter.java new file mode 100644 index 0000000000..399b611c6c --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverter.java @@ -0,0 +1,123 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; +import org.slf4j.event.KeyValuePair; + +import java.util.ArrayList; +import java.util.List; + +import static ch.qos.logback.classic.pattern.KeyValuePairConverter.*; + +/** + * Similar to {@link KeyValuePairConverter} with the added ability to mask the values of specified keys. + *

+ * Assuming the specified key is k2, and the kvp list of an event contains {k1, v1}, {k2, v2}, the String output + * will be "k1=v1 k2=XXX", without the quotes. + * + * Value quotes can be specified as the first option, e.g %maskedKvp{SINGLE, k1} + * + * @author Ceki Gülcü + * @since 1.5.7 + */ + + +public class MaskedKeyValuePairConverter extends ClassicConverter { + public static final String MASK = "XXX"; + List optionList; + List maskList = new ArrayList<>(); + KeyValuePairConverter.ValueQuoteSpecification valueQuoteSpec = KeyValuePairConverter.ValueQuoteSpecification.DOUBLE; + + public void start() { + this.optionList = getOptionList(); + KeyValuePairConverter.ValueQuoteSpecification extractedSpec = extractSpec(this.optionList); + if (extractedSpec == null) { + maskList = optionList; + } else { + valueQuoteSpec = extractedSpec; + maskList = optionList.subList(1, optionList.size()); + } + + checkMaskListForExtraQuoteSpecs(maskList); + + super.start(); + } + + private void checkMaskListForExtraQuoteSpecs(List maskList) { + if(maskList == null || maskList.isEmpty()) + return; + if(maskList.contains(DOUBLE_OPTION_STR)) { + addWarn("quote spec "+DOUBLE_OPTION_STR+ " found in the wrong order"); + } + if(maskList.contains(SINGLE_OPTION_STR)) { + addWarn("extra quote spec "+SINGLE_OPTION_STR+ " found in the wrong order"); + } + if(maskList.contains(NONE_OPTION_STR)) { + addWarn("extra quote spec "+NONE_OPTION_STR+ " found in the wrong order"); + } + } + + + KeyValuePairConverter.ValueQuoteSpecification extractSpec(List optionList) { + + if (optionList == null || optionList.isEmpty()) { + return null; + } + + String firstOption = optionList.get(0); + + if (DOUBLE_OPTION_STR.equalsIgnoreCase(firstOption)) { + return KeyValuePairConverter.ValueQuoteSpecification.DOUBLE; + } else if (SINGLE_OPTION_STR.equalsIgnoreCase(firstOption)) { + return KeyValuePairConverter.ValueQuoteSpecification.SINGLE; + } else if (NONE_OPTION_STR.equalsIgnoreCase(firstOption)) { + return KeyValuePairConverter.ValueQuoteSpecification.NONE; + } else { + return null; + } + } + + @Override + public String convert(ILoggingEvent event) { + + List kvpList = event.getKeyValuePairs(); + if (kvpList == null || kvpList.isEmpty()) { + return CoreConstants.EMPTY_STRING; + } + + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < kvpList.size(); i++) { + KeyValuePair kvp = kvpList.get(i); + if (i != 0) + sb.append(' '); + sb.append(String.valueOf(kvp.key)); + sb.append('='); + Character quoteChar = valueQuoteSpec.asChar(); + if (quoteChar != null) + sb.append(quoteChar); + if (maskList.contains(kvp.key)) + sb.append(MASK); + else + sb.append(String.valueOf(kvp.value)); + if (quoteChar != null) + sb.append(quoteChar); + } + + return sb.toString(); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MessageConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MessageConverter.java index 77c08a0a15..8f26a45cb2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MessageConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MessageConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MethodOfCallerConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MethodOfCallerConverter.java index 5327e1ef69..0a5e351aa1 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MethodOfCallerConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MethodOfCallerConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.classic.spi.CallerData; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; public class MethodOfCallerConverter extends ClassicConverter { @@ -23,7 +24,7 @@ public String convert(ILoggingEvent le) { if (cda != null && cda.length > 0) { return cda[0].getMethodName(); } else { - return CallerData.NA; + return CoreConstants.NA; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MicrosecondConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MicrosecondConverter.java new file mode 100644 index 0000000000..d5cb0544d8 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/MicrosecondConverter.java @@ -0,0 +1,41 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +/** + * Outputs the number of microseconds of the timestamp. + * + * + * @author ceki + * @since 1.3.0 + */ +public class MicrosecondConverter extends ClassicConverter { + + @Override + public String convert(ILoggingEvent event) { + int nanos = event.getNanoseconds(); + int millis_and_micros = nanos / 1000; + int micros = millis_and_micros % 1000; + + if (micros >= 100) + return Integer.toString(micros); + else if (micros >= 10) + return "0" + Integer.toString(micros); + else + return "00" + Integer.toString(micros); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java index 2148b18b74..cf123ebde3 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NamedConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,22 +13,79 @@ */ package ch.qos.logback.classic.pattern; +import java.util.LinkedHashMap; +import java.util.Map; + import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.util.OptionHelper; +/** + * In case abbreviation service is requested, NamedConverter will convert fully + * qualified class names to their abbreviated from. NamedConverter instances + * will store abbreviated names in an internal LRU cache. + * + * The cache will double in size if he cache miss rate is consistently above + * 30%. Assuming a high miss rate, the doubling until a maximum size of 2048 is + * attained. If at this point the cache miss rate is still too high, + * NamedConverter will revert to non cached behavior. + * + * The general assumption here is that a large majority of logger names are + * concentrated within a group of approximately 1000 logger names. + * + * @author Ceki Gulcu + * + */ public abstract class NamedConverter extends ClassicConverter { - Abbreviator abbreviator = null; + private static final String DISABLE_CACHE_SYSTEM_PROPERTY = "logback.namedConverter.disableCache"; + + private static final int INITIAL_CACHE_SIZE = 512; + private static final double LOAD_FACTOR = 0.75; // this is the JDK implementation default + + /** + * We don't let the cache map size to go over MAX_ALLOWED_REMOVAL_THRESHOLD + * elements + */ + private static final int MAX_ALLOWED_REMOVAL_THRESHOLD = (int) (2048 * LOAD_FACTOR); + + /** + * When the cache miss rate is above 30%, the cache is deemed inefficient. + */ + private static final double CACHE_MISSRATE_TRIGGER = 0.3d; + + /** + * We should have a sample size of minimal length before computing the cache + * miss rate. + */ + private static final int MIN_SAMPLE_SIZE = 1024; + + private static final double NEGATIVE = -1; + private volatile boolean cacheEnabled = true; + + private final NameCache cache = new NameCache(INITIAL_CACHE_SIZE); + + private Abbreviator abbreviator = null; + + private volatile int cacheMisses = 0; + private volatile int totalCalls = 0; /** * Gets fully qualified name from event. * - * @param event - * The LoggingEvent to process, cannot not be null. + * @param event The LoggingEvent to process, cannot not be null. * @return name, must not be null. */ protected abstract String getFullyQualifiedName(final ILoggingEvent event); public void start() { + + String disableCacheProp = OptionHelper.getSystemProperty(DISABLE_CACHE_SYSTEM_PROPERTY); + boolean disableCache = OptionHelper.toBoolean(disableCacheProp, false); + if (disableCache) { + addInfo("Disabling name cache via System.properties"); + this.cacheEnabled = false; + } + String optStr = getFirstOption(); if (optStr != null) { try { @@ -39,9 +96,10 @@ public void start() { abbreviator = new TargetLengthBasedClassNameAbbreviator(targetLen); } } catch (NumberFormatException nfe) { - // FIXME: better error reporting + addError("failed to parse integer string [" + optStr + "]", nfe); } } + super.start(); } public String convert(ILoggingEvent event) { @@ -50,7 +108,129 @@ public String convert(ILoggingEvent event) { if (abbreviator == null) { return fqn; } else { - return abbreviator.abbreviate(fqn); + if (cacheEnabled) { + return viaCache(fqn); + } else { + return abbreviator.abbreviate(fqn); + } + } + } + + /** + * This method is synchronized. It is the only place where the cache, a subclass + * of LinkedHashMap, is modified. + * + * The cache can be cleared via a call to disableCache(). However, the call to + * disableCache() is made indirectly from within viaCache(String). + * + * @param fqn + * @return + */ + private synchronized String viaCache(String fqn) { + totalCalls++; + String abbreviated = cache.get(fqn); + if (abbreviated == null) { + cacheMisses++; + abbreviated = abbreviator.abbreviate(fqn); + cache.put(fqn, abbreviated); + } + return abbreviated; + } + + private void disableCache() { + if (!cacheEnabled) + return; + this.cacheEnabled = false; + cache.clear(); + addInfo("Disabling cache at totalCalls=" + totalCalls); + } + + public double getCacheMissRate() { + return cache.cacheMissCalculator.getCacheMissRate(); + } + + public int getCacheMisses() { + return cacheMisses; + } + + private class NameCache extends LinkedHashMap { + + private static final long serialVersionUID = 1050866539278406045L; + + int removalThreshold; + CacheMissCalculator cacheMissCalculator = new CacheMissCalculator(); + + NameCache(int initialCapacity) { + super(initialCapacity); + this.removalThreshold = (int) (initialCapacity * LOAD_FACTOR); + } + + /** + * In the JDK tested, this method is called for every map insertion. + * + */ + @Override + protected boolean removeEldestEntry(Map.Entry entry) { + if (shouldDoubleRemovalThreshold()) { + removalThreshold *= 2; + + int missRate = (int) (cacheMissCalculator.getCacheMissRate() * 100); + + NamedConverter.this.addInfo("Doubling nameCache removalThreshold to " + removalThreshold + + " previous cacheMissRate=" + missRate + "%"); + cacheMissCalculator.updateMilestones(); + } + + if (size() >= removalThreshold) { + return true; + } else + return false; + } + + private boolean shouldDoubleRemovalThreshold() { + + double rate = cacheMissCalculator.getCacheMissRate(); + + // negative rate indicates insufficient sample size + if (rate < 0) + return false; + + if (rate < CACHE_MISSRATE_TRIGGER) + return false; + + // cannot double removalThreshold is already at max allowed size + if (this.removalThreshold >= MAX_ALLOWED_REMOVAL_THRESHOLD) { + NamedConverter.this.disableCache(); + return false; + } + + return true; } } + + class CacheMissCalculator { + + int totalsMilestone = 0; + int cacheMissesMilestone = 0; + + void updateMilestones() { + this.totalsMilestone = NamedConverter.this.totalCalls; + this.cacheMissesMilestone = NamedConverter.this.cacheMisses; + } + + double getCacheMissRate() { + + int effectiveTotal = NamedConverter.this.totalCalls - totalsMilestone; + + if (effectiveTotal < MIN_SAMPLE_SIZE) { + // cache miss rate cannot be negative. With a negative value, we signal the + // caller of insufficient sample size. + return NEGATIVE; + } + + int effectiveCacheMisses = NamedConverter.this.cacheMisses - cacheMissesMilestone; + return (1.0d * effectiveCacheMisses / effectiveTotal); + } + } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NopThrowableInformationConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NopThrowableInformationConverter.java index b359bdd11c..626817521d 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NopThrowableInformationConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/NopThrowableInformationConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,14 +21,14 @@ * Always returns an empty string. *

* This converter is useful to pretend that the converter chain for - * PatternLayout actually handles exceptions, when in fact it does not. - * By adding %nopex to the conversion pattern, the user can bypass - * the automatic addition of %ex conversion pattern for patterns - * which do not contain a converter handling exceptions. + * PatternLayout actually handles exceptions, when in fact it does not. By + * adding %nopex to the conversion pattern, the user can bypass the automatic + * addition of %ex conversion pattern for patterns which do not contain a + * converter handling exceptions. * - *

Users can ignore the existence of this converter, unless they - * want to suppress the automatic printing of exceptions by - * {@link PatternLayout}. + *

+ * Users can ignore the existence of this converter, unless they want to + * suppress the automatic printing of exceptions by {@link PatternLayout}. * * @author Ceki Gülcü */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PrefixCompositeConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PrefixCompositeConverter.java new file mode 100755 index 0000000000..dced0522c2 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PrefixCompositeConverter.java @@ -0,0 +1,56 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.CompositeConverter; +import ch.qos.logback.core.pattern.Converter; + +public class PrefixCompositeConverter extends CompositeConverter { + + public String convert(ILoggingEvent event) { + StringBuilder buf = new StringBuilder(); + Converter childConverter = this.getChildConverter(); + + for (Converter c = childConverter; c != null; c = c.getNext()) { + if (c instanceof MDCConverter) { + MDCConverter mdcConverter = (MDCConverter) c; + + String key = mdcConverter.getKey(); + if (key != null) { + buf.append(key).append("="); + } + } else if (c instanceof PropertyConverter) { + PropertyConverter pc = (PropertyConverter) c; + String key = pc.getKey(); + if (key != null) { + buf.append(key).append("="); + } + } else { + String classOfConverter = c.getClass().getName(); + + String key = PatternLayout.CONVERTER_CLASS_TO_KEY_MAP.get(classOfConverter); + if (key != null) + buf.append(key).append("="); + } + buf.append(c.convert(event)); + } + return buf.toString(); + } + + protected String transform(ILoggingEvent event, String in) { + throw new UnsupportedOperationException(); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PropertyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PropertyConverter.java index 98f70050ed..458e0f8c66 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PropertyConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/PropertyConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -30,6 +30,10 @@ public void start() { } } + public String getKey() { + return key; + } + public String convert(ILoggingEvent event) { if (key == null) { return "Property_HAS_NO_KEY"; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RelativeTimeConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RelativeTimeConverter.java index 2004e1927a..697f9853af 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RelativeTimeConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RelativeTimeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java index 1bc4c99bf7..01d2855d67 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -45,7 +45,8 @@ protected void recursiveAppendRootCauseFirst(StringBuilder sb, String prefix, in IThrowableProxy[] suppressed = tp.getSuppressed(); if (suppressed != null) { for (IThrowableProxy current : suppressed) { - recursiveAppendRootCauseFirst(sb, CoreConstants.SUPPRESSED, indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT, current); + recursiveAppendRootCauseFirst(sb, CoreConstants.SUPPRESSED, + indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT, current); } } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SequenceNumberConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SequenceNumberConverter.java new file mode 100644 index 0000000000..83ae60ff0d --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SequenceNumberConverter.java @@ -0,0 +1,46 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.CoreConstants; + +/** + * Return the event's sequence number. + * + * @author Bertrand Renuart + * @since 1.3.0 + */ +public class SequenceNumberConverter extends ClassicConverter { + + @Override + public void start() { + if(getContext() == null) { + // this should not happen + return; + } + + if (getContext().getSequenceNumberGenerator() == null) { + addWarn("It looks like no was defined in Logback configuration."); + } + super.start(); + } + + + @Override + public String convert(ILoggingEvent event) { + return Long.toString(event.getSequenceNumber()); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SyslogStartConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SyslogStartConverter.java index 6796f740fa..0fa18fe6c7 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SyslogStartConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/SyslogStartConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -78,9 +78,9 @@ public String convert(ILoggingEvent event) { } /** - * This method gets the network name of the machine we are running on. - * Returns "UNKNOWN_LOCALHOST" in the unlikely case where the host name - * cannot be found. + * This method gets the network name of the machine we are running on. Returns + * "UNKNOWN_LOCALHOST" in the unlikely case where the host name cannot be found. + * * @return String the name of the local host */ public String getLocalHostname() { @@ -95,15 +95,16 @@ public String getLocalHostname() { String computeTimeStampString(long now) { synchronized (this) { - // Since the formatted output is only precise to the second, we can use the same cached string if the + // Since the formatted output is only precise to the second, we can use the same + // cached string if the // current // second is the same (stripping off the milliseconds). if ((now / 1000) != lastTimestamp) { lastTimestamp = now / 1000; Date nowDate = new Date(now); calendar.setTime(nowDate); - timesmapStr = String.format("%s %2d %s", simpleMonthFormat.format(nowDate), calendar.get(Calendar.DAY_OF_MONTH), - simpleTimeFormat.format(nowDate)); + timesmapStr = String.format("%s %2d %s", simpleMonthFormat.format(nowDate), + calendar.get(Calendar.DAY_OF_MONTH), simpleTimeFormat.format(nowDate)); } return timesmapStr; } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java index 3a919b3635..b3eb7ac6c8 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,7 @@ */ package ch.qos.logback.classic.pattern; -import ch.qos.logback.classic.ClassicConstants; -import ch.qos.logback.core.CoreConstants; +import static ch.qos.logback.core.CoreConstants.DOT; public class TargetLengthBasedClassNameAbbreviator implements Abbreviator { @@ -25,7 +24,6 @@ public TargetLengthBasedClassNameAbbreviator(int targetLength) { } public String abbreviate(String fqClassName) { - StringBuilder buf = new StringBuilder(targetLength); if (fqClassName == null) { throw new IllegalArgumentException("Class name may not be null"); } @@ -35,92 +33,50 @@ public String abbreviate(String fqClassName) { return fqClassName; } - int[] dotIndexesArray = new int[ClassicConstants.MAX_DOTS]; - // a.b.c contains 2 dots but 2+1 parts. - // see also http://jira.qos.ch/browse/LBCLASSIC-110 - int[] lengthArray = new int[ClassicConstants.MAX_DOTS + 1]; + StringBuilder buf = new StringBuilder(inLen); - int dotCount = computeDotIndexes(fqClassName, dotIndexesArray); + int rightMostDotIndex = fqClassName.lastIndexOf(DOT); - // System.out.println(); - // System.out.println("Dot count for [" + className + "] is " + dotCount); - // if there are not dots than abbreviation is not possible - if (dotCount == 0) { + if (rightMostDotIndex == -1) return fqClassName; - } - // printArray("dotArray: ", dotArray); - computeLengthArray(fqClassName, dotIndexesArray, lengthArray, dotCount); - // printArray("lengthArray: ", lengthArray); - for (int i = 0; i <= dotCount; i++) { - if (i == 0) { - buf.append(fqClassName.substring(0, lengthArray[i] - 1)); - } else { - buf.append(fqClassName.substring(dotIndexesArray[i - 1], dotIndexesArray[i - 1] + lengthArray[i])); - } - // System.out.println("i=" + i + ", buf=" + buf); - } - return buf.toString(); - } + // length of last segment including the dot + int lastSegmentLength = inLen - rightMostDotIndex; - static int computeDotIndexes(final String className, int[] dotArray) { - int dotCount = 0; - int k = 0; - while (true) { - // ignore the $ separator in our computations. This is both convenient - // and sensible. - k = className.indexOf(CoreConstants.DOT, k); - if (k != -1 && dotCount < ClassicConstants.MAX_DOTS) { - dotArray[dotCount] = k; - dotCount++; - k++; - } else { - break; - } - } - return dotCount; - } + int leftSegments_TargetLen = targetLength - lastSegmentLength; + if (leftSegments_TargetLen < 0) + leftSegments_TargetLen = 0; - void computeLengthArray(final String className, int[] dotArray, int[] lengthArray, int dotCount) { - int toTrim = className.length() - targetLength; - // System.out.println("toTrim=" + toTrim); + int leftSegmentsLen = inLen - lastSegmentLength; - // int toTrimAvarage = 0; + // maxPossibleTrim denotes the maximum number of characters we aim to trim + // the actual number of character trimmed may be higher since segments, when + // reduced, are reduced to just one character + int maxPossibleTrim = leftSegmentsLen - leftSegments_TargetLen; - int len; - for (int i = 0; i < dotCount; i++) { - int previousDotPosition = -1; - if (i > 0) { - previousDotPosition = dotArray[i - 1]; - } - int available = dotArray[i] - previousDotPosition - 1; - // System.out.println("i=" + i + ", available = " + available); - - len = (available < 1) ? available : 1; - // System.out.println("i=" + i + ", toTrim = " + toTrim); + int trimmed = 0; + boolean inDotState = true; - if (toTrim > 0) { - len = (available < 1) ? available : 1; + int i = 0; + for (; i < rightMostDotIndex; i++) { + char c = fqClassName.charAt(i); + if (c == DOT) { + // if trimmed too many characters, let us stop + if (trimmed >= maxPossibleTrim) + break; + buf.append(c); + inDotState = true; } else { - len = available; + if (inDotState) { + buf.append(c); + inDotState = false; + } else { + trimmed++; + } } - toTrim -= (available - len); - lengthArray[i] = len + 1; } - - int lastDotIndex = dotCount - 1; - lengthArray[dotCount] = className.length() - dotArray[lastDotIndex]; - } - - static void printArray(String msg, int[] ia) { - System.out.print(msg); - for (int i = 0; i < ia.length; i++) { - if (i == 0) { - System.out.print(ia[i]); - } else { - System.out.print(", " + ia[i]); - } - } - System.out.println(); + // append from the position of i which may include the last seen DOT + buf.append(fqClassName.substring(i)); + return buf.toString(); } } \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator2.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator2.java new file mode 100644 index 0000000000..5ad6368d09 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviator2.java @@ -0,0 +1,136 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.core.CoreConstants; + +public class TargetLengthBasedClassNameAbbreviator2 implements Abbreviator { + + final int targetLength; + + public TargetLengthBasedClassNameAbbreviator2(int targetLength) { + this.targetLength = targetLength; + } + + public String abbreviate(String fqClassName) { + StringBuilder buf = new StringBuilder(targetLength); + if (fqClassName == null) { + throw new IllegalArgumentException("Class name may not be null"); + } + + int inLen = fqClassName.length(); + if (inLen < targetLength) { + return fqClassName; + } + + int[] dotIndexesArray = new int[ClassicConstants.MAX_DOTS]; + // a.b.c contains 2 dots but 2+1 parts. + // see also http://jira.qos.ch/browse/LOGBACK-437 + int[] lengthArray = new int[ClassicConstants.MAX_DOTS + 1]; + + int dotCount = computeDotIndexes(fqClassName, dotIndexesArray); + + // System.out.println(); + // System.out.println("Dot count for [" + className + "] is " + dotCount); + // if there are no dots than abbreviation is not possible + if (dotCount == 0) { + return fqClassName; + } + // printArray("dotArray: ", dotIndexesArray); + computeLengthArray(fqClassName, dotIndexesArray, lengthArray, dotCount); + // printArray("lengthArray: ", lengthArray); + for (int i = 0; i <= dotCount; i++) { + if (i == 0) { + buf.append(fqClassName.substring(0, lengthArray[i] - 1)); + } else { + buf.append(fqClassName.substring(dotIndexesArray[i - 1], dotIndexesArray[i - 1] + lengthArray[i])); + } + // System.out.println("i=" + i + ", buf=" + buf); + } + + return buf.toString(); + } + + /** + * Populate dotArray with the positions of the DOT character in className. + * Leftmost dot is placed at index 0 of dotArray. + * + * @param className + * @param dotArray + * @return the number of dots found + */ + static int computeDotIndexes(final String className, int[] dotArray) { + int dotCount = 0; + int k = 0; + while (true) { + // ignore the $ separator in our computations. This is both convenient + // and sensible. + k = className.indexOf(CoreConstants.DOT, k); + if (k != -1 && dotCount < ClassicConstants.MAX_DOTS) { + dotArray[dotCount] = k; + dotCount++; + k++; // move past the last found DOT + } else { + break; + } + } + return dotCount; + } + + void computeLengthArray(final String className, int[] dotArray, int[] lengthArray, int dotCount) { + int toTrim = className.length() - targetLength; + // System.out.println("dotCount=" + dotCount); + + int len; + for (int i = 0; i < dotCount; i++) { + // System.out.println("i=" + i + ", toTrim = " + toTrim); + + // if i==0, previousDotPosition = -1, otherwise dotArray[i - 1] + int previousDotPosition = (i == 0) ? -1 : dotArray[i - 1]; + // System.out.println("i="+i+ " previousDotPosition="+previousDotPosition); + + // number of characters within the segment, i.e/ within the previous dot + // position and the current dot position + int charactersInSegment = dotArray[i] - previousDotPosition - 1; + // System.out.println("i=" + i + ", charactersInSegment = " + + // charactersInSegment); + + if (toTrim > 0) { + len = (charactersInSegment < 1) ? charactersInSegment : 1; + } else { + len = charactersInSegment; + } + // System.out.println("i=" + i + ", len = " + len); + + toTrim -= (charactersInSegment - len); + lengthArray[i] = len + 1; + } + + int lastDotIndex = dotCount - 1; + lengthArray[dotCount] = className.length() - dotArray[lastDotIndex]; + } + + static void printArray(String msg, int[] ia) { + System.out.print(msg); + for (int i = 0; i < ia.length; i++) { + if (i == 0) { + System.out.print(ia[i]); + } else { + System.out.print(", " + ia[i]); + } + } + System.out.println(); + } +} \ No newline at end of file diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThreadConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThreadConverter.java index d442a39580..da2dca2a99 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThreadConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThreadConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java index 16c719f0d8..ba4c5e80e4 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableHandlingConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java index a09c11e5a1..d6055d9e4b 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/ThrowableProxyConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -72,8 +72,10 @@ public void start() { for (int i = 1; i < optionListSize; i++) { String evaluatorOrIgnoredStackTraceLine = (String) optionList.get(i); Context context = getContext(); - Map> evaluatorMap = (Map>) context.getObject(CoreConstants.EVALUATOR_MAP); - EventEvaluator ee = (EventEvaluator) evaluatorMap.get(evaluatorOrIgnoredStackTraceLine); + Map> evaluatorMap = (Map>) context + .getObject(CoreConstants.EVALUATOR_MAP); + EventEvaluator ee = (EventEvaluator) evaluatorMap + .get(evaluatorOrIgnoredStackTraceLine); if (ee != null) { addEvaluator(ee); } else { @@ -129,9 +131,10 @@ public String convert(ILoggingEvent event) { if (errorCount < CoreConstants.MAX_ERROR_COUNT) { addError("Exception thrown for evaluator named [" + ee.getName() + "]", eex); } else if (errorCount == CoreConstants.MAX_ERROR_COUNT) { - ErrorStatus errorStatus = new ErrorStatus("Exception thrown for evaluator named [" + ee.getName() + "].", this, eex); + ErrorStatus errorStatus = new ErrorStatus( + "Exception thrown for evaluator named [" + ee.getName() + "].", this, eex); errorStatus.add(new ErrorStatus("This was the last warning about this evaluator's errors." - + "We don't want the StatusManager to get flooded.", this)); + + "We don't want the StatusManager to get flooded.", this)); addStatus(errorStatus); } } @@ -162,7 +165,8 @@ private void recursiveAppend(StringBuilder sb, String prefix, int indent, IThrow IThrowableProxy[] suppressed = tp.getSuppressed(); if (suppressed != null) { for (IThrowableProxy current : suppressed) { - recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT, current); + recursiveAppend(sb, CoreConstants.SUPPRESSED, indent + ThrowableProxyUtil.SUPPRESSED_EXCEPTION_INDENT, + current); } } recursiveAppend(sb, CoreConstants.CAUSED_BY, indent, tp.getCause()); @@ -173,12 +177,9 @@ private void subjoinFirstLine(StringBuilder buf, String prefix, int indent, IThr if (prefix != null) { buf.append(prefix); } - subjoinExceptionMessage(buf, tp); + ThrowableProxyUtil.subjoinExceptionMessage(buf, tp); } - private void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) { - buf.append(tp.getClassName()).append(": ").append(tp.getMessage()); - } protected void subjoinSTEPArray(StringBuilder buf, int indent, IThrowableProxy tp) { StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); @@ -213,7 +214,8 @@ protected void subjoinSTEPArray(StringBuilder buf, int indent, IThrowableProxy t if (commonFrames > 0 && unrestrictedPrinting) { ThrowableProxyUtil.indent(buf, indent); - buf.append("... ").append(tp.getCommonFrames()).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR); + buf.append("... ").append(tp.getCommonFrames()).append(" common frames omitted") + .append(CoreConstants.LINE_SEPARATOR); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java index 319bd8b838..29fd1691af 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/Util.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.java b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.java index 144b18c4b2..0b970d4337 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,9 @@ import ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase; /** - * Highlights inner-text depending on the level, in bold red for events of level ERROR, in red for WARN, - * in BLUE for INFO, and in the default color for other levels. + * Highlights inner-text depending on the level, in bold red for events of level + * ERROR, in red for WARN, in BLUE for INFO, and in the default color for other + * levels. */ public class HighlightingCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/package.html index 5e4898f941..b81c99b9bc 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/pattern/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/pattern/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java b/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java index 5a4e0a777e..22e998428a 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -29,12 +29,12 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.util.ContextInitializer; -import ch.qos.logback.classic.util.JNDIUtil; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.status.InfoStatus; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.status.StatusUtil; import ch.qos.logback.core.status.WarnStatus; +import ch.qos.logback.core.util.JNDIUtil; import ch.qos.logback.core.util.Loader; import ch.qos.logback.core.util.StatusPrinter; @@ -83,7 +83,7 @@ public LoggerContext getLoggerContext() { // We first try to find the name of our // environment's LoggerContext ctx = JNDIUtil.getInitialContext(); - contextName = (String) JNDIUtil.lookup(ctx, JNDI_CONTEXT_NAME); + contextName = (String) JNDIUtil.lookupString(ctx, JNDI_CONTEXT_NAME); } catch (NamingException ne) { // We can't log here } @@ -124,14 +124,20 @@ private String conventionalConfigFileName(String contextName) { private URL findConfigFileURL(Context ctx, LoggerContext loggerContext) { StatusManager sm = loggerContext.getStatusManager(); - String jndiEntryForConfigResource = JNDIUtil.lookup(ctx, JNDI_CONFIGURATION_RESOURCE); + String jndiEntryForConfigResource = null; + + try { + jndiEntryForConfigResource = JNDIUtil.lookupString(ctx, JNDI_CONFIGURATION_RESOURCE); + } catch (NamingException e) { + sm.add(new WarnStatus("JNDI lookup failed", this, e)); + } // Do we have a dedicated configuration file? if (jndiEntryForConfigResource != null) { sm.add(new InfoStatus("Searching for [" + jndiEntryForConfigResource + "]", this)); URL url = urlByResourceName(sm, jndiEntryForConfigResource); if (url == null) { - String msg = "The jndi resource [" + jndiEntryForConfigResource + "] for context [" + loggerContext.getName() - + "] does not lead to a valid file"; + String msg = "The jndi resource [" + jndiEntryForConfigResource + "] for context [" + + loggerContext.getName() + "] does not lead to a valid file"; sm.add(new WarnStatus(msg, this)); } return url; @@ -183,8 +189,8 @@ public int getCount() { /** * These methods are used by the LoggerContextFilter. *

- * They provide a way to tell the selector which context to use, thus saving - * the cost of a JNDI call at each new request. + * They provide a way to tell the selector which context to use, thus saving the + * cost of a JNDI call at each new request. * * @param context */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java b/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java index dbc1ff518d..ec9e145316 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextSelector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ /** * An interface that provides access to different contexts. * - * It is used by the LoggerFactory to access the context - * it will use to retrieve loggers. + * It is used by the LoggerFactory to access the context it will use to retrieve + * loggers. * * @author Ceki Gülcü * @author Sébastien Pennec diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java b/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java index 0396000c64..fcad8bb924 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/selector/DefaultContextSelector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java b/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java index 63fb09623d..342f774e62 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,28 +17,28 @@ import javax.naming.Context; import javax.naming.NamingException; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; import org.slf4j.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.selector.ContextSelector; import ch.qos.logback.classic.util.ContextSelectorStaticBinder; -import ch.qos.logback.classic.util.JNDIUtil; +import ch.qos.logback.core.util.JNDIUtil; public class ContextDetachingSCL implements ServletContextListener { public void contextInitialized(ServletContextEvent arg0) { // do nothing } - + public void contextDestroyed(ServletContextEvent servletContextEvent) { String loggerContextName = null; try { Context ctx = JNDIUtil.getInitialContext(); - loggerContextName = (String) JNDIUtil.lookup(ctx, JNDI_CONTEXT_NAME); + loggerContextName = (String) JNDIUtil.lookupString(ctx, JNDI_CONTEXT_NAME); } catch (NamingException ne) { } @@ -63,6 +63,4 @@ public void contextDestroyed(ServletContextEvent servletContextEvent) { } } - - } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.java index 28f7d38b2d..7800c13faf 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,12 +15,12 @@ import java.io.IOException; -import javax.servlet.Filter; -import javax.servlet.FilterChain; -import javax.servlet.FilterConfig; -import javax.servlet.ServletException; -import javax.servlet.ServletRequest; -import javax.servlet.ServletResponse; +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.FilterConfig; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; import org.slf4j.LoggerFactory; @@ -30,22 +30,24 @@ import ch.qos.logback.classic.util.ContextSelectorStaticBinder; /** - * A servlet filter that puts the environment dependent LoggerContext in a + * A servlet filter that puts the environment-dependent LoggerContext in a * ThreadLocal variable, removing it after the request is processed. * - *

To use it, add the following lines to a web.xml file - * - * - * LoggerContextFilter - * - * ch.qos.logback.classic.selector.servlet.LoggerContextFilter - * - * - * - * LoggerContextFilter - * /* - * - * + *

+ * To use it, add the following lines to a web.xml file + *

+ *
+  <filter>
+    <filter-name>LoggerContextFilter</filter-name>
+    <filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter</filter-class>
+ </filter>
+
+ <filter-mapping>
+   <filter-name>LoggerContextFilter</filter-name>
+   <url-pattern>/*</url-pattern>
+ </filter-mapping>
+ 
+ * * @author Sébastien Pennec */ public class LoggerContextFilter implements Filter { @@ -54,7 +56,8 @@ public void destroy() { // do nothing } - public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); ContextSelector selector = ContextSelectorStaticBinder.getSingleton().getContextSelector(); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.java b/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.java index bb0fa07fa4..da2858f2a1 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.java @@ -1,18 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.servlet; import static ch.qos.logback.core.CoreConstants.DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY; import java.util.Set; -import javax.servlet.ServletContainerInitializer; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; +import jakarta.servlet.ServletContainerInitializer; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; import ch.qos.logback.classic.util.StatusViaSLF4JLoggerFactory; import ch.qos.logback.core.util.OptionHelper; /** - * Attaches a new instance of {@link LogbackServletContextListener} to the current web-applications {@link ServletContext}. + * Attaches a new instance of {@link LogbackServletContextListener} to the + * current web-applications {@link ServletContext}. * * @author Ceki Gulcu * @since 1.1.10 @@ -23,23 +38,25 @@ public class LogbackServletContainerInitializer implements ServletContainerIniti public void onStartup(Set> c, ServletContext ctx) throws ServletException { if (isDisabledByConfiguration(ctx)) { - StatusViaSLF4JLoggerFactory.addInfo("Due to deployment instructions will NOT register an instance of " + LogbackServletContextListener.class - + " to the current web-app", this); + StatusViaSLF4JLoggerFactory.addInfo("Due to deployment instructions will NOT register an instance of " + + LogbackServletContextListener.class + " to the current web-app", this); return; } - StatusViaSLF4JLoggerFactory.addInfo("Adding an instance of " + LogbackServletContextListener.class + " to the current web-app", this); + StatusViaSLF4JLoggerFactory.addInfo( + "Adding an instance of " + LogbackServletContextListener.class + " to the current web-app", this); LogbackServletContextListener lscl = new LogbackServletContextListener(); ctx.addListener(lscl); } /** - * Search for value of DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY in the web-app first, then as a system property and - * as an environment variable last. + * Search for value of DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY in the web-app + * first, then as a system property and as an environment variable last. * * @param ctx - * @return True if value of DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY is available and set to "true", false otherwise. + * @return True if value of DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY is + * available and set to "true", false otherwise. */ boolean isDisabledByConfiguration(ServletContext ctx) { String disableAttributeStr = null; @@ -48,19 +65,15 @@ boolean isDisabledByConfiguration(ServletContext ctx) { disableAttributeStr = (String) disableAttribute; } - if (OptionHelper.isEmpty(disableAttributeStr)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(disableAttributeStr)) { disableAttributeStr = OptionHelper.getSystemProperty(DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY); } - if (OptionHelper.isEmpty(disableAttributeStr)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(disableAttributeStr)) { disableAttributeStr = OptionHelper.getEnv(DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY); } - if (OptionHelper.isEmpty(disableAttributeStr)) - return false; - - return disableAttributeStr.equalsIgnoreCase("true"); - + return Boolean.parseBoolean(disableAttributeStr); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContextListener.java b/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContextListener.java index f4bc045a1d..f96593a13e 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContextListener.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/servlet/LogbackServletContextListener.java @@ -1,7 +1,21 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.servlet; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; +import jakarta.servlet.ServletContextEvent; +import jakarta.servlet.ServletContextListener; import org.slf4j.ILoggerFactory; import org.slf4j.LoggerFactory; @@ -11,7 +25,8 @@ import ch.qos.logback.core.spi.ContextAwareBase; /** - * Allows for graceful shutdown of the {@link LoggerContext} associated with this web-app. + * Allows for graceful shutdown of the {@link LoggerContext} associated with + * this web-app. * * @author Ceki Gulcu * @since 1.1.10 @@ -32,7 +47,8 @@ public void contextDestroyed(ServletContextEvent sce) { if (iLoggerFactory instanceof LoggerContext) { LoggerContext loggerContext = (LoggerContext) iLoggerFactory; contextAwareBase.setContext(loggerContext); - StatusViaSLF4JLoggerFactory.addInfo("About to stop " + loggerContext.getClass().getCanonicalName() + " [" + loggerContext.getName() + "]", this); + StatusViaSLF4JLoggerFactory.addInfo("About to stop " + loggerContext.getClass().getCanonicalName() + " [" + + loggerContext.getName() + "]", this); loggerContext.stop(); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/AppenderFactoryUsingJoran.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/AppenderFactoryUsingJoran.java deleted file mode 100644 index fb1ed77517..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/AppenderFactoryUsingJoran.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.sift; - -import java.util.List; -import java.util.Map; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.sift.AbstractAppenderFactoryUsingJoran; -import ch.qos.logback.core.sift.SiftingJoranConfiguratorBase; - -/** - * - */ -public class AppenderFactoryUsingJoran extends AbstractAppenderFactoryUsingJoran { - - AppenderFactoryUsingJoran(List eventList, String key, Map parentPropertyMap) { - super(eventList, key, parentPropertyMap); - } - - public SiftingJoranConfiguratorBase getSiftingJoranConfigurator(String discriminatingValue) { - return new SiftingJoranConfigurator(key, discriminatingValue, parentPropertyMap); - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/ContextBasedDiscriminator.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/ContextBasedDiscriminator.java index 13723658a9..8d633804c6 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/ContextBasedDiscriminator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/sift/ContextBasedDiscriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -61,8 +61,8 @@ public String getDefaultValue() { } /** - * The default context name in case the context name is not set for the - * current logging event. + * The default context name in case the context name is not set for the current + * logging event. * * @param defaultValue */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.java index 81d39ce517..7bcf863b0d 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -69,8 +69,8 @@ public String getDefaultValue() { } /** - * The default context name in case the context name is not set for the - * current logging event. + * The default context name in case the context name is not set for the current + * logging event. * * @param defaultValue */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java index d65f1b1070..e4e47df28e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/sift/MDCBasedDiscriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,7 +23,8 @@ * MDCBasedDiscriminator essentially returns the value mapped to an MDC key. If * the said value is null, then a default value is returned. *

- *

Both Key and the DefaultValue are user specified properties. + *

+ * Both Key and the DefaultValue are user specified properties. * * @author Ceki Gülcü */ @@ -33,9 +34,9 @@ public class MDCBasedDiscriminator extends AbstractDiscriminator private String defaultValue; /** - * Return the value associated with an MDC entry designated by the Key - * property. If that value is null, then return the value assigned to the - * DefaultValue property. + * Return the value associated with an MDC entry designated by the Key property. + * If that value is null, then return the value assigned to the DefaultValue + * property. */ public String getDiscriminatingValue(ILoggingEvent event) { // http://jira.qos.ch/browse/LBCLASSIC-213 @@ -54,11 +55,11 @@ public String getDiscriminatingValue(ILoggingEvent event) { @Override public void start() { int errors = 0; - if (OptionHelper.isEmpty(key)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(key)) { errors++; addError("The \"Key\" property must be set"); } - if (OptionHelper.isEmpty(defaultValue)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(defaultValue)) { errors++; addError("The \"DefaultValue\" property must be set"); } @@ -84,12 +85,13 @@ public String getDefaultValue() { } /** - * The default MDC value in case the MDC is not set for - * {@link #setKey(String) mdcKey}. + * The default MDC value in case the MDC is not set for {@link #setKey(String) + * mdcKey}. *

- *

For example, if {@link #setKey(String) Key} is set to the value - * "someKey", and the MDC is not set for "someKey", then this appender will - * use the default value, which you can set with the help of this method. + *

+ * For example, if {@link #setKey(String) Key} is set to the value "someKey", + * and the MDC is not set for "someKey", then this appender will use the default + * value, which you can set with the help of this method. * * @param defaultValue */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftAction.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftAction.java deleted file mode 100644 index 02bf4b4d07..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftAction.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.sift; - -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.InPlayListener; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -public class SiftAction extends Action implements InPlayListener { - List seList; - - @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - seList = new ArrayList(); - ic.addInPlayListener(this); - } - - @Override - public void end(InterpretationContext ic, String name) throws ActionException { - ic.removeInPlayListener(this); - Object o = ic.peekObject(); - if (o instanceof SiftingAppender) { - SiftingAppender sa = (SiftingAppender) o; - Map propertyMap = ic.getCopyOfPropertyMap(); - AppenderFactoryUsingJoran appenderFactory = new AppenderFactoryUsingJoran(seList, sa.getDiscriminatorKey(), propertyMap); - sa.setAppenderFactory(appenderFactory); - } - } - - public void inPlay(SaxEvent event) { - seList.add(event); - } - - public List getSeList() { - return seList; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingAppender.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingAppender.java index 5ef985efb2..6b7cf559a2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingAppender.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,10 +18,13 @@ import ch.qos.logback.core.joran.spi.DefaultClass; import ch.qos.logback.core.sift.Discriminator; import ch.qos.logback.core.sift.SiftingAppenderBase; + +import java.util.List; + import org.slf4j.Marker; /** - * This appender can contains other appenders which it can build dynamically + * This appender can contain other appenders which it can build dynamically * depending on MDC values. The built appender is specified as part of a * configuration file. * @@ -44,10 +47,14 @@ public void setDiscriminator(Discriminator discriminator) { } protected boolean eventMarksEndOfLife(ILoggingEvent event) { - Marker marker = event.getMarker(); - if (marker == null) + List markers = event.getMarkerList(); + if (markers == null) return false; - return marker.contains(ClassicConstants.FINALIZE_SESSION_MARKER); + for(Marker m: markers) { + if(m.contains(ClassicConstants.FINALIZE_SESSION_MARKER)) + return true; + } + return false; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingJoranConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingJoranConfigurator.java deleted file mode 100644 index cbaa50c26c..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/sift/SiftingJoranConfigurator.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.sift; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.util.DefaultNestedComponentRules; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.action.AppenderAction; -import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; -import ch.qos.logback.core.joran.spi.ElementPath; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.RuleStore; -import ch.qos.logback.core.sift.SiftingJoranConfiguratorBase; - -public class SiftingJoranConfigurator extends SiftingJoranConfiguratorBase { - - SiftingJoranConfigurator(String key, String value, Map parentPropertyMap) { - super(key, value, parentPropertyMap); - } - - @Override - protected ElementPath initialElementPath() { - return new ElementPath("configuration"); - } - - @Override - protected void addInstanceRules(RuleStore rs) { - super.addInstanceRules(rs); - rs.addRule(new ElementSelector("configuration/appender"), new AppenderAction()); - } - - @Override - protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { - DefaultNestedComponentRules.addDefaultNestedComponentRegistryRules(registry); - } - - @Override - protected void buildInterpreter() { - super.buildInterpreter(); - Map omap = interpreter.getInterpretationContext().getObjectMap(); - omap.put(ActionConst.APPENDER_BAG, new HashMap>()); - //omap.put(ActionConst.FILTER_CHAIN_BAG, new HashMap()); - Map propertiesMap = new HashMap(); - propertiesMap.putAll(parentPropertyMap); - propertiesMap.put(key, value); - interpreter.setInterpretationContextPropertiesMap(propertiesMap); - } - - @SuppressWarnings("unchecked") - public Appender getAppender() { - Map omap = interpreter.getInterpretationContext().getObjectMap(); - HashMap> appenderMap = (HashMap>) omap.get(ActionConst.APPENDER_BAG); - oneAndOnlyOneCheck(appenderMap); - Collection> values = appenderMap.values(); - if (values.size() == 0) { - return null; - } - return (Appender) values.iterator().next(); - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/CallerData.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/CallerData.java index c566be7b41..c967a02f21 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/CallerData.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/CallerData.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,19 +17,16 @@ import java.util.List; +import static ch.qos.logback.core.CoreConstants.NA; + /** - * This class computes caller data returning the result in the form - * of a StackTraceElement array. + * This class computes caller data returning the result in the form of a + * StackTraceElement array. * * @author Ceki Gülcü */ public class CallerData { - /** - * When caller information is not available this constant is used for file - * name, method name, etc. - */ - public static final String NA = "?"; // All logger call's in log4j-over-slf4j use the Category class private static final String LOG4J_CATEGORY = "org.apache.log4j.Category"; @@ -52,7 +49,8 @@ public class CallerData { * Extract caller data information as an array based on a Throwable passed as * parameter */ - public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, List frameworkPackageList) { + public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, final int maxDepth, + List frameworkPackageList) { if (t == null) { return null; } @@ -87,11 +85,13 @@ public static StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass return callerDataArray; } - static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass, List frameworkPackageList) { + static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass, + List frameworkPackageList) { // the check for org.apache.log4j.Category class is intended to support // log4j-over-slf4j. it solves http://bugzilla.slf4j.org/show_bug.cgi?id=66 - if (currentClass.equals(fqnOfInvokingClass) || currentClass.equals(LOG4J_CATEGORY) || currentClass.startsWith(SLF4J_BOUNDARY) - || isInFrameworkSpaceList(currentClass, frameworkPackageList)) { + if (currentClass.equals(fqnOfInvokingClass) || currentClass.equals(LOG4J_CATEGORY) + || currentClass.startsWith(SLF4J_BOUNDARY) + || isInFrameworkSpaceList(currentClass, frameworkPackageList)) { return true; } else { return false; @@ -99,7 +99,8 @@ static boolean isInFrameworkSpace(String currentClass, String fqnOfInvokingClass } /** - * Is currentClass present in the list of packages considered part of the logging framework? + * Is currentClass present in the list of packages considered part of the + * logging framework? */ private static boolean isInFrameworkSpaceList(String currentClass, List frameworkPackageList) { if (frameworkPackageList == null) @@ -113,7 +114,8 @@ private static boolean isInFrameworkSpaceList(String currentClass, List } /** - * Returns a StackTraceElement where all string fields are set to {@link #NA} and line number is set to {@link #LINE_NA}. + * Returns a StackTraceElement where all string fields are set to {@link CoreConstants#NA} + * and line number is set to {@link #LINE_NA}. * * @return StackTraceElement with values set to NA constants. * @since 1.0.10 diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java index 1482c9fd3b..2470639526 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ClassPackagingData.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,12 +15,17 @@ import java.io.Serializable; +@Deprecated public class ClassPackagingData implements Serializable { private static final long serialVersionUID = -804643281218337001L; - final String codeLocation; - final String version; - private final boolean exact; + String codeLocation; + String version; + boolean exact; + + public ClassPackagingData() { + + } public ClassPackagingData(String codeLocation, String version) { this.codeLocation = codeLocation; @@ -46,6 +51,18 @@ public boolean isExact() { return exact; } + public void setCodeLocation(String codeLocation) { + this.codeLocation = codeLocation; + } + + public void setVersion(String version) { + this.version = version; + } + + public void setExact(boolean exact) { + this.exact = exact; + } + @Override public int hashCode() { final int PRIME = 31; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/Configurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/Configurator.java index 1714e238ec..068fa786d2 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/Configurator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/Configurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,18 +16,30 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.core.spi.ContextAware; + /** - * Allows programmatic initialization and configuration of Logback. - * The ServiceLoader is typically used to instantiate implementations and - * thus implementations will need to follow the guidelines of the ServiceLoader - * specifically a no-arg constructor is required. + *

Allows programmatic initialization and configuration of Logback. The + * ServiceLoader is typically used to instantiate implementations and thus + * implementations will need to follow the guidelines of the ServiceLoader, + * in particular the no-arg constructor requirement.

+ * + *

The return type of {@link #configure(LoggerContext) configure} was changed from 'void' to + * {@link ExecutionStatus} in logback version 1.3.0. + *

*/ public interface Configurator extends ContextAware { + enum ExecutionStatus { + NEUTRAL, // let the caller decide + INVOKE_NEXT_IF_ANY, // invoke other + DO_NOT_INVOKE_NEXT_IF_ANY // the caller should not invoke further configurators even some are available + } + /** - * The context will also be set before this method is called via - * {@link ContextAware#setContext(ch.qos.logback.core.Context)}. + * Implementations of this method may expect that the {@link LoggerContext} is set with + * {@link ContextAware#setContext} before this method is invoked. + * */ - public void configure(LoggerContext loggerContext); + ExecutionStatus configure(LoggerContext context); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ConfiguratorRank.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ConfiguratorRank.java new file mode 100644 index 0000000000..9e026e369b --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ConfiguratorRank.java @@ -0,0 +1,42 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + + + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ConfiguratorRank { + + static public int FALLBACK = -10; + static public int NOMINAL = 0; + static public int SERIALIZED_MODEL = 10; + + static public int DEFAULT = 20; + + static public int CUSTOM_LOW_PRIORITY = DEFAULT; + + static public int CUSTOM_NORMAL_PRIORITY = 30; + + static public int CUSTOM_HIGH_PRIORITY = 40; + + static public int CUSTOM_TOP_PRIORITY = 50; + public int value() default DEFAULT; +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/EventArgUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/EventArgUtil.java index 058a150005..a1595419e9 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/EventArgUtil.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/EventArgUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -28,7 +28,8 @@ public static final Throwable extractThrowable(Object[] argArray) { } /** - * This method should be called only if {@link #successfulExtraction(Throwable)} returns true. + * This method should be called only if {@link #successfulExtraction(Throwable)} + * returns true. * * @param argArray * @return diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ILoggingEvent.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ILoggingEvent.java index 6b62cd3149..f56e30ec38 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ILoggingEvent.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ILoggingEvent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,12 @@ */ package ch.qos.logback.classic.spi; +import java.time.Instant; +import java.util.List; import java.util.Map; import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; import ch.qos.logback.classic.Level; import ch.qos.logback.core.spi.DeferredProcessingAware; @@ -56,18 +59,50 @@ public interface ILoggingEvent extends DeferredProcessingAware { StackTraceElement[] getCallerData(); /** - * If this event has caller data, then true is returned. Otherwise the - * returned value is null. + * If this event has caller data, then true is returned. Otherwise the returned + * value is null. * - *

Logback components wishing to use caller data if available without - * causing it to be computed can invoke this method before invoking + *

+ * Logback components wishing to use caller data if available without causing it + * to be computed can invoke this method before invoking * {@link #getCallerData()}. * * @return whether this event has caller data */ boolean hasCallerData(); - Marker getMarker(); + /** + * Returns the first marker is the marker list or null if no markers are + * available. + * + * This method is deprecated and exists solely for backward compatibility + * reasons. Logback components should use {@link #getMarkerList()} and cater for + * all available markers and not only the first one. + * + * @deprecated Replaced by {@link #getMarkerList()} + * @return the first marker in the marker list or null if no markers are + * available + */ + default Marker getMarker() { + List markers = getMarkerList(); + if (markers == null || markers.isEmpty()) + return null; + + // return the first marker. Assuming that only the first marker is useful + // is obviously incorrect. However, we have no other choice if we wish + // to preserve binary compatibility. + return markers.get(0); + } + + /** + * Since SLF4J 2.0.0, the slf4j logging API assumes the possibility of multiple + * Marker instances in a logging event. Consequently, ILoggingEvent needs to + * cater for this possibility. + * + * @return the marker list, may be null + * @since 1.3.0 + */ + List getMarkerList(); /** * Returns the MDC map. The returned value can be an empty map but not null. @@ -76,21 +111,60 @@ public interface ILoggingEvent extends DeferredProcessingAware { /** * Synonym for [@link #getMDCPropertyMap}. - * @deprecated Replaced by [@link #getMDCPropertyMap} + * + * @deprecated Replaced by [@link #getMDCPropertyMap} */ Map getMdc(); + /** + * Return the number of elapsed milliseconds since epoch. + * + * @return the number of elapsed milliseconds since epoch + * @since 1.3 + */ long getTimeStamp(); - + /** - * The sequence number associated with this event. + * Return the number of elapsed nanoseconds found in {@link #getInstant()} + * + * May return -1 if data unavailable. + * + * @return the number of elapsed nanoseconds as found in {@link #getInstant()} + * @since 1.3 + */ + int getNanoseconds(); + + /** + * Return the {@link java.time.Instant Instant} the event was created. + * + * Default implementation returns the instant corresponding to the value returned by @link + * {@link #getTimeStamp()}. + * + * @return the {@link java.time.Instant Instant} the event was created. + * @since 1.3 + */ + default Instant getInstant() { + return Instant.ofEpochMilli(getTimeStamp()); + } + + /** + * The sequence number associated with this event. + * + *

+ * Sequence numbers, if present, should be increasing monotonically. * - *

Sequence numbers, if present, should be increasing monotonically. - * * @since 1.3.0 */ long getSequenceNumber(); + /** + * A list of {@link KeyValuePair} objects. The returned list may be null. + * + * @return may be null + * @since 1.3.0 + */ + List getKeyValuePairs(); + void prepareForDeferredProcessing(); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/IThrowableProxy.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/IThrowableProxy.java index a5cc4de2de..3d0d9b210f 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/IThrowableProxy.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/IThrowableProxy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,6 +14,20 @@ package ch.qos.logback.classic.spi; public interface IThrowableProxy { + + /** + * Return the overriding message if any. This method returns null + * if there is no overriding message. + * + *

Overriding message exists only if the original throwable implementation overrides the toString() method.

+ * + * @return the overriding message or null + * @since 1.5.22 + */ + default String getOverridingMessage() { + return null; + } + String getMessage(); String getClassName(); @@ -25,4 +39,12 @@ public interface IThrowableProxy { IThrowableProxy getCause(); IThrowableProxy[] getSuppressed(); + + /** + * Is this instance the result of a cyclic exception? + * + * @return true if cyclic, false otherwise + * @since 1.3.0 + */ + boolean isCyclic(); } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LogbackServiceProvider.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LogbackServiceProvider.java index 08af967a95..c56abc1fa9 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LogbackServiceProvider.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LogbackServiceProvider.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.spi; import org.slf4j.ILoggerFactory; @@ -20,27 +34,29 @@ public class LogbackServiceProvider implements SLF4JServiceProvider { final static String NULL_CS_URL = CoreConstants.CODES_URL + "#null_CS"; /** - * Declare the version of the SLF4J API this implementation is compiled against. - * The value of this field is modified with each major release. + * Declare the version of the SLF4J API this implementation is compiled against. + * The value of this field is modified with each major release. */ // to avoid constant folding by the compiler, this field must *not* be final - public static String REQUESTED_API_VERSION = "1.8.99"; // !final - - private LoggerContext defaultLoggerContext; - private IMarkerFactory markerFactory; - private MDCAdapter mdcAdapter; - // private final ContextSelectorStaticBinder contextSelectorBinder = ContextSelectorStaticBinder.getSingleton(); -// private static Object KEY = new Object(); -// private volatile boolean initialized = false; - + public static String REQUESTED_API_VERSION = "2.0.99"; // !final + + private LoggerContext defaultLoggerContext = new LoggerContext(); + + + // org.slf4j.LoggerFactory expects providers to initialize markerFactory as early as possible. + private IMarkerFactory markerFactory = new BasicMarkerFactory(); + + // org.slf4j.LoggerFactory expects providers to initialize their MDCAdapter field + // as early as possible, preferably at construction time. + private LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter(); + @Override public void initialize() { - defaultLoggerContext = new LoggerContext(); defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME); + // set the MDCAdapter for the defaultLoggerContext immediately + defaultLoggerContext.setMDCAdapter(mdcAdapter); initializeLoggerContext(); - markerFactory = new BasicMarkerFactory(); - mdcAdapter = new LogbackMDCAdapter(); - //initialized = true; + defaultLoggerContext.start(); } private void initializeLoggerContext() { @@ -50,12 +66,12 @@ private void initializeLoggerContext() { } catch (JoranException je) { Util.report("Failed to auto configure default logger context", je); } - // logback-292 + // LOGBACK-292 if (!StatusUtil.contextHasStatusListener(defaultLoggerContext)) { StatusPrinter.printInCaseOfErrorsOrWarnings(defaultLoggerContext); } - //contextSelectorBinder.init(defaultLoggerContext, KEY); - + // contextSelectorBinder.init(defaultLoggerContext, KEY); + } catch (Exception t) { // see LOGBACK-1159 Util.report("Failed to instantiate [" + LoggerContext.class.getName() + "]", t); } @@ -65,15 +81,6 @@ private void initializeLoggerContext() { public ILoggerFactory getLoggerFactory() { return defaultLoggerContext; - -// if (!initialized) { -// return defaultLoggerContext; -// -// -// if (contextSelectorBinder.getContextSelector() == null) { -// throw new IllegalStateException("contextSelector cannot be null. See also " + NULL_CS_URL); -// } -// return contextSelectorBinder.getContextSelector().getLoggerContext(); } @Override @@ -87,7 +94,7 @@ public MDCAdapter getMDCAdapter() { } @Override - public String getRequesteApiVersion() { + public String getRequestedApiVersion() { return REQUESTED_API_VERSION; } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerComparator.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerComparator.java index 153a1514e5..0ce611a98e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerComparator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerComparator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAware.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAware.java index af85f90c30..b851e70513 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAware.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAware.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,13 +18,13 @@ public interface LoggerContextAware extends ContextAware { - /** - * Set owning logger context for this component. This operation can - * only be performed once. Once set, the owning context cannot be changed. - * + /** + * Set owning logger context for this component. This operation can only be + * performed once. Once set, the owning context cannot be changed. + * * @param context The context where this component is attached. - * @throws IllegalStateException If you try to change the context after it - * has been set. + * @throws IllegalStateException If you try to change the context after it has + * been set. **/ void setLoggerContext(LoggerContext context); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAwareBase.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAwareBase.java index b94ac0ec32..6e6975c7e8 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAwareBase.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextAwareBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,20 +20,21 @@ public class LoggerContextAwareBase extends ContextAwareBase implements LoggerContextAware { /** - * Set the owning context. The owning context cannot be set more than - * once. + * Set the owning context. The owning context cannot be set more than once. */ public void setLoggerContext(LoggerContext context) { super.setContext(context); } public void setContext(Context context) { - // check that the context is of type LoggerContext. Otherwise, throw an exception + // check that the context is of type LoggerContext. Otherwise, throw an + // exception // Context == null is a degenerate case but nonetheless permitted. if (context instanceof LoggerContext || context == null) { super.setContext(context); } else { - throw new IllegalArgumentException("LoggerContextAwareBase only accepts contexts of type c.l.classic.LoggerContext"); + throw new IllegalArgumentException( + "LoggerContextAwareBase only accepts contexts of type c.l.classic.LoggerContext"); } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java index 9871109583..3dcea6fae8 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,9 @@ public interface LoggerContextListener { /** - * Some listeners should not be removed when the LoggerContext is - * reset. Such listeners are said to be reset resistant. + * Some listeners should not be removed when the LoggerContext is reset. Such + * listeners are said to be reset resistant. + * * @return whether this listener is reset resistant or not. */ boolean isResetResistant(); @@ -33,4 +34,5 @@ public interface LoggerContextListener { void onStop(LoggerContext context); void onLevelChange(Logger logger, Level level); + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java index f9ae5337a8..6b94087803 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerContextVO.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -25,7 +25,7 @@ * *

* Some of the LoggerContext or Logger attributes MUST not survive - * serialization, e.g appenders, level values etc, as these attributes may have + * serialization, e.g. appenders, level values etc., as these attributes may have * other values on the remote platform. LoggerContextVO class exposes the * minimal and relevant attributes to the remote host, instead of having to deal * with an incomplete LoggerContext with many null references. @@ -37,9 +37,16 @@ public class LoggerContextVO implements Serializable { private static final long serialVersionUID = 5488023392483144387L; - final String name; - final Map propertyMap; - final long birthTime; + protected String name; + protected Map propertyMap; + protected long birthTime; + + /** + * No-arg constructor for serialization frameworks only. + * + * @since 1.5.21 + */ + public LoggerContextVO() {} public LoggerContextVO(LoggerContext lc) { this.name = lc.getName(); @@ -67,7 +74,8 @@ public long getBirthTime() { @Override public String toString() { - return "LoggerContextVO{" + "name='" + name + '\'' + ", propertyMap=" + propertyMap + ", birthTime=" + birthTime + '}'; + return "LoggerContextVO{" + "name='" + name + '\'' + ", propertyMap=" + propertyMap + ", birthTime=" + birthTime + + '}'; } @Override diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerRemoteView.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerRemoteView.java index 3c556c1828..6f6bad64f7 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerRemoteView.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggerRemoteView.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java index c5a6159311..f5aaeb5eae 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEvent.java @@ -1,26 +1,34 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.classic.spi; import java.io.IOException; import java.io.ObjectOutputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.time.Clock; +import java.time.Instant; +import java.util.ArrayList; import java.util.Collections; +import java.util.List; import java.util.Map; -import org.slf4j.MDC; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.StringUtil; import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; +import org.slf4j.spi.MDCAdapter; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -28,19 +36,16 @@ import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.spi.SequenceNumberGenerator; -import org.slf4j.spi.MDCAdapter; - /** - * The internal representation of logging events. When an affirmative decision - * is made to log then a LoggingEvent instance is created. This - * instance is passed around to the different logback-classic components. + * The internal representation of logging events. When an affirmative decision is made to log then a + * LoggingEvent instance is created. This instance is passed around to the different logback-classic + * components. *

*

- * Writers of logback-classic components such as appenders should be aware of - * that some of the LoggingEvent fields are initialized lazily. Therefore, an - * appender wishing to output data to be later correctly read by a receiver, - * must initialize "lazy" fields prior to writing them out. See the - * {@link #prepareForDeferredProcessing()} method for the exact list. + * Writers of logback-classic components such as appenders should be aware of that some of the LoggingEvent fields are + * initialized lazily. Therefore, an appender wishing to output data to be later correctly read by a receiver, must + * initialize "lazy" fields prior to writing them out. See the {@link #prepareForDeferredProcessing()} method for the + * exact list. *

* * @author Ceki Gülcü @@ -48,9 +53,11 @@ */ public class LoggingEvent implements ILoggingEvent { + public static final String VIRTUAL_THREAD_NAME_PREFIX = "virtual-"; + public static final String REGULAR_UNNAMED_THREAD_PREFIX = "unnamed-"; + /** - * Fully qualified name of the calling Logger class. This field does not - * survive serialization. + * Fully qualified name of the calling Logger class. This field does not survive serialization. *

*

* Note that the getCallerInformation() method relies on this fact. @@ -70,8 +77,7 @@ public class LoggingEvent implements ILoggingEvent { * Level of logging event. *

*

- * This field should not be accessed directly. You should use the - * {@link #getLevel} method instead. + * This field should not be accessed directly. You should use the {@link #getLevel} method instead. *

*/ private transient Level level; @@ -89,22 +95,30 @@ public class LoggingEvent implements ILoggingEvent { private StackTraceElement[] callerDataArray; - private Marker marker; + private List markerList; private Map mdcPropertyMap; /** - * The number of milliseconds elapsed from 1/1/1970 until logging event was - * created. + * @since 1.3.0 */ + List keyValuePairs; + + /** + * The number of milliseconds elapsed from 1/1/1970 until logging event was created. + */ + private Instant instant; + private long timeStamp; + private int nanoseconds; private long sequenceNumber; public LoggingEvent() { } - public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable, Object[] argArray) { + public LoggingEvent(String fqcn, Logger logger, Level level, String message, Throwable throwable, + Object[] argArray) { this.fqnOfLoggerClass = fqcn; this.loggerName = logger.getName(); this.loggerContext = logger.getLoggerContext(); @@ -114,31 +128,37 @@ public LoggingEvent(String fqcn, Logger logger, Level level, String message, Thr this.message = message; this.argumentArray = argArray; - timeStamp = System.currentTimeMillis(); - - if(loggerContext != null) { + Instant instant = Clock.systemUTC().instant(); + initTmestampFields(instant); + + if (loggerContext != null) { SequenceNumberGenerator sequenceNumberGenerator = loggerContext.getSequenceNumberGenerator(); - if(sequenceNumberGenerator != null) + if (sequenceNumberGenerator != null) sequenceNumber = sequenceNumberGenerator.nextSequenceNumber(); } - - + if (throwable == null) { - throwable = extractThrowableAnRearrangeArguments(argArray); + throwable = extractThrowableAndRearrangeArguments(argArray); } if (throwable != null) { this.throwableProxy = new ThrowableProxy(throwable); - + if (loggerContext != null && loggerContext.isPackagingDataEnabled()) { this.throwableProxy.calculatePackagingData(); } } + } - + void initTmestampFields(Instant instant) { + this.instant = instant; + long epochSecond = instant.getEpochSecond(); + this.nanoseconds = instant.getNano(); + long milliseconds = nanoseconds / 1000_000; + this.timeStamp = (epochSecond * 1000) + (milliseconds); } - private Throwable extractThrowableAnRearrangeArguments(Object[] argArray) { + private Throwable extractThrowableAndRearrangeArguments(Object[] argArray) { Throwable extractedThrowable = EventArgUtil.extractThrowable(argArray); if (EventArgUtil.successfulExtraction(extractedThrowable)) { this.argumentArray = EventArgUtil.trimmedCopy(argArray); @@ -157,6 +177,22 @@ public Object[] getArgumentArray() { return this.argumentArray; } + public void addKeyValuePair(KeyValuePair kvp) { + if (keyValuePairs == null) { + keyValuePairs = new ArrayList<>(4); + } + keyValuePairs.add(kvp); + } + + public void setKeyValuePairs(List kvpList) { + this.keyValuePairs = kvpList; + } + + @Override + public List getKeyValuePairs() { + return this.keyValuePairs; + } + public Level getLevel() { return level; } @@ -171,11 +207,56 @@ public void setLoggerName(String loggerName) { public String getThreadName() { if (threadName == null) { - threadName = (Thread.currentThread()).getName(); + threadName = extractThreadName(Thread.currentThread()); } return threadName; } + /** + * Extracts the name of aThread by calling {@link Thread#getName()}. If the value is null, then use the value + * returned by {@link Thread#getId()} prefixing with {@link #VIRTUAL_THREAD_NAME_PREFIX} if thread is virtual or + * with {@link #REGULAR_UNNAMED_THREAD_PREFIX} if regular. + * + * @param aThread + * @return + * @since 1.5.0 + */ + private String extractThreadName(Thread aThread) { + if (aThread == null) { + return CoreConstants.NA; + } + String threadName = aThread.getName(); + if (StringUtil.notNullNorEmpty(threadName)) + return threadName; + Long virtualThreadId = getVirtualThreadId(aThread); + if (virtualThreadId != null) { + return VIRTUAL_THREAD_NAME_PREFIX + virtualThreadId; + } else { + return REGULAR_UNNAMED_THREAD_PREFIX + aThread.getId(); + } + } + // + + + /** + * Return the threadId if running under JDK 21+ and the thread is a virtual thread, return null otherwise. + * + * @param aThread + * @return Return the threadId if the thread is a virtual thread, return null otherwise. + */ + Long getVirtualThreadId(Thread aThread) { + if (EnvUtil.isJDK21OrHigher()) { + try { + Method isVirtualMethod = Thread.class.getMethod("isVirtual"); + boolean isVirtual = (boolean) isVirtualMethod.invoke(aThread); + if (isVirtual) + return aThread.getId(); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + return null; + } + } + return null; + } + /** * @param threadName The threadName to set. * @throws IllegalStateException If threadName has been already set. @@ -207,12 +288,12 @@ public void setThrowableProxy(ThrowableProxy tp) { } /** - * This method should be called prior to serializing an event. It should also - * be called when using asynchronous or deferred logging. + * This method should be called prior to serializing an event. It should also be called when using asynchronous or + * deferred logging. *

*

- * Note that due to performance concerns, this method does NOT extract caller - * data. It is the responsibility of the caller to extract caller information. + * Note that due to performance concerns, this method does NOT extract caller data. It is the responsibility of the + * caller to extract caller information. */ public void prepareForDeferredProcessing() { this.getFormattedMessage(); @@ -221,6 +302,10 @@ public void prepareForDeferredProcessing() { this.getMDCPropertyMap(); } + public void setLoggerContext(LoggerContext lc) { + this.loggerContext = lc; + } + public LoggerContextVO getLoggerContextVO() { return loggerContextVO; } @@ -240,23 +325,62 @@ public void setMessage(String message) { this.message = message; } + /** + * Return the {@link Instant} corresponding to the creation of this event. + * + * @see {@link #getTimeStamp()} + * @since 1.3 + */ + public Instant getInstant() { + return instant; + } + + /** + * Set {@link Instant} corresponding to the creation of this event. + * + * The value of {@link #getTimeStamp()} will be overridden as well. + */ + public void setInstant(Instant instant) { + initTmestampFields(instant); + } + + /** + * Return the number of elapsed milliseconds since epoch in UTC. + */ public long getTimeStamp() { return timeStamp; } + /** + * Return the number of nanoseconds past the {@link #getTimeStamp() timestamp in seconds}. + * + * @since 1.3.0 + */ + @Override + public int getNanoseconds() { + return nanoseconds; + } + + /** + * Set the number of elapsed milliseconds since epoch in UTC. + * + * Setting the timestamp will override the value contained in {@link #getInstant}. Nanoseconds value will be + * computed form the provided millisecond value. + */ public void setTimeStamp(long timeStamp) { - this.timeStamp = timeStamp; + Instant instant = Instant.ofEpochMilli(timeStamp); + setInstant(instant); } @Override public long getSequenceNumber() { return sequenceNumber; } - - public void setSquenceNumber(long sn) { + + public void setSequenceNumber(long sn) { sequenceNumber = sn; } - + public void setLevel(Level level) { if (this.level != null) { throw new IllegalStateException("The level has been already set for this event."); @@ -265,19 +389,17 @@ public void setLevel(Level level) { } /** - * Get the caller information for this logging event. If caller information is - * null at the time of its invocation, this method extracts location - * information. The collected information is cached for future use. + * Get the caller information for this logging event. If caller information is null at the time of its invocation, + * this method extracts location information. The collected information is cached for future use. *

*

- * Note that after serialization it is impossible to correctly extract caller - * information. + * Note that after serialization it is impossible to correctly extract caller information. *

*/ public StackTraceElement[] getCallerData() { if (callerDataArray == null) { - callerDataArray = CallerData - .extract(new Throwable(), fqnOfLoggerClass, loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages()); + callerDataArray = CallerData.extract(new Throwable(), fqnOfLoggerClass, + loggerContext.getMaxCallerDataDepth(), loggerContext.getFrameworkPackages()); } return callerDataArray; } @@ -290,15 +412,18 @@ public void setCallerData(StackTraceElement[] callerDataArray) { this.callerDataArray = callerDataArray; } - public Marker getMarker() { - return marker; + public List getMarkerList() { + return markerList; } - public void setMarker(Marker marker) { - if (this.marker != null) { - throw new IllegalStateException("The marker has been already set for this event."); + public void addMarker(Marker marker) { + if (marker == null) { + return; } - this.marker = marker; + if (markerList == null) { + markerList = new ArrayList<>(4); + } + markerList.add(marker); } public long getContextBirthTime() { @@ -311,7 +436,13 @@ public String getFormattedMessage() { return formattedMessage; } if (argumentArray != null) { - formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage(); + if(throwableProxy == null) { + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage(); + } else { + // very rare case where the argument array ends with two exceptions + // See https://github.com/qos-ch/logback/issues/876 + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray, null).getMessage(); + } } else { formattedMessage = message; } @@ -322,11 +453,12 @@ public String getFormattedMessage() { public Map getMDCPropertyMap() { // populate mdcPropertyMap if null if (mdcPropertyMap == null) { - MDCAdapter mdc = MDC.getMDCAdapter(); - if (mdc instanceof LogbackMDCAdapter) - mdcPropertyMap = ((LogbackMDCAdapter) mdc).getPropertyMap(); - else - mdcPropertyMap = mdc.getCopyOfContextMap(); + MDCAdapter mdcAdapter = loggerContext.getMDCAdapter(); + if (mdcAdapter instanceof LogbackMDCAdapter) { + mdcPropertyMap = ((LogbackMDCAdapter) mdcAdapter).getPropertyMap(); + } else { + mdcPropertyMap = mdcAdapter.getCopyOfContextMap(); + } } // mdcPropertyMap still null, use emptyMap() if (mdcPropertyMap == null) @@ -368,15 +500,14 @@ public String toString() { } /** - * LoggerEventVO instances should be used for serialization. Use - * {@link LoggingEventVO#build(ILoggingEvent) build} method to create the LoggerEventVO instance. + * LoggerEventVO instances should be used for serialization. Use {@link LoggingEventVO#build(ILoggingEvent) build} + * method to create the LoggerEventVO instance. * * @since 1.0.11 */ private void writeObject(ObjectOutputStream out) throws IOException { throw new UnsupportedOperationException(this.getClass() + " does not support serialization. " - + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method."); + + "Use LoggerEventVO instance instead. See also LoggerEventVO.build method."); } - } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEventVO.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEventVO.java index 5c897e90cc..cb13b8b7f5 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEventVO.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/LoggingEventVO.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,17 +14,22 @@ package ch.qos.logback.classic.spi; import java.io.IOException; +import java.io.InvalidObjectException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.time.Instant; +import java.util.List; import java.util.Map; import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; import ch.qos.logback.classic.Level; // http://www.riehle.org/computer-science/research/1998/ubilab-tr-1998-10-1.html +// See also the paper https://www.riehle.org/computer-science/research/1998/ubilab-tr-1998-10-1.pdf /** * A read-only and serializable implementation of {@link ILoggingEvent}. @@ -38,6 +43,7 @@ public class LoggingEventVO implements ILoggingEvent, Serializable { private static final int NULL_ARGUMENT_ARRAY = -1; private static final String NULL_ARGUMENT_ARRAY_ELEMENT = "NULL_ARGUMENT_ARRAY_ELEMENT"; + private static final int ARGUMENT_ARRAY_DESERIALIZATION_LIMIT = 128; private String threadName; private String loggerName; @@ -55,9 +61,13 @@ public class LoggingEventVO implements ILoggingEvent, Serializable { private ThrowableProxyVO throwableProxy; private StackTraceElement[] callerDataArray; - private Marker marker; + private List markerList; + private List keyValuePairList; private Map mdcPropertyMap; - private long timeStamp; + + private long timestamp; + private int nanoseconds; + private long sequenceNumber; public static LoggingEventVO build(ILoggingEvent le) { @@ -68,13 +78,15 @@ public static LoggingEventVO build(ILoggingEvent le) { ledo.level = (le.getLevel()); ledo.message = (le.getMessage()); ledo.argumentArray = (le.getArgumentArray()); - ledo.marker = le.getMarker(); + ledo.markerList = le.getMarkerList(); + ledo.keyValuePairList = le.getKeyValuePairs(); ledo.mdcPropertyMap = le.getMDCPropertyMap(); - ledo.timeStamp = le.getTimeStamp(); - ledo.timeStamp = le.getSequenceNumber(); + ledo.timestamp = le.getTimeStamp(); + ledo.nanoseconds = le.getNanoseconds(); + ledo.sequenceNumber = le.getSequenceNumber(); ledo.throwableProxy = ThrowableProxyVO.build(le.getThrowableProxy()); // add caller data only if it is there already - // fixes http://jira.qos.ch/browse/LBCLASSIC-145 + // See also https://jira.qos.ch/browse/LOGBACK-480 if (le.hasCallerData()) { ledo.callerDataArray = le.getCallerData(); } @@ -131,18 +143,20 @@ public boolean hasCallerData() { return callerDataArray != null; } - public Marker getMarker() { - return marker; + public List getMarkerList() { + return markerList; } - public long getTimeStamp() { - return timeStamp; - } + @Override + public long getTimeStamp() { return timestamp; } + + @Override + public int getNanoseconds() { return nanoseconds; } public long getSequenceNumber() { return sequenceNumber; } - + public long getContextBirthTime() { return loggerContextVO.getBirthTime(); } @@ -159,6 +173,11 @@ public Map getMdc() { return mdcPropertyMap; } + @Override + public List getKeyValuePairs() { + return this.keyValuePairList; + } + public void prepareForDeferredProcessing() { } @@ -187,6 +206,12 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE level = Level.toLevel(levelInt); int argArrayLen = in.readInt(); + + // Prevent DOS attacks via large or negative arrays + if (argArrayLen < NULL_ARGUMENT_ARRAY || argArrayLen > ARGUMENT_ARRAY_DESERIALIZATION_LIMIT) { + throw new InvalidObjectException("Argument array length is invalid: " + argArrayLen); + } + if (argArrayLen != NULL_ARGUMENT_ARRAY) { argumentArray = new String[argArrayLen]; for (int i = 0; i < argArrayLen; i++) { @@ -201,10 +226,12 @@ private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundE @Override public int hashCode() { final int prime = 31; + long millis = getTimeStamp(); + int result = 1; result = prime * result + ((message == null) ? 0 : message.hashCode()); result = prime * result + ((threadName == null) ? 0 : threadName.hashCode()); - result = prime * result + (int) (timeStamp ^ (timeStamp >>> 32)); + result = prime * result + (int) (millis ^ (millis >>> 32)); return result; } @@ -234,13 +261,13 @@ public boolean equals(Object obj) { return false; } else if (!threadName.equals(other.threadName)) return false; - if (timeStamp != other.timeStamp) + if (getTimeStamp() != other.getTimeStamp()) return false; - if (marker == null) { - if (other.marker != null) + if (markerList == null) { + if (other.markerList != null) return false; - } else if (!marker.equals(other.marker)) + } else if (!markerList.equals(other.markerList)) return false; if (mdcPropertyMap == null) { @@ -250,4 +277,5 @@ public boolean equals(Object obj) { return false; return true; } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java index aad047dc26..fc798fd93d 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/PackagingDataCalculator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,14 +17,11 @@ import java.security.CodeSource; import java.util.HashMap; -//import sun.reflect.Reflection; - -// import java.security.AccessControlException; import java.security.AccessController;import java.security.PrivilegedAction; /** * Given a classname locate associated PackageInfo (jar name, version name). * * @author James Strachan - * @Ceki Gülcü + * @author Ceki Gülcü */ public class PackagingDataCalculator { @@ -42,8 +39,8 @@ public class PackagingDataCalculator { // sun.reflect.Reflection class. However, this class will *not compile* // on JDKs lacking sun.reflect.Reflection. try { - //Reflection.getCallerClass(2); - //GET_CALLER_CLASS_METHOD_AVAILABLE = true; + // Reflection.getCallerClass(2); + // GET_CALLER_CLASS_METHOD_AVAILABLE = true; } catch (NoClassDefFoundError e) { } catch (NoSuchMethodError e) { } catch (UnsupportedOperationException e) { @@ -83,7 +80,8 @@ void populateFrames(StackTraceElementProxy[] stepArray) { for (int i = 0; i < commonFrames; i++) { Class callerClass = null; if (GET_CALLER_CLASS_METHOD_AVAILABLE) { - //callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount + 1); + // callerClass = Reflection.getCallerClass(localFirstCommon + i - missfireCount + // + 1); } StackTraceElementProxy step = stepArray[stepFirstCommon + i]; String stepClassname = step.ste.getClassName(); @@ -105,7 +103,8 @@ void populateFrames(StackTraceElementProxy[] stepArray) { populateUncommonFrames(commonFrames, stepArray, firsExactClassLoader); } - void populateUncommonFrames(int commonFrames, StackTraceElementProxy[] stepArray, ClassLoader firstExactClassLoader) { + void populateUncommonFrames(int commonFrames, StackTraceElementProxy[] stepArray, + ClassLoader firstExactClassLoader) { int uncommonFrames = stepArray.length - commonFrames; for (int i = 0; i < uncommonFrames; i++) { StackTraceElementProxy step = stepArray[i]; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java index 3c89f7d6ad..dd3eaf6359 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/PlatformInfo.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ * This class provides information about the runtime platform. * * @author Ceki Gulcu - * */ + */ public class PlatformInfo { private static final int UNINITIALIZED = -1; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java index 94b408f2d5..802ed6c127 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/STEUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java index f1f3876877..58cb377791 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/StackTraceElementProxy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,7 +23,9 @@ public class StackTraceElementProxy implements Serializable { // save a byte or two during serialization, as we can // reconstruct this field from 'ste' transient private String steAsString; - private ClassPackagingData cpd; + + @Deprecated + ClassPackagingData classPackagingData; public StackTraceElementProxy(StackTraceElement ste) { if (ste == null) { @@ -44,14 +46,14 @@ public StackTraceElement getStackTraceElement() { } public void setClassPackagingData(ClassPackagingData cpd) { - if (this.cpd != null) { + if (this.classPackagingData != null) { throw new IllegalStateException("Packaging data has been already set"); } - this.cpd = cpd; + this.classPackagingData = cpd; } public ClassPackagingData getClassPackagingData() { - return cpd; + return classPackagingData; } @Override @@ -72,11 +74,11 @@ public boolean equals(Object obj) { if (!ste.equals(other.ste)) { return false; } - if (cpd == null) { - if (other.cpd != null) { + if (classPackagingData == null) { + if (other.classPackagingData != null) { return false; } - } else if (!cpd.equals(other.cpd)) { + } else if (!classPackagingData.equals(other.classPackagingData)) { return false; } return true; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java index 93775a28b0..dd94fe86db 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,14 +14,21 @@ package ch.qos.logback.classic.spi; import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.OptionHelper; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.IdentityHashMap; +import java.util.List; +import java.util.Set; public class ThrowableProxy implements IThrowableProxy { + static final StackTraceElementProxy[] EMPTY_STEP = new StackTraceElementProxy[0]; + private Throwable throwable; private String className; + private String overridingMessage; private String message; // package-private because of ThrowableProxyUtil StackTraceElementProxy[] stackTraceElementProxyArray; @@ -30,58 +37,82 @@ public class ThrowableProxy implements IThrowableProxy { private ThrowableProxy cause; private ThrowableProxy[] suppressed = NO_SUPPRESSED; + // private final Set alreadyProcessedSet; + private transient PackagingDataCalculator packagingDataCalculator; private boolean calculatedPackageData = false; - private static final Method GET_SUPPRESSED_METHOD; - - static { - Method method = null; - try { - method = Throwable.class.getMethod("getSuppressed"); - } catch (NoSuchMethodException e) { - // ignore, will get thrown in Java < 7 - } - GET_SUPPRESSED_METHOD = method; - } + // the getter is called isCyclic + private boolean cyclic; private static final ThrowableProxy[] NO_SUPPRESSED = new ThrowableProxy[0]; public ThrowableProxy(Throwable throwable) { + // use an identity set to detect cycles in the throwable chain + this(throwable, Collections.newSetFromMap(new IdentityHashMap<>())); + } + + // used for circular exceptions + private ThrowableProxy(Throwable circular, boolean isCyclic) { + this.throwable = circular; + this.className = circular.getClass().getName(); + this.message = circular.getMessage(); + this.stackTraceElementProxyArray = EMPTY_STEP; + this.cyclic = true; + } + + public ThrowableProxy(Throwable throwable, Set alreadyProcessedSet) { this.throwable = throwable; this.className = throwable.getClass().getName(); this.message = throwable.getMessage(); + this.overridingMessage = buildOverridingMessage(throwable); this.stackTraceElementProxyArray = ThrowableProxyUtil.steArrayToStepArray(throwable.getStackTrace()); + this.cyclic = false; - Throwable nested = throwable.getCause(); + alreadyProcessedSet.add(throwable); + Throwable nested = throwable.getCause(); if (nested != null) { - this.cause = new ThrowableProxy(nested); - this.cause.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(nested.getStackTrace(), stackTraceElementProxyArray); + if (alreadyProcessedSet.contains(nested)) { + this.cause = new ThrowableProxy(nested, true); + } else { + this.cause = new ThrowableProxy(nested, alreadyProcessedSet); + this.cause.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(nested.getStackTrace(), + stackTraceElementProxyArray); + } } - if (GET_SUPPRESSED_METHOD != null) { - // this will only execute on Java 7 - try { - Object obj = GET_SUPPRESSED_METHOD.invoke(throwable); - if (obj instanceof Throwable[]) { - Throwable[] throwableSuppressed = (Throwable[]) obj; - if (throwableSuppressed.length > 0) { - suppressed = new ThrowableProxy[throwableSuppressed.length]; - for (int i = 0; i < throwableSuppressed.length; i++) { - this.suppressed[i] = new ThrowableProxy(throwableSuppressed[i]); - this.suppressed[i].commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(throwableSuppressed[i].getStackTrace(), - stackTraceElementProxyArray); - } - } + + Throwable[] throwableSuppressed = throwable.getSuppressed(); + // while JDK's implementation of getSuppressed() will always return a non-null array, + // this might not be the case in mocked throwables. We are being extra defensive here. + if (OptionHelper.isNotEmtpy(throwableSuppressed)) { + List suppressedList = new ArrayList<>(throwableSuppressed.length); + for (Throwable sup : throwableSuppressed) { + if (alreadyProcessedSet.contains(sup)) { + ThrowableProxy throwableProxy = new ThrowableProxy(sup, true); + suppressedList.add(throwableProxy); + } else { + ThrowableProxy throwableProxy = new ThrowableProxy(sup, alreadyProcessedSet); + throwableProxy.commonFrames = ThrowableProxyUtil.findNumberOfCommonFrames(sup.getStackTrace(), + stackTraceElementProxyArray); + suppressedList.add(throwableProxy); } - } catch (IllegalAccessException e) { - // ignore - } catch (InvocationTargetException e) { - // ignore } + this.suppressed = suppressedList.toArray(new ThrowableProxy[suppressedList.size()]); } + } + private String buildOverridingMessage(Throwable throwable) { + StringBuilder sb = new StringBuilder(); + ThrowableProxyUtil.appendNominalFirstLine(sb, throwable.getClass().getName(), throwable.getMessage()); + String messageFromToString = throwable.toString(); + String nominalMessage = sb.toString(); + if (!nominalMessage.equals(messageFromToString)) { + return messageFromToString; + } else { + return null; + } } public Throwable getThrowable() { @@ -92,6 +123,16 @@ public String getMessage() { return message; } + /* + * (non-Javadoc) + * + * @see ch.qos.logback.classic.spi.IThrowableProxy#getOverridingMessage() + */ + @Override + public String getOverridingMessage() { + return overridingMessage; + } + /* * (non-Javadoc) * @@ -105,6 +146,11 @@ public StackTraceElementProxy[] getStackTraceElementProxyArray() { return stackTraceElementProxyArray; } + @Override + public boolean isCyclic() { + return cyclic; + } + public int getCommonFrames() { return commonFrames; } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java index 007c64407e..db5882a3f0 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -73,6 +73,10 @@ static int findNumberOfCommonFrames(StackTraceElement[] steArray, StackTraceElem return count; } + public static void appendNominalFirstLine(StringBuilder buf, String classname, String message) { + buf.append(classname).append(": ").append(message); + } + public static String asString(IThrowableProxy tp) { StringBuilder sb = new StringBuilder(BUILDER_CAPACITY); @@ -133,7 +137,8 @@ public static void subjoinSTEP(StringBuilder sb, StackTraceElementProxy step) { /** * @param sb The StringBuilder the STEPs are appended to. * @param tp the IThrowableProxy containing the STEPs. - * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) instead. + * @deprecated Use subjoinSTEPArray(StringBuilder sb, int indentLevel, + * IThrowableProxy tp) instead. */ public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) { // not called anymore - but it is public @@ -141,9 +146,10 @@ public static void subjoinSTEPArray(StringBuilder sb, IThrowableProxy tp) { } /** - * @param sb The StringBuilder the STEPs are appended to. - * @param indentLevel indentation level used for the STEPs, usually REGULAR_EXCEPTION_INDENT. - * @param tp the IThrowableProxy containing the STEPs. + * @param sb The StringBuilder the STEPs are appended to. + * @param indentLevel indentation level used for the STEPs, usually + * REGULAR_EXCEPTION_INDENT. + * @param tp the IThrowableProxy containing the STEPs. */ public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowableProxy tp) { StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray(); @@ -158,7 +164,8 @@ public static void subjoinSTEPArray(StringBuilder sb, int indentLevel, IThrowabl if (commonFrames > 0) { indent(sb, indentLevel); - sb.append("... ").append(commonFrames).append(" common frames omitted").append(CoreConstants.LINE_SEPARATOR); + sb.append("... ").append(commonFrames).append(" common frames omitted") + .append(CoreConstants.LINE_SEPARATOR); } } @@ -178,7 +185,21 @@ public static void subjoinFirstLineRootCauseFirst(StringBuilder buf, IThrowableP subjoinExceptionMessage(buf, tp); } - private static void subjoinExceptionMessage(StringBuilder buf, IThrowableProxy tp) { - buf.append(tp.getClassName()).append(": ").append(tp.getMessage()); + public static void subjoinExceptionMessage(StringBuilder stringBuilder, IThrowableProxy tp) { + if (tp.isCyclic()) { + stringBuilder.append("[CIRCULAR REFERENCE: "); + appendNominalOrOverridingMessage(stringBuilder, tp); + stringBuilder.append(']'); + } else { + appendNominalOrOverridingMessage(stringBuilder, tp); + } + } + + private static void appendNominalOrOverridingMessage(StringBuilder stringBuilder, IThrowableProxy tp) { + if(tp.getOverridingMessage() == null) { + appendNominalFirstLine(stringBuilder, tp.getClassName(), tp.getMessage()); + } else { + stringBuilder.append(tp.getOverridingMessage()); + } } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyVO.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyVO.java index d6105c3f5e..9bb59dde78 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyVO.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/ThrowableProxyVO.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,15 +21,28 @@ public class ThrowableProxyVO implements IThrowableProxy, Serializable { private static final long serialVersionUID = -773438177285807139L; private String className; + private String overridingMessage; private String message; private int commonFramesCount; private StackTraceElementProxy[] stackTraceElementProxyArray; private IThrowableProxy cause; private IThrowableProxy[] suppressed; + private boolean cyclic; public String getMessage() { return message; } + /** + * Return the overriding message if any. This method returns null + * if there is no overriding message. + * + *

Overriding message exists only if the original throwable implementation overrides the toString() method.

+ * + * @return the overriding message or null + * @since 1.5.22 + */ + @Override + public String getOverridingMessage() { return overridingMessage;} public String getClassName() { return className; @@ -51,6 +64,10 @@ public IThrowableProxy[] getSuppressed() { return suppressed; } + public boolean isCyclic() { + return cyclic; + } + @Override public int hashCode() { final int prime = 31; @@ -97,8 +114,11 @@ public static ThrowableProxyVO build(IThrowableProxy throwableProxy) { ThrowableProxyVO tpvo = new ThrowableProxyVO(); tpvo.className = throwableProxy.getClassName(); tpvo.message = throwableProxy.getMessage(); + tpvo.overridingMessage = throwableProxy.getOverridingMessage(); tpvo.commonFramesCount = throwableProxy.getCommonFrames(); tpvo.stackTraceElementProxyArray = throwableProxy.getStackTraceElementProxyArray(); + tpvo.cyclic = throwableProxy.isCyclic(); + IThrowableProxy cause = throwableProxy.getCause(); if (cause != null) { tpvo.cause = ThrowableProxyVO.build(cause); @@ -110,6 +130,7 @@ public static ThrowableProxyVO build(IThrowableProxy throwableProxy) { tpvo.suppressed[i] = ThrowableProxyVO.build(suppressed[i]); } } + return tpvo; } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/TurboFilterList.java b/logback-classic/src/main/java/ch/qos/logback/classic/spi/TurboFilterList.java index 5bbc495c38..6b12a4e6ac 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/TurboFilterList.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/TurboFilterList.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -24,7 +24,7 @@ /** * Implementation of TurboFilterAttachable. - * + * * @author Ceki Gülcü */ final public class TurboFilterList extends CopyOnWriteArrayList { @@ -32,49 +32,78 @@ final public class TurboFilterList extends CopyOnWriteArrayList { private static final long serialVersionUID = 1L; /** - * Loop through the filters in the chain. As soon as a filter decides on - * ACCEPT or DENY, then that value is returned. If all of the filters return - * NEUTRAL, then NEUTRAL is returned. + * Loop through the filters in the chain. As soon as a filter decides on ACCEPT + * or DENY, then that value is returned. If all turbo filters return NEUTRAL, + * then NEUTRAL is returned. */ - public FilterReply getTurboFilterChainDecision(final Marker marker, final Logger logger, final Level level, final String format, final Object[] params, - final Throwable t) { + public FilterReply getTurboFilterChainDecision(final Marker marker, final Logger logger, final Level level, + final String format, final Object[] params, final Throwable t) { final int size = size(); - // if (size == 0) { - // return FilterReply.NEUTRAL; - // } + // caller may have already performed this check, but we do it here as well to be sure + if (size == 0) { + return FilterReply.NEUTRAL; + } + if (size == 1) { try { TurboFilter tf = get(0); return tf.decide(marker, logger, level, format, params, t); } catch (IndexOutOfBoundsException iobe) { + // concurrent modification detected, fall through to the general case return FilterReply.NEUTRAL; } } - Object[] tfa = toArray(); - final int len = tfa.length; - for (int i = 0; i < len; i++) { - // for (TurboFilter tf : this) { - final TurboFilter tf = (TurboFilter) tfa[i]; + + for (TurboFilter tf : this) { final FilterReply r = tf.decide(marker, logger, level, format, params, t); if (r == FilterReply.DENY || r == FilterReply.ACCEPT) { return r; } } + return FilterReply.NEUTRAL; } - // public boolean remove(TurboFilter turboFilter) { - // return tfList.remove(turboFilter); - // } - // - // public TurboFilter remove(int index) { - // return tfList.remove(index); - // } - // - // final public int size() { - // return tfList.size(); - // } + + /** + * Loop through the filters in the chain. As soon as a filter decides on ACCEPT + * or DENY, then that value is returned. If all turbo filters return NEUTRAL, + * then NEUTRAL is returned. + * + * @param logger the logger requesting a decision + * @param slf4jEvent the SLF4J logging event + * @return the decision of the turbo filter chain + * @since 1.5.21 + */ + public FilterReply getTurboFilterChainDecision(Logger logger, org.slf4j.event.LoggingEvent slf4jEvent) { + + final int size = size(); + // caller may have already performed this check, but we do it here as well to be sure + if (size == 0) { + return FilterReply.NEUTRAL; + } + + if (size == 1) { + try { + TurboFilter tf = get(0); + return tf.decide(logger, slf4jEvent); + } catch (IndexOutOfBoundsException iobe) { + // concurrent modification detected, fall through to the general case + return FilterReply.NEUTRAL; + } + } + + + for (TurboFilter tf : this) { + final FilterReply r = tf.decide(logger, slf4jEvent); + if (r == FilterReply.DENY || r == FilterReply.ACCEPT) { + return r; + } + } + + return FilterReply.NEUTRAL; + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/spi/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/spi/package.html index 460c189441..251bb060ab 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/spi/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/spi/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java index f023541aaa..41d4d30690 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DuplicateMessageFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,7 +21,7 @@ /** * - * See {@link http://logback.qos.ch/manual/filters.html#DuplicateMessageFilter} + * See DuplicateMessageFilter * for details. * * @author Ceki Gulcu diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DynamicThresholdFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DynamicThresholdFilter.java index ab7ff12118..936a827509 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DynamicThresholdFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/DynamicThresholdFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,26 +27,29 @@ * such as product name or company name that would be associated with requests * as they are processed. * - *

This filter will allow you to associate threshold levels to a key put in - * the MDC. This key can be any value specified by the user. Furthermore, you - * can pass MDC value and level threshold associations, which are then looked up - * to find the level threshold to apply to the current logging request. If no - * level threshold could be found, then a 'default' value specified by the user - * is applied. We call this value 'levelAssociatedWithMDCValue'. + *

+ * This filter will allow you to associate threshold levels to a key put in the + * MDC. This key can be any value specified by the user. Furthermore, you can + * pass MDC value and level threshold associations, which are then looked up to + * find the level threshold to apply to the current logging request. If no level + * threshold could be found, then a 'default' value specified by the user is + * applied. We call this value 'levelAssociatedWithMDCValue'. * - *

If 'levelAssociatedWithMDCValue' is higher or equal to the level of the + *

+ * If 'levelAssociatedWithMDCValue' is higher or equal to the level of the * current logger request, the * {@link #decide(Marker, Logger, Level, String, Object[], Throwable) decide()} - * method returns the value of {@link #getOnHigherOrEqual() onHigherOrEqual}, - * if it is lower then the value of {@link #getOnLower() onLower} is returned. - * Both 'onHigherOrEqual' and 'onLower' can be set by the user. By default, + * method returns the value of {@link #getOnHigherOrEqual() onHigherOrEqual}, if + * it is lower than the value of {@link #getOnLower() onLower} is returned. Both + * 'onHigherOrEqual' and 'onLower' can be set by the user. By default, * 'onHigherOrEqual' is set to NEUTRAL and 'onLower' is set to DENY. Thus, if * the current logger request's level is lower than * 'levelAssociatedWithMDCValue', then the request is denied, and if it is * higher or equal, then this filter decides NEUTRAL letting subsequent filters * to make the decision on the fate of the logging request. * - *

The example below illustrates how logging could be enabled for only + *

+ * The example below illustrates how logging could be enabled for only * individual users. In this example all events for logger names matching * "com.mycompany" will be logged if they are for 'user1' and at a level higher * than equals to DEBUG, and for 'user2' if they are at a level higher than or @@ -162,8 +165,8 @@ public void setDefaultThreshold(Level defaultThreshold) { } /** - * Get the FilterReply when the effective level is higher or equal to the - * level of current logging request + * Get the FilterReply when the effective level is higher or equal to the level + * of current logging request * * @return FilterReply */ @@ -215,11 +218,11 @@ public void start() { * This method first finds the MDC value for 'key'. It then finds the level * threshold associated with this MDC value from the list of MDCValueLevelPair * passed to this filter. This value is stored in a variable called - * 'levelAssociatedWithMDCValue'. If it null, then it is set to the + * 'levelAssociatedWithMDCValue'. If it is null, then it is set to the * * @{link #defaultThreshold} value. * - * If no such value exists, then + * If no such value exists, then * * * @param marker @@ -232,7 +235,8 @@ public void start() { * @return FilterReply - this filter's decision */ @Override - public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, Throwable throwable) { + public FilterReply decide(Marker marker, Logger logger, Level level, String s, Object[] objects, + Throwable throwable) { String mdcValue = MDC.get(this.key); if (!isStarted()) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java index 5f779c3666..59141d5628 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/LRUMessageCache.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,9 @@ import java.util.Map; /** - * Clients of this class should only use the {@link #getMessageCountAndThenIncrement} method. - * Other methods inherited via LinkedHashMap are not thread safe. + * Clients of this class should only use the + * {@link #getMessageCountAndThenIncrement} method. Other methods inherited via + * LinkedHashMap are not thread safe. */ class LRUMessageCache extends LinkedHashMap { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCFilter.java index ff22f4dd8c..dc98c4b036 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -24,20 +24,17 @@ * This class allows output for a given MDC value. * *

- * When the given value is identified by this TurboFilter, - * the reply is based on the OnMatch option. - * The information is taken from the MDC. For this TurboFilter to work, - * one must set the key that will be used to - * access the information in the MDC. + * When the given value is identified by this TurboFilter, the reply is based on + * the OnMatch option. The information is taken from the MDC. For this + * TurboFilter to work, one must set the key that will be used to access the + * information in the MDC. * *

- * To allow output for the value, set the OnMatch option - * to ACCEPT. To disable output for the given value, set - * the OnMatch option to DENY. + * To allow output for the value, set the OnMatch option to ACCEPT. To disable + * output for the given value, set the OnMatch option to DENY. * *

- * By default, values of the OnMatch and OnMisMatch - * options are set to NEUTRAL. + * By default, values of the OnMatch and OnMisMatch options are set to NEUTRAL. * * * @author Ceki Gülcü @@ -48,25 +45,25 @@ public class MDCFilter extends MatchingFilter { String MDCKey; String value; - @Override public void start() { int errorCount = 0; - if(value == null) { + if (value == null) { addError("\'value\' parameter is mandatory. Cannot start."); errorCount++; } - if(MDCKey == null) { + if (MDCKey == null) { addError("\'MDCKey\' parameter is mandatory. Cannot start."); errorCount++; } - - if(errorCount == 0) + + if (errorCount == 0) this.start = true; } + @Override public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { - + if (!isStarted()) { return FilterReply.NEUTRAL; } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCValueLevelPair.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCValueLevelPair.java index 013ffa15a4..481dc76e63 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCValueLevelPair.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MDCValueLevelPair.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MarkerFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MarkerFilter.java index 3158f9e923..486464c3de 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MarkerFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MarkerFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,7 +21,7 @@ import ch.qos.logback.core.spi.FilterReply; /** - * Checks whether the marker in the event matches the marker specified by the + * Checks whether the marker in the event matches the marker specified by the * user. */ public class MarkerFilter extends MatchingFilter { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MatchingFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MatchingFilter.java index c19737d57f..51e5a81c2e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MatchingFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/MatchingFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,9 +16,9 @@ import ch.qos.logback.core.spi.FilterReply; /** - * An abstract class containing support for {@link #onMatch} on {@link #onMismatch} - * attributes, shared by many but not all turbo filters. - * + * An abstract class containing support for {@link #onMatch} on + * {@link #onMismatch} attributes, shared by many but not all turbo filters. + * * @author Ceki Gulcu */ public abstract class MatchingFilter extends TurboFilter { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java index 629de509dd..4ca3f42206 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,32 +13,34 @@ */ package ch.qos.logback.classic.turbo; +import static ch.qos.logback.core.CoreConstants.MILLIS_IN_ONE_SECOND; + import java.io.File; import java.net.URL; import java.util.List; -import ch.qos.logback.classic.util.EnvUtil; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ConfigurationWatchList; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.status.StatusUtil; import org.slf4j.Marker; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.spi.ConfigurationWatchList; import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelUtil; import ch.qos.logback.core.spi.FilterReply; - -import static ch.qos.logback.core.CoreConstants.MILLIS_IN_ONE_SECOND; +import ch.qos.logback.core.status.StatusUtil; /** * Reconfigure a LoggerContext when the configuration file changes. * * @author Ceki Gulcu + * @deprecated replaced by {@link ch.qos.logback.classic.joran.ReconfigureOnChangeTask} */ +@Deprecated public class ReconfigureOnChangeFilter extends TurboFilter { /** @@ -57,7 +59,7 @@ public class ReconfigureOnChangeFilter extends TurboFilter { public void start() { configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(context); if (configurationWatchList != null) { - mainConfigurationURL = configurationWatchList.getMainURL(); + mainConfigurationURL = configurationWatchList.getTopURL(); if (mainConfigurationURL == null) { addWarn("Due to missing top level configuration file, automatic reconfiguration is impossible."); return; @@ -120,19 +122,23 @@ public FilterReply decide(Marker marker, Logger logger, Level level, String form return FilterReply.NEUTRAL; } - // experiments indicate that even for CPU intensive applications with 200 or more threads MASK + // experiments indicate that even for CPU intensive applications with 200 or + // more threads MASK // values in the order of 0xFFFF is appropriate private static final int MAX_MASK = 0xFFFF; - // if less than MASK_INCREASE_THRESHOLD milliseconds elapse between invocations of updateMaskIfNecessary() method, + // if less than MASK_INCREASE_THRESHOLD milliseconds elapse between invocations + // of updateMaskIfNecessary() method, // then the mask should be increased private static final long MASK_INCREASE_THRESHOLD = 100; - // if more than MASK_DECREASE_THRESHOLD milliseconds elapse between invocations of updateMaskIfNecessary() method, + // if more than MASK_DECREASE_THRESHOLD milliseconds elapse between invocations + // of updateMaskIfNecessary() method, // then the mask should be decreased private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8; - // update the mask so as to execute change detection code about once every 100 to 8000 milliseconds. + // update the mask so as to execute change detection code about once every 100 + // to 8000 milliseconds. private void updateMaskIfNecessary(long now) { final long timeElapsedSinceLastMaskUpdateCheck = now - lastMaskCheck; lastMaskCheck = now; @@ -148,7 +154,7 @@ private void updateMaskIfNecessary(long now) { // reader lock. void detachReconfigurationToNewThread() { addInfo("Detected change in [" + configurationWatchList.getCopyOfFileWatchList() + "]"); - context.getScheduledExecutorService().submit(new ReconfiguringThread()); + context.getExecutorService().submit(new ReconfiguringThread()); } void updateNextCheck(long now) { @@ -158,7 +164,8 @@ void updateNextCheck(long now) { protected boolean changeDetected(long now) { if (now >= nextCheck) { updateNextCheck(now); - return configurationWatchList.changeDetected(); + File file = configurationWatchList.changeDetected(); + return file != null; } return false; } @@ -186,17 +193,7 @@ public void run() { if (mainConfigurationURL.toString().endsWith("xml")) { performXMLConfiguration(lc); } else if (mainConfigurationURL.toString().endsWith("groovy")) { - if (EnvUtil.isGroovyAvailable()) { - lc.reset(); - // avoid directly referring to GafferConfigurator so as to avoid - // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214 - - // GafferUtil.runGafferConfiguratorOn(lc, this, mainConfigurationURL); - addError("Groovy configuration disabled due to Java 9 compilation issues."); - - } else { - addError("Groovy classes are not available on the class path. ABORTING INITIALIZATION."); - } + addError("Groovy configuration disabled due to Java 9 compilation issues."); } } @@ -204,32 +201,33 @@ private void performXMLConfiguration(LoggerContext lc) { JoranConfigurator jc = new JoranConfigurator(); jc.setContext(context); StatusUtil statusUtil = new StatusUtil(context); - List eventList = jc.recallSafeConfiguration(); + Model failSafeTop = jc.recallSafeConfiguration(); URL mainURL = ConfigurationWatchListUtil.getMainWatchURL(context); lc.reset(); long threshold = System.currentTimeMillis(); try { jc.doConfigure(mainConfigurationURL); if (statusUtil.hasXMLParsingErrors(threshold)) { - fallbackConfiguration(lc, eventList, mainURL); + fallbackConfiguration(lc, failSafeTop, mainURL); } } catch (JoranException e) { - fallbackConfiguration(lc, eventList, mainURL); + fallbackConfiguration(lc, failSafeTop, mainURL); } } - private void fallbackConfiguration(LoggerContext lc, List eventList, URL mainURL) { + private void fallbackConfiguration(LoggerContext lc, Model failSafeTop, URL mainURL) { JoranConfigurator joranConfigurator = new JoranConfigurator(); joranConfigurator.setContext(context); - if (eventList != null) { + if (failSafeTop != null) { addWarn("Falling back to previously registered safe configuration."); try { lc.reset(); JoranConfigurator.informContextOfURLUsedForConfiguration(context, mainURL); - joranConfigurator.doConfigure(eventList); + ModelUtil.resetForReuse(failSafeTop); + joranConfigurator.processModel(failSafeTop); addInfo("Re-registering previous fallback configuration once more as a fallback configuration point"); - joranConfigurator.registerSafeConfiguration(eventList); - } catch (JoranException e) { + joranConfigurator.registerSafeConfiguration(failSafeTop); + } catch (Exception e) { addError("Unexpected exception thrown by a configuration considered safe.", e); } } else { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/TurboFilter.java b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/TurboFilter.java index d6c4d4cbf9..356eaf8f31 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/turbo/TurboFilter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/turbo/TurboFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,8 @@ */ package ch.qos.logback.classic.turbo; +import ch.qos.logback.classic.LoggerContext; +import org.slf4j.LoggerFactory; import org.slf4j.Marker; import ch.qos.logback.classic.Level; @@ -21,14 +23,16 @@ import ch.qos.logback.core.spi.FilterReply; import ch.qos.logback.core.spi.LifeCycle; +import java.util.List; + /** - * TurboFilter is a specialized filter with a decide method that takes a bunch - * of parameters instead of a single event object. The latter is cleaner but - * the first is much more performant. + * TurboFilter is a specialized filter with a decide method that takes a bunch + * of parameters instead of a single event object. The latter is cleaner but the + * first is much more performant. *

- * For more information about turbo filters, please refer to the online manual at - * http://logback.qos.ch/manual/filters.html#TurboFilter - * + * For more information about turbo filters, please refer to the online manual + * at https://logback.qos.ch/manual/filters.html#TurboFilter + * * @author Ceki Gulcu */ public abstract class TurboFilter extends ContextAwareBase implements LifeCycle { @@ -37,10 +41,11 @@ public abstract class TurboFilter extends ContextAwareBase implements LifeCycle boolean start = false; /** - * Make a decision based on the multiple parameters passed as arguments. - * The returned value should be one of {@link FilterReply#DENY}, - * {@link FilterReply#NEUTRAL}, or {@link FilterReply#ACCEPT}. - + * Make a decision based on the multiple parameters passed as arguments. The + * returned value should be one of {@link FilterReply#DENY}, + * {@link FilterReply#NEUTRAL}, or + * {@link FilterReply#ACCEPT}. + * * @param marker * @param logger * @param level @@ -49,7 +54,52 @@ public abstract class TurboFilter extends ContextAwareBase implements LifeCycle * @param t * @return */ - public abstract FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t); + public abstract FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, + Throwable t); + + + /** + *

This method is intended to be called via SLF4J's fluent API and more specifically by + * {@link Logger#log(org.slf4j.event.LoggingEvent slf4jEvent)}. Derived classes are strongly + * encouraged to override this method with a better suited and more specialized + * implementation. + *

+ * + *

The present default implementation translates the given SLF4J {@code LoggingEvent} into the + * set of parameters required by {@link #decide(Marker, Logger, Level, String, Object[], Throwable)} + * and delegate the decision to that method. + *

+ * + *

Concretely, this method: + *

    + *
  • extracts the first marker (if any) from the event's marker list,
  • + *
  • maps the SLF4J level to Logback's {@link Level},
  • + *
  • and forwards the event message, arguments and throwable.
  • + *
+ * + *

Returns the {@link ch.qos.logback.core.spi.FilterReply} produced by + * {@code decide(...)}, which should be one of DENY, NEUTRAL or ACCEPT. + * + *

Derived classes are strongly encouraged to override this method with a + * better suited and more specialized implementation.

+ * + * @param logger the Logger that is logging the event; non-null + * @param slf4jEvent the SLF4J logging event to translate and evaluate; may be non-null + * @return the filter decision ({@code DENY}, {@code NEUTRAL} or {@code ACCEPT}) + * + * @since 1.5.21 + */ + public FilterReply decide(Logger logger, org.slf4j.event.LoggingEvent slf4jEvent) { + List markers = slf4jEvent.getMarkers(); + Marker firstMarker = (markers != null && !markers.isEmpty()) ? markers.get(0) : null; + + Level logbackLevel = Level.convertAnSLF4JLevel(slf4jEvent.getLevel()); + String format = slf4jEvent.getMessage(); + Object[] params = slf4jEvent.getArgumentArray(); + Throwable t = slf4jEvent.getThrowable(); + + return decide(firstMarker, logger, logbackLevel, format, params, t); + } public void start() { this.start = true; diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/tyler/TylerConfiguratorBase.java b/logback-classic/src/main/java/ch/qos/logback/classic/tyler/TylerConfiguratorBase.java new file mode 100644 index 0000000000..a8696f7c40 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/tyler/TylerConfiguratorBase.java @@ -0,0 +1,188 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.tyler; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.classic.util.LevelUtil; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.GenericXMLConfigurator; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.util.PropertyModelHandlerHelper; +import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.util.StatusListenerConfigHelper; +import ch.qos.logback.core.util.StringUtil; + +import java.util.Map; +import java.util.function.Supplier; + +public class TylerConfiguratorBase extends ContextAwareBase implements ContextAwarePropertyContainer { + + public static final String SET_CONTEXT_METHOD_NAME = "setContext"; + public static final String SET_CONTEXT_NAME_METHOD_NAME = "setContextName"; + public static final String SETUP_LOGGER_METHOD_NAME = "setupLogger"; + public static final String VARIABLE_SUBSTITUTIONS_HELPER_FIELD_NAME = "variableSubstitutionsHelper"; + public static final String PROPERTY_MODEL_HANDLER_HELPER_FIELD_NAME = "propertyModelHandlerHelper"; + + // initialized via #setContext + protected VariableSubstitutionsHelper variableSubstitutionsHelper; + // context set in #setContext + protected PropertyModelHandlerHelper propertyModelHandlerHelper = new PropertyModelHandlerHelper(this); + + protected Logger setupLogger(String loggerName, String levelString, Boolean additivity) { + LoggerContext loggerContext = (LoggerContext) context; + Logger logger = loggerContext.getLogger(loggerName); + if (!OptionHelper.isNullOrEmptyOrAllSpaces(levelString)) { + Level level = LevelUtil.levelStringToLevel(levelString); + logger.setLevel(level); + } + if (additivity != null) { + logger.setAdditive(additivity); + } + return logger; + } + + @Override + public void setContext(Context context) { + super.setContext(context); + variableSubstitutionsHelper = new VariableSubstitutionsHelper(context); + propertyModelHandlerHelper.setContext(context); + } + + protected void setContextName(String name) { + if (StringUtil.isNullOrEmpty(name)) { + addError("Cannot set context name to null or empty string"); + return; + } + try { + String substName = subst(name); + addInfo("Setting context name to [" + substName + "]"); + context.setName(substName); + } catch (IllegalStateException e) { + addError("Failed to rename context as [" + name + "]"); + } + } + + protected void addOnConsoleStatusListener() { + StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); + } + + /** + * Performs variable substitution. + * + * @param ref + * @return + */ + @Override + public String subst(String ref) { + return variableSubstitutionsHelper.subst(ref); + } + + @Override + public void addSubstitutionProperty(String key, String value) { + variableSubstitutionsHelper.addSubstitutionProperty(key, value); + } + + /** + * If a key is found in propertiesMap then return it. + */ + @Override + public String getProperty(String key) { + return variableSubstitutionsHelper.getProperty(key); + } + + @Override + public Map getCopyOfPropertyMap() { + return variableSubstitutionsHelper.getCopyOfPropertyMap(); + } + + public boolean isNull(String k) { + String val = OptionHelper.propertyLookup(k, this, context); + return (val == null); + } + + /** + * Method used in conditional evaluation + * + * @param k a property name + * @return true if the property is defined + * @since 1.5.4 + */ + public boolean isDefined(String k) { + String val = OptionHelper.propertyLookup(k, this, context); + return (val != null); + } + + /** + * Shorthand for {@link #property(String)}. + * + * @param k a property name + * @return value of property k + * @since 1.5.4 + */ + public String p(String k) { + return property(k); + } + + /** + * Return the value of the property named k. If the value is null, then the + * empty string is returned to avoid null checks. + * + * @param k property name + * @return the value of the property named k + * @since 1.5.4 + */ + public String property(String k) { + String val = OptionHelper.propertyLookup(k, this, context); + if (val != null) + return val; + else + return ""; + } + + private JoranConfigurator makeAnotherInstance() { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(context); + return jc; + } + + /** + * Return a supplier which supplies an instance of {@link JoranConfigurator} set to + * the same context the context of 'this'. + * @since 1.5.11 + */ + @Override + public Supplier getConfiguratorSupplier() { + Supplier supplier = () -> this.makeAnotherInstance(); + return supplier; + } + + // used by TylerIncludeModelHandler in logback-tyler + protected void processModelFromIncludedFile(Model modelFromIncludedFile) { + Supplier configuratorSupplier = this.getConfiguratorSupplier(); + GenericXMLConfigurator genericXMLConfigurator = configuratorSupplier.get(); + ConfigurationModel configurationModel = new ConfigurationModel(); + configurationModel.addSubModel(modelFromIncludedFile); + genericXMLConfigurator.processModel(configurationModel); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/tyler/VariableModelHelper.java b/logback-classic/src/main/java/ch/qos/logback/classic/tyler/VariableModelHelper.java new file mode 100644 index 0000000000..cda2b73e13 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/tyler/VariableModelHelper.java @@ -0,0 +1,122 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.tyler; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.model.ModelConstants; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.util.PropertyModelHandlerHelper; +import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.ContextUtil; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +public class VariableModelHelper extends ContextAwareBase { + + TylerConfiguratorBase tylerConfiguratorBase; + VariableSubstitutionsHelper variableSubstitutionsHelper; + + VariableModelHelper(Context context, TylerConfiguratorBase tylerConfiguratorBase) { + super( tylerConfiguratorBase); + this.context = context; + this.tylerConfiguratorBase = tylerConfiguratorBase; + this.variableSubstitutionsHelper = new VariableSubstitutionsHelper(context); + } + + void updateProperties(PropertyModel propertyModel) { + + ActionUtil.Scope scope = ActionUtil.stringToScope(propertyModel.getScopeStr()); + if (PropertyModelHandlerHelper.checkFileAttributeSanity(propertyModel)) { + String file = propertyModel.getFile(); + file = tylerConfiguratorBase.subst(file); + try (FileInputStream istream = new FileInputStream(file)) { + loadAndSetProperties(istream, scope); + } catch (FileNotFoundException e) { + addError("Could not find properties file [" + file + "]."); + } catch (IOException |IllegalArgumentException e1) { // IllegalArgumentException is thrown in case the file + // is badly malformed, i.e a binary. + addError("Could not read properties file [" + file + "].", e1); + } + } else if (PropertyModelHandlerHelper.checkResourceAttributeSanity(propertyModel)) { + String resource = propertyModel.getResource(); + resource = tylerConfiguratorBase.subst(resource); + URL resourceURL = Loader.getResourceBySelfClassLoader(resource); + if (resourceURL == null) { + addError("Could not find resource [" + resource + "]."); + } else { + try ( InputStream istream = resourceURL.openStream();) { + loadAndSetProperties(istream, scope); + } catch (IOException e) { + addError("Could not read resource file [" + resource + "].", e); + } + } + } else if (PropertyModelHandlerHelper.checkValueNameAttributesSanity(propertyModel)) { + // earlier versions performed Java '\' escapes for '\\' '\t' etc. Howevver, there is no + // need to do this. See RegularEscapeUtil.__UNUSED__basicEscape + String value = propertyModel.getValue(); + + // now remove both leading and trailing spaces + value = value.trim(); + value = tylerConfiguratorBase.subst(value); + setProperty(propertyModel.getName(), value, scope); + + } else { + addError(ModelConstants.INVALID_ATTRIBUTES); + } + } + + void loadAndSetProperties(InputStream istream, ActionUtil.Scope scope) throws IOException { + Properties props = new Properties(); + props.load(istream); + setProperties(props, scope); + } + + + public void setProperties(Properties props, ActionUtil.Scope scope) { + switch (scope) { + case LOCAL: + variableSubstitutionsHelper.addSubstitutionProperties(props); + break; + case CONTEXT: + ContextUtil cu = new ContextUtil(getContext()); + cu.addProperties(props); + break; + case SYSTEM: + OptionHelper.setSystemProperties(this, props); + } + } + + public void setProperty(String key, String value, ActionUtil.Scope scope) { + switch (scope) { + case LOCAL: + variableSubstitutionsHelper.addSubstitutionProperty(key, value); + break; + case CONTEXT: + getContext().putProperty(key, value); + break; + case SYSTEM: + OptionHelper.setSystemProperty(this, key, value); + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicEnvUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicEnvUtil.java new file mode 100644 index 0000000000..4b63db59b0 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicEnvUtil.java @@ -0,0 +1,74 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.util; + +import java.lang.module.ModuleDescriptor; +import java.util.*; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.VersionUtil; + +/** + * @author Ceki Gülcü + */ +public class ClassicEnvUtil { + + /* + * Used to replace the ClassLoader that the ServiceLoader uses for unit testing. + * We need this to mock the resources the ServiceLoader attempts to load from + * /META-INF/services thus keeping the projects src/test/resources clean (see + * src/test/resources/README.txt). + */ + //static ClassLoader testServiceLoaderClassLoader = null; + + static public boolean isGroovyAvailable() { + return EnvUtil.isClassAvailable(ClassicEnvUtil.class, "groovy.lang.Binding"); + } +// +// private static ClassLoader getServiceLoaderClassLoader() { +// return testServiceLoaderClassLoader == null ? Loader.getClassLoaderOfClass(ClassicEnvUtil.class) +// : testServiceLoaderClassLoader; +// } + + public static List loadFromServiceLoader(Class c, ClassLoader classLoader) { + ServiceLoader loader = ServiceLoader.load(c, classLoader); + List listOfT = new ArrayList<>(); + Iterator it = loader.iterator(); + while(it.hasNext()) { + T t = it.next(); + listOfT.add(t); + } + return listOfT; + } + + /** + *

Returns the current version of logback-classic, or null if data is not + * available. + *

+ * + * @since 1.5.15 + * @return current version or null if missing version data + * @deprecated See {@link ch.qos.logback.core.util.VersionUtil#getVersionOfArtifact(Class)} + */ + @Deprecated + static public String getVersionOfLogbackClassic() { + try { + return VersionUtil.getVersionOfArtifact(ClassicConstants.class); + } catch (NoClassDefFoundError e) { + return null; + } + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicVersionUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicVersionUtil.java new file mode 100644 index 0000000000..583b6123f0 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/ClassicVersionUtil.java @@ -0,0 +1,58 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.util; + +import ch.qos.logback.classic.ClassicConstants; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +public class ClassicVersionUtil { + + // copied from VersionUtil + static String getVersionBySelfDeclaredProperties(Class aClass, String moduleName) { + Properties props = new Properties(); + // example propertiesFileName: logback-core-version.properties + // + String propertiesFileName = moduleName + "-version.properties"; + String propertyKey = moduleName+"-version"; + try (InputStream is = aClass.getResourceAsStream(propertiesFileName)) { + if (is != null) { + props.load(is); + return props.getProperty(propertyKey); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + /** + * Retrieves the version information for the "logback-classic" module based on its self-declared properties. + * The method looks for a properties file named "logback-classic-version.properties" within the classpath, + * reads its contents, and fetches the value associated with the "logback-classic-version" key. + * + * @return the version string of the "logback-classic" module if found, or null if the properties file or version + * key is not present or an error occurs while reading the properties file. + * + * @since 1.5.26 + */ + static public String getVersionBySelfDeclaredProperties() { + return getVersionBySelfDeclaredProperties(ClassicConstants.class, "logback-classic"); + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextInitializer.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextInitializer.java index 1a0b7bbacd..e2a0a510b1 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextInitializer.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextInitializer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,26 +13,21 @@ */ package ch.qos.logback.classic.util; -import java.io.File; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Set; - -import ch.qos.logback.classic.BasicConfigurator; import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.classic.spi.ConfiguratorRank; import ch.qos.logback.core.LogbackException; import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.status.ErrorStatus; -import ch.qos.logback.core.status.InfoStatus; -import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.status.WarnStatus; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.ContextAwareImpl; +import ch.qos.logback.core.util.CoreVersionUtil; import ch.qos.logback.core.util.Loader; -import ch.qos.logback.core.util.OptionHelper; import ch.qos.logback.core.util.StatusListenerConfigHelper; +import ch.qos.logback.core.util.VersionUtil; + +import java.util.Comparator; +import java.util.List; // contributors // Ted Graham, Matt Fowles, see also http://jira.qos.ch/browse/LBCORE-32 @@ -44,158 +39,163 @@ */ public class ContextInitializer { - final public static String GROOVY_AUTOCONFIG_FILE = "logback.groovy"; - final public static String AUTOCONFIG_FILE = "logback.xml"; - final public static String TEST_AUTOCONFIG_FILE = "logback-test.xml"; + /** + * @deprecated Please use ClassicConstants.AUTOCONFIG_FILE instead + */ + final public static String AUTOCONFIG_FILE = ClassicConstants.AUTOCONFIG_FILE; + /** + * @deprecated Please use ClassicConstants.TEST_AUTOCONFIG_FILE instead + */ + final public static String TEST_AUTOCONFIG_FILE = ClassicConstants.TEST_AUTOCONFIG_FILE; /** * @deprecated Please use ClassicConstants.CONFIG_FILE_PROPERTY instead */ final public static String CONFIG_FILE_PROPERTY = ClassicConstants.CONFIG_FILE_PROPERTY; + String[] INTERNAL_CONFIGURATOR_CLASSNAME_LIST = {"ch.qos.logback.classic.util.DefaultJoranConfigurator", "ch.qos.logback.classic.BasicConfigurator"}; + final LoggerContext loggerContext; + final ContextAware contextAware; + public ContextInitializer(LoggerContext loggerContext) { this.loggerContext = loggerContext; + this.contextAware = new ContextAwareImpl(loggerContext, this); } - public void configureByResource(URL url) throws JoranException { - if (url == null) { - throw new IllegalArgumentException("URL argument cannot be null"); - } - final String urlString = url.toString(); - if (urlString.endsWith("groovy")) { - if (EnvUtil.isGroovyAvailable()) { - // avoid directly referring to GafferConfigurator so as to avoid - // loading groovy.lang.GroovyObject . See also http://jira.qos.ch/browse/LBCLASSIC-214 - //GafferUtil.runGafferConfiguratorOn(loggerContext, this, url); - - StatusManager sm = loggerContext.getStatusManager(); - sm.add(new ErrorStatus("Groovy configuration disabled due to Java 9 compilation issues.", loggerContext)); - - } else { - StatusManager sm = loggerContext.getStatusManager(); - sm.add(new ErrorStatus("Groovy classes are not available on the class path. ABORTING INITIALIZATION.", loggerContext)); - } - } else if (urlString.endsWith("xml")) { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(loggerContext); - configurator.doConfigure(url); - } else { - throw new LogbackException("Unexpected filename extension of file [" + url.toString() + "]. Should be either .groovy or .xml"); - } + public void autoConfig() throws JoranException { + autoConfig(Configurator.class.getClassLoader()); } - void joranConfigureByResource(URL url) throws JoranException { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(loggerContext); - configurator.doConfigure(url); - } - private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boolean updateStatus) { - String logbackConfigFile = OptionHelper.getSystemProperty(CONFIG_FILE_PROPERTY); - if (logbackConfigFile != null) { - URL result = null; - try { - result = new URL(logbackConfigFile); - return result; - } catch (MalformedURLException e) { - // so, resource is not a URL: - // attempt to get the resource from the class path - result = Loader.getResource(logbackConfigFile, classLoader); - if (result != null) { - return result; - } - File f = new File(logbackConfigFile); - if (f.exists() && f.isFile()) { - try { - result = f.toURI().toURL(); - return result; - } catch (MalformedURLException e1) { - } - } - } finally { - if (updateStatus) { - statusOnResourceSearch(logbackConfigFile, classLoader, result); - } - } - } - return null; - } + public void autoConfig(ClassLoader classLoader) throws JoranException { + + // see https://github.com/qos-ch/logback/issues/715 + classLoader = Loader.systemClassloaderIfNull(classLoader); - public URL findURLOfDefaultConfigurationFile(boolean updateStatus) { - ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); - URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus); - if (url != null) { - return url; + checkVersions(); + + StatusListenerConfigHelper.installIfAsked(loggerContext); + + + // invoke custom configurators + List configuratorList = ClassicEnvUtil.loadFromServiceLoader(Configurator.class, classLoader); + configuratorList.sort(rankComparator); + if (configuratorList.isEmpty()) { + contextAware.addInfo("No custom configurators were discovered as a service."); + } else { + printConfiguratorOrder(configuratorList); } - url = getResource(TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus); - if (url != null) { - return url; + for (Configurator c : configuratorList) { + if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY) + return; } - url = getResource(GROOVY_AUTOCONFIG_FILE, myClassLoader, updateStatus); - if (url != null) { - return url; + // invoke internal configurators + for (String configuratorClassName : INTERNAL_CONFIGURATOR_CLASSNAME_LIST) { + contextAware.addInfo("Trying to configure with "+configuratorClassName); + Configurator c = instantiateConfiguratorByClassName(configuratorClassName, classLoader); + if(c == null) + continue; + if (invokeConfigure(c) == Configurator.ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY) + return; } + } - return getResource(AUTOCONFIG_FILE, myClassLoader, updateStatus); + private void checkVersions() { + try { + String coreVersion = CoreVersionUtil.getCoreVersionBySelfDeclaredProperties(); + String classicVersion = ClassicVersionUtil.getVersionBySelfDeclaredProperties(); + VersionUtil.checkForVersionEquality(loggerContext, coreVersion, classicVersion, "logback-core", "logback-classic"); + } catch(NoClassDefFoundError e) { + contextAware.addWarn("Missing ch.logback.core.util.VersionUtil class on classpath. The version of logback-core is probably older than 1.5.26."); + } catch (NoSuchMethodError e) { + contextAware.addWarn(e.toString()); + contextAware.addWarn("The version of logback-core is probably older than 1.5.26."); + } } - private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) { - URL url = Loader.getResource(filename, myClassLoader); - if (updateStatus) { - statusOnResourceSearch(filename, myClassLoader, url); + private Configurator instantiateConfiguratorByClassName(String configuratorClassName, ClassLoader classLoader) { + try { + Class classObj = classLoader.loadClass(configuratorClassName); + return (Configurator) classObj.getConstructor().newInstance(); + } catch (ReflectiveOperationException e) { + contextAware.addInfo("Instantiation failure: " + e.toString()); + return null; } - return url; } - public void autoConfig() throws JoranException { - StatusListenerConfigHelper.installIfAsked(loggerContext); - URL url = findURLOfDefaultConfigurationFile(true); - if (url != null) { - configureByResource(url); - } else { - Configurator c = EnvUtil.loadFromServiceLoader(Configurator.class); - if (c != null) { - try { - c.setContext(loggerContext); - c.configure(loggerContext); - } catch (Exception e) { - throw new LogbackException(String.format("Failed to initialize Configurator: %s using ServiceLoader", c != null ? c.getClass() - .getCanonicalName() : "null"), e); - } - } else { - BasicConfigurator basicConfigurator = new BasicConfigurator(); - basicConfigurator.setContext(loggerContext); - basicConfigurator.configure(loggerContext); - } + /** + * + * @param configurator + * @return ExecutionStatus + */ + private Configurator.ExecutionStatus invokeConfigure(Configurator configurator) { + try { + long start = System.currentTimeMillis(); + contextAware.addInfo("Constructed configurator of type " + configurator.getClass()); + configurator.setContext(loggerContext); + Configurator.ExecutionStatus status = configurator.configure(loggerContext); + printDuration(start, configurator, status); + return status; + + } catch (Exception e) { + throw new LogbackException(String.format("Failed to initialize or to run Configurator: %s", + configurator != null ? configurator.getClass().getCanonicalName() : "null"), e); } } - private void statusOnResourceSearch(String resourceName, ClassLoader classLoader, URL url) { - StatusManager sm = loggerContext.getStatusManager(); - if (url == null) { - sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", loggerContext)); - } else { - sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", loggerContext)); - multiplicityWarning(resourceName, classLoader); + private void printConfiguratorOrder(List configuratorList) { + contextAware.addInfo("Here is a list of configurators discovered as a service, by rank: "); + for(Configurator c: configuratorList) { + contextAware.addInfo(" "+c.getClass().getName()); } + contextAware.addInfo("They will be invoked in order until ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY is returned."); } - private void multiplicityWarning(String resourceName, ClassLoader classLoader) { - Set urlSet = null; - StatusManager sm = loggerContext.getStatusManager(); + private void printDuration(long start, Configurator configurator, Configurator.ExecutionStatus executionStatus) { + long end = System.currentTimeMillis(); + long diff = end - start; + contextAware.addInfo( configurator.getClass().getName()+".configure() call lasted "+diff + " milliseconds. ExecutionStatus="+executionStatus); + } + + private Configurator.ExecutionStatus attemptConfigurationUsingJoranUsingReflexion(ClassLoader classLoader) { + try { - urlSet = Loader.getResources(resourceName, classLoader); - } catch (IOException e) { - sm.add(new ErrorStatus("Failed to get url list for resource [" + resourceName + "]", loggerContext, e)); + Class djcClass = classLoader.loadClass("ch.qos.logback.classic.util.DefaultJoranConfigurator"); + Configurator c = (Configurator) djcClass.newInstance(); + c.setContext(loggerContext); + return c.configure(loggerContext); + } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) { + contextAware.addError("unexpected exception while instantiating DefaultJoranConfigurator", e); + return Configurator.ExecutionStatus.INVOKE_NEXT_IF_ANY; } - if (urlSet != null && urlSet.size() > 1) { - sm.add(new WarnStatus("Resource [" + resourceName + "] occurs multiple times on the classpath.", loggerContext)); - for (URL url : urlSet) { - sm.add(new WarnStatus("Resource [" + resourceName + "] occurs at [" + url.toString() + "]", loggerContext)); - } + + } + + Comparator rankComparator = new Comparator() { + @Override + public int compare(Configurator c1, Configurator c2) { + + ConfiguratorRank r1 = c1.getClass().getAnnotation(ConfiguratorRank.class); + ConfiguratorRank r2 = c2.getClass().getAnnotation(ConfiguratorRank.class); + + int value1 = r1 == null ? ConfiguratorRank.DEFAULT : r1.value(); + int value2 = r2 == null ? ConfiguratorRank.DEFAULT : r2.value(); + + int result = compareRankValue(value1, value2); + // reverse the result for high to low sort + return (-result); } + }; + + private int compareRankValue(int value1, int value2) { + if(value1 > value2) + return 1; + else if (value1 == value2) + return 0; + else return -1; + } } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextSelectorStaticBinder.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextSelectorStaticBinder.java index 3b846cf1ce..79a168c83a 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextSelectorStaticBinder.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/ContextSelectorStaticBinder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -42,8 +42,8 @@ public static ContextSelectorStaticBinder getSingleton() { } /** - * FOR INTERNAL USE. This method is intended for use by StaticLoggerBinder. - * + * FOR INTERNAL USE. This method is intended for use by StaticLoggerBinder. + * * @param defaultLoggerContext * @throws ClassNotFoundException * @throws NoSuchMethodException @@ -51,8 +51,8 @@ public static ContextSelectorStaticBinder getSingleton() { * @throws IllegalAccessException * @throws InvocationTargetException */ - public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, - IllegalAccessException, InvocationTargetException { + public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNotFoundException, + NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException { if (this.key == null) { this.key = key; } else if (this.key != key) { @@ -85,9 +85,9 @@ public void init(LoggerContext defaultLoggerContext, Object key) throws ClassNot * @throws IllegalAccessException * @throws InvocationTargetException */ - static ContextSelector dynamicalContextSelector(LoggerContext defaultLoggerContext, String contextSelectorStr) throws ClassNotFoundException, - SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, - InvocationTargetException { + static ContextSelector dynamicalContextSelector(LoggerContext defaultLoggerContext, String contextSelectorStr) + throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, + InstantiationException, IllegalAccessException, InvocationTargetException { Class contextSelectorClass = Loader.loadClass(contextSelectorStr); Constructor cons = contextSelectorClass.getConstructor(new Class[] { LoggerContext.class }); return (ContextSelector) cons.newInstance(defaultLoggerContext); diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/CopyOnInheritThreadLocal.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/CopyOnInheritThreadLocal.java index fdec49e196..375c355f33 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/CopyOnInheritThreadLocal.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/CopyOnInheritThreadLocal.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java new file mode 100644 index 0000000000..f20e063cf4 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultJoranConfigurator.java @@ -0,0 +1,165 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.util; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.spi.ConfiguratorRank; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.LogbackException; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.status.InfoStatus; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Set; + +/** + * @since 1.3.0-beta1 + */ +// Note that DefaultJoranConfigurator is invoked via reflection +@ConfiguratorRank(value = ConfiguratorRank.NOMINAL) +public class DefaultJoranConfigurator extends ContextAwareBase implements Configurator { + + @Override + public ExecutionStatus configure(LoggerContext context) { + URL url = performMultiStepConfigurationFileSearch(true); + if (url != null) { + try { + configureByResource(url); + } catch (JoranException e) { + e.printStackTrace(); + } + // You tried and that counts Mary. + return ExecutionStatus.DO_NOT_INVOKE_NEXT_IF_ANY; + } else { + return ExecutionStatus.INVOKE_NEXT_IF_ANY; + } + } + + private URL performMultiStepConfigurationFileSearch(boolean updateStatus) { + ClassLoader myClassLoader = Loader.getClassLoaderOfObject(this); + URL url = findConfigFileURLFromSystemProperties(myClassLoader, updateStatus); + if (url != null) { + return url; + } + + url = getResource(ClassicConstants.TEST_AUTOCONFIG_FILE, myClassLoader, updateStatus); + if (url != null) { + return url; + } + + return getResource(ClassicConstants.AUTOCONFIG_FILE, myClassLoader, updateStatus); + } + public void configureByResource(URL url) throws JoranException { + if (url == null) { + throw new IllegalArgumentException("URL argument cannot be null"); + } + final String urlString = url.toString(); + if (urlString.endsWith("xml")) { + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(context); + configurator.doConfigure(url); + } else { + throw new LogbackException( + "Unexpected filename extension of file [" + url.toString() + "]. Should be .xml"); + } + } + + + /** + * Perform multi-search for configuration file + * @param updateStatus + * @return + * + * @deprecated with no replacement + */ + public URL findURLOfDefaultConfigurationFile(boolean updateStatus) { + return performMultiStepConfigurationFileSearch(updateStatus); + } + + private URL findConfigFileURLFromSystemProperties(ClassLoader classLoader, boolean updateStatus) { + String logbackConfigFile = OptionHelper.getSystemProperty(ClassicConstants.CONFIG_FILE_PROPERTY); + if (logbackConfigFile != null) { + URL result = null; + try { + result = new URL(logbackConfigFile); + return result; + } catch (MalformedURLException e) { + // so, resource is not a URL: + // attempt to get the resource from the class path + result = Loader.getResource(logbackConfigFile, classLoader); + if (result != null) { + return result; + } + // if the above fails, try to find as a file + File f = new File(logbackConfigFile); + if (f.exists() && f.isFile()) { + try { + result = f.toURI().toURL(); + return result; + } catch (MalformedURLException e1) { + } + } + } finally { + if (updateStatus) { + statusOnResourceSearch(logbackConfigFile, classLoader, result); + } + } + } + return null; + } + + private URL getResource(String filename, ClassLoader myClassLoader, boolean updateStatus) { + URL url = Loader.getResource(filename, myClassLoader); + if (updateStatus) { + statusOnResourceSearch(filename, myClassLoader, url); + } + return url; + } + + private void statusOnResourceSearch(String resourceName, ClassLoader classLoader, URL url) { + StatusManager sm = context.getStatusManager(); + if (url == null) { + sm.add(new InfoStatus("Could NOT find resource [" + resourceName + "]", context)); + } else { + sm.add(new InfoStatus("Found resource [" + resourceName + "] at [" + url.toString() + "]", context)); + multiplicityWarning(resourceName, classLoader); + } + } + + private void multiplicityWarning(String resourceName, ClassLoader classLoader) { + Set urlSet = null; + try { + urlSet = Loader.getResources(resourceName, classLoader); + } catch (IOException e) { + addError("Failed to get url list for resource [" + resourceName + "]", e); + } + if (urlSet != null && urlSet.size() > 1) { + addWarn("Resource [" + resourceName + "] occurs multiple times on the classpath."); + for (URL url : urlSet) { + addWarn("Resource [" + resourceName + "] occurs at [" + url.toString() + "]"); + } + } + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultNestedComponentRules.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultNestedComponentRules.java deleted file mode 100644 index ed8337b43c..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/DefaultNestedComponentRules.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.util; - -import ch.qos.logback.classic.PatternLayout; -import ch.qos.logback.classic.boolex.JaninoEventEvaluator; -import ch.qos.logback.classic.encoder.PatternLayoutEncoder; -import ch.qos.logback.core.AppenderBase; -import ch.qos.logback.core.UnsynchronizedAppenderBase; -import ch.qos.logback.core.filter.EvaluatorFilter; -import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; -import ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules; - -/** - * Contains mappings for the default type of nested components in - * logback-classic. - * - * @author Ceki Gulcu - * - */ -public class DefaultNestedComponentRules { - - static public void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { - registry.add(AppenderBase.class, "layout", PatternLayout.class); - registry.add(UnsynchronizedAppenderBase.class, "layout", PatternLayout.class); - - registry.add(AppenderBase.class, "encoder", PatternLayoutEncoder.class); - registry.add(UnsynchronizedAppenderBase.class, "encoder", PatternLayoutEncoder.class); - - registry.add(EvaluatorFilter.class, "evaluator", JaninoEventEvaluator.class); - - SSLNestedComponentRegistryRules.addDefaultNestedComponentRegistryRules(registry); - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/EnvUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/EnvUtil.java deleted file mode 100644 index 47219b7453..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/EnvUtil.java +++ /dev/null @@ -1,55 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.util; - -import java.util.Iterator; -import java.util.ServiceLoader; - -import ch.qos.logback.core.util.Loader; - -/** - * @author Ceki Gülcü - */ -public class EnvUtil { - - /* - * Used to replace the ClassLoader that the ServiceLoader uses for unit testing. We need this to mock the resources - * the ServiceLoader attempts to load from /META-INF/services thus keeping the projects src/test/resources clean - * (see src/test/resources/README.txt). - */ - static ClassLoader testServiceLoaderClassLoader = null; - - static public boolean isGroovyAvailable() { - ClassLoader classLoader = Loader.getClassLoaderOfClass(EnvUtil.class); - try { - Class bindingClass = classLoader.loadClass("groovy.lang.Binding"); - return (bindingClass != null); - } catch (ClassNotFoundException e) { - return false; - } - } - - private static ClassLoader getServiceLoaderClassLoader() { - return testServiceLoaderClassLoader == null ? Loader.getClassLoaderOfClass(EnvUtil.class) : testServiceLoaderClassLoader; - } - - public static T loadFromServiceLoader(Class c) { - ServiceLoader loader = ServiceLoader.load(c, getServiceLoaderClassLoader()); - Iterator it = loader.iterator(); - if (it.hasNext()) - return it.next(); - return null; - } - -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java deleted file mode 100644 index efa5ff169f..0000000000 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/JNDIUtil.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.util; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; - -/** - * A simple utility class to create and use a JNDI Context. - * - * @author Ceki Gülcü - * @author Sébastien Pennec - */ - -public class JNDIUtil { - - public static Context getInitialContext() throws NamingException { - return new InitialContext(); - } - - public static String lookup(Context ctx, String name) { - if (ctx == null) { - return null; - } - try { - Object lookup = ctx.lookup(name); - return lookup == null ? null : lookup.toString(); - } catch (NamingException e) { - return null; - } - } -} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelToSyslogSeverity.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelToSyslogSeverity.java index c5e6c960d1..f6e6a4d5b4 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelToSyslogSeverity.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelToSyslogSeverity.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ public class LevelToSyslogSeverity { /* - * Convert a level to equivalent syslog severity. Only levels for printing methods i.e TRACE, DEBUG, WARN, INFO and - * ERROR are converted. + * Convert a level to equivalent syslog severity. Only levels for printing + * methods i.e. TRACE, DEBUG, WARN, INFO and ERROR are converted. */ static public int convert(ILoggingEvent event) { diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelUtil.java new file mode 100644 index 0000000000..f86bd50fe5 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/LevelUtil.java @@ -0,0 +1,51 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.util; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.util.OptionHelper; + +import static ch.qos.logback.core.joran.JoranConstants.NULL; + +/** + * + * Utility methods for transforming string values to Level. + * + * @since 1.5.1 + */ +public class LevelUtil { + + + public static boolean isInheritedLevelString(String levelStr) { + if (JoranConstants.INHERITED.equalsIgnoreCase(levelStr) || NULL.equalsIgnoreCase(levelStr)) { + return true; + } else + return false; + } + + public static Level levelStringToLevel(String levelStr) { + if (!OptionHelper.isNullOrEmptyOrAllSpaces(levelStr)) { + if (isInheritedLevelString(levelStr)) { + return null; + } else { + Level level = Level.toLevel(levelStr); + return level; + } + } + return null; + } + +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapter.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapter.java index 34505960d0..c525cb0059 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapter.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,76 +13,40 @@ */ package ch.qos.logback.classic.util; +import org.slf4j.helpers.ThreadLocalMapOfStacks; +import org.slf4j.spi.MDCAdapter; + import java.util.Collections; +import java.util.Deque; import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.slf4j.spi.MDCAdapter; - /** * A Mapped Diagnostic Context, or MDC in short, is an instrument for * distinguishing interleaved log output from different sources. Log output is * typically interleaved when a server handles multiple clients * near-simultaneously. *

- * The MDC is managed on a per thread basis. A child thread - * automatically inherits a copy of the mapped diagnostic context of - * its parent. + * The MDC is managed on a per thread basis. Note that a child + * thread does not inherit the mapped diagnostic context of its parent. *

*

* For more information about MDC, please refer to the online manual at * http://logback.qos.ch/manual/mdc.html * * @author Ceki Gülcü + * @author Michael Franz */ -public class LogbackMDCAdapter implements MDCAdapter { - - // The internal map is copied so as - - // We wish to avoid unnecessarily copying of the map. To ensure - // efficient/timely copying, we have a variable keeping track of the last - // operation. A copy is necessary on 'put' or 'remove' but only if the last - // operation was a 'get'. Get operations never necessitate a copy nor - // successive 'put/remove' operations, only a get followed by a 'put/remove' - // requires copying the map. - // See http://jira.qos.ch/browse/LOGBACK-620 for the original discussion. - - // We no longer use CopyOnInheritThreadLocal in order to solve LBCLASSIC-183 - // Initially the contents of the thread local in parent and child threads - // reference the same map. However, as soon as a thread invokes the put() - // method, the maps diverge as they should. - final ThreadLocal> copyOnThreadLocal = new ThreadLocal>(); - - private static final int WRITE_OPERATION = 1; - private static final int MAP_COPY_OPERATION = 2; - - // keeps track of the last operation performed - final ThreadLocal lastOperation = new ThreadLocal(); - - private Integer getAndSetLastOperation(int op) { - Integer lastOp = lastOperation.get(); - lastOperation.set(op); - return lastOp; - } +public class LogbackMDCAdapter implements MDCAdapter { - private boolean wasLastOpReadOrNull(Integer lastOp) { - return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION; - } - private Map duplicateAndInsertNewMap(Map oldMap) { - Map newMap = Collections.synchronizedMap(new HashMap()); - if (oldMap != null) { - // we don't want the parent thread modifying oldMap while we are - // iterating over it - synchronized (oldMap) { - newMap.putAll(oldMap); - } - } + // BEWARE: Keys or values placed in a ThreadLocal should not be of a type/class + // not included in the JDK. See also https://jira.qos.ch/browse/LOGBACK-450 - copyOnThreadLocal.set(newMap); - return newMap; - } + final ThreadLocal> readWriteThreadLocalMap = new ThreadLocal>(); + final ThreadLocal> readOnlyThreadLocalMap = new ThreadLocal>(); + private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks(); /** * Put a context value (the val parameter) as identified with the @@ -92,6 +56,12 @@ private Map duplicateAndInsertNewMap(Map oldMap) *

* If the current thread does not have a context map it is created as a side * effect of this call. + *

+ *

+ * Each time a value is added, a new instance of the map is created. This is + * to be certain that the serialization process will operate on the updated + * map and not send a reference to the old map, thus not allowing the remote + * logback component to see the latest changes. * * @throws IllegalArgumentException in case the "key" parameter is null */ @@ -99,68 +69,94 @@ public void put(String key, String val) throws IllegalArgumentException { if (key == null) { throw new IllegalArgumentException("key cannot be null"); } + Map current = readWriteThreadLocalMap.get(); + if (current == null) { + current = new HashMap(); + readWriteThreadLocalMap.set(current); + } - Map oldMap = copyOnThreadLocal.get(); - Integer lastOp = getAndSetLastOperation(WRITE_OPERATION); + current.put(key, val); + nullifyReadOnlyThreadLocalMap(); + } - if (wasLastOpReadOrNull(lastOp) || oldMap == null) { - Map newMap = duplicateAndInsertNewMap(oldMap); - newMap.put(key, val); + /** + * Get the context identified by the key parameter. + *

+ *

+ * This method has no side effects. + */ + @Override + public String get(String key) { + Map hashMap = readWriteThreadLocalMap.get(); + + if ((hashMap != null) && (key != null)) { + return hashMap.get(key); } else { - oldMap.put(key, val); + return null; } } /** - * Remove the the context identified by the key parameter. + *

Remove the context identified by the key parameter. *

*/ + @Override public void remove(String key) { if (key == null) { return; } - Map oldMap = copyOnThreadLocal.get(); - if (oldMap == null) - return; - Integer lastOp = getAndSetLastOperation(WRITE_OPERATION); - - if (wasLastOpReadOrNull(lastOp)) { - Map newMap = duplicateAndInsertNewMap(oldMap); - newMap.remove(key); - } else { - oldMap.remove(key); + Map current = readWriteThreadLocalMap.get(); + if (current != null) { + current.remove(key); + nullifyReadOnlyThreadLocalMap(); } } + private void nullifyReadOnlyThreadLocalMap() { + readOnlyThreadLocalMap.set(null); + } + /** * Clear all entries in the MDC. */ + @Override public void clear() { - lastOperation.set(WRITE_OPERATION); - copyOnThreadLocal.remove(); + readWriteThreadLocalMap.set(null); + nullifyReadOnlyThreadLocalMap(); } /** - * Get the context identified by the key parameter. - *

+ *

Get the current thread's MDC as a map. This method is intended to be used + * internally.

+ * + * The returned map is unmodifiable (since version 1.3.2/1.4.2). */ - public String get(String key) { - final Map map = copyOnThreadLocal.get(); - if ((map != null) && (key != null)) { - return map.get(key); - } else { - return null; + @SuppressWarnings("unchecked") + public Map getPropertyMap() { + Map readOnlyMap = readOnlyThreadLocalMap.get(); + if (readOnlyMap == null) { + Map current = readWriteThreadLocalMap.get(); + if (current != null) { + final Map tempMap = new HashMap(current); + readOnlyMap = Collections.unmodifiableMap(tempMap); + readOnlyThreadLocalMap.set(readOnlyMap); + } } + return readOnlyMap; } /** - * Get the current thread's MDC as a map. This method is intended to be used - * internally. + * Return a copy of the current thread's context map. Returned value may be + * null. */ - public Map getPropertyMap() { - lastOperation.set(MAP_COPY_OPERATION); - return copyOnThreadLocal.get(); + public Map getCopyOfContextMap() { + Map readOnlyMap = getPropertyMap(); + if (readOnlyMap == null) { + return null; + } else { + return new HashMap(readOnlyMap); + } } /** @@ -168,35 +164,44 @@ public Map getPropertyMap() { * null. */ public Set getKeys() { - Map map = getPropertyMap(); + Map readOnlyMap = getPropertyMap(); - if (map != null) { - return map.keySet(); + if (readOnlyMap != null) { + return readOnlyMap.keySet(); } else { return null; } } - /** - * Return a copy of the current thread's context map. Returned value may be - * null. - */ - public Map getCopyOfContextMap() { - Map hashMap = copyOnThreadLocal.get(); - if (hashMap == null) { - return null; + @SuppressWarnings("unchecked") + public void setContextMap(Map contextMap) { + if (contextMap != null) { + readWriteThreadLocalMap.set(new HashMap(contextMap)); } else { - return new HashMap(hashMap); + readWriteThreadLocalMap.set(null); } + nullifyReadOnlyThreadLocalMap(); } - public void setContextMap(Map contextMap) { - lastOperation.set(WRITE_OPERATION); - Map newMap = Collections.synchronizedMap(new HashMap()); - newMap.putAll(contextMap); + @Override + public void pushByKey(String key, String value) { + threadLocalMapOfDeques.pushByKey(key, value); + } + + @Override + public String popByKey(String key) { + return threadLocalMapOfDeques.popByKey(key); + } - // the newMap replaces the old one for serialisation's sake - copyOnThreadLocal.set(newMap); + @Override + public Deque getCopyOfDequeByKey(String key) { + return threadLocalMapOfDeques.getCopyOfDequeByKey(key); } + + @Override + public void clearDequeByKey(String key) { + threadLocalMapOfDeques.clearDequeByKey(key); + } + } diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapterSimple.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapterSimple.java new file mode 100644 index 0000000000..dea9131e08 --- /dev/null +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/LogbackMDCAdapterSimple.java @@ -0,0 +1,211 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.util; + +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.slf4j.helpers.ThreadLocalMapOfStacks; +import org.slf4j.spi.MDCAdapter; + +/** + * A Mapped Diagnostic Context, or MDC in short, is an instrument for + * distinguishing interleaved log output from different sources. Log output is + * typically interleaved when a server handles multiple clients + * near-simultaneously. + *

+ * The MDC is managed on a per thread basis. Note that a child + * thread does not inherit the mapped diagnostic context of its parent. + *

+ *

+ * For more information about MDC, please refer to the online manual at + * http://logback.qos.ch/manual/mdc.html + * + * @author Ceki Gülcü + */ +public class LogbackMDCAdapterSimple implements MDCAdapter { + + + // We wish to avoid unnecessarily copying of the map. To ensure + // efficient/timely copying, we have a variable keeping track of the previous + // operation. A copy is necessary on 'put' or 'remove' but only if the last + // operation was a 'get'. Get operations never necessitate a copy nor + // successive 'put/remove' operations, only a get followed by a 'put/remove' + // requires copying the map. + // See http://jira.qos.ch/browse/LOGBACK-620 for the original discussion. + + // We no longer use CopyOnInheritThreadLocal in order to solve LBCLASSIC-183 + // Initially the contents of the thread local in parent and child threads + // reference the same map. However, as soon as a thread invokes the put() + // method, the maps diverge as they should. +// final ThreadLocal> modifiableMap = new ThreadLocal>(); + + final ThreadLocal> threadLocalUnmodifiableMap = new ThreadLocal>(); + +// private static final int WRITE_OPERATION = 1; +// private static final int MAP_COPY_OPERATION = 2; + + // keeps track of the previous operation performed +// final ThreadLocal previousOperation = new ThreadLocal(); + + private final ThreadLocalMapOfStacks threadLocalMapOfDeques = new ThreadLocalMapOfStacks(); + +// private Integer getAndSetPreviousOperation(int op) { +// Integer penultimateOp = previousOperation.get(); +// previousOperation.set(op); +// return penultimateOp; +// } + +// private boolean wasPreviousOpReadOrNull(Integer lastOp) { +// return lastOp == null || lastOp.intValue() == MAP_COPY_OPERATION; +// } + +// private Map duplicateAndInsertNewMap(Map oldMap) { +// Map newMap = duplicateOldMap(oldMap); +// modifiableMap.set(newMap); +// return newMap; +// } + + private Map duplicateMap(Map oldMap) { + if(oldMap != null) + return new HashMap<>(oldMap); + else + return new HashMap<>(); + } + + /** + * Put a context value (the val parameter) as identified with the + * key parameter into the current thread's context map. Note that + * contrary to log4j, the val parameter can be null. + *

+ *

+ * If the current thread does not have a context map it is created as a side + * effect of this call. + * + * @throws IllegalArgumentException in case the "key" parameter is null + */ + public void put(String key, String val) throws IllegalArgumentException { + if (key == null) { + throw new IllegalArgumentException("key cannot be null"); + } + + Map oldMap = threadLocalUnmodifiableMap.get(); + Map newMap = duplicateMap(oldMap); + newMap.put(key, val); + makeUnmodifiableAndThreadLocalSet(newMap); + } + + private void makeUnmodifiableAndThreadLocalSet(Map aMap) { + Map unmodifiable = Collections.unmodifiableMap(aMap); + threadLocalUnmodifiableMap.set(unmodifiable); + } + + /** + * Remove the context identified by the key parameter. + *

+ */ + public void remove(String key) { + if (key == null) { + return; + } + Map oldMap = threadLocalUnmodifiableMap.get(); + if (oldMap == null) + return; + + Map newMap = duplicateMap(oldMap); + newMap.remove(key); + makeUnmodifiableAndThreadLocalSet(newMap); + } + + /** + * Clear all entries in the MDC. + */ + public void clear() { + threadLocalUnmodifiableMap.remove(); + } + + /** + * Get the context identified by the key parameter. + *

+ */ + public String get(String key) { + final Map map = threadLocalUnmodifiableMap.get(); + if ((map != null) && (key != null)) { + return map.get(key); + } else { + return null; + } + } + + /** + * Get the current thread's MDC as a map. This method is intended to be used + * internally. + */ + public Map getPropertyMap() { + return threadLocalUnmodifiableMap.get(); + } + + /** + * Returns the keys in the MDC as a {@link Set}. The returned value can be null. + */ + public Set getKeys() { + Map map = getPropertyMap(); + + if (map != null) { + return map.keySet(); + } else { + return null; + } + } + + /** + * Return a copy of the current thread's context map. Returned value may be + * null. + */ + public Map getCopyOfContextMap() { + Map hashMap = threadLocalUnmodifiableMap.get(); + return duplicateMap(hashMap); + } + + /** + * Set the MDC map to the map passed as parameter. + * + * @param contextMap the new map + */ + public void setContextMap(Map contextMap) { + duplicateMap(contextMap); + } + + @Override + public void pushByKey(String key, String value) { + threadLocalMapOfDeques.pushByKey(key, value); + } + + @Override + public String popByKey(String key) { + return threadLocalMapOfDeques.popByKey(key); + } + + @Override + public Deque getCopyOfDequeByKey(String key) { + return threadLocalMapOfDeques.getCopyOfDequeByKey(key); + } + @Override + public void clearDequeByKey(String key) { + threadLocalMapOfDeques.clearDequeByKey(key); + } +} diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java index 6fb40ebb05..9740026723 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/LoggerNameUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/StatusViaSLF4JLoggerFactory.java b/logback-classic/src/main/java/ch/qos/logback/classic/util/StatusViaSLF4JLoggerFactory.java index 95ac27133d..f4732fcf10 100755 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/StatusViaSLF4JLoggerFactory.java +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/StatusViaSLF4JLoggerFactory.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.util; import org.slf4j.ILoggerFactory; @@ -10,7 +24,9 @@ import ch.qos.logback.core.status.Status; /** - * Add a status message to the {@link LoggerContext} returned by {@link LoggerFactory#getILoggerFactory}. + * Add a status message to the {@link LoggerContext} returned by + * {@link LoggerFactory#getILoggerFactory}. + * * @author ceki * @since 1.1.10 */ diff --git a/logback-classic/src/main/java/ch/qos/logback/classic/util/package.html b/logback-classic/src/main/java/ch/qos/logback/classic/util/package.html index ac31665981..a390243c3e 100644 --- a/logback-classic/src/main/java/ch/qos/logback/classic/util/package.html +++ b/logback-classic/src/main/java/ch/qos/logback/classic/util/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/main/java/module-info.java b/logback-classic/src/main/java/module-info.java old mode 100755 new mode 100644 index 311300abab..c0f962469e --- a/logback-classic/src/main/java/module-info.java +++ b/logback-classic/src/main/java/module-info.java @@ -1,27 +1,39 @@ -module ch.qos.logback.classic { - requires org.slf4j; +import ch.qos.logback.classic.spi.Configurator; + +module ch.qos.logback.classic { + // requires static means optional requires static java.management; - requires static javax.servlet.api; + + // used by the optional ContextJNDISelector component + requires static java.naming; + + // used by the optional LevelChangePropagator component + requires static java.logging; + + // used by the optional ContextJNDISelector, MDCInsertingServletFilter among other components + requires static jakarta.servlet; + + requires static jakarta.mail; + + requires org.slf4j; + requires ch.qos.logback.core; - uses ch.qos.logback.classic.spi.Configurator; provides org.slf4j.spi.SLF4JServiceProvider with ch.qos.logback.classic.spi.LogbackServiceProvider; - - + uses ch.qos.logback.classic.spi.Configurator; + exports ch.qos.logback.classic; exports ch.qos.logback.classic.boolex; - exports ch.qos.logback.classic.db; - exports ch.qos.logback.classic.db.names; exports ch.qos.logback.classic.encoder; exports ch.qos.logback.classic.filter; - //exports ch.qos.logback.classic.gaffer; exports ch.qos.logback.classic.helpers; exports ch.qos.logback.classic.html; - exports ch.qos.logback.classic.jmx; exports ch.qos.logback.classic.joran; exports ch.qos.logback.classic.joran.action; exports ch.qos.logback.classic.jul; exports ch.qos.logback.classic.layout; exports ch.qos.logback.classic.log4j; + exports ch.qos.logback.classic.model; + exports ch.qos.logback.classic.model.processor; exports ch.qos.logback.classic.net; exports ch.qos.logback.classic.net.server; exports ch.qos.logback.classic.pattern; @@ -32,6 +44,7 @@ exports ch.qos.logback.classic.sift; exports ch.qos.logback.classic.spi; exports ch.qos.logback.classic.turbo; + exports ch.qos.logback.classic.tyler; exports ch.qos.logback.classic.util; } diff --git a/logback-classic/src/main/java/org/slf4j/package.html b/logback-classic/src/main/java/org/slf4j/package.html deleted file mode 100644 index 86c8624682..0000000000 --- a/logback-classic/src/main/java/org/slf4j/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - -

Contains SLF4J implementations binding logback classes to the SLF4J framework. The - complete javadoc for SLF4J can be found on the SLF4J website.

- - - \ No newline at end of file diff --git a/logback-classic/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer b/logback-classic/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer new file mode 100755 index 0000000000..bc45c2ca2b --- /dev/null +++ b/logback-classic/src/main/resources/META-INF/services/jakarta.servlet.ServletContainerInitializer @@ -0,0 +1 @@ +ch.qos.logback.classic.servlet.LogbackServletContainerInitializer diff --git a/logback-classic/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/logback-classic/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer deleted file mode 100755 index 23269e7834..0000000000 --- a/logback-classic/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer +++ /dev/null @@ -1 +0,0 @@ -ch.qos.logback.classic.servlet.LogbackServletContainerInitializer \ No newline at end of file diff --git a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegateTest.groovy b/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegateTest.groovy deleted file mode 100644 index b1c539c73d..0000000000 --- a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/ConfigurationDelegateTest.groovy +++ /dev/null @@ -1,317 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import ch.qos.logback.classic.LoggerContext -import org.junit.Before -import org.junit.Test - -import javax.management.InstanceNotFoundException -import javax.management.ObjectName -import java.lang.management.ManagementFactory - -import static org.junit.Assert.* - -import ch.qos.logback.classic.turbo.TurboFilter -import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter -import ch.qos.logback.classic.Level -import ch.qos.logback.core.testUtil.CoreTestConstants -import ch.qos.logback.core.testUtil.RandomUtil -import ch.qos.logback.core.testUtil.StatusChecker -import ch.qos.logback.classic.Logger -import ch.qos.logback.core.Appender -import ch.qos.logback.core.helpers.NOPAppender -import ch.qos.logback.core.ConsoleAppender -import ch.qos.logback.core.encoder.LayoutWrappingEncoder -import ch.qos.logback.classic.PatternLayout -import ch.qos.logback.core.util.FileSize -import ch.qos.logback.core.util.StatusPrinter -import ch.qos.logback.classic.net.SMTPAppender -import ch.qos.logback.core.rolling.RollingFileAppender -import ch.qos.logback.core.rolling.TimeBasedRollingPolicy -import ch.qos.logback.classic.encoder.PatternLayoutEncoder -import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP -import ch.qos.logback.core.joran.action.TimestampAction - -/** - * @author Ceki Gücü - */ -class ConfigurationDelegateTest { - - LoggerContext context = new LoggerContext() - ConfigurationDelegate configurationDelegate = new ConfigurationDelegate(); - StatusChecker statusChecker = new StatusChecker(context) - int diff = RandomUtil.getPositiveInt(); - - String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; - - @Before - void setUp() { - context.name = "ConfigurationDelegateTest" - configurationDelegate.context = context; - } - - @Test - void contextAwareMixin() { - configurationDelegate.addInfo("smoke") - assertTrue(statusChecker.containsMatch("smoke")) - } - - @Test - void scan() { - configurationDelegate.scan("10seconds") - assertTrue(statusChecker.containsMatch("Setting ReconfigureOnChangeTask")) - } - - @Test - void timestamp() { - String result = configurationDelegate.timestamp("yyyy") - long year = Calendar.getInstance().get(Calendar.YEAR); - assertEquals(year.toString(), result) - } - - @Test - void timestampWithContextBirthAsReference() { - String result = configurationDelegate.timestamp("yyyy", context.birthTime) - long year = Calendar.getInstance().get(Calendar.YEAR); - assertEquals(year.toString(), result) - } - - - @Test - void loggerWithoutName() { - configurationDelegate.logger("", Level.DEBUG) - assertTrue(statusChecker.containsMatch("No name attribute for logger")) - } - - @Test - void loggerSetLevel() { - configurationDelegate.logger("setLevel" + diff, Level.INFO) - Logger smokeLogger = context.getLogger("setLevel" + diff); - assertEquals(Level.INFO, smokeLogger.level) - } - - - @Test - void loggerAppenderRef() { - Appender fooAppender = new NOPAppender(); - fooAppender.name = "FOO" - configurationDelegate.appenderList = [fooAppender] - configurationDelegate.logger("test" + diff, Level.INFO, ["FOO"]) - Logger logger = context.getLogger("test" + diff); - assertEquals(Level.INFO, logger.level) - assertEquals(fooAppender, logger.getAppender("FOO")) - } - - @Test - void loggerAdditivity() { - Appender fooAppender = new NOPAppender(); - fooAppender.name = "FOO" - configurationDelegate.appenderList = [fooAppender] - configurationDelegate.logger("test" + diff, Level.INFO, ["FOO"], false) - Logger logger = context.getLogger("test" + diff); - assertEquals(Level.INFO, logger.level) - assertEquals(fooAppender, logger.getAppender("FOO")) - assertEquals(false, logger.additive) - } - - @Test - void loggerAdditivittWithEmptyList() { - configurationDelegate.logger("test" + diff, Level.INFO, [], false) - Logger logger = context.getLogger("test" + diff); - assertEquals(Level.INFO, logger.level) - assertEquals(null, logger.getAppender("FOO")) - assertEquals(false, logger.additive) - } - - @Test - void root_LEVEL() { - configurationDelegate.root(Level.ERROR) - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); - assertEquals(Level.ERROR, root.level) - assertEquals(null, root.getAppender("FOO")) - } - - @Test - void root_WithList() { - Appender fooAppender = new NOPAppender(); - fooAppender.name = "FOO" - configurationDelegate.appenderList = [fooAppender] - configurationDelegate.root(Level.WARN, ["FOO"]) - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); - assertEquals(Level.WARN, root.level) - assertEquals(fooAppender, root.getAppender("FOO")) - } - - @Test - void appender0() { - configurationDelegate.appender("A", NOPAppender); - Appender back = configurationDelegate.appenderList.find {it.name = "A"} - assertNotNull(back) - assertEquals("A", back.name) - } - - @Test - void appender1() { - configurationDelegate.appender("C", ConsoleAppender) { - target = "System.err" - } - Appender back = configurationDelegate.appenderList.find {it.name = "C"} - assertNotNull(back) - assertEquals("C", back.name) - assertEquals("System.err", back.target) - } - - - @Test - void appenderWithEncoder() { - configurationDelegate.appender("C", ConsoleAppender) { - encoder(LayoutWrappingEncoder) { - layout(PatternLayout) { - pattern = "%m%n" - } - } - } - Appender back = configurationDelegate.appenderList.find {it.name = "C"} - assertNotNull(back) - assertEquals("C", back.name) - ConsoleAppender ca = back - assertNotNull(ca.encoder) - assertNotNull(ca.encoder.layout) - PatternLayout layout = ca.encoder.layout - assertEquals("%m%n", layout.pattern) - - } - - @Test - void appenderSMTP() { - configurationDelegate.appender("SMTP", SMTPAppender) { - to = "a" - to = "b" - layout(PatternLayout) { - pattern = "%m%n" - } - } - //StatusPrinter.print context - Appender back = configurationDelegate.appenderList.find {it.name = "SMTP"} - assertNotNull(back) - assertEquals("SMTP", back.name) - SMTPAppender sa = back - PatternLayout layout = sa.layout - assertEquals("%m%n", layout.pattern) - - assertEquals(["a%nopex", "b%nopex"], sa.getToAsListOfString().sort()); - } - - // test parent injection - - @Test - void appenderRolling() { - - String logFile = randomOutputDir + "log.txt"; - - configurationDelegate.appender("ROLLING", RollingFileAppender) { - file = logFile - rollingPolicy(TimeBasedRollingPolicy) { - fileNamePattern = randomOutputDir + "log.%d{yyyy-MM}.log.zip" - } - encoder(PatternLayoutEncoder) { - pattern = '%msg%n' - } - } - // StatusPrinter.print context - RollingFileAppender back = configurationDelegate.appenderList.find {it.name = "ROLLING"} - assertNotNull(back) - assertEquals(logFile, back.rollingPolicy.getParentsRawFileProperty()) - } - - - // See LOGBACK-458 - @Test - void withSizeAndTimeBasedFNATP() { - withSizeAndTimeBasedFNATP(false); - } - - // See LOGBACK-1232 - @Test - void withSizeAndTimeBasedFNATP_AsString() { - withSizeAndTimeBasedFNATP(true); - } - - void withSizeAndTimeBasedFNATP(boolean asString) { - String logFile = randomOutputDir + "log.txt"; - configurationDelegate.appender("ROLLING", RollingFileAppender) { - file = logFile - rollingPolicy(TimeBasedRollingPolicy) { - fileNamePattern = "mylog-%d{yyyy-MM-dd}.%i.txt" - timeBasedFileNamingAndTriggeringPolicy(SizeAndTimeBasedFNATP) { - if(asString) - maxFileSize = "100MB" - else - maxFileSize = FileSize.valueOf("100MB") - } - } - encoder(PatternLayoutEncoder) { - pattern = "%msg%n" - } - } - RollingFileAppender back = configurationDelegate.appenderList.find {it.name = "ROLLING"} - StatusPrinter.print(context) - assertNotNull(back) - assertEquals(logFile, back.rollingPolicy.getParentsRawFileProperty()) - assertTrue(back.rollingPolicy.timeBasedFileNamingAndTriggeringPolicy.isStarted()) - } - - @Test - void jmxConfiguratorWithDefaults() { - ObjectName name = new ObjectName( - "ch.qos.logback.classic:Name=ConfigurationDelegateTest,Type=ch.qos.logback.classic.jmx.JMXConfigurator") - try { - ManagementFactory.platformMBeanServer.getObjectInstance(name) - fail("Should not have found JMXConfigurator MBean") - } catch (InstanceNotFoundException expected) { - } - configurationDelegate.jmxConfigurator() - def mbean = ManagementFactory.platformMBeanServer.getObjectInstance(name) - assertNotNull(mbean) - } - - @Test - void jmxConfiguratorWithNonDefaultContextName() { - ObjectName name = new ObjectName( - "ch.qos.logback.classic:Name=CustomName,Type=ch.qos.logback.classic.jmx.JMXConfigurator") - try { - ManagementFactory.platformMBeanServer.getObjectInstance(name) - fail("Should not have found JMXConfigurator MBean") - } catch (InstanceNotFoundException expected) { - } - configurationDelegate.jmxConfigurator("CustomName") - def mbean = ManagementFactory.platformMBeanServer.getObjectInstance(name) - assertNotNull(mbean) - } - - @Test - void jmxConfiguratorWithNonDefaultObjectName() { - ObjectName name = new ObjectName("customDomain:Name=JMX") - try { - ManagementFactory.platformMBeanServer.getObjectInstance(name) - fail("Should not have found JMXConfigurator MBean") - } catch (InstanceNotFoundException expected) { - } - configurationDelegate.jmxConfigurator("customDomain:Name=JMX") - def mbean = ManagementFactory.platformMBeanServer.getObjectInstance(name) - assertNotNull(mbean) - } - -} diff --git a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/GafferConfiguratorTest.groovy b/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/GafferConfiguratorTest.groovy deleted file mode 100644 index 74023d6ac2..0000000000 --- a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/GafferConfiguratorTest.groovy +++ /dev/null @@ -1,194 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import ch.qos.logback.classic.ClassicTestConstants -import ch.qos.logback.classic.LoggerContext -import org.junit.Before -import ch.qos.logback.core.testUtil.RandomUtil -import org.junit.Ignore -import org.junit.Test -import ch.qos.logback.classic.Logger -import ch.qos.logback.classic.Level -import static junit.framework.Assert.assertNotNull -import static junit.framework.Assert.assertEquals -import ch.qos.logback.core.ConsoleAppender -import ch.qos.logback.classic.AsyncAppender -import ch.qos.logback.classic.PatternLayout -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.testUtil.StringListAppender -import ch.qos.logback.classic.testUtil.SampleConverter -import ch.qos.logback.core.util.StatusPrinter - -import ch.qos.logback.classic.boolex.JaninoEventEvaluator -import ch.qos.logback.core.filter.EvaluatorFilter -import ch.qos.logback.core.boolex.Matcher -import static org.junit.Assert.assertTrue - -/** - * @author Ceki Gücü - */ -class GafferConfiguratorTest { - - LoggerContext context = new LoggerContext(); - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME) - Logger logger = context.getLogger(this.getClass()) - int diff = RandomUtil.getPositiveInt(); - GafferConfigurator configurator = new GafferConfigurator(context); - final shouldFail = new GroovyTestCase().&shouldFail - - @Before - void setUp() { - - } - - @Test - void smoke() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "smoke.groovy") - String dslText = file.text - configurator.run dslText - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); - assertEquals(Level.WARN, root.level) - assertNotNull(root.getAppender("C")) - ConsoleAppender ca = root.getAppender("C") - assertNotNull(ca.encoder) - assertNotNull(ca.encoder.layout) - PatternLayout layout = ca.encoder.layout - assertEquals("%m%n", layout.pattern) - } - - @Test - void onTheFly() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "onTheFly.groovy") - String dslText = file.text - configurator.run dslText - } - - @Test - void contextName() { - String dslText = "context.name = 'a'" - configurator.run dslText - assertEquals("a", context.name) - } - - @Test - void contextProperty() { - String dslText = "context.putProperty('x', 'a')" - configurator.run dslText - assertEquals("a", context.getProperty("x")) - } - - @Test - void conversionRule() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "conversionRule.groovy") - String dslText = file.text - configurator.run dslText - - StringListAppender sla = (StringListAppender) root.getAppender("LIST"); - assertNotNull(sla); - assertEquals(0, sla.strList.size()); - - assertEquals(Level.DEBUG, root.level); - - String msg = "Simon says"; - logger.debug(msg); - StatusPrinter.print context - assertEquals(1, sla.strList.size()); - assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0)); - } - - @Test - void evaluatorWithMatcher() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "evaluatorWithMatcher.groovy") - String dslText = file.text - configurator.run dslText - - ConsoleAppender ca = (ConsoleAppender) root.getAppender("STDOUT"); - assertTrue ca.isStarted() - - EvaluatorFilter ef = ca.getCopyOfAttachedFiltersList()[0]; - assertTrue ef.isStarted() - - JaninoEventEvaluator jee = ef.evaluator - assertTrue jee.isStarted() - Matcher m = jee.matcherList[0] - assertTrue m.isStarted() - } - - @Test - void propertyCascading0() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "propertyCascading0.groovy") - String dslText = file.text - configurator.run dslText - - ConsoleAppender ca = (ConsoleAppender) root.getAppender("STDOUT"); - assertTrue ca.isStarted() - - assertEquals("HELLO %m%n", ca.encoder.layout.pattern) - } - - @Test - void propertyCascading1() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "propertyCascading1.groovy") - String dslText = file.text - configurator.run dslText - - ConsoleAppender ca = (ConsoleAppender) root.getAppender("STDOUT"); - assertTrue ca.isStarted() - assertEquals("HELLO %m%n", ca.encoder.getLayout().pattern) - } - - @Test - void propertyCascading2() { - context.putProperty("p", "HELLO"); - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "propertyCascading2.groovy") - String dslText = file.text - configurator.run dslText - - ConsoleAppender ca = (ConsoleAppender) root.getAppender("STDOUT"); - assertTrue ca.isStarted() - assertEquals("HELLO %m%n", ca.encoder.getLayout().pattern) - } - - - @Test - @Ignore - void receiver() { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "propertyCascading2.groovy") - String dslText = file.text - configurator.run dslText - } - - @Test - void appenderRefShouldWork() { - context.putProperty("p", "HELLO"); - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "asyncAppender.groovy") - configurator.run file.text - - def aa = (AsyncAppender) root.getAppender('STDOUT-ASYNC'); - assertTrue aa.isStarted() - def stdout = (ConsoleAppender) aa.getAppender('STDOUT') - assertNotNull stdout - } - - @Test - void appenderRefWithNonAppenderAttachable() { - context.putProperty("p", "HELLO"); - String message = shouldFail(IllegalArgumentException) { - File file = new File(ClassicTestConstants.GAFFER_INPUT_PREFIX + "appenderRefWithNonAppenderAttachable.groovy") - configurator.run file.text - } - assertEquals message, "ch.qos.logback.core.ConsoleAppender does not implement ch.qos.logback.core.spi.AppenderAttachable." - } -} diff --git a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/PropertyUtilTest.groovy b/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/PropertyUtilTest.groovy deleted file mode 100644 index 17a7d54732..0000000000 --- a/logback-classic/src/test/groovy/ch/qos/logback/classic/gaffer/PropertyUtilTest.groovy +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.gaffer - -import org.junit.Test -import static junit.framework.Assert.assertEquals - -/** - * @author Ceki Gücü - */ -class PropertyUtilTest { - - - @Test - void empty() { - assertEquals("", PropertyUtil.upperCaseFirstLetter("")); - assertEquals(null, PropertyUtil.upperCaseFirstLetter(null)); - } - - - - @Test - void smoke() { - assertEquals("Hello", PropertyUtil.upperCaseFirstLetter("hello")); - } - - -} diff --git a/logback-classic/src/test/groovy/issues/logback811/LineNumTest.groovy b/logback-classic/src/test/groovy/issues/logback811/LineNumTest.groovy deleted file mode 100644 index 93cda53dae..0000000000 --- a/logback-classic/src/test/groovy/issues/logback811/LineNumTest.groovy +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package issues.logback811 - -import ch.qos.logback.core.util.StatusPrinter -import org.junit.Test -import org.slf4j.Logger -import org.slf4j.LoggerFactory - -class LineNumTest { - - // move logback.groovy to src/test/resources and runManually() - - @Test - void runMannually() { - Logger logger = LoggerFactory.getLogger(this.class) - logger.debug("hello from logger on line 28") - } -} diff --git a/logback-classic/src/test/groovy/issues/logback811/logback._groovy b/logback-classic/src/test/groovy/issues/logback811/logback._groovy deleted file mode 100644 index 716212633d..0000000000 --- a/logback-classic/src/test/groovy/issues/logback811/logback._groovy +++ /dev/null @@ -1,12 +0,0 @@ - -import ch.qos.logback.classic.encoder.PatternLayoutEncoder -import ch.qos.logback.core.ConsoleAppender -import static ch.qos.logback.classic.Level.* - -appender("STDOUT", ConsoleAppender) { - encoder(PatternLayoutEncoder) { - pattern = "%date [%-5level] [%4.4line] - %msg%n" - } -} - -root(DEBUG, ["STDOUT"]) \ No newline at end of file diff --git a/logback-classic/src/test/input/fqcn.txt b/logback-classic/src/test/input/fqcn.txt new file mode 100644 index 0000000000..6179b87b6a --- /dev/null +++ b/logback-classic/src/test/input/fqcn.txt @@ -0,0 +1,7300 @@ +ch.qos.logback.access.AccessConstants +ch.qos.logback.access.boolex.JaninoEventEvaluator +ch.qos.logback.access.db.DBAppender +ch.qos.logback.access.filter.CountingFilter +ch.qos.logback.access.filter.PeriodicStats +ch.qos.logback.access.filter.StatisticalView +ch.qos.logback.access.filter.StatisticalViewImpl +ch.qos.logback.access.filter.StatsByDay +ch.qos.logback.access.filter.StatsByHour +ch.qos.logback.access.filter.StatsByMinute +ch.qos.logback.access.filter.StatsByMonth +ch.qos.logback.access.filter.StatsByWeek +ch.qos.logback.access.html.DefaultCssBuilder +ch.qos.logback.access.html.HTMLLayout +ch.qos.logback.access.html.UrlCssBuilder +ch.qos.logback.access.jetty.JettyServerAdapter +ch.qos.logback.access.jetty.RequestLogImpl +ch.qos.logback.access.jetty.RequestLogRegistry +ch.qos.logback.access.joran.action.AccessEvaluatorAction +ch.qos.logback.access.joran.action.ConfigurationAction +ch.qos.logback.access.joran.JoranConfigurator +ch.qos.logback.access.model.ConfigurationModel +ch.qos.logback.access.model.processor.ConfigurationModelHandler +ch.qos.logback.access.net.AccessEventPreSerializationTransformer +ch.qos.logback.access.net.HardenedAccessEventInputStream +ch.qos.logback.access.net.server.ServerSocketAppender +ch.qos.logback.access.net.server.SSLServerSocketAppender +ch.qos.logback.access.net.SimpleSocketServer +ch.qos.logback.access.net.SMTPAppender +ch.qos.logback.access.net.SocketAppender +ch.qos.logback.access.net.SocketNode +ch.qos.logback.access.net.SSLSocketAppender +ch.qos.logback.access.net.URLEvaluator +ch.qos.logback.access.pattern.AccessConverter +ch.qos.logback.access.pattern.ContentLengthConverter +ch.qos.logback.access.pattern.DateConverter +ch.qos.logback.access.pattern.ElapsedSecondsConverter +ch.qos.logback.access.pattern.ElapsedTimeConverter +ch.qos.logback.access.pattern.EnsureLineSeparation +ch.qos.logback.access.pattern.FullRequestConverter +ch.qos.logback.access.pattern.FullResponseConverter +ch.qos.logback.access.pattern.LineSeparatorConverter +ch.qos.logback.access.pattern.LocalIPAddressConverter +ch.qos.logback.access.pattern.LocalPortConverter +ch.qos.logback.access.pattern.NAConverter +ch.qos.logback.access.pattern.QueryStringConverter +ch.qos.logback.access.pattern.RemoteHostConverter +ch.qos.logback.access.pattern.RemoteIPAddressConverter +ch.qos.logback.access.pattern.RemoteUserConverter +ch.qos.logback.access.pattern.RequestAttributeConverter +ch.qos.logback.access.pattern.RequestContentConverter +ch.qos.logback.access.pattern.RequestCookieConverter +ch.qos.logback.access.pattern.RequestHeaderConverter +ch.qos.logback.access.pattern.RequestMethodConverter +ch.qos.logback.access.pattern.RequestParameterConverter +ch.qos.logback.access.pattern.RequestProtocolConverter +ch.qos.logback.access.pattern.RequestURIConverter +ch.qos.logback.access.pattern.RequestURLConverter +ch.qos.logback.access.pattern.ResponseContentConverter +ch.qos.logback.access.pattern.ResponseHeaderConverter +ch.qos.logback.access.pattern.ServerNameConverter +ch.qos.logback.access.pattern.SessionIDConverter +ch.qos.logback.access.pattern.StatusCodeConverter +ch.qos.logback.access.pattern.ThreadNameConverter +ch.qos.logback.access.PatternLayout +ch.qos.logback.access.PatternLayoutEncoder +ch.qos.logback.access.servlet.TeeFilter +ch.qos.logback.access.servlet.TeeHttpServletRequest +ch.qos.logback.access.servlet.TeeHttpServletResponse +ch.qos.logback.access.servlet.TeeServletInputStream +ch.qos.logback.access.servlet.TeeServletOutputStream +ch.qos.logback.access.servlet.Util +ch.qos.logback.access.spi.AccessContext +ch.qos.logback.access.spi.AccessEvent +ch.qos.logback.access.spi.IAccessEvent +ch.qos.logback.access.spi.ServerAdapter +ch.qos.logback.access.spi.Util +ch.qos.logback.access.tomcat.LogbackValve +ch.qos.logback.access.tomcat.TomcatServerAdapter +ch.qos.logback.access.ViewStatusMessagesServlet +ch.qos.logback.access.AccessTestConstants +ch.qos.logback.access.boolex.JaninoEventEvaluatorTest +ch.qos.logback.access.db.DBAppenderHSQLTest +ch.qos.logback.access.db.DBAppenderHSQLTestFixture +ch.qos.logback.access.db.DBAppenderIntegrationTest +ch.qos.logback.access.dummy.DummyAccessEventBuilder +ch.qos.logback.access.dummy.DummyRequest +ch.qos.logback.access.dummy.DummyResponse +ch.qos.logback.access.dummy.DummyServerAdapter +ch.qos.logback.access.dummy.DummyServletOutputStream +ch.qos.logback.access.filter.StatsByDayTest +ch.qos.logback.access.jetty.JettyBasicTest +ch.qos.logback.access.jetty.JettyFixtureBase +ch.qos.logback.access.jetty.JettyFixtureWithListAndConsoleAppenders +ch.qos.logback.access.joran.ConditionalTest +ch.qos.logback.access.joran.JoranConfiguratorTest +ch.qos.logback.access.net.NOPOutputStream +ch.qos.logback.access.net.SerializationPerfTest +ch.qos.logback.access.net.URLEvaluatorTest +ch.qos.logback.access.pattern.ConverterTest +ch.qos.logback.access.servlet.TeeFilterTest +ch.qos.logback.access.servlet.TeeHttpServletResponseTest +ch.qos.logback.access.spi.AccessEventSerializationTest +ch.qos.logback.access.spi.AccessEventTest +ch.qos.logback.access.testUtil.NotifyingListAppender +ch.qos.logback.access.tomcat.LogbackValveTest +ch.qos.logback.classic.LoggerCreation +ch.qos.logback.classic.LoggerEventCreationTest +ch.qos.logback.classic.pattern.ClassNameAbbreviatorSpeed +ch.qos.logback.classic.pattern.WriteSpeed +ch.qos.logback.classic.RetreivalOfExistingLoggerSpeed +ch.qos.logback.classic.SpeedOfDisabledDebug +ch.qos.logback.reflect.Fruit +ch.qos.logback.reflect.JaninoTest +ch.qos.logback.reflect.JEXLTest +ch.qos.logback.reflect.ReflectionSpeed +ch.qos.logback.classic.AsyncAppender +ch.qos.logback.classic.BasicConfigurator +ch.qos.logback.classic.boolex.IEvaluator +ch.qos.logback.classic.boolex.JaninoEventEvaluator +ch.qos.logback.classic.boolex.OnErrorEvaluator +ch.qos.logback.classic.boolex.OnMarkerEvaluator +ch.qos.logback.classic.ClassicConstants +ch.qos.logback.classic.db.DBAppender +ch.qos.logback.classic.db.DBHelper +ch.qos.logback.classic.db.names.ColumnName +ch.qos.logback.classic.db.names.DBNameResolver +ch.qos.logback.classic.db.names.DefaultDBNameResolver +ch.qos.logback.classic.db.names.SimpleDBNameResolver +ch.qos.logback.classic.db.names.TableName +ch.qos.logback.classic.db.SQLBuilder +ch.qos.logback.classic.encoder.PatternLayoutEncoder +ch.qos.logback.classic.filter.LevelFilter +ch.qos.logback.classic.filter.ThresholdFilter +ch.qos.logback.classic.helpers.MDCInsertingServletFilter +ch.qos.logback.classic.html.DefaultCssBuilder +ch.qos.logback.classic.html.DefaultThrowableRenderer +ch.qos.logback.classic.html.HTMLLayout +ch.qos.logback.classic.html.UrlCssBuilder +ch.qos.logback.classic.jmx.JMXConfigurator +ch.qos.logback.classic.jmx.JMXConfiguratorMBean +ch.qos.logback.classic.jmx.MBeanUtil +ch.qos.logback.classic.joran.action.ClassicEvaluatorAction +ch.qos.logback.classic.joran.action.ConfigurationAction +ch.qos.logback.classic.joran.action.ConsolePluginAction +ch.qos.logback.classic.joran.action.ContextNameAction +ch.qos.logback.classic.joran.action.InsertFromJNDIAction +ch.qos.logback.classic.joran.action.JMXConfiguratorAction +ch.qos.logback.classic.joran.action.LevelAction +ch.qos.logback.classic.joran.action.LoggerAction +ch.qos.logback.classic.joran.action.LoggerContextListenerAction +ch.qos.logback.classic.joran.action.ReceiverAction +ch.qos.logback.classic.joran.action.RootLoggerAction +ch.qos.logback.classic.joran.action.RootLoggerAction2 +ch.qos.logback.classic.joran.JoranConfigurator +ch.qos.logback.classic.joran.ReconfigureOnChangeTask +ch.qos.logback.classic.joran.ReconfigureOnChangeTaskListener +ch.qos.logback.classic.jul.JULHelper +ch.qos.logback.classic.jul.LevelChangePropagator +ch.qos.logback.classic.layout.TTLLLayout +ch.qos.logback.classic.Level +ch.qos.logback.classic.log4j.XMLLayout +ch.qos.logback.classic.Logger +ch.qos.logback.classic.LoggerContext +ch.qos.logback.classic.model.ConfigurationModel +ch.qos.logback.classic.model.ContextNameModel +ch.qos.logback.classic.model.LevelModel +ch.qos.logback.classic.model.LoggerContextListenerModel +ch.qos.logback.classic.model.LoggerModel +ch.qos.logback.classic.model.processor.ConfigurationModelHandler +ch.qos.logback.classic.model.processor.ContextNameModelHandler +ch.qos.logback.classic.model.processor.LevelModelHandler +ch.qos.logback.classic.model.processor.LoggerContextListenerModelHandler +ch.qos.logback.classic.model.processor.LoggerModelHandler +ch.qos.logback.classic.model.processor.RootLoggerModelHandler +ch.qos.logback.classic.model.RootLoggerModel +ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer +ch.qos.logback.classic.net.ReceiverBase +ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream +ch.qos.logback.classic.net.server.RemoteAppenderClient +ch.qos.logback.classic.net.server.RemoteAppenderServerListener +ch.qos.logback.classic.net.server.RemoteAppenderServerRunner +ch.qos.logback.classic.net.server.RemoteAppenderStreamClient +ch.qos.logback.classic.net.server.ServerSocketAppender +ch.qos.logback.classic.net.server.ServerSocketReceiver +ch.qos.logback.classic.net.server.SSLServerSocketAppender +ch.qos.logback.classic.net.server.SSLServerSocketReceiver +ch.qos.logback.classic.net.SimpleSocketServer +ch.qos.logback.classic.net.SimpleSSLSocketServer +ch.qos.logback.classic.net.SMTPAppender +ch.qos.logback.classic.net.SocketAcceptor +ch.qos.logback.classic.net.SocketAppender +ch.qos.logback.classic.net.SocketNode +ch.qos.logback.classic.net.SocketReceiver +ch.qos.logback.classic.net.SSLSocketAppender +ch.qos.logback.classic.net.SSLSocketReceiver +ch.qos.logback.classic.net.SyslogAppender +ch.qos.logback.classic.pattern.Abbreviator +ch.qos.logback.classic.pattern.CallerDataConverter +ch.qos.logback.classic.pattern.ClassicConverter +ch.qos.logback.classic.pattern.ClassNameOnlyAbbreviator +ch.qos.logback.classic.pattern.ClassOfCallerConverter +ch.qos.logback.classic.pattern.color.HighlightingCompositeConverter +ch.qos.logback.classic.pattern.ContextNameConverter +ch.qos.logback.classic.pattern.DateConverter +ch.qos.logback.classic.pattern.EnsureExceptionHandling +ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverter +ch.qos.logback.classic.pattern.FileOfCallerConverter +ch.qos.logback.classic.pattern.LevelConverter +ch.qos.logback.classic.pattern.LineOfCallerConverter +ch.qos.logback.classic.pattern.LineSeparatorConverter +ch.qos.logback.classic.pattern.LocalSequenceNumberConverter +ch.qos.logback.classic.pattern.LoggerConverter +ch.qos.logback.classic.pattern.MarkerConverter +ch.qos.logback.classic.pattern.MDCConverter +ch.qos.logback.classic.pattern.MessageConverter +ch.qos.logback.classic.pattern.MethodOfCallerConverter +ch.qos.logback.classic.pattern.NamedConverter +ch.qos.logback.classic.pattern.NopThrowableInformationConverter +ch.qos.logback.classic.pattern.PrefixCompositeConverter +ch.qos.logback.classic.pattern.PropertyConverter +ch.qos.logback.classic.pattern.RelativeTimeConverter +ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverter +ch.qos.logback.classic.pattern.SyslogStartConverter +ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator +ch.qos.logback.classic.pattern.ThreadConverter +ch.qos.logback.classic.pattern.ThrowableHandlingConverter +ch.qos.logback.classic.pattern.ThrowableProxyConverter +ch.qos.logback.classic.pattern.Util +ch.qos.logback.classic.PatternLayout +ch.qos.logback.classic.selector.ContextJNDISelector +ch.qos.logback.classic.selector.ContextSelector +ch.qos.logback.classic.selector.DefaultContextSelector +ch.qos.logback.classic.selector.servlet.ContextDetachingSCL +ch.qos.logback.classic.selector.servlet.LoggerContextFilter +ch.qos.logback.classic.servlet.LogbackServletContainerInitializer +ch.qos.logback.classic.servlet.LogbackServletContextListener +ch.qos.logback.classic.sift.ContextBasedDiscriminator +ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator +ch.qos.logback.classic.sift.MDCBasedDiscriminator +ch.qos.logback.classic.spi.CallerData +ch.qos.logback.classic.spi.ClassPackagingData +ch.qos.logback.classic.spi.Configurator +ch.qos.logback.classic.spi.EventArgUtil +ch.qos.logback.classic.spi.ILoggingEvent +ch.qos.logback.classic.spi.IThrowableProxy +ch.qos.logback.classic.spi.LogbackLoggingEventBuilder +ch.qos.logback.classic.spi.LogbackServiceProvider +ch.qos.logback.classic.spi.LoggerComparator +ch.qos.logback.classic.spi.LoggerContextAware +ch.qos.logback.classic.spi.LoggerContextAwareBase +ch.qos.logback.classic.spi.LoggerContextListener +ch.qos.logback.classic.spi.LoggerContextVO +ch.qos.logback.classic.spi.LoggerRemoteView +ch.qos.logback.classic.spi.LoggingEvent +ch.qos.logback.classic.spi.LoggingEventVO +ch.qos.logback.classic.spi.PackagingDataCalculator +ch.qos.logback.classic.spi.PlatformInfo +ch.qos.logback.classic.spi.StackTraceElementProxy +ch.qos.logback.classic.spi.STEUtil +ch.qos.logback.classic.spi.ThrowableProxy +ch.qos.logback.classic.spi.ThrowableProxyUtil +ch.qos.logback.classic.spi.ThrowableProxyVO +ch.qos.logback.classic.spi.TurboFilterList +ch.qos.logback.classic.turbo.DuplicateMessageFilter +ch.qos.logback.classic.turbo.DynamicThresholdFilter +ch.qos.logback.classic.turbo.LRUMessageCache +ch.qos.logback.classic.turbo.MarkerFilter +ch.qos.logback.classic.turbo.MatchingFilter +ch.qos.logback.classic.turbo.MDCFilter +ch.qos.logback.classic.turbo.MDCValueLevelPair +ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter +ch.qos.logback.classic.turbo.TurboFilter +ch.qos.logback.classic.util.ContextInitializer +ch.qos.logback.classic.util.ContextSelectorStaticBinder +ch.qos.logback.classic.util.CopyOnInheritThreadLocal +ch.qos.logback.classic.util.DefaultNestedComponentRules +ch.qos.logback.classic.util.EnvUtil +ch.qos.logback.classic.util.JNDIUtil +ch.qos.logback.classic.util.LevelToSyslogSeverity +ch.qos.logback.classic.util.LogbackMDCAdapter +ch.qos.logback.classic.util.LoggerNameUtil +ch.qos.logback.classic.util.StatusViaSLF4JLoggerFactory +ch.qos.logback.classic.ViewStatusMessagesServlet +ch.qos.logback.classic.AsyncAppenderTest +ch.qos.logback.classic.boolex.JaninoEventEvaluatorTest +ch.qos.logback.classic.boolex.OnMarkerEvaluatorTest +ch.qos.logback.classic.ClassicTestConstants +ch.qos.logback.classic.control.ControlLogger +ch.qos.logback.classic.control.ControlLoggerContext +ch.qos.logback.classic.control.ControlLoggerContextTest +ch.qos.logback.classic.control.CreateLogger +ch.qos.logback.classic.control.Scenario +ch.qos.logback.classic.control.ScenarioAction +ch.qos.logback.classic.control.ScenarioMaker +ch.qos.logback.classic.control.ScenarioRandomUtil +ch.qos.logback.classic.control.SetLevel +ch.qos.logback.classic.corpus.Corpus +ch.qos.logback.classic.corpus.CorpusModel +ch.qos.logback.classic.corpus.ExceptionBuilder +ch.qos.logback.classic.corpus.LogStatement +ch.qos.logback.classic.corpus.MessageArgumentTuple +ch.qos.logback.classic.corpus.RandomUtil +ch.qos.logback.classic.corpus.TextFileUtil +ch.qos.logback.classic.corpusTest.RandomUtilTest +ch.qos.logback.classic.corpusTest.TextFileUtilTest +ch.qos.logback.classic.db.DBAppenderH2Test +ch.qos.logback.classic.db.DBAppenderH2TestFixture +ch.qos.logback.classic.db.DBAppenderHSQLTest +ch.qos.logback.classic.db.DBAppenderHSQLTestFixture +ch.qos.logback.classic.db.DBAppenderIntegrationTest +ch.qos.logback.classic.db.names.DefaultDBNameResolverTest +ch.qos.logback.classic.db.names.SimpleDBNameResolverTest +ch.qos.logback.classic.db.SQLBuilderTest +ch.qos.logback.classic.encoder.LayoutInsteadOfEncoderTest +ch.qos.logback.classic.encoder.PatternLayoutEncoderTest +ch.qos.logback.classic.Foo +ch.qos.logback.classic.HLogger +ch.qos.logback.classic.HLoggerContext +ch.qos.logback.classic.html.HTMLLayoutTest +ch.qos.logback.classic.html.XHTMLEntityResolver +ch.qos.logback.classic.issue.lbclassic135.lbclassic139.Accessor +ch.qos.logback.classic.issue.lbclassic135.lbclassic139.LB139_DeadlockTest +ch.qos.logback.classic.issue.lbclassic135.lbclassic139.Worker +ch.qos.logback.classic.blackbox.issue.lbclassic135.LoggingRunnable +ch.qos.logback.classic.blackbox.issue.lbclassic135.LoggingToFileThroughput +ch.qos.logback.classic.issue.lbclassic180.HtmlEscapedMessageConverter +ch.qos.logback.classic.issue.lbclassic180.Main +ch.qos.logback.classic.issue.lbclassic323.Barebones +ch.qos.logback.classic.issue.lbclassic323.Simple +ch.qos.logback.classic.issue.lbclassic330.Main +ch.qos.logback.classic.issue.lbclassic36.DateFormatOriginal_tzest +ch.qos.logback.classic.issue.lbclassic36.DateFormatPerf_Tapp +ch.qos.logback.classic.issue.lbclassic36.DateFormattingThreadedThroughputCalculator +ch.qos.logback.classic.issue.lbclassic36.SelectiveDateFormattingRunnable +ch.qos.logback.classic.issue.lbcore211.Lbcore211 +ch.qos.logback.classic.issue.lbcore224.Reduce +ch.qos.logback.classic.issue.lbcore243.Common +ch.qos.logback.classic.issue.lbcore243.PerformanceComparatorLog4j +ch.qos.logback.classic.issue.lbcore243.PerformanceComparatorLogback +ch.qos.logback.classic.issue.lbcore26.Main +ch.qos.logback.classic.issue.LBCORE63 +ch.qos.logback.classic.issue.lbcore_155.Main +ch.qos.logback.classic.issue.lbcore_155.OThread +ch.qos.logback.classic.util.LogbackListener1159 +ch.qos.logback.classic.issue.logback1159.LogbackListenerTest +ch.qos.logback.classic.issue.logback1159.LoggingError +ch.qos.logback.classic.LoggingAppender474 +ch.qos.logback.classic.issue.logback_1162.Main +ch.qos.logback.classic.issue.logback_1277.Main +ch.qos.logback.classic.issue.logback_1361.Main +ch.qos.logback.classic.jmx.JMXConfiguratorTest +ch.qos.logback.classic.blackbox.joran.conditional.ConditionalTest +ch.qos.logback.classic.joran.EvaluatorJoranTest +ch.qos.logback.classic.joran.JoranConfiguratorTest +ch.qos.logback.classic.blackbox.joran.ReconfigureOnChangeTaskTest +ch.qos.logback.classic.jul.LevelChangePropagatorTest +ch.qos.logback.classic.layout.TTLLLayoutTest +ch.qos.logback.classic.LevelTest +ch.qos.logback.classic.LoggerContextConcurrentResetTest +ch.qos.logback.classic.LoggerContextDeadlockTest +ch.qos.logback.classic.LoggerContextPerfTest +ch.qos.logback.classic.LoggerContextTest +ch.qos.logback.classic.LoggerMessageFormattingTest +ch.qos.logback.classic.LoggerPerfTest +ch.qos.logback.classic.LoggerSerializationTest +ch.qos.logback.classic.LoggerTest +ch.qos.logback.classic.LoggerTestHelper +ch.qos.logback.classic.MDCTest +ch.qos.logback.classic.MDCTestThread +ch.qos.logback.classic.multiJVM.Checker +ch.qos.logback.classic.multiJVM.FileAppenderPerf +ch.qos.logback.classic.multiJVM.LoggingThread +ch.qos.logback.classic.multiJVM.SafeModeFileAppender +ch.qos.logback.classic.multiJVM.SafeModeRollingFileAppender +ch.qos.logback.classic.blackbox.net.CounterBasedEvaluator +ch.qos.logback.classic.net.DilutedSMTPAppenderTest +ch.qos.logback.classic.net.ExternalMockSocketServer +ch.qos.logback.classic.net.mock.MockAppender +ch.qos.logback.classic.net.mock.MockSyslogServer +ch.qos.logback.classic.net.NOPOutputStream +ch.qos.logback.classic.net.SerializationPerfTest +ch.qos.logback.classic.net.server.InstrumentedServerSocketReceiver +ch.qos.logback.classic.net.server.MockSSLConfiguration +ch.qos.logback.classic.net.server.MockSSLParametersConfiguration +ch.qos.logback.classic.net.server.RemoteAppenderStreamClientTest +ch.qos.logback.classic.net.server.ServerSocketReceiverFunctionalTest +ch.qos.logback.classic.net.server.ServerSocketReceiverTest +ch.qos.logback.classic.net.server.SSLServerSocketReceiverTest +ch.qos.logback.classic.blackbox.net.SMTPAppender_GreenTest +ch.qos.logback.classic.net.SocketAppenderMessageLossTest +ch.qos.logback.classic.net.SocketMin +ch.qos.logback.classic.net.SocketReceiverTest +ch.qos.logback.classic.net.SSLSocketReceiverTest +ch.qos.logback.classic.net.SyslogAppenderTest +ch.qos.logback.classic.net.testObjectBuilders.Builder +ch.qos.logback.classic.net.testObjectBuilders.LoggingEventBuilderInContext +ch.qos.logback.classic.net.testObjectBuilders.LoggingEventWithParametersBuilder +ch.qos.logback.classic.net.testObjectBuilders.MinimalSer +ch.qos.logback.classic.net.testObjectBuilders.MinimalSerBuilder +ch.qos.logback.classic.net.testObjectBuilders.TrivialLoggingEventBuilder +ch.qos.logback.classic.net.testObjectBuilders.TrivialLoggingEventVOBuilder +ch.qos.logback.classic.pattern.ConverterTest +ch.qos.logback.classic.pattern.EnsureExceptionHandlingTest +ch.qos.logback.classic.pattern.ExtendedThrowableProxyConverterTest +ch.qos.logback.classic.pattern.MarkerConverterTest +ch.qos.logback.classic.pattern.MDCConverterTest +ch.qos.logback.classic.pattern.RootCauseFirstThrowableProxyConverterTest +ch.qos.logback.classic.pattern.SyslogStartConverterTest +ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviatorTest +ch.qos.logback.classic.pattern.ThrowableProxyConverterTest +ch.qos.logback.classic.pattern.XCompositeConverter +ch.qos.logback.classic.pattern.XThrowableHandlingConverter +ch.qos.logback.classic.PatternLayoutTest +ch.qos.logback.classic.rolling.TimeBasedRollingWithConfigFileTest +ch.qos.logback.classic.rolling.UniqueFileTest +ch.qos.logback.classic.ScenarioBasedLoggerContextTest +ch.qos.logback.classic.selector.ContextDetachingSCLTest +ch.qos.logback.classic.selector.ContextJNDISelectorTest +ch.qos.logback.classic.servlet.LogbackServletContainerInitializerTest +ch.qos.logback.classic.sift.MDCBasedDiscriminatorTest +ch.qos.logback.classic.spi.BasicContextListener +ch.qos.logback.classic.spi.BogusClassLoader +ch.qos.logback.classic.spi.CallerDataTest +ch.qos.logback.classic.spi.ContextListenerTest +ch.qos.logback.classic.spi.CPDCSpecial +ch.qos.logback.classic.spi.PubThrowableProxy +ch.qos.logback.classic.spi.LocalFirstClassLoader +ch.qos.logback.classic.spi.LoggerComparatorTest +ch.qos.logback.classic.spi.LoggingEventSerializationPerfTest +ch.qos.logback.classic.spi.LoggingEventSerializationTest +ch.qos.logback.classic.spi.LoggingEventTest +ch.qos.logback.classic.spi.LuckyCharms +ch.qos.logback.classic.spi.PackagingDataCalculatorTest +ch.qos.logback.classic.spi.PubLoggingEventVO +ch.qos.logback.classic.spi.special.CPDCSpecialImpl +ch.qos.logback.classic.spi.ThrowableProxyTest +ch.qos.logback.classic.pattern.SampleConverter +ch.qos.logback.classic.turbo.DebugUsersTurboFilter +ch.qos.logback.classic.turbo.DuplicateMessageFilterTest +ch.qos.logback.classic.turbo.lru.Event +ch.qos.logback.classic.turbo.lru.LRUCache +ch.qos.logback.classic.turbo.lru.LRUCacheTest +ch.qos.logback.classic.turbo.lru.Simulator +ch.qos.logback.classic.turbo.lru.T_Entry +ch.qos.logback.classic.turbo.lru.T_LRUCache +ch.qos.logback.classic.turbo.lru.X_LRUCache +ch.qos.logback.classic.turbo.LRUMessageCacheTest +ch.qos.logback.classic.turbo.MarkerFilterTest +ch.qos.logback.classic.turbo.MDCFilterTest +ch.qos.logback.classic.turbo.NOPTurboFilter +ch.qos.logback.classic.turbo.ReconfigureOnChangeTest +ch.qos.logback.classic.turbo.ReconfigurePerf +ch.qos.logback.classic.TurboFilteringInLoggerTest +ch.qos.logback.classic.util.ContextInitializerAutoConfigTest +ch.qos.logback.classic.util.ContextInitializerTest +ch.qos.logback.classic.util.InitializationIntegrationTest +ch.qos.logback.classic.util.LevelToSyslogSeverityTest +ch.qos.logback.classic.util.LogbackMDCAdapterTest +ch.qos.logback.classic.util.LoggerNameUtilTest +ch.qos.logback.classic.util.MockConfigurator +ch.qos.logback.classic.util.MockInitialContext +ch.qos.logback.classic.util.MockInitialContextFactory +ch.qos.logback.classic.util.TestHelper +integrator.Activator +org.dummy.DummyLBAppender +org.dummy.Log4jInvocation +org.slf4j.implTest.InitializationOutputTest +org.slf4j.implTest.MultithreadedInitializationTest +org.slf4j.implTest.RecursiveInitializationTest +ch.qos.logback.classic.RecursiveLBAppender +org.slf4j.test_osgi.BundleTest +org.slf4j.test_osgi.CheckingBundleListener +org.slf4j.test_osgi.FelixHost +org.slf4j.test_osgi.FrameworkErrorListener +ch.qos.logback.core.Appender +ch.qos.logback.core.AppenderBase +ch.qos.logback.core.AsyncAppenderBase +ch.qos.logback.core.BasicStatusManager +ch.qos.logback.core.boolex.EvaluationException +ch.qos.logback.core.boolex.EventEvaluator +ch.qos.logback.core.boolex.EventEvaluatorBase +ch.qos.logback.core.boolex.JaninoEventEvaluatorBase +ch.qos.logback.core.boolex.Matcher +ch.qos.logback.core.ConsoleAppender +ch.qos.logback.core.Context +ch.qos.logback.core.ContextBase +ch.qos.logback.core.CoreConstants +ch.qos.logback.core.db.BindDataSourceToJNDIAction +ch.qos.logback.core.db.ConnectionSource +ch.qos.logback.core.db.ConnectionSourceBase +ch.qos.logback.core.db.DataSourceConnectionSource +ch.qos.logback.core.db.DBAppenderBase +ch.qos.logback.core.db.DBHelper +ch.qos.logback.core.db.dialect.DBUtil +ch.qos.logback.core.db.dialect.H2Dialect +ch.qos.logback.core.db.dialect.HSQLDBDialect +ch.qos.logback.core.db.dialect.MsSQLDialect +ch.qos.logback.core.db.dialect.MySQLDialect +ch.qos.logback.core.db.dialect.OracleDialect +ch.qos.logback.core.db.dialect.PostgreSQLDialect +ch.qos.logback.core.db.dialect.SQLDialect +ch.qos.logback.core.db.dialect.SQLDialectCode +ch.qos.logback.core.db.dialect.SQLiteDialect +ch.qos.logback.core.db.dialect.SybaseSqlAnywhereDialect +ch.qos.logback.core.db.DriverManagerConnectionSource +ch.qos.logback.core.db.JNDIConnectionSource +ch.qos.logback.core.encoder.ByteArrayUtil +ch.qos.logback.core.encoder.EchoEncoder +ch.qos.logback.core.encoder.Encoder +ch.qos.logback.core.encoder.EncoderBase +ch.qos.logback.core.encoder.LayoutWrappingEncoder +ch.qos.logback.core.encoder.NonClosableInputStream +ch.qos.logback.core.FileAppender +ch.qos.logback.core.filter.AbstractMatcherFilter +ch.qos.logback.core.filter.EvaluatorFilter +ch.qos.logback.core.filter.Filter +ch.qos.logback.core.helpers.CyclicBuffer +ch.qos.logback.core.helpers.NOPAppender +ch.qos.logback.core.helpers.ThrowableToStringArray +ch.qos.logback.core.helpers.Transform +ch.qos.logback.core.hook.DefaultShutdownHook +ch.qos.logback.core.hook.ShutdownHook +ch.qos.logback.core.hook.ShutdownHookBase +ch.qos.logback.core.html.CssBuilder +ch.qos.logback.core.html.HTMLLayoutBase +ch.qos.logback.core.html.IThrowableRenderer +ch.qos.logback.core.html.NOPThrowableRenderer +ch.qos.logback.core.joran.action.Action +ch.qos.logback.core.joran.action.ActionUtil +ch.qos.logback.core.joran.action.AppenderAction +ch.qos.logback.core.joran.action.AppenderRefAction +ch.qos.logback.core.joran.action.BaseModelAction +ch.qos.logback.core.joran.action.ContextPropertyAction +ch.qos.logback.core.joran.action.ConversionRuleAction +ch.qos.logback.core.joran.action.DefinePropertyAction +ch.qos.logback.core.joran.action.EventEvaluatorAction +ch.qos.logback.core.joran.action.ImcplicitActionDataForBasicProperty +ch.qos.logback.core.joran.action.ImplicitActionDataBase +ch.qos.logback.core.joran.action.ImplicitActionDataForComplexProperty +ch.qos.logback.core.joran.action.ImplicitModelAction +ch.qos.logback.core.joran.action.IncludeModelAction +ch.qos.logback.core.joran.action.NewRuleAction +ch.qos.logback.core.joran.action.NOPAction +ch.qos.logback.core.joran.action.ParamAction +ch.qos.logback.core.joran.action.PreconditionValidator +ch.qos.logback.core.joran.action.PropertyAction +ch.qos.logback.core.joran.action.SequenceNumberGeneratorAction +ch.qos.logback.core.joran.action.ShutdownHookAction +ch.qos.logback.core.joran.action.StatusListenerAction +ch.qos.logback.core.joran.action.TimestampAction +ch.qos.logback.core.joran.conditional.Condition +ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder +ch.qos.logback.core.joran.conditional.PropertyWrapperForScripts +ch.qos.logback.core.joran.event.BodyEvent +ch.qos.logback.core.joran.event.EndEvent +ch.qos.logback.core.joran.event.SaxEvent +ch.qos.logback.core.joran.event.SaxEventRecorder +ch.qos.logback.core.joran.event.StartEvent +ch.qos.logback.core.joran.event.stax.BodyEvent +ch.qos.logback.core.joran.event.stax.EndEvent +ch.qos.logback.core.joran.event.stax.StartEvent +ch.qos.logback.core.joran.event.stax.StaxEvent +ch.qos.logback.core.joran.event.stax.StaxEventRecorder +ch.qos.logback.core.joran.GenericConfigurator +ch.qos.logback.core.joran.JoranConfiguratorBase +ch.qos.logback.core.joran.JoranConstants +ch.qos.logback.core.joran.node.ComponentNode +ch.qos.logback.core.joran.ParamModelHandler +ch.qos.logback.core.joran.spi.ActionException +ch.qos.logback.core.joran.spi.ConfigurationWatchList +ch.qos.logback.core.joran.spi.ConsoleTarget +ch.qos.logback.core.joran.spi.DefaultClass +ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry +ch.qos.logback.core.joran.spi.ElementPath +ch.qos.logback.core.joran.spi.ElementSelector +ch.qos.logback.core.joran.spi.EventPlayer +ch.qos.logback.core.joran.spi.HostClassAndPropertyDouble +ch.qos.logback.core.joran.spi.InterpretationContext +ch.qos.logback.core.joran.spi.JoranException +ch.qos.logback.core.joran.spi.NoAutoStart +ch.qos.logback.core.joran.spi.NoAutoStartUtil +ch.qos.logback.core.joran.spi.RuleStore +ch.qos.logback.core.joran.spi.SaxEventInterpreter +ch.qos.logback.core.joran.spi.SimpleRuleStore +ch.qos.logback.core.joran.spi.XMLUtil +ch.qos.logback.core.joran.util.beans.BeanDescription +ch.qos.logback.core.joran.util.beans.BeanDescriptionCache +ch.qos.logback.core.joran.util.beans.BeanDescriptionFactory +ch.qos.logback.core.joran.util.beans.BeanUtil +ch.qos.logback.core.joran.util.ConfigurationWatchListUtil +ch.qos.logback.core.joran.util.PropertySetter +ch.qos.logback.core.joran.util.StringToObjectConverter +ch.qos.logback.core.layout.EchoLayout +ch.qos.logback.core.Layout +ch.qos.logback.core.LayoutBase +ch.qos.logback.core.LifeCycleManager +ch.qos.logback.core.LogbackException +ch.qos.logback.core.model.AppenderModel +ch.qos.logback.core.model.AppenderRefModel +ch.qos.logback.core.model.ComponentModel +ch.qos.logback.core.model.DefineModel +ch.qos.logback.core.model.EventEvaluatorModel +ch.qos.logback.core.model.ImplicitModel +ch.qos.logback.core.model.INamedModel +ch.qos.logback.core.model.IncludeModel +ch.qos.logback.core.model.Model +ch.qos.logback.core.model.ModelFactoryMethod +ch.qos.logback.core.model.NamedComponentModel +ch.qos.logback.core.model.NamedModel +ch.qos.logback.core.model.ParamModel +ch.qos.logback.core.model.processor.AllowAllModelFilter +ch.qos.logback.core.model.processor.AllowModelFilter +ch.qos.logback.core.model.processor.AppenderModelHandler +ch.qos.logback.core.model.processor.AppenderRefDependencyAnalyser +ch.qos.logback.core.model.processor.AppenderRefModelHandler +ch.qos.logback.core.model.processor.ChainedModelFilter +ch.qos.logback.core.model.processor.DefaultProcessor +ch.qos.logback.core.model.processor.DefineModelHandler +ch.qos.logback.core.model.processor.DenyAllModelFilter +ch.qos.logback.core.model.processor.DenyModelFilter +ch.qos.logback.core.model.processor.EventEvaluatorModelHandler +ch.qos.logback.core.model.processor.ImplicitModelHandler +ch.qos.logback.core.model.processor.IncludeModelHandler +ch.qos.logback.core.model.processor.ModelFiler +ch.qos.logback.core.model.processor.ModelHandlerBase +ch.qos.logback.core.model.processor.ModelHandlerException +ch.qos.logback.core.model.processor.ProcessorException +ch.qos.logback.core.model.processor.PropertyModelHandler +ch.qos.logback.core.model.processor.RefContainerDependencyAnalyser +ch.qos.logback.core.model.processor.ShutdownHookModelHandler +ch.qos.logback.core.model.processor.StatusListenerModelHandler +ch.qos.logback.core.model.processor.TimestampModelHandler +ch.qos.logback.core.model.PropertyModel +ch.qos.logback.core.model.ShutdownHookModel +ch.qos.logback.core.model.StatusListenerModel +ch.qos.logback.core.model.TimestampModel +ch.qos.logback.core.net.AbstractSocketAppender +ch.qos.logback.core.net.AbstractSSLSocketAppender +ch.qos.logback.core.net.AutoFlushingObjectWriter +ch.qos.logback.core.net.DefaultSocketConnector +ch.qos.logback.core.net.HardenedObjectInputStream +ch.qos.logback.core.net.LoginAuthenticator +ch.qos.logback.core.net.ObjectWriter +ch.qos.logback.core.net.ObjectWriterFactory +ch.qos.logback.core.net.QueueFactory +ch.qos.logback.core.net.server.AbstractServerSocketAppender +ch.qos.logback.core.net.server.Client +ch.qos.logback.core.net.server.ClientVisitor +ch.qos.logback.core.net.server.ConcurrentServerRunner +ch.qos.logback.core.net.server.RemoteReceiverClient +ch.qos.logback.core.net.server.RemoteReceiverServerListener +ch.qos.logback.core.net.server.RemoteReceiverServerRunner +ch.qos.logback.core.net.server.RemoteReceiverStreamClient +ch.qos.logback.core.net.server.ServerListener +ch.qos.logback.core.net.server.ServerRunner +ch.qos.logback.core.net.server.ServerSocketListener +ch.qos.logback.core.net.server.SSLServerSocketAppenderBase +ch.qos.logback.core.net.SMTPAppenderBase +ch.qos.logback.core.net.SocketConnector +ch.qos.logback.core.net.ssl.ConfigurableSSLServerSocketFactory +ch.qos.logback.core.net.ssl.ConfigurableSSLSocketFactory +ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean +ch.qos.logback.core.net.ssl.KeyStoreFactoryBean +ch.qos.logback.core.net.ssl.SecureRandomFactoryBean +ch.qos.logback.core.net.ssl.SSL +ch.qos.logback.core.net.ssl.SSLComponent +ch.qos.logback.core.net.ssl.SSLConfigurable +ch.qos.logback.core.net.ssl.SSLConfigurableServerSocket +ch.qos.logback.core.net.ssl.SSLConfigurableSocket +ch.qos.logback.core.net.ssl.SSLConfiguration +ch.qos.logback.core.net.ssl.SSLContextFactoryBean +ch.qos.logback.core.net.ssl.SSLNestedComponentRegistryRules +ch.qos.logback.core.net.ssl.SSLParametersConfiguration +ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBean +ch.qos.logback.core.net.SyslogAppenderBase +ch.qos.logback.core.net.SyslogConstants +ch.qos.logback.core.net.SyslogOutputStream +ch.qos.logback.core.OutputStreamAppender +ch.qos.logback.core.pattern.color.ANSIConstants +ch.qos.logback.core.pattern.color.BlackCompositeConverter +ch.qos.logback.core.pattern.color.BlueCompositeConverter +ch.qos.logback.core.pattern.color.BoldBlueCompositeConverter +ch.qos.logback.core.pattern.color.BoldCyanCompositeConverter +ch.qos.logback.core.pattern.color.BoldGreenCompositeConverter +ch.qos.logback.core.pattern.color.BoldMagentaCompositeConverter +ch.qos.logback.core.pattern.color.BoldRedCompositeConverter +ch.qos.logback.core.pattern.color.BoldWhiteCompositeConverter +ch.qos.logback.core.pattern.color.BoldYellowCompositeConverter +ch.qos.logback.core.pattern.color.CyanCompositeConverter +ch.qos.logback.core.pattern.color.ForegroundCompositeConverterBase +ch.qos.logback.core.pattern.color.GrayCompositeConverter +ch.qos.logback.core.pattern.color.GreenCompositeConverter +ch.qos.logback.core.pattern.color.MagentaCompositeConverter +ch.qos.logback.core.pattern.color.RedCompositeConverter +ch.qos.logback.core.pattern.color.WhiteCompositeConverter +ch.qos.logback.core.pattern.color.YellowCompositeConverter +ch.qos.logback.core.pattern.CompositeConverter +ch.qos.logback.core.pattern.Converter +ch.qos.logback.core.pattern.ConverterUtil +ch.qos.logback.core.pattern.DynamicConverter +ch.qos.logback.core.pattern.FormatInfo +ch.qos.logback.core.pattern.FormattingConverter +ch.qos.logback.core.pattern.IdentityCompositeConverter +ch.qos.logback.core.pattern.LiteralConverter +ch.qos.logback.core.pattern.parser.Compiler +ch.qos.logback.core.pattern.parser.CompositeNode +ch.qos.logback.core.pattern.parser.FormattingNode +ch.qos.logback.core.pattern.parser.Node +ch.qos.logback.core.pattern.parser.OptionTokenizer +ch.qos.logback.core.pattern.parser.Parser +ch.qos.logback.core.pattern.parser.SimpleKeywordNode +ch.qos.logback.core.pattern.parser.Token +ch.qos.logback.core.pattern.parser.TokenStream +ch.qos.logback.core.pattern.PatternLayoutBase +ch.qos.logback.core.pattern.PatternLayoutEncoderBase +ch.qos.logback.core.pattern.PostCompileProcessor +ch.qos.logback.core.pattern.ReplacingCompositeConverter +ch.qos.logback.core.pattern.SpacePadder +ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil +ch.qos.logback.core.pattern.util.AsIsEscapeUtil +ch.qos.logback.core.pattern.util.IEscapeUtil +ch.qos.logback.core.pattern.util.RegularEscapeUtil +ch.qos.logback.core.pattern.util.RestrictedEscapeUtil +ch.qos.logback.core.property.CanonicalHostNamePropertyDefiner +ch.qos.logback.core.property.FileExistsPropertyDefiner +ch.qos.logback.core.property.ResourceExistsPropertyDefiner +ch.qos.logback.core.PropertyDefinerBase +ch.qos.logback.core.read.CyclicBufferAppender +ch.qos.logback.core.read.ListAppender +ch.qos.logback.core.recovery.RecoveryCoordinator +ch.qos.logback.core.recovery.ResilientFileOutputStream +ch.qos.logback.core.recovery.ResilientOutputStreamBase +ch.qos.logback.core.recovery.ResilientSyslogOutputStream +ch.qos.logback.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy +ch.qos.logback.core.rolling.FixedWindowRollingPolicy +ch.qos.logback.core.rolling.helper.ArchiveRemover +ch.qos.logback.core.rolling.helper.CompressionMode +ch.qos.logback.core.rolling.helper.Compressor +ch.qos.logback.core.rolling.helper.DateTokenConverter +ch.qos.logback.core.rolling.helper.FileFilterUtil +ch.qos.logback.core.rolling.helper.FileNamePattern +ch.qos.logback.core.rolling.helper.FileStoreUtil +ch.qos.logback.core.rolling.helper.IntegerTokenConverter +ch.qos.logback.core.rolling.helper.MonoTypedConverter +ch.qos.logback.core.rolling.helper.PeriodicityType +ch.qos.logback.core.rolling.helper.RenameUtil +ch.qos.logback.core.rolling.helper.RollingCalendar +ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover +ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover +ch.qos.logback.core.rolling.helper.TokenConverter +ch.qos.logback.core.rolling.RollingFileAppender +ch.qos.logback.core.rolling.RollingPolicy +ch.qos.logback.core.rolling.RollingPolicyBase +ch.qos.logback.core.rolling.RolloverFailure +ch.qos.logback.core.rolling.SizeAndTimeBasedFileNamingAndTriggeringPolicy +ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy +ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy +ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy +ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicyBase +ch.qos.logback.core.rolling.TimeBasedRollingPolicy +ch.qos.logback.core.rolling.TriggeringPolicy +ch.qos.logback.core.rolling.TriggeringPolicyBase +ch.qos.logback.core.sift.AbstractDiscriminator +ch.qos.logback.core.sift.AppenderFactory +ch.qos.logback.core.sift.AppenderTracker +ch.qos.logback.core.sift.DefaultDiscriminator +ch.qos.logback.core.sift.Discriminator +ch.qos.logback.core.spi.AbstractComponentTracker +ch.qos.logback.core.spi.AppenderAttachable +ch.qos.logback.core.spi.AppenderAttachableImpl +ch.qos.logback.core.spi.BasicSequenceNumberGenerator +ch.qos.logback.core.spi.ComponentTracker +ch.qos.logback.core.spi.ContextAware +ch.qos.logback.core.spi.ContextAwareBase +ch.qos.logback.core.spi.ContextAwareImpl +ch.qos.logback.core.spi.CyclicBufferTracker +ch.qos.logback.core.spi.DeferredProcessingAware +ch.qos.logback.core.spi.FilterAttachable +ch.qos.logback.core.spi.FilterAttachableImpl +ch.qos.logback.core.spi.FilterReply +ch.qos.logback.core.spi.LifeCycle +ch.qos.logback.core.spi.LogbackLock +ch.qos.logback.core.spi.PreSerializationTransformer +ch.qos.logback.core.spi.PropertyContainer +ch.qos.logback.core.spi.PropertyDefiner +ch.qos.logback.core.spi.ScanException +ch.qos.logback.core.spi.SequenceNumberGenerator +ch.qos.logback.core.status.ErrorStatus +ch.qos.logback.core.status.InfoStatus +ch.qos.logback.core.status.NopStatusListener +ch.qos.logback.core.status.OnConsoleStatusListener +ch.qos.logback.core.status.OnErrorConsoleStatusListener +ch.qos.logback.core.status.OnPrintStreamStatusListenerBase +ch.qos.logback.core.status.Status +ch.qos.logback.core.status.StatusBase +ch.qos.logback.core.status.StatusListener +ch.qos.logback.core.status.StatusListenerAsList +ch.qos.logback.core.status.StatusManager +ch.qos.logback.core.status.StatusUtil +ch.qos.logback.core.status.ViewStatusMessagesServletBase +ch.qos.logback.core.status.WarnStatus +ch.qos.logback.core.subst.Node +ch.qos.logback.core.subst.NodeToStringTransformer +ch.qos.logback.core.subst.Parser +ch.qos.logback.core.subst.Token +ch.qos.logback.core.subst.Tokenizer +ch.qos.logback.core.UnsynchronizedAppenderBase +ch.qos.logback.core.util.AggregationType +ch.qos.logback.core.util.CachingDateFormatter +ch.qos.logback.core.util.CharSequenceState +ch.qos.logback.core.util.CharSequenceToRegexMapper +ch.qos.logback.core.util.CloseUtil +ch.qos.logback.core.util.ContentTypeUtil +ch.qos.logback.core.util.ContextUtil +ch.qos.logback.core.util.COWArrayList +ch.qos.logback.core.util.DatePatternToRegexUtil +ch.qos.logback.core.util.DefaultInvocationGate +ch.qos.logback.core.util.DelayStrategy +ch.qos.logback.core.util.Duration +ch.qos.logback.core.util.DynamicClassLoadingException +ch.qos.logback.core.util.EnvUtil +ch.qos.logback.core.util.ExecutorServiceUtil +ch.qos.logback.core.util.FileSize +ch.qos.logback.core.util.FileUtil +ch.qos.logback.core.util.FixedDelay +ch.qos.logback.core.util.IncompatibleClassException +ch.qos.logback.core.util.InterruptUtil +ch.qos.logback.core.util.InvocationGate +ch.qos.logback.core.util.Loader +ch.qos.logback.core.util.LocationUtil +ch.qos.logback.core.util.NetworkAddressUtil +ch.qos.logback.core.util.OptionHelper +ch.qos.logback.core.util.PropertySetterException +ch.qos.logback.core.util.StatusListenerConfigHelper +ch.qos.logback.core.util.StatusPrinter +ch.qos.logback.core.util.StringCollectionUtil +ch.qos.logback.core.util.SystemInfo +ch.qos.logback.core.util.TimeUtil +ch.qos.logback.core.appender.AbstractAppenderTest +ch.qos.logback.core.appender.ConsoleAppenderTest +ch.qos.logback.core.appender.DummyAppenderTest +ch.qos.logback.core.appender.DummyWriterAppender +ch.qos.logback.core.appender.FileAppenderTest +ch.qos.logback.core.testUtil.XTeeOutputStream +ch.qos.logback.core.AsyncAppenderBaseTest +ch.qos.logback.core.BasicStatusManagerTest +ch.qos.logback.core.boolex.MatcherTest +ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness +ch.qos.logback.core.contention.MultiThreadedHarness +ch.qos.logback.core.testUtil.RunnableWithCounterAndDone +ch.qos.logback.core.contention.ThreadedThroughputCalculator +ch.qos.logback.core.contention.WaitOnExecutionMultiThreadedHarness +ch.qos.logback.core.ContextBaseTest +ch.qos.logback.core.encoder.ByteArrayUtilTest +ch.qos.logback.core.testUtil.DummyEncoder +ch.qos.logback.core.encoder.NopEncoder +ch.qos.logback.core.FileAppenderResilienceTest +ch.qos.logback.core.FileAppenderResilience_AS_ROOT_Test +ch.qos.logback.core.helpers.CyclicBufferTest +ch.qos.logback.core.helpers.FileFilterUtilTest +ch.qos.logback.core.helpers.ThrowableToStringArrayTest +ch.qos.logback.core.issue.lbcore258.FileLockSimulator +ch.qos.logback.core.issue.LBCORE97 +ch.qos.logback.core.issue.LockingInJava +ch.qos.logback.core.issue.LockThroughput +ch.qos.logback.core.issue.LOGBACK_849.Basic +ch.qos.logback.core.issue.NoLockingInJava +ch.qos.logback.core.issue.NoLockThroughput +ch.qos.logback.core.issue.SelectiveLockRunnable +ch.qos.logback.core.joran.action.AsLowerCasePropertyDefiner +ch.qos.logback.core.joran.action.DefinePropertyActionTest +ch.qos.logback.core.joran.action.DummyAttributes +ch.qos.logback.core.joran.action.ext.BadBeginAction +ch.qos.logback.core.joran.action.ext.BadEndAction +ch.qos.logback.core.joran.action.ext.HelloAction +ch.qos.logback.core.joran.action.ext.IncAction +ch.qos.logback.core.joran.action.ext.StackAction +ch.qos.logback.core.joran.action.ext.TouchAction +ch.qos.logback.core.joran.action.IncludeModelHandlerTest +ch.qos.logback.core.joran.action.PropertyActionTest +ch.qos.logback.core.joran.action.TopElementAction +ch.qos.logback.core.blackbox.joran.conditional.PropertyEvalScriptBuilderTest +ch.qos.logback.core.joran.event.SaxEventRecorderTest +ch.qos.logback.core.joran.event.stax.StaxEventRecorderTest +ch.qos.logback.core.joran.implicitAction.Cake +ch.qos.logback.core.joran.implicitAction.Fruit +ch.qos.logback.core.joran.implicitAction.FruitContext +ch.qos.logback.core.joran.implicitAction.FruitContextAction +ch.qos.logback.core.joran.implicitAction.FruitContextModel +ch.qos.logback.core.joran.implicitAction.FruitContextModelHandler +ch.qos.logback.core.joran.implicitAction.ImplicitActionTest +ch.qos.logback.core.joran.SimpleConfigurator +ch.qos.logback.core.joran.SkippingInInterpreterTest +ch.qos.logback.core.joran.spi.CaseCombinator +ch.qos.logback.core.joran.spi.CaseCombinatorTest +ch.qos.logback.core.joran.spi.ConfigurationWatchListTest +ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistryTest +ch.qos.logback.core.joran.spi.DoNotAutoStart +ch.qos.logback.core.joran.spi.ElementSelectorTest +ch.qos.logback.core.joran.spi.NoAutoStartUtilTest +ch.qos.logback.core.joran.spi.SimpleRuleStoreTest +ch.qos.logback.core.joran.TrivialConfigurator +ch.qos.logback.core.joran.TrivialConfiguratorTest +ch.qos.logback.core.joran.util.Citrus +ch.qos.logback.core.joran.util.House +ch.qos.logback.core.joran.util.Orange +ch.qos.logback.core.joran.util.PropertySetterTest +ch.qos.logback.core.joran.util.Window +ch.qos.logback.core.layout.DummyLayout +ch.qos.logback.core.layout.NopLayout +ch.qos.logback.core.LifeCycleManagerTest +ch.qos.logback.core.MockLifeCycleComponent +ch.qos.logback.core.model.FruitShellModel +ch.qos.logback.core.model.processor.NOPModelHandler +ch.qos.logback.core.model.TopModel +ch.qos.logback.core.net.AbstractSocketAppenderIntegrationTest +ch.qos.logback.core.net.AbstractSSLSocketAppenderTest +ch.qos.logback.core.net.AutoFlushingObjectWriterTest +ch.qos.logback.core.net.DefaultSocketConnectorTest +ch.qos.logback.core.net.HardenedObjectInputStreamTest +ch.qos.logback.core.net.Innocent +ch.qos.logback.core.net.mock.MockContext +ch.qos.logback.core.net.mock.MockScheduledExecutorService +ch.qos.logback.core.net.server.AbstractServerSocketAppenderTest +ch.qos.logback.core.net.server.ConcurrentServerRunnerTest +ch.qos.logback.core.net.server.InstrumentedServerSocketAppenderBase +ch.qos.logback.core.net.server.MockClient +ch.qos.logback.core.net.server.MockClientVisitor +ch.qos.logback.core.net.server.MockEventQueue +ch.qos.logback.core.net.server.RemoteReceiverStreamClientTest +ch.qos.logback.core.net.server.ServerSocketAppenderBaseFunctionalTest +ch.qos.logback.core.net.server.ServerSocketListenerTest +ch.qos.logback.core.net.server.SSLServerSocketAppenderBaseTest +ch.qos.logback.core.net.server.test.MockServerListener +ch.qos.logback.core.net.server.test.MockServerRunner +ch.qos.logback.core.net.server.test.ServerSocketUtil +ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBeanTest +ch.qos.logback.core.net.ssl.KeyStoreFactoryBeanTest +ch.qos.logback.core.net.ssl.mock.MockContextAware +ch.qos.logback.core.net.ssl.mock.MockKeyManagerFactoryFactoryBean +ch.qos.logback.core.net.ssl.mock.MockKeyStoreFactoryBean +ch.qos.logback.core.net.ssl.mock.MockSecureRandomFactoryBean +ch.qos.logback.core.net.ssl.mock.MockSSLConfigurable +ch.qos.logback.core.net.ssl.mock.MockTrustManagerFactoryFactoryBean +ch.qos.logback.core.net.ssl.SecureRandomFactoryBeanTest +ch.qos.logback.core.net.ssl.SSLConfigurationTest +ch.qos.logback.core.net.ssl.SSLContextFactoryBeanTest +ch.qos.logback.core.net.ssl.SSLParametersConfigurationTest +ch.qos.logback.core.net.ssl.SSLTestConstants +ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBeanTest +ch.qos.logback.core.net.SyslogAppenderBaseTest +ch.qos.logback.core.OutputStreamAppenderTest +ch.qos.logback.core.pattern.Converter123 +ch.qos.logback.core.pattern.ConverterHello +ch.qos.logback.core.pattern.ExceptionalConverter +ch.qos.logback.core.pattern.parser.CompilerTest +ch.qos.logback.core.pattern.parser.FormatInfoTest +ch.qos.logback.core.pattern.parser.OptionTokenizerTest +ch.qos.logback.core.pattern.parser.ParserTest +ch.qos.logback.core.pattern.parser.SamplePatternLayout +ch.qos.logback.core.pattern.parser.SamplePatternLayoutTest +ch.qos.logback.core.pattern.parser.test.AbstractPatternLayoutBaseTest +ch.qos.logback.core.pattern.parser.TokenStreamTest +ch.qos.logback.core.pattern.SpacePadderTest +ch.qos.logback.core.PrudentFileAppenderInterruptTest +ch.qos.logback.core.read.CyclicBufferAppenderTest +ch.qos.logback.core.recovery.RecoveryCoordinatorTest +ch.qos.logback.core.recovery.ResilientOutputStreamTest +ch.qos.logback.core.rolling.CollisionDetectionTest +ch.qos.logback.core.rolling.ConfigParameters +ch.qos.logback.core.rolling.DefaultRolloverChecker +ch.qos.logback.core.rolling.FileMatchFunction +ch.qos.logback.core.rolling.FileOpener +ch.qos.logback.core.blackbox.rolling.helper.CompressTest +ch.qos.logback.core.rolling.helper.FileNamePatternTest +ch.qos.logback.core.rolling.helper.FileStoreUtilTest +ch.qos.logback.core.rolling.helper.RollingCalendarTest +ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemoverTest +ch.qos.logback.core.rolling.JVMExitBeforeCompressionISDoneTest +ch.qos.logback.core.rolling.MultiThreadedRollingTest +ch.qos.logback.core.rolling.RenameUtilTest +ch.qos.logback.core.rolling.RollingFileAppenderTest +ch.qos.logback.core.rolling.RolloverChecker +ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP_Test +ch.qos.logback.core.rolling.SizeBasedRollingTest +ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests +ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicyBaseTest +ch.qos.logback.core.rolling.TimeBasedRollingTest +ch.qos.logback.core.rolling.TimeBasedRollingWithArchiveRemoval_Test +ch.qos.logback.core.rolling.ZRolloverChecker +ch.qos.logback.core.sift.AppenderTrackerTest +ch.qos.logback.core.spi.AppenderAttachableImplLockTest +ch.qos.logback.core.spi.AppenderAttachableImplTest +ch.qos.logback.core.spi.CyclicBufferTrackerSimulator +ch.qos.logback.core.spi.CyclicBufferTrackerT +ch.qos.logback.core.spi.CyclicBufferTrackerTest +ch.qos.logback.core.spi.ScenarioBasedCyclicBufferTrackerTest +ch.qos.logback.core.status.StatusBaseTest +ch.qos.logback.core.status.StatusUtilTest +ch.qos.logback.core.subst.NodeToStringTransformerTest +ch.qos.logback.core.subst.ParserTest +ch.qos.logback.core.subst.TokenizerTest +ch.qos.logback.core.testUtil.CoreTestConstants +ch.qos.logback.core.testUtil.DelayingListAppender +ch.qos.logback.core.testUtil.EnvUtilForTests +ch.qos.logback.core.testUtil.FileTestUtil +ch.qos.logback.core.testUtil.FileToBufferUtil +ch.qos.logback.core.testUtil.NPEAppender +ch.qos.logback.core.testUtil.RandomUtil +ch.qos.logback.core.status.testUtil.StatusChecker +ch.qos.logback.core.testUtil.StringListAppender +ch.qos.logback.core.testUtil.TeeOutputStream +ch.qos.logback.core.testUtil.TrivialStatusListener +ch.qos.logback.core.util.CachingFotmatterTest +ch.qos.logback.core.util.CharSequenceToRegexMapperTest +ch.qos.logback.core.util.Compare +ch.qos.logback.core.util.ContentTypeUtilTest +ch.qos.logback.core.util.COWArrayListTest +ch.qos.logback.core.util.DatePatternToRegexTest +ch.qos.logback.core.util.DefaultInvocationGateTest +ch.qos.logback.core.util.DurationTest +ch.qos.logback.core.util.EnvUtilTest +ch.qos.logback.core.util.FileSizeTest +ch.qos.logback.core.util.FileUtilTest +ch.qos.logback.core.util.FixedRateInvocationGate +ch.qos.logback.core.util.LocationUtilTest +ch.qos.logback.core.util.OptionHelperTest +ch.qos.logback.core.util.ResilienceUtil +ch.qos.logback.core.util.StatusListenerConfigHelperTest +ch.qos.logback.core.util.StatusPrinterTest +ch.qos.logback.core.util.StringCollectionUtilTest +ch.qos.logback.core.util.TimeUtilTest +chapters.appenders.ConfigurationTester +chapters.appenders.CountingConsoleAppender +chapters.appenders.IO +chapters.appenders.IOPerformance +chapters.appenders.mail.CounterBasedEvaluator +chapters.appenders.mail.EMail +chapters.appenders.mail.Marked_EMail +chapters.appenders.sift.SiftExample +chapters.appenders.socket.ConsolePluginClient +chapters.appenders.socket.SocketClient1 +chapters.appenders.socket.SocketClient2 +chapters.appenders.sub.sample.Bar +chapters.architecture.Bar +chapters.architecture.MyAppWithConfigFile +chapters.architecture.SelectionRule +chapters.configuration.AddStatusListenerApp +chapters.configuration.Foo +chapters.configuration.MyApp1 +chapters.configuration.MyApp2 +chapters.configuration.MyApp3 +chapters.filters.FilterEvents +chapters.filters.GoMDC +chapters.filters.SampleFilter +chapters.filters.SampleTurboFilter +chapters.introduction.HelloWorld1 +chapters.introduction.HelloWorld2 +chapters.layouts.CallerEvaluatorExample +chapters.layouts.ExceptionEvaluatorExample +chapters.layouts.MySampleConverter +chapters.layouts.MySampleLayout +chapters.layouts.MySampleLayout2 +chapters.layouts.PatternSample +chapters.layouts.SampleLogging +chapters.layouts.TestException +chapters.layouts.TrivialMain +chapters.mdc.NumberCruncher +chapters.mdc.NumberCruncherClient +chapters.mdc.NumberCruncherServer +chapters.mdc.SimpleMDC +chapters.mdc.UserServletFilter +chapters.migrationFromLog4j.Log4jMain +chapters.migrationFromLog4j.LogbackMain +chapters.migrationFromLog4j.TrivialLog4jAppender +chapters.migrationFromLog4j.TrivialLog4jLayout +chapters.migrationFromLog4j.TrivialLogbackAppender +chapters.migrationFromLog4j.TrivialLogbackLayout +chapters.onJoran.calculator.AddAction +chapters.onJoran.calculator.Calculator1 +chapters.onJoran.calculator.Calculator2 +chapters.onJoran.calculator.ComputationAction1 +chapters.onJoran.calculator.ComputationAction2 +chapters.onJoran.calculator.LiteralAction +chapters.onJoran.calculator.MultiplyAction +chapters.onJoran.helloWorld.HelloWorld +chapters.onJoran.helloWorld.HelloWorldAction +chapters.onJoran.implicit.NOPAction +chapters.onJoran.implicit.PrintMe +chapters.onJoran.implicit.PrintMeImplicitAction +chapters.onJoran.newRule.NewRuleCalculator +chapters.onJoran.SimpleConfigurator +chapters.receivers.socket.AppenderExample +chapters.receivers.socket.ReceiverExample +integrator.Activator +org.slf4j.CompatibilityAssertionTest +org.slf4j.IncompatibleMultiBindingAssertionTest +org.slf4j.issues.Issue324Test +org.slf4j.MultiBindingAssertionTest +org.slf4j.NoProviderAssertionTest +org.slf4j.OldAPIVersionMismatchAssertionTest +org.slf4j.OutputVerifier +org.slf4j.StringPrintStream +org.slf4j.test_osgi.BundleTest +org.slf4j.test_osgi.CheckingBundleListener +org.slf4j.test_osgi.FelixHost +org.slf4j.test_osgi.FrameworkErrorListener +org.apache.commons.logging.impl.NoOpLog +org.apache.commons.logging.impl.SimpleLog +org.apache.commons.logging.impl.SLF4JLocationAwareLog +org.apache.commons.logging.impl.SLF4JLog +org.apache.commons.logging.impl.SLF4JLogFactory +org.apache.commons.logging.Log +org.apache.commons.logging.LogConfigurationException +org.apache.commons.logging.LogFactory +org.apache.commons.logging.test.InvokeJCLTest +org.apache.commons.logging.test.SerializationTest +org.slf4j.bridge.SLF4JBridgeHandler +org.slf4j.bridge.ListAppender +org.slf4j.bridge.SLF4JBridgeHandlerPerfTest +org.slf4j.bridge.SLF4JBridgeHandlerTest +src.main.test.DummyObject +src.main.test.Log4j12Calls +src.main.test.Log4j13Calls +src.main.test.LoggerTest +org.apache.log4j.Appender +org.apache.log4j.AppenderSkeleton +org.apache.log4j.BasicConfigurator +org.apache.log4j.Category +org.apache.log4j.ConsoleAppender +org.apache.log4j.FileAppender +org.apache.log4j.helpers.LogLog +org.apache.log4j.helpers.NullEnumeration +org.apache.log4j.Layout +org.apache.log4j.Level +org.apache.log4j.Log4jLoggerFactory +org.apache.log4j.Logger +org.apache.log4j.LogManager +org.apache.log4j.MDC +org.apache.log4j.NDC +org.apache.log4j.PatternLayout +org.apache.log4j.Priority +org.apache.log4j.PropertyConfigurator +org.apache.log4j.RollingFileAppender +org.apache.log4j.SimpleLayout +org.apache.log4j.spi.Configurator +org.apache.log4j.spi.ErrorHandler +org.apache.log4j.spi.Filter +org.apache.log4j.spi.HierarchyEventListener +org.apache.log4j.spi.LoggerFactory +org.apache.log4j.spi.LoggerRepository +org.apache.log4j.spi.LoggingEvent +org.apache.log4j.spi.OptionHandler +org.apache.log4j.WriterAppender +org.apache.log4j.xml.DOMConfigurator +org.apache.log4j.test.NDCTest +org.apache.log4j.test.Trivial +org.dummy.Bug131 +org.dummy.Bug139 +org.dummy.ListHandler +org.slf4j.osgi.logservice.impl.Activator +org.slf4j.osgi.logservice.impl.LogServiceFactory +org.slf4j.osgi.logservice.impl.LogServiceImpl +org.slf4j.event.DefaultLoggingEvent +org.slf4j.event.EventConstants +org.slf4j.event.EventRecodingLogger +org.slf4j.event.KeyValuePair +org.slf4j.event.Level +org.slf4j.event.LoggingEvent +org.slf4j.event.SubstituteLoggingEvent +org.slf4j.helpers.AbstractLogger +org.slf4j.helpers.BasicMarker +org.slf4j.helpers.BasicMarkerFactory +org.slf4j.helpers.BasicMDCAdapter +org.slf4j.helpers.FormattingTuple +org.slf4j.helpers.LegacyAbstractLogger +org.slf4j.helpers.MarkerIgnoringBase +org.slf4j.helpers.MessageFormatter +org.slf4j.helpers.NamedLoggerBase +org.slf4j.helpers.NOPLogger +org.slf4j.helpers.NOPLoggerFactory +org.slf4j.helpers.NOPMDCAdapter +org.slf4j.helpers.NOP_FallbackServiceProvider +org.slf4j.helpers.NormalizedParameters +org.slf4j.helpers.SubstituteLogger +org.slf4j.helpers.SubstituteLoggerFactory +org.slf4j.helpers.SubstituteServiceProvider +org.slf4j.helpers.Util +org.slf4j.ILoggerFactory +org.slf4j.IMarkerFactory +org.slf4j.Logger +org.slf4j.LoggerFactory +org.slf4j.LoggerFactoryFriend +org.slf4j.Marker +org.slf4j.MarkerFactory +org.slf4j.MDC +org.slf4j.spi.DefaultLoggingEventBuilder +org.slf4j.spi.LocationAwareLogger +org.slf4j.spi.LoggerFactoryBinder +org.slf4j.spi.LoggingEventAware +org.slf4j.spi.LoggingEventBuilder +org.slf4j.spi.MarkerFactoryBinder +org.slf4j.spi.MDCAdapter +org.slf4j.spi.NOPLoggingEventBuilder +org.slf4j.spi.SLF4JServiceProvider +.module-info +org.slf4j.basicTests.BasicMarkerTest +org.slf4j.basicTests.Differentiator +org.slf4j.basicTests.DoubleCheckedInt +org.slf4j.basicTests.FluentAPIUsage +org.slf4j.basicTests.NoBindingMultithreadedInitializationTest +org.slf4j.basicTests.NoBindingTest +org.slf4j.eventTest.EventRecodingLoggerTest +org.slf4j.helpers.BasicMDCAdapterTest +org.slf4j.helpers.MDCAdapterTestBase +org.slf4j.helpers.MessageFormatterTest +org.slf4j.helpers.SubstitutableLoggerTest +org.slf4j.helpers.SubstituteLoggerFactoryTest +org.slf4j.LoggerAccessingThread +org.slf4j.rule.BlaTest +org.slf4j.rule.RunInNewThread +org.slf4j.rule.RunInNewThreadRule +org.slf4j.rule.RunInNewThreadStatement +org.slf4j.testHarness.MultithreadedInitializationTest +org.slf4j.agent.AgentOptions +org.slf4j.agent.AgentPremain +org.slf4j.cal10n.LocLogger +org.slf4j.cal10n.LocLoggerFactory +org.slf4j.ext.LoggerWrapper +org.slf4j.ext.XLogger +org.slf4j.ext.XLoggerFactory +org.slf4j.instrumentationssistHelper +org.slf4j.instrumentation.LogTransformer +org.slf4j.instrumentation.ToStringHelper +org.slf4j.NDC +org.slf4j.profiler.DurationUnit +org.slf4j.profiler.Profiler +org.slf4j.profiler.ProfilerRegistry +org.slf4j.profiler.SpacePadder +org.slf4j.profiler.StopWatch +org.slf4j.profiler.TimeInstrument +org.slf4j.profiler.TimeInstrumentStatus +org.slf4j.profiler.Util +org.slf4j.cal10n_dummy.LocLoggerTest +org.slf4j.cal10n_dummy.Months +org.slf4j.cal10n_dummy.MyApplication +org.slf4j.cal10n_dummy.PackageTest +org.slf4j.cal10n_dummy.Production +org.slf4j.dummyExt.ListAppender +org.slf4j.dummyExt.PackageTest +org.slf4j.dummyExt.XLoggerTest +org.slf4j.instrumentation.ToStringHelperTest +org.slf4j.NDCTest +org.slf4j.profiler.BasicProfilerDemo +org.slf4j.profiler.NestedProfilerDemo +org.slf4j.profiler.NestedProfilerDemo2 +org.slf4j.profiler.PackageTest +org.slf4j.profiler.ProfilerTest +org.slf4j.profiler.RandomIntegerArrayGenerator +org.slf4j.profiler.SortAndPruneComposites +org.slf4j.profiler.UtilTest +org.slf4j.jul.JDK14LoggerAdapter +org.slf4j.jul.JDK14LoggerFactory +org.slf4j.jul.JULServiceProvider +org.slf4j.issue.CallerInfoTest +org.slf4j.issue.LoggerSerializationTest +org.slf4j.jul.CountingHandler +org.slf4j.jul.FluentApiInvocationTest +org.slf4j.jul.InvocationTest +org.slf4j.jul.JDK14AdapterLoggerNameTest +org.slf4j.jul.JDK14MultithreadedInitializationTest +org.slf4j.jul.ListHandler +org.apache.log4j.MDCFriend +org.slf4j.log4j12.Log4j12ServiceProvider +org.slf4j.log4j12.Log4jLoggerAdapter +org.slf4j.log4j12.Log4jLoggerFactory +org.slf4j.log4j12.Log4jMDCAdapter +org.slf4j.log4j12.VersionUtil +org.slf4j.log4j12.InvocationTest +org.slf4j.log4j12.ListAppender +org.slf4j.log4j12.Log4j12MultithreadedInitializationTest +org.slf4j.log4j12.Log4jMDCAdapterTest +org.slf4j.log4j12.MDCFriendTest +org.slf4j.log4j12.RecursiveInitializationTest +org.slf4j.log4j12.testHarness.RecursiveAppender +org.slf4j.log4j12.UtilVersionTest +org.slf4j.migrator.Constant +org.slf4j.migrator.ConversionException +org.slf4j.migrator.FileSelector +org.slf4j.migrator.helper.Abbreviator +org.slf4j.migrator.helper.SpringLayoutHelper +org.slf4j.migrator.InplaceFileConverter +org.slf4j.migrator.internal.ConversionTask +org.slf4j.migrator.internal.MigratorFrame +org.slf4j.migrator.internal.ProgressListener +org.slf4j.migrator.internal.ProgressListenerImpl +org.slf4j.migrator.line.ConversionRule +org.slf4j.migrator.line.EmptyRuleSet +org.slf4j.migrator.line.JCLRuleSet +org.slf4j.migrator.line.JULRuleSet +org.slf4j.migrator.line.LineConverter +org.slf4j.migrator.line.Log4jRuleSet +org.slf4j.migrator.line.MultiGroupConversionRule +org.slf4j.migrator.line.RuleSet +org.slf4j.migrator.line.SingleConversionRule +org.slf4j.migrator.Main +org.slf4j.migrator.ProjectConverter +org.slf4j.migrator.RuleSetFactory +org.slf4j.migrator.AllTest +org.slf4j.migrator.AternativeApproach +org.slf4j.migrator.FileConverterTest +org.slf4j.migrator.helper.AbbreviatorTest +org.slf4j.migrator.helper.PackageTest +org.slf4j.migrator.helper.RandomHelper +org.slf4j.migrator.internal.NopProgressListener +org.slf4j.migrator.line.JCLRuleSetTest +org.slf4j.migrator.line.Log4jRuleSetTest +org.slf4j.migrator.line.NoConversionTest +org.slf4j.migrator.line.PackageTest +org.slf4j.migrator.line.TrivialMatcher +org.slf4j.migrator.line.TrivialMatcherTest +org.slf4j.migrator.PackageTest +org.slf4j.migrator.ProjectConverterTest +org.slf4j.nop.NOPServiceProvider +org.slf4j.nop.InvocationTest +org.slf4j.nop.MultithreadedInitializationTest +org.slf4j.simple.OutputChoice +org.slf4j.simple.SimpleLogger +org.slf4j.simple.SimpleLoggerConfiguration +org.slf4j.simple.SimpleLoggerFactory +org.slf4j.simple.SimpleServiceProvider +org.slf4j.simple.DetectLoggerNameMismatchTest +org.slf4j.simple.DoubleInitializationPitfallTest +org.slf4j.simple.InvocationTest +org.slf4j.simple.multiThreadedExecution.MultithereadedExecutionTest +org.slf4j.simple.multiThreadedExecution.StateCheckingPrintStream +org.slf4j.simple.SilentPrintStream +org.slf4j.simple.SimpleLoggerMultithreadedInitializationTest +org.slf4j.simple.SimpleLoggerTest +org.slf4j.simple.StringPrintStream +com.sun.activation.registries.LogSupport +com.sun.activation.registries.MailcapFile +com.sun.activation.registries.MailcapParseException +com.sun.activation.registries.MailcapTokenizer +com.sun.activation.registries.MimeTypeEntry +com.sun.activation.registries.MimeTypeFile +javax.activation.ActivationDataFlavor +javax.activation.CommandInfo +javax.activation.CommandMap +javax.activation.CommandObject +javax.activation.DataContentHandler +javax.activation.DataContentHandlerFactory +javax.activation.DataHandler +javax.activation.DataSource +javax.activation.FileDataSource +javax.activation.FileTypeMap +javax.activation.MailcapCommandMap +javax.activation.MimeType +javax.activation.MimeTypeParameterList +javax.activation.MimeTypeParseException +javax.activation.MimetypesFileTypeMap +javax.activation.SecuritySupport +javax.activation.UnsupportedDataTypeException +javax.activation.URLDataSource +com.sun.mail.dsn.DeliveryStatus +com.sun.mail.dsn.DispositionNotification +com.sun.mail.dsn.MessageHeaders +com.sun.mail.dsn.message_deliverystatus +com.sun.mail.dsn.message_dispositionnotification +com.sun.mail.dsn.MultipartReport +com.sun.mail.dsn.multipart_report +com.sun.mail.dsn.Report +com.sun.mail.dsn.text_rfc822headers +com.sun.mail.dsn.MultipartReportTest +com.sun.mail.dsn.NullOutputStream +com.sun.mail.gimap.GmailFolder +com.sun.mail.gimap.GmailMessage +com.sun.mail.gimap.GmailMsgIdTerm +com.sun.mail.gimap.GmailProvider +com.sun.mail.gimap.GmailRawSearchTerm +com.sun.mail.gimap.GmailSSLProvider +com.sun.mail.gimap.GmailSSLStore +com.sun.mail.gimap.GmailStore +com.sun.mail.gimap.GmailThrIdTerm +com.sun.mail.gimap.LongTerm +com.sun.mail.gimap.protocol.GmailProtocol +com.sun.mail.gimap.protocol.GmailSearchSequence +com.sun.mail.auth.MD4 +com.sun.mail.auth.Ntlm +com.sun.mail.auth.OAuth2SaslClient +com.sun.mail.auth.OAuth2SaslClientFactory +com.sun.mail.handlers.handler_base +com.sun.mail.handlers.image_gif +com.sun.mail.handlers.image_jpeg +com.sun.mail.handlers.message_rfc822 +com.sun.mail.handlers.multipart_mixed +com.sun.mail.handlers.text_html +com.sun.mail.handlers.text_plain +com.sun.mail.handlers.text_xml +com.sun.mail.iap.Argument +com.sun.mail.iap.BadCommandException +com.sun.mail.iap.ByteArray +com.sun.mail.iap.CommandFailedException +com.sun.mail.iap.ConnectionException +com.sun.mail.iap.Literal +com.sun.mail.iap.LiteralException +com.sun.mail.iap.ParsingException +com.sun.mail.iap.Protocol +com.sun.mail.iap.ProtocolException +com.sun.mail.iap.Response +com.sun.mail.iap.ResponseHandler +com.sun.mail.iap.ResponseInputStream +com.sun.mail.imap.ACL +com.sun.mail.imap.AppendUID +com.sun.mail.imap.CopyUID +com.sun.mail.imap.DefaultFolder +com.sun.mail.imap.IdleManager +com.sun.mail.imap.IMAPBodyPart +com.sun.mail.imap.IMAPFolder +com.sun.mail.imap.IMAPInputStream +com.sun.mail.imap.IMAPMessage +com.sun.mail.imap.IMAPMultipartDataSource +com.sun.mail.imap.IMAPNestedMessage +com.sun.mail.imap.IMAPProvider +com.sun.mail.imap.IMAPSSLProvider +com.sun.mail.imap.IMAPSSLStore +com.sun.mail.imap.IMAPStore +com.sun.mail.imap.MessageCache +com.sun.mail.imap.MessageVanishedEvent +com.sun.mail.imap.ModifiedSinceTerm +com.sun.mail.imap.OlderTerm +com.sun.mail.imap.protocol.BASE64MailboxDecoder +com.sun.mail.imap.protocol.BASE64MailboxEncoder +com.sun.mail.imap.protocol.BODY +com.sun.mail.imap.protocol.BODYSTRUCTURE +com.sun.mail.imap.protocol.ENVELOPE +com.sun.mail.imap.protocol.FetchItem +com.sun.mail.imap.protocol.FetchResponse +com.sun.mail.imap.protocol.FLAGS +com.sun.mail.imap.protocol.ID +com.sun.mail.imap.protocol.IMAPProtocol +com.sun.mail.imap.protocol.IMAPReferralException +com.sun.mail.imap.protocol.IMAPResponse +com.sun.mail.imap.protocol.IMAPSaslAuthenticator +com.sun.mail.imap.protocol.INTERNALDATE +com.sun.mail.imap.protocol.Item +com.sun.mail.imap.protocol.ListInfo +com.sun.mail.imap.protocol.MailboxInfo +com.sun.mail.imap.protocol.MessageSet +com.sun.mail.imap.protocol.MODSEQ +com.sun.mail.imap.protocol.Namespaces +com.sun.mail.imap.protocol.RFC822DATA +com.sun.mail.imap.protocol.RFC822SIZE +com.sun.mail.imap.protocol.SaslAuthenticator +com.sun.mail.imap.protocol.SearchSequence +com.sun.mail.imap.protocol.Status +com.sun.mail.imap.protocol.UID +com.sun.mail.imap.protocol.UIDSet +com.sun.mail.imap.ReferralException +com.sun.mail.imap.ResyncData +com.sun.mail.imap.Rights +com.sun.mail.imap.SortTerm +com.sun.mail.imap.Utility +com.sun.mail.imap.YoungerTerm +com.sun.mail.pop3.AppendStream +com.sun.mail.pop3.DefaultFolder +com.sun.mail.pop3.POP3Folder +com.sun.mail.pop3.POP3Message +com.sun.mail.pop3.POP3Provider +com.sun.mail.pop3.POP3SSLProvider +com.sun.mail.pop3.POP3SSLStore +com.sun.mail.pop3.POP3Store +com.sun.mail.pop3.Protocol +com.sun.mail.pop3.Status +com.sun.mail.pop3.TempFile +com.sun.mail.pop3.WritableSharedFile +com.sun.mail.smtp.DigestMD5 +com.sun.mail.smtp.SaslAuthenticator +com.sun.mail.smtp.SMTPAddressFailedException +com.sun.mail.smtp.SMTPAddressSucceededException +com.sun.mail.smtp.SMTPMessage +com.sun.mail.smtp.SMTPOutputStream +com.sun.mail.smtp.SMTPProvider +com.sun.mail.smtp.SMTPSaslAuthenticator +com.sun.mail.smtp.SMTPSenderFailedException +com.sun.mail.smtp.SMTPSendFailedException +com.sun.mail.smtp.SMTPSSLProvider +com.sun.mail.smtp.SMTPSSLTransport +com.sun.mail.smtp.SMTPTransport +com.sun.mail.util.ASCIIUtility +com.sun.mail.util.BASE64DecoderStream +com.sun.mail.util.BASE64EncoderStream +com.sun.mail.util.BEncoderStream +com.sun.mail.util.CRLFOutputStream +com.sun.mail.util.DecodingException +com.sun.mail.util.FolderClosedIOException +com.sun.mail.util.LineInputStream +com.sun.mail.util.LineOutputStream +com.sun.mail.util.logging.CollectorFormatter +com.sun.mail.util.logging.CompactFormatter +com.sun.mail.util.logging.DurationFilter +com.sun.mail.util.logging.LogManagerProperties +com.sun.mail.util.logging.MailHandler +com.sun.mail.util.logging.SeverityComparator +com.sun.mail.util.LogOutputStream +com.sun.mail.util.MailConnectException +com.sun.mail.util.MailLogger +com.sun.mail.util.MailSSLSocketFactory +com.sun.mail.util.MessageRemovedIOException +com.sun.mail.util.MimeUtil +com.sun.mail.util.PropUtil +com.sun.mail.util.QDecoderStream +com.sun.mail.util.QEncoderStream +com.sun.mail.util.QPDecoderStream +com.sun.mail.util.QPEncoderStream +com.sun.mail.util.ReadableMime +com.sun.mail.util.SharedByteArrayOutputStream +com.sun.mail.util.SocketConnectException +com.sun.mail.util.SocketFetcher +com.sun.mail.util.TraceInputStream +com.sun.mail.util.TraceOutputStream +com.sun.mail.util.UUDecoderStream +com.sun.mail.util.UUEncoderStream +com.sun.mail.util.WriteTimeoutSocket +javax.mail.Address +javax.mail.AuthenticationFailedException +javax.mail.Authenticator +javax.mail.BodyPart +javax.mail.EncodingAware +javax.mail.event.ConnectionAdapter +javax.mail.event.ConnectionEvent +javax.mail.event.ConnectionListener +javax.mail.event.FolderAdapter +javax.mail.event.FolderEvent +javax.mail.event.FolderListener +javax.mail.event.MailEvent +javax.mail.event.MessageChangedEvent +javax.mail.event.MessageChangedListener +javax.mail.event.MessageCountAdapter +javax.mail.event.MessageCountEvent +javax.mail.event.MessageCountListener +javax.mail.event.StoreEvent +javax.mail.event.StoreListener +javax.mail.event.TransportAdapter +javax.mail.event.TransportEvent +javax.mail.event.TransportListener +javax.mail.EventQueue +javax.mail.FetchProfile +javax.mail.Flags +javax.mail.Folder +javax.mail.FolderClosedException +javax.mail.FolderNotFoundException +javax.mail.Header +javax.mail.IllegalWriteException +javax.mail.internet.AddressException +javax.mail.internet.ContentDisposition +javax.mail.internet.ContentType +javax.mail.internet.HeaderTokenizer +javax.mail.internet.InternetAddress +javax.mail.internet.InternetHeaders +javax.mail.internet.MailDateFormat +javax.mail.internet.MimeBodyPart +javax.mail.internet.MimeMessage +javax.mail.internet.MimeMultipart +javax.mail.internet.MimePart +javax.mail.internet.MimePartDataSource +javax.mail.internet.MimeUtility +javax.mail.internet.NewsAddress +javax.mail.internet.ParameterList +javax.mail.internet.ParseException +javax.mail.internet.PreencodedMimeBodyPart +javax.mail.internet.SharedInputStream +javax.mail.internet.UniqueValue +javax.mail.MailSessionDefinition +javax.mail.MailSessionDefinitions +javax.mail.Message +javax.mail.MessageAware +javax.mail.MessageContext +javax.mail.MessageRemovedException +javax.mail.MessagingException +javax.mail.MethodNotSupportedException +javax.mail.Multipart +javax.mail.MultipartDataSource +javax.mail.NoSuchProviderException +javax.mail.Part +javax.mail.PasswordAuthentication +javax.mail.Provider +javax.mail.Quota +javax.mail.QuotaAwareStore +javax.mail.ReadOnlyFolderException +javax.mail.search.AddressStringTerm +javax.mail.search.AddressTerm +javax.mail.search.AndTerm +javax.mail.search.BodyTerm +javax.mail.search.ComparisonTerm +javax.mail.search.DateTerm +javax.mail.search.FlagTerm +javax.mail.search.FromStringTerm +javax.mail.search.FromTerm +javax.mail.search.HeaderTerm +javax.mail.search.IntegerComparisonTerm +javax.mail.search.MessageIDTerm +javax.mail.search.MessageNumberTerm +javax.mail.search.NotTerm +javax.mail.search.OrTerm +javax.mail.search.ReceivedDateTerm +javax.mail.search.RecipientStringTerm +javax.mail.search.RecipientTerm +javax.mail.search.SearchException +javax.mail.search.SearchTerm +javax.mail.search.SentDateTerm +javax.mail.search.SizeTerm +javax.mail.search.StringTerm +javax.mail.search.SubjectTerm +javax.mail.SendFailedException +javax.mail.Service +javax.mail.Session +javax.mail.Store +javax.mail.StoreClosedException +javax.mail.Transport +javax.mail.UIDFolder +javax.mail.URLName +javax.mail.util.ByteArrayDataSource +javax.mail.util.SharedByteArrayInputStream +javax.mail.util.SharedFileInputStream +com.sun.mail.handlers.TextXmlTest +com.sun.mail.iap.ProtocolTest +com.sun.mail.iap.ResponseInputStreamTest +com.sun.mail.iap.ResponseTest +com.sun.mail.imap.IMAPAlertTest +com.sun.mail.imap.IMAPAuthDebugTest +com.sun.mail.imap.IMAPCloseFailureTest +com.sun.mail.imap.IMAPConnectFailureTest +com.sun.mail.imap.IMAPFetchProfileTest +com.sun.mail.imap.IMAPFolderTest +com.sun.mail.imap.IMAPHandler +com.sun.mail.imap.IMAPIdleManagerTest +com.sun.mail.imap.IMAPIdleStateTest +com.sun.mail.imap.IMAPIdleUntaggedResponseTest +com.sun.mail.imap.IMAPIDTest +com.sun.mail.imap.IMAPLoginCapabilitiesTest +com.sun.mail.imap.IMAPLoginFailureTest +com.sun.mail.imap.IMAPLoginHandler +com.sun.mail.imap.IMAPLoginReferralTest +com.sun.mail.imap.IMAPMessageNumberOutOfRangeTest +com.sun.mail.imap.IMAPMessageTest +com.sun.mail.imap.IMAPPlainHandler +com.sun.mail.imap.IMAPResponseEventTest +com.sun.mail.imap.IMAPSaslHandler +com.sun.mail.imap.IMAPSaslLoginTest +com.sun.mail.imap.IMAPSearchTest +com.sun.mail.imap.IMAPStoreTest +com.sun.mail.imap.IMAPUidExpungeTest +com.sun.mail.imap.MessageCacheTest +com.sun.mail.imap.protocol.BODYSTRUCTURETest +com.sun.mail.imap.protocol.EnvelopeTest +com.sun.mail.imap.protocol.IMAPProtocolTest +com.sun.mail.imap.protocol.MODSEQTest +com.sun.mail.imap.protocol.NamespacesTest +com.sun.mail.imap.protocol.StatusTest +com.sun.mail.imap.protocol.StratoImapBugfixTest +com.sun.mail.imap.protocol.UIDSetTest +com.sun.mail.pop3.POP3AuthDebugTest +com.sun.mail.pop3.POP3FolderClosedExceptionTest +com.sun.mail.pop3.POP3Handler +com.sun.mail.pop3.POP3MessageTest +com.sun.mail.pop3.POP3ReadableMimeTest +com.sun.mail.pop3.POP3StoreTest +com.sun.mail.smtp.NopServer +com.sun.mail.smtp.SMTPAuthDebugTest +com.sun.mail.smtp.SMTPBdatTest +com.sun.mail.smtp.SMTPCloseTest +com.sun.mail.smtp.SMTPConnectFailureTest +com.sun.mail.smtp.SMTPHandler +com.sun.mail.smtp.SMTPIOExceptionTest +com.sun.mail.smtp.SMTPLoginHandler +com.sun.mail.smtp.SMTPSaslHandler +com.sun.mail.smtp.SMTPSaslLoginTest +com.sun.mail.smtp.SMTPUtf8Test +com.sun.mail.smtp.SMTPWriteTimeoutTest +com.sun.mail.test.AsciiStringInputStream +com.sun.mail.test.ClassLoaderSuite +com.sun.mail.test.NullOutputStream +com.sun.mail.test.ProtocolHandler +com.sun.mail.test.SavedSocketFactory +com.sun.mail.test.TestServer +com.sun.mail.test.TestSocketFactory +com.sun.mail.test.TestSSLSocketFactory +com.sun.mail.util.BASE64Test +com.sun.mail.util.ContentTypeCleaner +com.sun.mail.util.LineInputStreamTest +com.sun.mail.util.logging.AbstractLogging +com.sun.mail.util.logging.CollectorFormatterTest +com.sun.mail.util.logging.CompactFormatterTest +com.sun.mail.util.logging.DurationFilterTest +com.sun.mail.util.logging.LogManagerPropertiesTest +com.sun.mail.util.logging.MailHandlerTest +com.sun.mail.util.logging.SeverityComparatorTest +com.sun.mail.util.MimeUtilTestSuite +com.sun.mail.util.PropUtilTest +com.sun.mail.util.QPEncoderStreamTest +com.sun.mail.util.SocketFetcherTest +com.sun.mail.util.UUDecoderStreamTest +com.sun.mail.util.WriteTimeoutSocketTest +javax.mail.internet.AddAddressHeaderTest +javax.mail.internet.AddFromTest +javax.mail.internet.AllowEncodedMessages +javax.mail.internet.AppleFileNames +javax.mail.internet.ContentDispositionNoStrict +javax.mail.internet.ContentDispositionStrict +javax.mail.internet.ContentDispositionTestSuite +javax.mail.internet.ContentTypeTest +javax.mail.internet.DecodeParameters +javax.mail.internet.EncodeFileName +javax.mail.internet.EncodeFileNameNoEncodeParameters +javax.mail.internet.FoldTest +javax.mail.internet.GetLocalAddressTest +javax.mail.internet.HeaderTokenizerTest +javax.mail.internet.InternetAddressExtraTest +javax.mail.internet.InternetAddressFoldTest +javax.mail.internet.InternetAddressTest +javax.mail.internet.InternetHeadersTest +javax.mail.internet.MailDateFormatTest +javax.mail.internet.MimeBodyPartTest +javax.mail.internet.MimeBodyPartTestSuite +javax.mail.internet.MimeMessageTest +javax.mail.internet.MimeMessageTestSuite +javax.mail.internet.MimeMultipartParseTest +javax.mail.internet.MimeMultipartPreambleTest +javax.mail.internet.MimeMultipartPropertyTest +javax.mail.internet.MimeUtilityTest +javax.mail.internet.ModifyMessageTest +javax.mail.internet.NewsAddressTest +javax.mail.internet.NoEncodeFileName +javax.mail.internet.NonAsciiBoundaryTest +javax.mail.internet.NonAsciiFileNames +javax.mail.internet.ParameterListDecode +javax.mail.internet.ParameterListTests +javax.mail.internet.ParameterListTestSuite +javax.mail.internet.ParametersNoStrict +javax.mail.internet.ReferencesTest +javax.mail.internet.RestrictEncodingTest +javax.mail.internet.WindowsFileNames +javax.mail.search.SearchTermSerializationTest +javax.mail.URLNameTest +com.sun.mail.mbox.ContentLengthCounter +com.sun.mail.mbox.ContentLengthUpdater +com.sun.mail.mbox.DefaultMailbox +com.sun.mail.mbox.FileInterface +com.sun.mail.mbox.InboxFile +com.sun.mail.mbox.LineCounter +com.sun.mail.mbox.Mailbox +com.sun.mail.mbox.MailFile +com.sun.mail.mbox.MboxFolder +com.sun.mail.mbox.MboxMessage +com.sun.mail.mbox.MboxProvider +com.sun.mail.mbox.MboxStore +com.sun.mail.mbox.MessageLoader +com.sun.mail.mbox.NewlineOutputStream +com.sun.mail.mbox.SolarisMailbox +com.sun.mail.mbox.SunOSMailbox +com.sun.mail.mbox.SunV3BodyPart +com.sun.mail.mbox.SunV3Multipart +com.sun.mail.mbox.TempFile +com.sun.mail.mbox.UNIXFile +com.sun.mail.mbox.UNIXFolder +com.sun.mail.mbox.UNIXInbox +com.sun.mail.remote.POP3RemoteProvider +com.sun.mail.remote.POP3RemoteStore +com.sun.mail.remote.RemoteDefaultFolder +com.sun.mail.remote.RemoteInbox +com.sun.mail.remote.RemoteStore +com.sun.mail.mbox.MboxFolderExpungeTest +com.sun.mail.mbox.MboxFolderTest +com.sun.mail.dsn.DeliveryStatus +com.sun.mail.dsn.DispositionNotification +com.sun.mail.dsn.MessageHeaders +com.sun.mail.dsn.message_deliverystatus +com.sun.mail.dsn.message_dispositionnotification +com.sun.mail.dsn.MultipartReport +com.sun.mail.dsn.multipart_report +com.sun.mail.dsn.Report +com.sun.mail.dsn.text_rfc822headers +com.sun.mail.dsn.MultipartReportTest +com.sun.mail.dsn.NullOutputStream +com.sun.mail.gimap.GmailFolder +com.sun.mail.gimap.GmailMessage +com.sun.mail.gimap.GmailMsgIdTerm +com.sun.mail.gimap.GmailProvider +com.sun.mail.gimap.GmailRawSearchTerm +com.sun.mail.gimap.GmailSSLProvider +com.sun.mail.gimap.GmailSSLStore +com.sun.mail.gimap.GmailStore +com.sun.mail.gimap.GmailThrIdTerm +com.sun.mail.gimap.LongTerm +com.sun.mail.gimap.protocol.GmailProtocol +com.sun.mail.gimap.protocol.GmailSearchSequence +com.sun.mail.auth.MD4 +com.sun.mail.auth.Ntlm +com.sun.mail.auth.OAuth2SaslClient +com.sun.mail.auth.OAuth2SaslClientFactory +com.sun.mail.handlers.handler_base +com.sun.mail.handlers.image_gif +com.sun.mail.handlers.image_jpeg +com.sun.mail.handlers.message_rfc822 +com.sun.mail.handlers.multipart_mixed +com.sun.mail.handlers.text_html +com.sun.mail.handlers.text_plain +com.sun.mail.handlers.text_xml +com.sun.mail.iap.Argument +com.sun.mail.iap.BadCommandException +com.sun.mail.iap.ByteArray +com.sun.mail.iap.CommandFailedException +com.sun.mail.iap.ConnectionException +com.sun.mail.iap.Literal +com.sun.mail.iap.LiteralException +com.sun.mail.iap.ParsingException +com.sun.mail.iap.Protocol +com.sun.mail.iap.ProtocolException +com.sun.mail.iap.Response +com.sun.mail.iap.ResponseHandler +com.sun.mail.iap.ResponseInputStream +com.sun.mail.imap.ACL +com.sun.mail.imap.AppendUID +com.sun.mail.imap.CopyUID +com.sun.mail.imap.DefaultFolder +com.sun.mail.imap.IdleManager +com.sun.mail.imap.IMAPBodyPart +com.sun.mail.imap.IMAPFolder +com.sun.mail.imap.IMAPInputStream +com.sun.mail.imap.IMAPMessage +com.sun.mail.imap.IMAPMultipartDataSource +com.sun.mail.imap.IMAPNestedMessage +com.sun.mail.imap.IMAPProvider +com.sun.mail.imap.IMAPSSLProvider +com.sun.mail.imap.IMAPSSLStore +com.sun.mail.imap.IMAPStore +com.sun.mail.imap.MessageCache +com.sun.mail.imap.MessageVanishedEvent +com.sun.mail.imap.ModifiedSinceTerm +com.sun.mail.imap.OlderTerm +com.sun.mail.imap.protocol.BASE64MailboxDecoder +com.sun.mail.imap.protocol.BASE64MailboxEncoder +com.sun.mail.imap.protocol.BODY +com.sun.mail.imap.protocol.BODYSTRUCTURE +com.sun.mail.imap.protocol.ENVELOPE +com.sun.mail.imap.protocol.FetchItem +com.sun.mail.imap.protocol.FetchResponse +com.sun.mail.imap.protocol.FLAGS +com.sun.mail.imap.protocol.ID +com.sun.mail.imap.protocol.IMAPProtocol +com.sun.mail.imap.protocol.IMAPReferralException +com.sun.mail.imap.protocol.IMAPResponse +com.sun.mail.imap.protocol.IMAPSaslAuthenticator +com.sun.mail.imap.protocol.INTERNALDATE +com.sun.mail.imap.protocol.Item +com.sun.mail.imap.protocol.ListInfo +com.sun.mail.imap.protocol.MailboxInfo +com.sun.mail.imap.protocol.MessageSet +com.sun.mail.imap.protocol.MODSEQ +com.sun.mail.imap.protocol.Namespaces +com.sun.mail.imap.protocol.RFC822DATA +com.sun.mail.imap.protocol.RFC822SIZE +com.sun.mail.imap.protocol.SaslAuthenticator +com.sun.mail.imap.protocol.SearchSequence +com.sun.mail.imap.protocol.Status +com.sun.mail.imap.protocol.UID +com.sun.mail.imap.protocol.UIDSet +com.sun.mail.imap.ReferralException +com.sun.mail.imap.ResyncData +com.sun.mail.imap.Rights +com.sun.mail.imap.SortTerm +com.sun.mail.imap.Utility +com.sun.mail.imap.YoungerTerm +com.sun.mail.pop3.AppendStream +com.sun.mail.pop3.DefaultFolder +com.sun.mail.pop3.POP3Folder +com.sun.mail.pop3.POP3Message +com.sun.mail.pop3.POP3Provider +com.sun.mail.pop3.POP3SSLProvider +com.sun.mail.pop3.POP3SSLStore +com.sun.mail.pop3.POP3Store +com.sun.mail.pop3.Protocol +com.sun.mail.pop3.Status +com.sun.mail.pop3.TempFile +com.sun.mail.pop3.WritableSharedFile +com.sun.mail.smtp.DigestMD5 +com.sun.mail.smtp.SaslAuthenticator +com.sun.mail.smtp.SMTPAddressFailedException +com.sun.mail.smtp.SMTPAddressSucceededException +com.sun.mail.smtp.SMTPMessage +com.sun.mail.smtp.SMTPOutputStream +com.sun.mail.smtp.SMTPProvider +com.sun.mail.smtp.SMTPSaslAuthenticator +com.sun.mail.smtp.SMTPSenderFailedException +com.sun.mail.smtp.SMTPSendFailedException +com.sun.mail.smtp.SMTPSSLProvider +com.sun.mail.smtp.SMTPSSLTransport +com.sun.mail.smtp.SMTPTransport +com.sun.mail.util.ASCIIUtility +com.sun.mail.util.BASE64DecoderStream +com.sun.mail.util.BASE64EncoderStream +com.sun.mail.util.BEncoderStream +com.sun.mail.util.CRLFOutputStream +com.sun.mail.util.DecodingException +com.sun.mail.util.DefaultProvider +com.sun.mail.util.FolderClosedIOException +com.sun.mail.util.LineInputStream +com.sun.mail.util.LineOutputStream +com.sun.mail.util.logging.CollectorFormatter +com.sun.mail.util.logging.CompactFormatter +com.sun.mail.util.logging.DurationFilter +com.sun.mail.util.logging.LogManagerProperties +com.sun.mail.util.logging.MailHandler +com.sun.mail.util.logging.SeverityComparator +com.sun.mail.util.LogOutputStream +com.sun.mail.util.MailConnectException +com.sun.mail.util.MailLogger +com.sun.mail.util.MailSSLSocketFactory +com.sun.mail.util.MessageRemovedIOException +com.sun.mail.util.MimeUtil +com.sun.mail.util.PropUtil +com.sun.mail.util.QDecoderStream +com.sun.mail.util.QEncoderStream +com.sun.mail.util.QPDecoderStream +com.sun.mail.util.QPEncoderStream +com.sun.mail.util.ReadableMime +com.sun.mail.util.SharedByteArrayOutputStream +com.sun.mail.util.SocketConnectException +com.sun.mail.util.SocketFetcher +com.sun.mail.util.TraceInputStream +com.sun.mail.util.TraceOutputStream +com.sun.mail.util.UUDecoderStream +com.sun.mail.util.UUEncoderStream +com.sun.mail.util.WriteTimeoutSocket +jakarta.mail.Address +jakarta.mail.AuthenticationFailedException +jakarta.mail.Authenticator +jakarta.mail.BodyPart +jakarta.mail.EncodingAware +jakarta.mail.event.ConnectionAdapter +jakarta.mail.event.ConnectionEvent +jakarta.mail.event.ConnectionListener +jakarta.mail.event.FolderAdapter +jakarta.mail.event.FolderEvent +jakarta.mail.event.FolderListener +jakarta.mail.event.MailEvent +jakarta.mail.event.MessageChangedEvent +jakarta.mail.event.MessageChangedListener +jakarta.mail.event.MessageCountAdapter +jakarta.mail.event.MessageCountEvent +jakarta.mail.event.MessageCountListener +jakarta.mail.event.StoreEvent +jakarta.mail.event.StoreListener +jakarta.mail.event.TransportAdapter +jakarta.mail.event.TransportEvent +jakarta.mail.event.TransportListener +jakarta.mail.EventQueue +jakarta.mail.FetchProfile +jakarta.mail.Flags +jakarta.mail.Folder +jakarta.mail.FolderClosedException +jakarta.mail.FolderNotFoundException +jakarta.mail.Header +jakarta.mail.IllegalWriteException +jakarta.mail.internet.AddressException +jakarta.mail.internet.ContentDisposition +jakarta.mail.internet.ContentType +jakarta.mail.internet.HeaderTokenizer +jakarta.mail.internet.InternetAddress +jakarta.mail.internet.InternetHeaders +jakarta.mail.internet.MailDateFormat +jakarta.mail.internet.MimeBodyPart +jakarta.mail.internet.MimeMessage +jakarta.mail.internet.MimeMultipart +jakarta.mail.internet.MimePart +jakarta.mail.internet.MimePartDataSource +jakarta.mail.internet.MimeUtility +jakarta.mail.internet.NewsAddress +jakarta.mail.internet.ParameterList +jakarta.mail.internet.ParseException +jakarta.mail.internet.PreencodedMimeBodyPart +jakarta.mail.internet.SharedInputStream +jakarta.mail.internet.UniqueValue +jakarta.mail.MailSessionDefinition +jakarta.mail.MailSessionDefinitions +jakarta.mail.Message +jakarta.mail.MessageAware +jakarta.mail.MessageContext +jakarta.mail.MessageRemovedException +jakarta.mail.MessagingException +jakarta.mail.MethodNotSupportedException +jakarta.mail.Multipart +jakarta.mail.MultipartDataSource +jakarta.mail.NoSuchProviderException +jakarta.mail.Part +jakarta.mail.PasswordAuthentication +jakarta.mail.Provider +jakarta.mail.Quota +jakarta.mail.QuotaAwareStore +jakarta.mail.ReadOnlyFolderException +jakarta.mail.search.AddressStringTerm +jakarta.mail.search.AddressTerm +jakarta.mail.search.AndTerm +jakarta.mail.search.BodyTerm +jakarta.mail.search.ComparisonTerm +jakarta.mail.search.DateTerm +jakarta.mail.search.FlagTerm +jakarta.mail.search.FromStringTerm +jakarta.mail.search.FromTerm +jakarta.mail.search.HeaderTerm +jakarta.mail.search.IntegerComparisonTerm +jakarta.mail.search.MessageIDTerm +jakarta.mail.search.MessageNumberTerm +jakarta.mail.search.NotTerm +jakarta.mail.search.OrTerm +jakarta.mail.search.ReceivedDateTerm +jakarta.mail.search.RecipientStringTerm +jakarta.mail.search.RecipientTerm +jakarta.mail.search.SearchException +jakarta.mail.search.SearchTerm +jakarta.mail.search.SentDateTerm +jakarta.mail.search.SizeTerm +jakarta.mail.search.StringTerm +jakarta.mail.search.SubjectTerm +jakarta.mail.SendFailedException +jakarta.mail.Service +jakarta.mail.Session +jakarta.mail.Store +jakarta.mail.StoreClosedException +jakarta.mail.Transport +jakarta.mail.UIDFolder +jakarta.mail.URLName +jakarta.mail.util.ByteArrayDataSource +jakarta.mail.util.SharedByteArrayInputStream +jakarta.mail.util.SharedFileInputStream +jakarta.mail.internet.HeaderTokenizerTest +jakarta.mail.internet.InternetAddressExtraTest +jakarta.mail.internet.InternetAddressFoldTest +jakarta.mail.internet.InternetAddressTest +jakarta.mail.internet.InternetHeadersTest +jakarta.mail.internet.MailDateFormatTest +jakarta.mail.internet.MimeBodyPartTest +jakarta.mail.internet.MimeBodyPartTestSuite +jakarta.mail.internet.MimeMessageTest +jakarta.mail.internet.MimeMessageTestSuite +jakarta.mail.internet.MimeMultipartBCSIndexTest +jakarta.mail.internet.MimeMultipartParseTest +jakarta.mail.internet.MimeMultipartPreambleTest +jakarta.mail.internet.MimeMultipartPropertyTest +jakarta.mail.internet.MimeUtilityTest +jakarta.mail.internet.ModifyMessageTest +jakarta.mail.internet.NewsAddressTest +jakarta.mail.internet.NoEncodeFileName +jakarta.mail.internet.NoEncodeFileNameNoEncodeParameters +jakarta.mail.internet.NonAsciiBoundaryTest +jakarta.mail.internet.NonAsciiFileNames +jakarta.mail.internet.ParameterListDecode +jakarta.mail.internet.ParameterListTests +jakarta.mail.internet.ParameterListTestSuite +jakarta.mail.internet.ParametersNoStrict +jakarta.mail.internet.ReferencesTest +jakarta.mail.internet.RestrictEncodingTest +jakarta.mail.internet.Utf8Address +jakarta.mail.internet.WindowsFileNames +jakarta.mail.search.SearchTermSerializationTest +jakarta.mail.URLNameTest +com.sun.mail.mbox.ContentLengthCounter +com.sun.mail.mbox.ContentLengthUpdater +com.sun.mail.mbox.DefaultMailbox +com.sun.mail.mbox.FileInterface +com.sun.mail.mbox.InboxFile +com.sun.mail.mbox.LineCounter +com.sun.mail.mbox.Mailbox +com.sun.mail.mbox.MailFile +com.sun.mail.mbox.MboxFolder +com.sun.mail.mbox.MboxMessage +com.sun.mail.mbox.MboxProvider +com.sun.mail.mbox.MboxStore +com.sun.mail.mbox.MessageLoader +com.sun.mail.mbox.NewlineOutputStream +com.sun.mail.mbox.SolarisMailbox +com.sun.mail.mbox.SunOSMailbox +com.sun.mail.mbox.SunV3BodyPart +com.sun.mail.mbox.SunV3Multipart +com.sun.mail.mbox.TempFile +com.sun.mail.mbox.UNIXFile +com.sun.mail.mbox.UNIXFolder +com.sun.mail.mbox.UNIXInbox +com.sun.mail.remote.POP3RemoteProvider +com.sun.mail.remote.POP3RemoteStore +com.sun.mail.remote.RemoteDefaultFolder +com.sun.mail.remote.RemoteInbox +com.sun.mail.remote.RemoteStore +demo.ListAttachmentsTag +demo.ListAttachmentsTEI +demo.ListMessagesTag +demo.ListMessagesTEI +demo.MessageInfo +demo.MessageTag +demo.MessageTEI +demo.SendTag +demo.AttachmentServlet +demo.FilterServlet +demo.MailUserBean +org.apache.maven.AbstractCoreMavenComponentTestCase +org.apache.maven.AbstractMavenLifecycleParticipant +org.apache.maven.artifact.AbstractArtifactComponentTestCase +org.apache.maven.artifact.Artifact +org.apache.maven.artifact.ArtifactScopeEnum +org.apache.maven.artifact.ArtifactStatus +org.apache.maven.artifact.ArtifactUtils +org.apache.maven.artifact.ArtifactUtilsTest +org.apache.maven.artifact.DefaultArtifact +org.apache.maven.artifact.DefaultArtifactTest +org.apache.maven.artifact.DependencyResolutionRequiredException +org.apache.maven.artifact.deployer.ArtifactDeployer +org.apache.maven.artifact.deployer.ArtifactDeployerTest +org.apache.maven.artifact.deployer.ArtifactDeploymentException +org.apache.maven.artifact.deployer.DefaultArtifactDeployer +org.apache.maven.artifact.deployer.SimpleArtifactMetadataSource +org.apache.maven.artifact.factory.ArtifactFactory +org.apache.maven.artifact.factory.DefaultArtifactFactory +org.apache.maven.artifact.factory.DefaultArtifactFactoryTest +org.apache.maven.artifact.handler.ArtifactHandler +org.apache.maven.artifact.handler.ArtifactHandlerMock +org.apache.maven.artifact.handler.ArtifactHandlerTest +org.apache.maven.artifact.handler.DefaultArtifactHandler +org.apache.maven.artifact.handler.manager.ArtifactHandlerManager +org.apache.maven.artifact.handler.manager.DefaultArtifactHandlerManager +org.apache.maven.artifact.installer.ArtifactInstallationException +org.apache.maven.artifact.installer.ArtifactInstaller +org.apache.maven.artifact.installer.ArtifactInstallerTest +org.apache.maven.artifact.installer.DefaultArtifactInstaller +org.apache.maven.artifact.InvalidArtifactRTException +org.apache.maven.artifact.InvalidRepositoryException +org.apache.maven.artifact.manager.DefaultWagonManager +org.apache.maven.artifact.manager.WagonConfigurationException +org.apache.maven.artifact.manager.WagonManager +org.apache.maven.artifact.metadata.AbstractArtifactMetadata +org.apache.maven.artifact.metadata.ArtifactMetadata +org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException +org.apache.maven.artifact.metadata.ArtifactMetadataSource +org.apache.maven.artifact.metadata.ResolutionGroup +org.apache.maven.artifact.metadata.TestMetadataSource +org.apache.maven.artifact.repository.ArtifactRepository +org.apache.maven.artifact.repository.ArtifactRepositoryFactory +org.apache.maven.artifact.repository.ArtifactRepositoryPolicy +org.apache.maven.artifact.repository.Authentication +org.apache.maven.artifact.repository.DefaultArtifactRepository +org.apache.maven.artifact.repository.DefaultArtifactRepositoryFactory +org.apache.maven.artifact.repository.DefaultRepositoryRequest +org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout +org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout2 +org.apache.maven.artifact.repository.layout.DefaultRepositoryLayout +org.apache.maven.artifact.repository.layout.FlatRepositoryLayout +org.apache.maven.artifact.repository.LegacyLocalRepositoryManager +org.apache.maven.artifact.repository.MavenArtifactRepository +org.apache.maven.artifact.repository.MavenArtifactRepositoryTest +org.apache.maven.artifact.repository.metadata.AbstractRepositoryMetadata +org.apache.maven.artifact.repository.metadata.ArtifactRepositoryMetadata +org.apache.maven.artifact.repository.metadata.DefaultRepositoryMetadataManager +org.apache.maven.artifact.repository.metadata.GroupRepositoryMetadata +org.apache.maven.artifact.repository.metadata.io.DefaultMetadataReader +org.apache.maven.artifact.repository.metadata.io.MetadataParseException +org.apache.maven.artifact.repository.metadata.io.MetadataReader +org.apache.maven.artifact.repository.metadata.MetadataBridge +org.apache.maven.artifact.repository.metadata.MetadataUtils +org.apache.maven.artifact.repository.metadata.RepositoryMetadata +org.apache.maven.artifact.repository.metadata.RepositoryMetadataDeploymentException +org.apache.maven.artifact.repository.metadata.RepositoryMetadataInstallationException +org.apache.maven.artifact.repository.metadata.RepositoryMetadataManager +org.apache.maven.artifact.repository.metadata.RepositoryMetadataReadException +org.apache.maven.artifact.repository.metadata.RepositoryMetadataResolutionException +org.apache.maven.artifact.repository.metadata.RepositoryMetadataStoreException +org.apache.maven.artifact.repository.metadata.SnapshotArtifactRepositoryMetadata +org.apache.maven.artifact.repository.RepositoryCache +org.apache.maven.artifact.repository.RepositoryRequest +org.apache.maven.artifact.resolver.AbstractArtifactResolutionException +org.apache.maven.artifact.resolver.ArtifactCollector +org.apache.maven.artifact.resolver.ArtifactNotFoundException +org.apache.maven.artifact.resolver.ArtifactResolutionException +org.apache.maven.artifact.resolver.ArtifactResolutionExceptionTest +org.apache.maven.artifact.resolver.ArtifactResolutionRequest +org.apache.maven.artifact.resolver.ArtifactResolutionResult +org.apache.maven.artifact.resolver.ArtifactResolver +org.apache.maven.artifact.resolver.ArtifactResolverTest +org.apache.maven.artifact.resolver.CyclicDependencyException +org.apache.maven.artifact.resolver.DebugResolutionListener +org.apache.maven.artifact.resolver.DefaultArtifactCollector +org.apache.maven.artifact.resolver.DefaultArtifactResolver +org.apache.maven.artifact.resolver.DefaultArtifactResolverTest +org.apache.maven.artifact.resolver.DefaultResolutionErrorHandler +org.apache.maven.artifact.resolver.filter.AbstractScopeArtifactFilter +org.apache.maven.artifact.resolver.filter.AndArtifactFilter +org.apache.maven.artifact.resolver.filter.AndArtifactFilterTest +org.apache.maven.artifact.resolver.filter.ArtifactFilter +org.apache.maven.artifact.resolver.filter.CumulativeScopeArtifactFilter +org.apache.maven.artifact.resolver.filter.ExcludesArtifactFilter +org.apache.maven.artifact.resolver.filter.ExclusionArtifactFilter +org.apache.maven.artifact.resolver.filter.ExclusionArtifactFilterTest +org.apache.maven.artifact.resolver.filter.ExclusionSetFilter +org.apache.maven.artifact.resolver.filter.FilterHashEqualsTest +org.apache.maven.artifact.resolver.filter.IncludesArtifactFilter +org.apache.maven.artifact.resolver.filter.InversionArtifactFilter +org.apache.maven.artifact.resolver.filter.OrArtifactFilter +org.apache.maven.artifact.resolver.filter.OrArtifactFilterTest +org.apache.maven.artifact.resolver.filter.ScopeArtifactFilter +org.apache.maven.artifact.resolver.filter.ScopeArtifactFilterTest +org.apache.maven.artifact.resolver.filter.TypeArtifactFilter +org.apache.maven.artifact.resolver.MultipleArtifactsNotFoundException +org.apache.maven.artifact.resolver.ResolutionErrorHandler +org.apache.maven.artifact.resolver.ResolutionListener +org.apache.maven.artifact.resolver.ResolutionListenerForDepMgmt +org.apache.maven.artifact.resolver.ResolutionNode +org.apache.maven.artifact.resolver.TestFileWagon +org.apache.maven.artifact.resolver.TestTransferListener +org.apache.maven.artifact.resolver.UnresolvedArtifacts +org.apache.maven.artifact.resolver.WarningResolutionListener +org.apache.maven.artifact.testutils.TestFileManager +org.apache.maven.artifact.transform.TransformationManagerTest +org.apache.maven.artifact.UnknownRepositoryLayoutException +org.apache.maven.artifact.versioning.ArtifactVersion +org.apache.maven.artifact.versioning.ComparableVersion +org.apache.maven.artifact.versioning.ComparableVersionIT +org.apache.maven.artifact.versioning.ComparableVersionTest +org.apache.maven.artifact.versioning.DefaultArtifactVersion +org.apache.maven.artifact.versioning.DefaultArtifactVersionTest +org.apache.maven.artifact.versioning.InvalidVersionSpecificationException +org.apache.maven.artifact.versioning.ManagedVersionMap +org.apache.maven.artifact.versioning.OverConstrainedVersionException +org.apache.maven.artifact.versioning.Restriction +org.apache.maven.artifact.versioning.VersionRange +org.apache.maven.artifact.versioning.VersionRangeTest +org.apache.maven.ArtifactFilterManager +org.apache.maven.ArtifactFilterManagerDelegate +org.apache.maven.bridge.MavenRepositorySystem +org.apache.maven.BuildAbort +org.apache.maven.BuildFailureException +org.apache.maven.building.DefaultProblem +org.apache.maven.building.DefaultProblemCollector +org.apache.maven.building.DefaultProblemCollectorTest +org.apache.maven.building.DefaultProblemTest +org.apache.maven.building.FileSource +org.apache.maven.building.FileSourceTest +org.apache.maven.building.Problem +org.apache.maven.building.ProblemCollector +org.apache.maven.building.ProblemCollectorFactory +org.apache.maven.building.ProblemCollectorFactoryTest +org.apache.maven.building.Source +org.apache.maven.building.StringSource +org.apache.maven.building.StringSourceTest +org.apache.maven.building.UrlSource +org.apache.maven.building.UrlSourceTest +org.apache.maven.classrealm.ArtifactClassRealmConstituent +org.apache.maven.classrealm.ClassRealmConstituent +org.apache.maven.classrealm.ClassRealmManager +org.apache.maven.classrealm.ClassRealmManagerDelegate +org.apache.maven.classrealm.ClassRealmRequest +org.apache.maven.classrealm.DefaultClassRealmManager +org.apache.maven.classrealm.DefaultClassRealmRequest +org.apache.maven.cli.CleanArgument +org.apache.maven.cli.CleanArgumentTest +org.apache.maven.cli.CLIManager +org.apache.maven.cli.CLIManagerDocumentationTest +org.apache.maven.cli.CLIManagerTest +org.apache.maven.cli.CLIReportingUtils +org.apache.maven.cli.CLIReportingUtilsTest +org.apache.maven.cli.CliRequest +org.apache.maven.cli.configuration.ConfigurationProcessor +org.apache.maven.cli.configuration.SettingsXmlConfigurationProcessor +org.apache.maven.cli.event.DefaultEventSpyContext +org.apache.maven.cli.event.ExecutionEventLogger +org.apache.maven.cli.event.ExecutionEventLoggerTest +org.apache.maven.cli.internal.BootstrapCoreExtensionManager +org.apache.maven.cli.logging.BaseSlf4jConfiguration +org.apache.maven.cli.logging.impl.Log4j2Configuration +org.apache.maven.cli.logging.impl.LogbackConfiguration +org.apache.maven.cli.logging.impl.Slf4jSimpleConfiguration +org.apache.maven.cli.logging.impl.UnsupportedSlf4jBindingConfiguration +org.apache.maven.cli.logging.Slf4jConfiguration +org.apache.maven.cli.logging.Slf4jConfigurationFactory +org.apache.maven.cli.logging.Slf4jLogger +org.apache.maven.cli.logging.Slf4jLoggerManager +org.apache.maven.cli.logging.Slf4jStdoutLogger +org.apache.maven.cli.MavenCli +org.apache.maven.cli.MavenCliTest +org.apache.maven.cli.ResolveFile +org.apache.maven.cli.transfer.AbstractMavenTransferListener +org.apache.maven.cli.transfer.BatchModeMavenTransferListener +org.apache.maven.cli.transfer.ConsoleMavenTransferListener +org.apache.maven.cli.transfer.FileSizeFormatTest +org.apache.maven.cli.transfer.QuietMavenTransferListener +org.apache.maven.cli.transfer.Slf4jMavenTransferListener +org.apache.maven.configuration.BasedirBeanConfigurationPathTranslator +org.apache.maven.configuration.BeanConfigurationException +org.apache.maven.configuration.BeanConfigurationPathTranslator +org.apache.maven.configuration.BeanConfigurationRequest +org.apache.maven.configuration.BeanConfigurationValuePreprocessor +org.apache.maven.configuration.BeanConfigurator +org.apache.maven.configuration.DefaultBeanConfigurationRequest +org.apache.maven.configuration.DefaultBeanConfiguratorTest +org.apache.maven.configuration.internal.DefaultBeanConfigurator +org.apache.maven.DefaultArtifactFilterManager +org.apache.maven.DefaultMaven +org.apache.maven.DefaultMavenTest +org.apache.maven.DefaultProjectDependenciesResolver +org.apache.maven.DuplicateProjectException +org.apache.maven.embedder.App +org.apache.maven.embedder.AppTest +org.apache.maven.eventspy.AbstractEventSpy +org.apache.maven.eventspy.EventSpy +org.apache.maven.eventspy.internal.EventSpyDispatcher +org.apache.maven.eventspy.internal.EventSpyExecutionListener +org.apache.maven.eventspy.internal.EventSpyRepositoryListener +org.apache.maven.exception.DefaultExceptionHandler +org.apache.maven.exception.DefaultExceptionHandlerTest +org.apache.maven.exception.ExceptionHandler +org.apache.maven.exception.ExceptionSummary +org.apache.maven.execution.AbstractExecutionListener +org.apache.maven.execution.ActivationSettings +org.apache.maven.execution.BuildFailure +org.apache.maven.execution.BuildResumptionAnalyzer +org.apache.maven.execution.BuildResumptionData +org.apache.maven.execution.BuildResumptionDataRepository +org.apache.maven.execution.BuildResumptionPersistenceException +org.apache.maven.execution.BuildSuccess +org.apache.maven.execution.BuildSummary +org.apache.maven.execution.DefaultBuildResumptionAnalyzer +org.apache.maven.execution.DefaultBuildResumptionAnalyzerTest +org.apache.maven.execution.DefaultBuildResumptionDataRepository +org.apache.maven.execution.DefaultBuildResumptionDataRepositoryTest +org.apache.maven.execution.DefaultMavenExecutionRequest +org.apache.maven.execution.DefaultMavenExecutionRequestPopulator +org.apache.maven.execution.DefaultMavenExecutionRequestPopulatorTest +org.apache.maven.execution.DefaultMavenExecutionResult +org.apache.maven.execution.DefaultMavenExecutionTest +org.apache.maven.execution.DefaultRuntimeInformation +org.apache.maven.execution.ExecutionEvent +org.apache.maven.execution.ExecutionListener +org.apache.maven.execution.MavenExecutionRequest +org.apache.maven.execution.MavenExecutionRequestPopulationException +org.apache.maven.execution.MavenExecutionRequestPopulator +org.apache.maven.execution.MavenExecutionResult +org.apache.maven.execution.MavenSession +org.apache.maven.execution.MojoExecutionEvent +org.apache.maven.execution.MojoExecutionListener +org.apache.maven.execution.ProfileActivation +org.apache.maven.execution.ProjectActivation +org.apache.maven.execution.ProjectDependencyGraph +org.apache.maven.execution.ProjectExecutionEvent +org.apache.maven.execution.ProjectExecutionListener +org.apache.maven.execution.ReactorManager +org.apache.maven.execution.RuntimeInformation +org.apache.maven.execution.scope.internal.MojoExecutionScope +org.apache.maven.execution.scope.internal.MojoExecutionScopeCoreModule +org.apache.maven.execution.scope.internal.MojoExecutionScopeModule +org.apache.maven.execution.scope.internal.MojoExecutionScopeTest +org.apache.maven.execution.scope.MojoExecutionScoped +org.apache.maven.execution.scope.WeakMojoExecutionListener +org.apache.maven.execution.SettingsAdapter +org.apache.maven.extension.internal.CoreExports +org.apache.maven.extension.internal.CoreExportsProvider +org.apache.maven.extension.internal.CoreExtensionEntry +org.apache.maven.feature.Features +org.apache.maven.graph.DefaultGraphBuilder +org.apache.maven.graph.DefaultGraphBuilderTest +org.apache.maven.graph.DefaultProjectDependencyGraph +org.apache.maven.graph.DefaultProjectDependencyGraphTest +org.apache.maven.graph.FilteredProjectDependencyGraph +org.apache.maven.graph.GraphBuilder +org.apache.maven.internal.aether.ConsumerModelSourceTransformer +org.apache.maven.internal.aether.ConsumerModelSourceTransformerTest +org.apache.maven.internal.aether.DefaultRepositorySystemSessionFactory +org.apache.maven.internal.aether.LoggingRepositoryListener +org.apache.maven.InternalErrorException +org.apache.maven.lifecycle.DefaultLifecycleExecutor +org.apache.maven.lifecycle.DefaultLifecycles +org.apache.maven.lifecycle.DefaultLifecyclesTest +org.apache.maven.lifecycle.DelegatingMojoExecutionListener +org.apache.maven.lifecycle.DelegatingProjectExecutionListener +org.apache.maven.lifecycle.EmptyLifecyclePluginAnalyzer +org.apache.maven.lifecycle.internal.builder.Builder +org.apache.maven.lifecycle.internal.builder.BuilderCommon +org.apache.maven.lifecycle.internal.builder.BuilderNotFoundException +org.apache.maven.lifecycle.internal.builder.multithreaded.ConcurrencyDependencyGraph +org.apache.maven.lifecycle.internal.builder.multithreaded.ConcurrencyDependencyGraphTest +org.apache.maven.lifecycle.internal.builder.multithreaded.MultiThreadedBuilder +org.apache.maven.lifecycle.internal.builder.multithreaded.ThreadOutputMuxer +org.apache.maven.lifecycle.internal.builder.multithreaded.ThreadOutputMuxerTest +org.apache.maven.lifecycle.internal.builder.singlethreaded.SingleThreadedBuilder +org.apache.maven.lifecycle.internal.BuilderCommonTest +org.apache.maven.lifecycle.internal.BuildListCalculator +org.apache.maven.lifecycle.internal.BuildListCalculatorTest +org.apache.maven.lifecycle.internal.BuildThreadFactory +org.apache.maven.lifecycle.internal.CompoundProjectExecutionListener +org.apache.maven.lifecycle.internal.ConcurrencyDependencyGraphTest +org.apache.maven.lifecycle.internal.DefaultExecutionEvent +org.apache.maven.lifecycle.internal.DefaultExecutionEventCatapult +org.apache.maven.lifecycle.internal.DefaultLifecycleExecutionPlanCalculator +org.apache.maven.lifecycle.internal.DefaultLifecycleMappingDelegate +org.apache.maven.lifecycle.internal.DefaultLifecyclePluginAnalyzer +org.apache.maven.lifecycle.internal.DefaultLifecycleTaskSegmentCalculator +org.apache.maven.lifecycle.internal.DefaultMojoExecutionConfigurator +org.apache.maven.lifecycle.internal.DefaultProjectArtifactFactory +org.apache.maven.lifecycle.internal.DependencyContext +org.apache.maven.lifecycle.internal.ExecutionEventCatapult +org.apache.maven.lifecycle.internal.ExecutionPlanItem +org.apache.maven.lifecycle.internal.GoalTask +org.apache.maven.lifecycle.internal.LifecycleDebugLogger +org.apache.maven.lifecycle.internal.LifecycleDependencyResolver +org.apache.maven.lifecycle.internal.LifecycleDependencyResolverTest +org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculator +org.apache.maven.lifecycle.internal.LifecycleExecutionPlanCalculatorTest +org.apache.maven.lifecycle.internal.LifecycleModuleBuilder +org.apache.maven.lifecycle.internal.LifecyclePluginResolver +org.apache.maven.lifecycle.internal.LifecycleStarter +org.apache.maven.lifecycle.internal.LifecycleTask +org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculator +org.apache.maven.lifecycle.internal.LifecycleTaskSegmentCalculatorImplTest +org.apache.maven.lifecycle.internal.MojoDescriptorCreator +org.apache.maven.lifecycle.internal.MojoExecutor +org.apache.maven.lifecycle.internal.PhaseRecorder +org.apache.maven.lifecycle.internal.PhaseRecorderTest +org.apache.maven.lifecycle.internal.ProjectArtifactFactory +org.apache.maven.lifecycle.internal.ProjectBuildList +org.apache.maven.lifecycle.internal.ProjectBuildListTest +org.apache.maven.lifecycle.internal.ProjectIndex +org.apache.maven.lifecycle.internal.ProjectSegment +org.apache.maven.lifecycle.internal.ReactorBuildStatus +org.apache.maven.lifecycle.internal.ReactorContext +org.apache.maven.lifecycle.internal.stub.BuildPluginManagerStub +org.apache.maven.lifecycle.internal.stub.CompletionServiceStub +org.apache.maven.lifecycle.internal.stub.DefaultLifecyclesStub +org.apache.maven.lifecycle.internal.stub.ExecutionEventCatapultStub +org.apache.maven.lifecycle.internal.stub.LifecycleExecutionPlanCalculatorStub +org.apache.maven.lifecycle.internal.stub.LifeCyclePluginAnalyzerStub +org.apache.maven.lifecycle.internal.stub.LifecycleTaskSegmentCalculatorStub +org.apache.maven.lifecycle.internal.stub.LoggerStub +org.apache.maven.lifecycle.internal.stub.MojoExecutorStub +org.apache.maven.lifecycle.internal.stub.PluginPrefixResolverStub +org.apache.maven.lifecycle.internal.stub.PluginVersionResolverStub +org.apache.maven.lifecycle.internal.stub.ProjectDependenciesResolverStub +org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStub +org.apache.maven.lifecycle.internal.stub.ProjectDependencyGraphStubTest +org.apache.maven.lifecycle.internal.TaskSegment +org.apache.maven.lifecycle.Lifecycle +org.apache.maven.lifecycle.LifecycleExecutionException +org.apache.maven.lifecycle.LifecycleExecutor +org.apache.maven.lifecycle.LifecycleExecutorSubModulesTest +org.apache.maven.lifecycle.LifecycleExecutorTest +org.apache.maven.lifecycle.LifecycleMappingDelegate +org.apache.maven.lifecycle.LifecycleNotFoundException +org.apache.maven.lifecycle.LifecyclePhaseNotFoundException +org.apache.maven.lifecycle.LifeCyclePluginAnalyzer +org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping +org.apache.maven.lifecycle.mapping.Lifecycle +org.apache.maven.lifecycle.mapping.LifecycleMapping +org.apache.maven.lifecycle.mapping.LifecycleMojo +org.apache.maven.lifecycle.mapping.LifecyclePhase +org.apache.maven.lifecycle.mapping.LifecyclePhaseTest +org.apache.maven.lifecycle.MavenExecutionPlan +org.apache.maven.lifecycle.MavenExecutionPlanTest +org.apache.maven.lifecycle.MissingProjectException +org.apache.maven.lifecycle.MojoExecutionConfigurator +org.apache.maven.lifecycle.MojoExecutionXPathContainer +org.apache.maven.lifecycle.NoGoalSpecifiedException +org.apache.maven.lifecycle.test.App +org.apache.maven.lifecycle.test.AppTest +org.apache.maven.logwrapper.LogLevelRecorder +org.apache.maven.logwrapper.LogLevelRecorderTest +org.apache.maven.logwrapper.MavenSlf4jWrapperFactory +org.apache.maven.Maven +org.apache.maven.MavenExecutionException +org.apache.maven.MavenLifecycleParticipantTest +org.apache.maven.MavenTest +org.apache.maven.MissingModuleException +org.apache.maven.MissingProfilesException +org.apache.maven.model.ActivationFileTest +org.apache.maven.model.ActivationOSTest +org.apache.maven.model.ActivationPropertyTest +org.apache.maven.model.ActivationTest +org.apache.maven.model.building.AbstractModelBuildingListener +org.apache.maven.model.building.AbstractModelSourceTransformer +org.apache.maven.model.building.ArtifactModelSource +org.apache.maven.model.building.BuildModelSourceTransformer +org.apache.maven.model.building.ComplexActivationTest +org.apache.maven.model.building.DefaultBuildPomXMLFilterFactory +org.apache.maven.model.building.DefaultModelBuilder +org.apache.maven.model.building.DefaultModelBuilderFactory +org.apache.maven.model.building.DefaultModelBuilderFactoryTest +org.apache.maven.model.building.DefaultModelBuilderTest +org.apache.maven.model.building.DefaultModelBuildingEvent +org.apache.maven.model.building.DefaultModelBuildingRequest +org.apache.maven.model.building.DefaultModelBuildingResult +org.apache.maven.model.building.DefaultModelProblem +org.apache.maven.model.building.DefaultModelProblemCollector +org.apache.maven.model.building.DefaultModelProcessor +org.apache.maven.model.building.DefaultModelSourceTransformer +org.apache.maven.model.building.DefaultTransformerContext +org.apache.maven.model.building.FileModelSource +org.apache.maven.model.building.FileModelSourceTest +org.apache.maven.model.building.FileToRawModelMerger +org.apache.maven.model.building.FileToRawModelMergerTest +org.apache.maven.model.building.FilterModelBuildingRequest +org.apache.maven.model.building.ModelBuilder +org.apache.maven.model.building.ModelBuildingEvent +org.apache.maven.model.building.ModelBuildingEventCatapult +org.apache.maven.model.building.ModelBuildingException +org.apache.maven.model.building.ModelBuildingListener +org.apache.maven.model.building.ModelBuildingRequest +org.apache.maven.model.building.ModelBuildingResult +org.apache.maven.model.building.ModelCache +org.apache.maven.model.building.ModelCacheTag +org.apache.maven.model.building.ModelData +org.apache.maven.model.building.ModelProblem +org.apache.maven.model.building.ModelProblemCollector +org.apache.maven.model.building.ModelProblemCollectorExt +org.apache.maven.model.building.ModelProblemCollectorRequest +org.apache.maven.model.building.ModelProblemUtils +org.apache.maven.model.building.ModelProcessor +org.apache.maven.model.building.ModelSource +org.apache.maven.model.building.ModelSource2 +org.apache.maven.model.building.ModelSourceTransformer +org.apache.maven.model.building.Result +org.apache.maven.model.building.SimpleProblemCollector +org.apache.maven.model.building.StringModelSource +org.apache.maven.model.building.TransformerContext +org.apache.maven.model.building.TransformerContextBuilder +org.apache.maven.model.building.TransformerException +org.apache.maven.model.building.UrlModelSource +org.apache.maven.model.BuildTest +org.apache.maven.model.CiManagementTest +org.apache.maven.model.composition.DefaultDependencyManagementImporter +org.apache.maven.model.composition.DependencyManagementImporter +org.apache.maven.model.ContributorTest +org.apache.maven.model.DependencyManagementTest +org.apache.maven.model.DependencyTest +org.apache.maven.model.DeploymentRepositoryTest +org.apache.maven.model.DeveloperTest +org.apache.maven.model.DistributionManagementTest +org.apache.maven.model.ExclusionTest +org.apache.maven.model.ExtensionTest +org.apache.maven.model.inheritance.DefaultInheritanceAssembler +org.apache.maven.model.inheritance.DefaultInheritanceAssemblerTest +org.apache.maven.model.inheritance.InheritanceAssembler +org.apache.maven.model.interpolation.AbstractModelInterpolatorTest +org.apache.maven.model.interpolation.AbstractStringBasedModelInterpolator +org.apache.maven.model.interpolation.BuildTimestampValueSource +org.apache.maven.model.interpolation.MavenBuildTimestamp +org.apache.maven.model.interpolation.MavenBuildTimestampTest +org.apache.maven.model.interpolation.ModelInterpolator +org.apache.maven.model.interpolation.PathTranslatingPostProcessor +org.apache.maven.model.interpolation.ProblemDetectingValueSource +org.apache.maven.model.interpolation.StringSearchModelInterpolator +org.apache.maven.model.interpolation.StringSearchModelInterpolatorTest +org.apache.maven.model.interpolation.StringVisitorModelInterpolator +org.apache.maven.model.interpolation.StringVisitorModelInterpolatorTest +org.apache.maven.model.interpolation.UrlNormalizingPostProcessor +org.apache.maven.model.io.DefaultModelReader +org.apache.maven.model.io.DefaultModelWriter +org.apache.maven.model.io.ModelParseException +org.apache.maven.model.io.ModelReader +org.apache.maven.model.io.ModelWriter +org.apache.maven.model.io.xpp3.package-info +org.apache.maven.model.IssueManagementTest +org.apache.maven.model.LicenseTest +org.apache.maven.model.locator.DefaultModelLocator +org.apache.maven.model.locator.ModelLocator +org.apache.maven.model.MailingListTest +org.apache.maven.model.management.DefaultDependencyManagementInjector +org.apache.maven.model.management.DefaultPluginManagementInjector +org.apache.maven.model.management.DependencyManagementInjector +org.apache.maven.model.management.PluginManagementInjector +org.apache.maven.model.merge.MavenModelMerger +org.apache.maven.model.merge.MavenModelMergerTest +org.apache.maven.model.merge.ModelMerger +org.apache.maven.model.merge.ModelMergerTest +org.apache.maven.model.merge.package-info +org.apache.maven.model.ModelTest +org.apache.maven.model.normalization.DefaultModelNormalizer +org.apache.maven.model.normalization.ModelNormalizer +org.apache.maven.model.NotifierTest +org.apache.maven.model.OrganizationTest +org.apache.maven.model.package-info +org.apache.maven.model.ParentTest +org.apache.maven.model.path.DefaultModelPathTranslator +org.apache.maven.model.path.DefaultModelUrlNormalizer +org.apache.maven.model.path.DefaultPathTranslator +org.apache.maven.model.path.DefaultUrlNormalizer +org.apache.maven.model.path.DefaultUrlNormalizerTest +org.apache.maven.model.path.ModelPathTranslator +org.apache.maven.model.path.ModelUrlNormalizer +org.apache.maven.model.path.PathTranslator +org.apache.maven.model.path.ProfileActivationFilePathInterpolator +org.apache.maven.model.path.UrlNormalizer +org.apache.maven.model.plugin.DefaultLifecycleBindingsInjector +org.apache.maven.model.plugin.DefaultPluginConfigurationExpander +org.apache.maven.model.plugin.DefaultReportConfigurationExpander +org.apache.maven.model.plugin.DefaultReportingConverter +org.apache.maven.model.plugin.LifecycleBindingsInjector +org.apache.maven.model.plugin.PluginConfigurationExpander +org.apache.maven.model.plugin.ReportConfigurationExpander +org.apache.maven.model.plugin.ReportingConverter +org.apache.maven.model.PluginConfigurationTest +org.apache.maven.model.PluginContainerTest +org.apache.maven.model.PluginExecutionTest +org.apache.maven.model.PluginManagementTest +org.apache.maven.model.PluginTest +org.apache.maven.model.PrerequisitesTest +org.apache.maven.model.profile.activation.AbstractProfileActivatorTest +org.apache.maven.model.profile.activation.FileProfileActivator +org.apache.maven.model.profile.activation.FileProfileActivatorTest +org.apache.maven.model.profile.activation.JdkVersionProfileActivator +org.apache.maven.model.profile.activation.JdkVersionProfileActivatorTest +org.apache.maven.model.profile.activation.OperatingSystemProfileActivator +org.apache.maven.model.profile.activation.ProfileActivator +org.apache.maven.model.profile.activation.PropertyProfileActivator +org.apache.maven.model.profile.activation.PropertyProfileActivatorTest +org.apache.maven.model.profile.DefaultProfileActivationContext +org.apache.maven.model.profile.DefaultProfileInjector +org.apache.maven.model.profile.DefaultProfileSelector +org.apache.maven.model.profile.ProfileActivationContext +org.apache.maven.model.profile.ProfileInjector +org.apache.maven.model.profile.ProfileSelector +org.apache.maven.model.ProfileTest +org.apache.maven.model.RelocationTest +org.apache.maven.model.ReportingTest +org.apache.maven.model.ReportPluginTest +org.apache.maven.model.ReportSetTest +org.apache.maven.model.RepositoryPolicyTest +org.apache.maven.model.RepositoryTest +org.apache.maven.model.resolution.InvalidRepositoryException +org.apache.maven.model.resolution.ModelResolver +org.apache.maven.model.resolution.UnresolvableModelException +org.apache.maven.model.resolution.WorkspaceModelResolver +org.apache.maven.model.ResourceTest +org.apache.maven.model.ScmTest +org.apache.maven.model.SiteTest +org.apache.maven.model.superpom.DefaultSuperPomProvider +org.apache.maven.model.superpom.SuperPomProvider +org.apache.maven.model.transform.AbstractEventXMLFilter +org.apache.maven.model.transform.AbstractXMLFilterTests +org.apache.maven.model.transform.BuildToRawPomXMLFilter +org.apache.maven.model.transform.BuildToRawPomXMLFilterFactory +org.apache.maven.model.transform.BuildToRawPomXMLFilterListener +org.apache.maven.model.transform.CiFriendlyXMLFilter +org.apache.maven.model.transform.CiFriendlyXMLFilterTest +org.apache.maven.model.transform.ConsumerPomXMLFilterTest +org.apache.maven.model.transform.DependencyKey +org.apache.maven.model.transform.FastForwardFilter +org.apache.maven.model.transform.ModulesXMLFilter +org.apache.maven.model.transform.ModulesXMLFilterTest +org.apache.maven.model.transform.ParentXMLFilter +org.apache.maven.model.transform.ParentXMLFilterTest +org.apache.maven.model.transform.RawToConsumerPomXMLFilter +org.apache.maven.model.transform.RawToConsumerPomXMLFilterFactory +org.apache.maven.model.transform.ReactorDependencyXMLFilter +org.apache.maven.model.transform.ReactorDependencyXMLFilterTest +org.apache.maven.model.transform.RelativePathXMLFilter +org.apache.maven.model.transform.RelativePathXMLFilterTest +org.apache.maven.model.transform.RelativeProject +org.apache.maven.model.transform.sax.AbstractSAXFilter +org.apache.maven.model.transform.sax.ChainedFilterTest +org.apache.maven.model.transform.sax.CommentRenormalizer +org.apache.maven.model.transform.sax.CommentRenormalizerTest +org.apache.maven.model.transform.sax.Factories +org.apache.maven.model.transform.sax.SAXEvent +org.apache.maven.model.transform.sax.SAXEventFactory +org.apache.maven.model.transform.sax.SAXEventUtils +org.apache.maven.model.transform.sax.SAXEventUtilsTest +org.apache.maven.model.validation.DefaultModelValidator +org.apache.maven.model.validation.DefaultModelValidatorTest +org.apache.maven.model.validation.ModelValidator +org.apache.maven.monitor.event.EventDispatcher +org.apache.maven.monitor.event.EventMonitor +org.apache.maven.monitor.logging.DefaultLog +org.apache.maven.plugin.AbstractMojo +org.apache.maven.plugin.AbstractMojoExecutionException +org.apache.maven.plugin.BuildPluginManager +org.apache.maven.plugin.CacheUtils +org.apache.maven.plugin.CompoundMojoExecutionListener +org.apache.maven.plugin.ContextEnabled +org.apache.maven.plugin.CycleDetectedInPluginGraphException +org.apache.maven.plugin.DebugConfigurationListener +org.apache.maven.plugin.DefaultBuildPluginManager +org.apache.maven.plugin.DefaultExtensionRealmCache +org.apache.maven.plugin.DefaultPluginArtifactsCache +org.apache.maven.plugin.DefaultPluginDescriptorCache +org.apache.maven.plugin.DefaultPluginRealmCache +org.apache.maven.plugin.descriptor.DuplicateMojoDescriptorException +org.apache.maven.plugin.descriptor.DuplicateParameterException +org.apache.maven.plugin.descriptor.InvalidParameterException +org.apache.maven.plugin.descriptor.InvalidPluginDescriptorException +org.apache.maven.plugin.descriptor.MojoDescriptor +org.apache.maven.plugin.descriptor.Parameter +org.apache.maven.plugin.descriptor.PluginDescriptor +org.apache.maven.plugin.descriptor.PluginDescriptorBuilder +org.apache.maven.plugin.descriptor.PluginDescriptorBuilderTest +org.apache.maven.plugin.descriptor.Requirement +org.apache.maven.plugin.ExtensionRealmCache +org.apache.maven.plugin.internal.DefaultLegacySupport +org.apache.maven.plugin.internal.DefaultLegacySupportTest +org.apache.maven.plugin.internal.DefaultMavenPluginManager +org.apache.maven.plugin.internal.DefaultMavenPluginValidator +org.apache.maven.plugin.internal.DefaultPluginDependenciesResolver +org.apache.maven.plugin.internal.DefaultPluginManager +org.apache.maven.plugin.internal.MavenPluginValidator +org.apache.maven.plugin.internal.MavenPluginValidatorTest +org.apache.maven.plugin.internal.PluginDependenciesResolver +org.apache.maven.plugin.internal.PluginDependencyResolutionListener +org.apache.maven.plugin.internal.ValidatingConfigurationListener +org.apache.maven.plugin.internal.WagonExcluder +org.apache.maven.plugin.InvalidPluginDescriptorException +org.apache.maven.plugin.InvalidPluginException +org.apache.maven.plugin.LegacySupport +org.apache.maven.plugin.logging.Log +org.apache.maven.plugin.logging.SystemStreamLog +org.apache.maven.plugin.MavenPluginManager +org.apache.maven.plugin.Mojo +org.apache.maven.plugin.MojoExecution +org.apache.maven.plugin.MojoExecutionException +org.apache.maven.plugin.MojoFailureException +org.apache.maven.plugin.MojoNotFoundException +org.apache.maven.plugin.PluginArtifactsCache +org.apache.maven.plugin.PluginConfigurationException +org.apache.maven.plugin.PluginContainerException +org.apache.maven.plugin.PluginDescriptorCache +org.apache.maven.plugin.PluginDescriptorParsingException +org.apache.maven.plugin.PluginExecutionException +org.apache.maven.plugin.PluginIncompatibleException +org.apache.maven.plugin.PluginLoaderException +org.apache.maven.plugin.PluginManager +org.apache.maven.plugin.PluginManagerException +org.apache.maven.plugin.PluginManagerTest +org.apache.maven.plugin.PluginNotFoundException +org.apache.maven.plugin.PluginParameterException +org.apache.maven.plugin.PluginParameterExceptionTest +org.apache.maven.plugin.PluginParameterExpressionEvaluator +org.apache.maven.plugin.PluginParameterExpressionEvaluatorTest +org.apache.maven.plugin.PluginRealmCache +org.apache.maven.plugin.PluginResolutionException +org.apache.maven.plugin.prefix.DefaultPluginPrefixRequest +org.apache.maven.plugin.prefix.internal.DefaultPluginPrefixResolver +org.apache.maven.plugin.prefix.internal.DefaultPluginPrefixResult +org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException +org.apache.maven.plugin.prefix.PluginPrefixRequest +org.apache.maven.plugin.prefix.PluginPrefixResolver +org.apache.maven.plugin.prefix.PluginPrefixResult +org.apache.maven.plugin.version.DefaultPluginVersionRequest +org.apache.maven.plugin.version.internal.DefaultPluginVersionResolver +org.apache.maven.plugin.version.internal.DefaultPluginVersionResult +org.apache.maven.plugin.version.PluginVersionNotFoundException +org.apache.maven.plugin.version.PluginVersionRequest +org.apache.maven.plugin.version.PluginVersionResolutionException +org.apache.maven.plugin.version.PluginVersionResolver +org.apache.maven.plugin.version.PluginVersionResult +org.apache.maven.profiles.activation.DetectedProfileActivator +org.apache.maven.profiles.activation.FileProfileActivator +org.apache.maven.profiles.activation.JdkPrefixProfileActivator +org.apache.maven.profiles.activation.OperatingSystemProfileActivator +org.apache.maven.profiles.activation.ProfileActivationException +org.apache.maven.profiles.activation.ProfileActivator +org.apache.maven.profiles.activation.SystemPropertyProfileActivator +org.apache.maven.profiles.DefaultMavenProfilesBuilder +org.apache.maven.profiles.DefaultProfileManager +org.apache.maven.profiles.manager.DefaultProfileManagerTest +org.apache.maven.profiles.MavenProfilesBuilder +org.apache.maven.profiles.ProfileManager +org.apache.maven.profiles.ProfilesConversionUtils +org.apache.maven.project.AbstractMavenProjectTestCase +org.apache.maven.project.AbstractMavenProjectTestCase +org.apache.maven.project.artifact.ActiveProjectArtifact +org.apache.maven.project.artifact.ArtifactWithDependencies +org.apache.maven.project.artifact.AttachedArtifact +org.apache.maven.project.artifact.DefaultMavenMetadataCache +org.apache.maven.project.artifact.DefaultMavenMetadataCacheTest +org.apache.maven.project.artifact.DefaultMetadataSource +org.apache.maven.project.artifact.DefaultProjectArtifactsCache +org.apache.maven.project.artifact.DefaultProjectArtifactsCacheTest +org.apache.maven.project.artifact.InvalidDependencyVersionException +org.apache.maven.project.artifact.MavenMetadataCache +org.apache.maven.project.artifact.MavenMetadataSource +org.apache.maven.project.artifact.MavenMetadataSourceTest +org.apache.maven.project.artifact.PluginArtifact +org.apache.maven.project.artifact.ProjectArtifact +org.apache.maven.project.artifact.ProjectArtifactMetadata +org.apache.maven.project.artifact.ProjectArtifactsCache +org.apache.maven.project.canonical.CanonicalProjectBuilderTest +org.apache.maven.project.ClasspathArtifactResolver +org.apache.maven.project.collector.DefaultProjectsSelector +org.apache.maven.project.collector.MultiModuleCollectionStrategy +org.apache.maven.project.collector.PomlessCollectionStrategy +org.apache.maven.project.collector.ProjectCollectionStrategy +org.apache.maven.project.collector.ProjectsSelector +org.apache.maven.project.collector.RequestPomCollectionStrategy +org.apache.maven.project.DefaultDependencyResolutionRequest +org.apache.maven.project.DefaultDependencyResolutionResult +org.apache.maven.project.DefaultMavenProjectBuilder +org.apache.maven.project.DefaultMavenProjectBuilderTest +org.apache.maven.project.DefaultMavenProjectHelper +org.apache.maven.project.DefaultModelBuildingListener +org.apache.maven.project.DefaultProjectBuilder +org.apache.maven.project.DefaultProjectBuilderConfiguration +org.apache.maven.project.DefaultProjectBuildingHelper +org.apache.maven.project.DefaultProjectBuildingRequest +org.apache.maven.project.DefaultProjectBuildingResult +org.apache.maven.project.DefaultProjectDependenciesResolver +org.apache.maven.project.DefaultProjectRealmCache +org.apache.maven.project.DependencyResolutionException +org.apache.maven.project.DependencyResolutionRequest +org.apache.maven.project.DependencyResolutionResult +org.apache.maven.project.DuplicateArtifactAttachmentException +org.apache.maven.project.DuplicateProjectException +org.apache.maven.project.EmptyLifecycleExecutor +org.apache.maven.project.EmptyLifecycleExecutor +org.apache.maven.project.EmptyLifecyclePluginAnalyzer +org.apache.maven.project.EmptyProjectBuildingHelper +org.apache.maven.project.ExtensionDescriptor +org.apache.maven.project.ExtensionDescriptorBuilder +org.apache.maven.project.ExtensionDescriptorBuilderTest +org.apache.maven.project.harness.PomTestWrapper +org.apache.maven.project.harness.Xpp3DomAttributeIterator +org.apache.maven.project.harness.Xpp3DomAttributePointer +org.apache.maven.project.harness.Xpp3DomNodeIterator +org.apache.maven.project.harness.Xpp3DomNodePointer +org.apache.maven.project.harness.Xpp3DomPointerFactory +org.apache.maven.project.inheritance.AbstractProjectInheritanceTestCase +org.apache.maven.project.inheritance.DefaultModelInheritanceAssembler +org.apache.maven.project.inheritance.ModelInheritanceAssembler +org.apache.maven.project.inheritance.t00.ProjectInheritanceTest +org.apache.maven.project.inheritance.t01.ProjectInheritanceTest +org.apache.maven.project.inheritance.t02.ProjectInheritanceTest +org.apache.maven.project.inheritance.t03.ProjectInheritanceTest +org.apache.maven.project.inheritance.t04.ProjectInheritanceTest +org.apache.maven.project.inheritance.t05.ProjectInheritanceTest +org.apache.maven.project.inheritance.t06.ProjectInheritanceTest +org.apache.maven.project.inheritance.t07.ProjectInheritanceTest +org.apache.maven.project.inheritance.t08.ProjectInheritanceTest +org.apache.maven.project.inheritance.t09.ProjectInheritanceTest +org.apache.maven.project.inheritance.t10.ProjectInheritanceTest +org.apache.maven.project.inheritance.t11.ProjectInheritanceTest +org.apache.maven.project.inheritance.t12.ProjectInheritanceTest +org.apache.maven.project.inheritance.t12scm.ProjectInheritanceTest +org.apache.maven.project.interpolation.AbstractStringBasedModelInterpolator +org.apache.maven.project.interpolation.BuildTimestampValueSource +org.apache.maven.project.interpolation.ModelInterpolationException +org.apache.maven.project.interpolation.ModelInterpolator +org.apache.maven.project.interpolation.PathTranslatingPostProcessor +org.apache.maven.project.interpolation.RegexBasedModelInterpolator +org.apache.maven.project.interpolation.StringSearchModelInterpolator +org.apache.maven.project.InvalidProjectModelException +org.apache.maven.project.InvalidProjectVersionException +org.apache.maven.project.LegacyLocalRepositoryManager +org.apache.maven.project.LegacyLocalRepositoryManager +org.apache.maven.project.MavenProject +org.apache.maven.project.MavenProjectBuilder +org.apache.maven.project.MavenProjectHelper +org.apache.maven.project.MavenProjectTest +org.apache.maven.project.MissingRepositoryElementException +org.apache.maven.project.ModelUtils +org.apache.maven.project.ModelUtilsTest +org.apache.maven.project.path.DefaultPathTranslator +org.apache.maven.project.path.DefaultPathTranslatorTest +org.apache.maven.project.path.PathTranslator +org.apache.maven.project.PomConstructionTest +org.apache.maven.project.ProjectBuilder +org.apache.maven.project.ProjectBuilderConfiguration +org.apache.maven.project.ProjectBuilderTest +org.apache.maven.project.ProjectBuildingException +org.apache.maven.project.ProjectBuildingHelper +org.apache.maven.project.ProjectBuildingRequest +org.apache.maven.project.ProjectBuildingResult +org.apache.maven.project.ProjectClasspathTest +org.apache.maven.project.ProjectDependenciesResolver +org.apache.maven.project.ProjectModelResolver +org.apache.maven.project.ProjectModelResolverTest +org.apache.maven.project.ProjectRealmCache +org.apache.maven.project.ProjectSorter +org.apache.maven.project.ProjectSorterTest +org.apache.maven.project.ProjectUtils +org.apache.maven.project.ReactorModelPool +org.apache.maven.project.RepositorySessionDecorator +org.apache.maven.project.TestArtifactResolver +org.apache.maven.project.TestMavenRepositorySystem +org.apache.maven.project.TestMetadataSource +org.apache.maven.project.TestProjectBuilder +org.apache.maven.project.validation.DefaultModelValidator +org.apache.maven.project.validation.ModelValidationResult +org.apache.maven.project.validation.ModelValidator +org.apache.maven.ProjectBuildFailureException +org.apache.maven.ProjectCycleException +org.apache.maven.ProjectDependenciesResolver +org.apache.maven.ProjectDependenciesResolverTest +org.apache.maven.properties.internal.EnvironmentUtils +org.apache.maven.properties.internal.SystemProperties +org.apache.maven.ReactorReader +org.apache.maven.reporting.MavenReportException +org.apache.maven.repository.ArtifactDoesNotExistException +org.apache.maven.repository.ArtifactTransferEvent +org.apache.maven.repository.ArtifactTransferFailedException +org.apache.maven.repository.ArtifactTransferListener +org.apache.maven.repository.ArtifactTransferResource +org.apache.maven.repository.DefaultMirrorSelector +org.apache.maven.repository.DefaultMirrorSelectorTest +org.apache.maven.repository.DelegatingLocalArtifactRepository +org.apache.maven.repository.internal.AbstractRepositoryTestCase +org.apache.maven.repository.internal.ArtifactDescriptorReaderDelegate +org.apache.maven.repository.internal.ArtifactDescriptorUtils +org.apache.maven.repository.internal.DefaultArtifactDescriptorReader +org.apache.maven.repository.internal.DefaultArtifactDescriptorReaderTest +org.apache.maven.repository.internal.DefaultModelCache +org.apache.maven.repository.internal.DefaultModelResolver +org.apache.maven.repository.internal.DefaultModelResolverTest +org.apache.maven.repository.internal.DefaultVersionRangeResolver +org.apache.maven.repository.internal.DefaultVersionResolver +org.apache.maven.repository.internal.DefaultVersionResolverTest +org.apache.maven.repository.internal.LocalSnapshotMetadata +org.apache.maven.repository.internal.LocalSnapshotMetadataGenerator +org.apache.maven.repository.internal.MavenMetadata +org.apache.maven.repository.internal.MavenRepositorySystemUtils +org.apache.maven.repository.internal.MavenRepositorySystemUtilsTest +org.apache.maven.repository.internal.MavenResolverModule +org.apache.maven.repository.internal.MavenSnapshotMetadata +org.apache.maven.repository.internal.MavenWorkspaceReader +org.apache.maven.repository.internal.package-info +org.apache.maven.repository.internal.RelocatedArtifact +org.apache.maven.repository.internal.RemoteSnapshotMetadata +org.apache.maven.repository.internal.RemoteSnapshotMetadataGenerator +org.apache.maven.repository.internal.RemoteSnapshotMetadataTest +org.apache.maven.repository.internal.RepositorySystemTest +org.apache.maven.repository.internal.SnapshotMetadataGeneratorFactory +org.apache.maven.repository.internal.util.ConsoleRepositoryListener +org.apache.maven.repository.internal.util.ConsoleTransferListener +org.apache.maven.repository.internal.VersionsMetadata +org.apache.maven.repository.internal.VersionsMetadataGenerator +org.apache.maven.repository.internal.VersionsMetadataGeneratorFactory +org.apache.maven.repository.legacy.ChecksumFailedException +org.apache.maven.repository.legacy.DefaultUpdateCheckManager +org.apache.maven.repository.legacy.DefaultUpdateCheckManagerTest +org.apache.maven.repository.legacy.DefaultWagonManager +org.apache.maven.repository.legacy.DefaultWagonManagerTest +org.apache.maven.repository.legacy.LegacyRepositorySystem +org.apache.maven.repository.legacy.LegacyRepositorySystemTest +org.apache.maven.repository.legacy.MavenArtifact +org.apache.maven.repository.legacy.metadata.AbstractArtifactMetadata +org.apache.maven.repository.legacy.metadata.ArtifactMetadata +org.apache.maven.repository.legacy.metadata.ArtifactMetadataRetrievalException +org.apache.maven.repository.legacy.metadata.ArtifactMetadataSource +org.apache.maven.repository.legacy.metadata.DefaultMetadataResolutionRequest +org.apache.maven.repository.legacy.metadata.MetadataResolutionRequest +org.apache.maven.repository.legacy.metadata.ResolutionGroup +org.apache.maven.repository.legacy.PerLookupWagon +org.apache.maven.repository.legacy.repository.ArtifactRepositoryFactory +org.apache.maven.repository.legacy.repository.DefaultArtifactRepositoryFactory +org.apache.maven.repository.legacy.resolver.conflict.AbstractConflictResolverTest +org.apache.maven.repository.legacy.resolver.conflict.ConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.ConflictResolverFactory +org.apache.maven.repository.legacy.resolver.conflict.ConflictResolverNotFoundException +org.apache.maven.repository.legacy.resolver.conflict.DefaultConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.DefaultConflictResolverFactory +org.apache.maven.repository.legacy.resolver.conflict.FarthestConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.FarthestConflictResolverTest +org.apache.maven.repository.legacy.resolver.conflict.NearestConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.NearestConflictResolverTest +org.apache.maven.repository.legacy.resolver.conflict.NewestConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.NewestConflictResolverTest +org.apache.maven.repository.legacy.resolver.conflict.OldestConflictResolver +org.apache.maven.repository.legacy.resolver.conflict.OldestConflictResolverTest +org.apache.maven.repository.legacy.resolver.DefaultArtifactCollectorTest +org.apache.maven.repository.legacy.resolver.DefaultLegacyArtifactCollector +org.apache.maven.repository.legacy.resolver.LegacyArtifactCollector +org.apache.maven.repository.legacy.resolver.transform.AbstractVersionTransformation +org.apache.maven.repository.legacy.resolver.transform.ArtifactTransformation +org.apache.maven.repository.legacy.resolver.transform.ArtifactTransformationManager +org.apache.maven.repository.legacy.resolver.transform.DefaultArtifactTransformationManager +org.apache.maven.repository.legacy.resolver.transform.LatestArtifactTransformation +org.apache.maven.repository.legacy.resolver.transform.ReleaseArtifactTransformation +org.apache.maven.repository.legacy.resolver.transform.SnapshotTransformation +org.apache.maven.repository.legacy.StringWagon +org.apache.maven.repository.legacy.TransferListenerAdapter +org.apache.maven.repository.legacy.UpdateCheckManager +org.apache.maven.repository.legacy.WagonA +org.apache.maven.repository.legacy.WagonB +org.apache.maven.repository.legacy.WagonC +org.apache.maven.repository.legacy.WagonConfigurationException +org.apache.maven.repository.legacy.WagonManager +org.apache.maven.repository.legacy.WagonMock +org.apache.maven.repository.LegacyRepositorySystemTest +org.apache.maven.repository.LocalArtifactRepository +org.apache.maven.repository.LocalRepositoryNotAccessibleException +org.apache.maven.repository.MavenArtifactMetadata +org.apache.maven.repository.metadata.ArtifactMetadata +org.apache.maven.repository.metadata.ClasspathContainer +org.apache.maven.repository.metadata.ClasspathTransformation +org.apache.maven.repository.metadata.DefaultClasspathTransformation +org.apache.maven.repository.metadata.DefaultClasspathTransformationTest +org.apache.maven.repository.metadata.DefaultGraphConflictResolutionPolicy +org.apache.maven.repository.metadata.DefaultGraphConflictResolutionPolicyTest +org.apache.maven.repository.metadata.DefaultGraphConflictResolver +org.apache.maven.repository.metadata.DefaultGraphConflictResolverTest +org.apache.maven.repository.metadata.GraphConflictResolutionException +org.apache.maven.repository.metadata.GraphConflictResolutionPolicy +org.apache.maven.repository.metadata.GraphConflictResolver +org.apache.maven.repository.metadata.MetadataGraph +org.apache.maven.repository.metadata.MetadataGraphEdge +org.apache.maven.repository.metadata.MetadataGraphTransformationException +org.apache.maven.repository.metadata.MetadataGraphVertex +org.apache.maven.repository.metadata.MetadataResolution +org.apache.maven.repository.metadata.MetadataResolutionException +org.apache.maven.repository.metadata.MetadataResolutionRequest +org.apache.maven.repository.metadata.MetadataResolutionRequestTypeEnum +org.apache.maven.repository.metadata.MetadataResolutionResult +org.apache.maven.repository.metadata.MetadataRetrievalException +org.apache.maven.repository.metadata.MetadataSource +org.apache.maven.repository.metadata.MetadataTreeNode +org.apache.maven.repository.metadata.TestMetadataSource +org.apache.maven.repository.MetadataGraph +org.apache.maven.repository.MetadataGraphNode +org.apache.maven.repository.MetadataResolutionRequest +org.apache.maven.repository.MetadataResolutionResult +org.apache.maven.repository.MirrorProcessorTest +org.apache.maven.repository.MirrorSelector +org.apache.maven.repository.Proxy +org.apache.maven.repository.RepositorySystem +org.apache.maven.repository.TestArtifactHandler +org.apache.maven.repository.TestRepositoryConnector +org.apache.maven.repository.TestRepositoryConnectorFactory +org.apache.maven.repository.TestRepositorySystem +org.apache.maven.repository.UserLocalArtifactRepository +org.apache.maven.repository.VersionNotFoundException +org.apache.maven.RepositoryUtils +org.apache.maven.rtinfo.internal.DefaultRuntimeInformation +org.apache.maven.rtinfo.internal.DefaultRuntimeInformationTest +org.apache.maven.rtinfo.RuntimeInformation +org.apache.maven.session.scope.internal.SessionScope +org.apache.maven.session.scope.internal.SessionScopeModule +org.apache.maven.SessionScoped +org.apache.maven.settings.building.DefaultSettingsBuilder +org.apache.maven.settings.building.DefaultSettingsBuilderFactory +org.apache.maven.settings.building.DefaultSettingsBuilderFactoryTest +org.apache.maven.settings.building.DefaultSettingsBuildingRequest +org.apache.maven.settings.building.DefaultSettingsBuildingResult +org.apache.maven.settings.building.DefaultSettingsProblem +org.apache.maven.settings.building.DefaultSettingsProblemCollector +org.apache.maven.settings.building.FileSettingsSource +org.apache.maven.settings.building.SettingsBuilder +org.apache.maven.settings.building.SettingsBuildingException +org.apache.maven.settings.building.SettingsBuildingRequest +org.apache.maven.settings.building.SettingsBuildingResult +org.apache.maven.settings.building.SettingsProblem +org.apache.maven.settings.building.SettingsProblemCollector +org.apache.maven.settings.building.SettingsSource +org.apache.maven.settings.building.StringSettingsSource +org.apache.maven.settings.building.UrlSettingsSource +org.apache.maven.settings.crypto.DefaultSettingsDecrypter +org.apache.maven.settings.crypto.DefaultSettingsDecryptionRequest +org.apache.maven.settings.crypto.DefaultSettingsDecryptionResult +org.apache.maven.settings.crypto.SettingsDecrypter +org.apache.maven.settings.crypto.SettingsDecryptionRequest +org.apache.maven.settings.crypto.SettingsDecryptionResult +org.apache.maven.settings.DefaultMavenSettingsBuilder +org.apache.maven.settings.GlobalSettingsTest +org.apache.maven.settings.io.DefaultSettingsReader +org.apache.maven.settings.io.DefaultSettingsWriter +org.apache.maven.settings.io.SettingsParseException +org.apache.maven.settings.io.SettingsReader +org.apache.maven.settings.io.SettingsWriter +org.apache.maven.settings.MavenSettingsBuilder +org.apache.maven.settings.merge.MavenSettingsMerger +org.apache.maven.settings.PomConstructionWithSettingsTest +org.apache.maven.settings.RuntimeInfo +org.apache.maven.settings.SettingsConfigurationException +org.apache.maven.settings.SettingsUtils +org.apache.maven.settings.SettingsUtilsTest +org.apache.maven.settings.validation.DefaultSettingsValidator +org.apache.maven.settings.validation.DefaultSettingsValidatorTest +org.apache.maven.settings.validation.SettingsValidator +org.apache.maven.toolchain.building.DefaultToolchainsBuilder +org.apache.maven.toolchain.building.DefaultToolchainsBuilderTest +org.apache.maven.toolchain.building.DefaultToolchainsBuildingRequest +org.apache.maven.toolchain.building.DefaultToolchainsBuildingResult +org.apache.maven.toolchain.building.ToolchainsBuilder +org.apache.maven.toolchain.building.ToolchainsBuildingException +org.apache.maven.toolchain.building.ToolchainsBuildingExceptionTest +org.apache.maven.toolchain.building.ToolchainsBuildingRequest +org.apache.maven.toolchain.building.ToolchainsBuildingResult +org.apache.maven.toolchain.DefaultToolchain +org.apache.maven.toolchain.DefaultToolchainManager +org.apache.maven.toolchain.DefaultToolchainManagerPrivate +org.apache.maven.toolchain.DefaultToolchainManagerPrivateTest +org.apache.maven.toolchain.DefaultToolchainManagerTest +org.apache.maven.toolchain.DefaultToolchainsBuilder +org.apache.maven.toolchain.DefaultToolchainTest +org.apache.maven.toolchain.io.DefaultToolchainsReader +org.apache.maven.toolchain.io.DefaultToolchainsWriter +org.apache.maven.toolchain.io.ToolchainsParseException +org.apache.maven.toolchain.io.ToolchainsReader +org.apache.maven.toolchain.io.ToolchainsWriter +org.apache.maven.toolchain.merge.MavenToolchainMerger +org.apache.maven.toolchain.merge.MavenToolchainMergerTest +org.apache.maven.toolchain.MisconfiguredToolchainException +org.apache.maven.toolchain.RequirementMatcher +org.apache.maven.toolchain.RequirementMatcherFactory +org.apache.maven.toolchain.RequirementMatcherFactoryTest +org.apache.maven.toolchain.Toolchain +org.apache.maven.toolchain.ToolchainFactory +org.apache.maven.toolchain.ToolchainManager +org.apache.maven.toolchain.ToolchainManagerPrivate +org.apache.maven.toolchain.ToolchainPrivate +org.apache.maven.toolchain.ToolchainsBuilder +org.apache.maven.usability.plugin.ExpressionDocumentationException +org.apache.maven.usability.plugin.ExpressionDocumenter +org.apache.maven.wrapper.BootstrapMainStarter +org.apache.maven.wrapper.DefaultDownloader +org.apache.maven.wrapper.Downloader +org.apache.maven.wrapper.DownloaderTest +org.apache.maven.wrapper.Installer +org.apache.maven.wrapper.InstallerTest +org.apache.maven.wrapper.Logger +org.apache.maven.wrapper.MavenWrapperMain +org.apache.maven.wrapper.PathAssembler +org.apache.maven.wrapper.PathAssemblerTest +org.apache.maven.wrapper.WrapperConfiguration +org.apache.maven.wrapper.WrapperExecutor +org.apache.maven.wrapper.WrapperExecutorTest +org.apache.maven.xml.internal.DefaultConsumerPomXMLFilterFactory +org.apache.felix.bundlerepository.Capability +org.apache.felix.bundlerepository.DataModelHelper +org.apache.felix.bundlerepository.impl.Activator +org.apache.felix.bundlerepository.impl.Base64Encoder +org.apache.felix.bundlerepository.impl.CapabilityImpl +org.apache.felix.bundlerepository.impl.DataModelHelperImpl +org.apache.felix.bundlerepository.impl.FelixCapabilityAdapter +org.apache.felix.bundlerepository.impl.FelixPropertyAdapter +org.apache.felix.bundlerepository.impl.FelixRequirementAdapter +org.apache.felix.bundlerepository.impl.FelixResourceAdapter +org.apache.felix.bundlerepository.impl.FileUtil +org.apache.felix.bundlerepository.impl.LazyLocalResourceImpl +org.apache.felix.bundlerepository.impl.LazyStringMap +org.apache.felix.bundlerepository.impl.LocalRepositoryImpl +org.apache.felix.bundlerepository.impl.LocalResourceImpl +org.apache.felix.bundlerepository.impl.NamespaceTranslator +org.apache.felix.bundlerepository.impl.ObrCommandImpl +org.apache.felix.bundlerepository.impl.ObrGogoCommand +org.apache.felix.bundlerepository.impl.ObrURLStreamHandlerService +org.apache.felix.bundlerepository.impl.OSGiCapabilityAdapter +org.apache.felix.bundlerepository.impl.OSGiRepositoryImpl +org.apache.felix.bundlerepository.impl.OSGiRequirementAdapter +org.apache.felix.bundlerepository.impl.OSGiResourceHelper +org.apache.felix.bundlerepository.impl.OSGiResourceImpl +org.apache.felix.bundlerepository.impl.PropertyImpl +org.apache.felix.bundlerepository.impl.PullParser +org.apache.felix.bundlerepository.impl.ReasonImpl +org.apache.felix.bundlerepository.impl.Referral +org.apache.felix.bundlerepository.impl.RepositoryAdminImpl +org.apache.felix.bundlerepository.impl.RepositoryImpl +org.apache.felix.bundlerepository.impl.RepositoryParser +org.apache.felix.bundlerepository.impl.RequirementImpl +org.apache.felix.bundlerepository.impl.ResolverImpl +org.apache.felix.bundlerepository.impl.ResourceCapability +org.apache.felix.bundlerepository.impl.ResourceCapabilityImpl +org.apache.felix.bundlerepository.impl.ResourceComparator +org.apache.felix.bundlerepository.impl.ResourceImpl +org.apache.felix.bundlerepository.impl.SpecXMLPullParser +org.apache.felix.bundlerepository.impl.StaxParser +org.apache.felix.bundlerepository.impl.SystemRepositoryImpl +org.apache.felix.bundlerepository.impl.wrapper.CapabilityWrapper +org.apache.felix.bundlerepository.impl.wrapper.ConvertedResource +org.apache.felix.bundlerepository.impl.wrapper.RepositoryAdminWrapper +org.apache.felix.bundlerepository.impl.wrapper.RepositoryWrapper +org.apache.felix.bundlerepository.impl.wrapper.RequirementWrapper +org.apache.felix.bundlerepository.impl.wrapper.ResolverWrapper +org.apache.felix.bundlerepository.impl.wrapper.ResourceWrapper +org.apache.felix.bundlerepository.impl.wrapper.Wrapper +org.apache.felix.bundlerepository.impl.XmlWriter +org.apache.felix.bundlerepository.InterruptedResolutionException +org.apache.felix.bundlerepository.LocalResource +org.apache.felix.bundlerepository.Property +org.apache.felix.bundlerepository.Reason +org.apache.felix.bundlerepository.Repository +org.apache.felix.bundlerepository.RepositoryAdmin +org.apache.felix.bundlerepository.Requirement +org.apache.felix.bundlerepository.Resolver +org.apache.felix.bundlerepository.Resource +org.apache.felix.bundlerepository.impl.CapabilityImplTest +org.apache.felix.bundlerepository.impl.DataModelHelperTest +org.apache.felix.bundlerepository.impl.FelixRequirementAdapterTest +org.apache.felix.bundlerepository.impl.LazyStringMapTest +org.apache.felix.bundlerepository.impl.NamespaceTranslatorTest +org.apache.felix.bundlerepository.impl.OSGiRepositoryImplTest +org.apache.felix.bundlerepository.impl.OSGiRepositoryXMLTest +org.apache.felix.bundlerepository.impl.OSGiRequirementAdapterTest +org.apache.felix.bundlerepository.impl.RepositoryAdminTest +org.apache.felix.bundlerepository.impl.RepositoryImplTest +org.apache.felix.bundlerepository.impl.ResolverImplTest +org.apache.felix.bundlerepository.impl.ResourceImplTest +org.apache.felix.bundlerepository.impl.StaxParserTest +org.apache.felix.bundlerepository.impl.Streams +org.apache.felix.bundlerepository.osgict.Activator +org.apache.felix.cm.json.ConfigurationReader +org.apache.felix.cm.json.ConfigurationResource +org.apache.felix.cm.json.Configurations +org.apache.felix.cm.json.ConfigurationWriter +org.apache.felix.cm.json.impl.ConfigurationReaderImpl +org.apache.felix.cm.json.impl.ConfigurationWriterImpl +org.apache.felix.cm.json.impl.JsonSupport +org.apache.felix.cm.json.impl.OrderedDictionary +org.apache.felix.cm.json.impl.TypeConverter +org.apache.felix.cm.json.package-info +org.apache.felix.cm.json.impl.ConfigurationReaderImplTest +org.apache.felix.cm.json.impl.JsonSupportTest +org.apache.felix.cm.json.impl.OrderedDictionaryTest +org.apache.felix.cm.json.impl.TypeConverterTest +org.apache.felix.cm.file.ConfigurationHandler +org.apache.felix.cm.file.FilePersistenceManager +org.apache.felix.cm.file.package-info +org.apache.felix.cm.impl.Activator +org.apache.felix.cm.impl.ActivatorWorkerQueue +org.apache.felix.cm.impl.CaseInsensitiveDictionary +org.apache.felix.cm.impl.ConfigurationAdapter +org.apache.felix.cm.impl.ConfigurationAdminFactory +org.apache.felix.cm.impl.ConfigurationAdminImpl +org.apache.felix.cm.impl.ConfigurationAdminStarter +org.apache.felix.cm.impl.ConfigurationImpl +org.apache.felix.cm.impl.ConfigurationManager +org.apache.felix.cm.impl.CoordinatorUtil +org.apache.felix.cm.impl.DependencyTracker +org.apache.felix.cm.impl.DynamicBindings +org.apache.felix.cm.impl.helper.BaseTracker +org.apache.felix.cm.impl.helper.ConfigurationMap +org.apache.felix.cm.impl.helper.ManagedServiceConfigurationMap +org.apache.felix.cm.impl.helper.ManagedServiceFactoryConfigurationMap +org.apache.felix.cm.impl.helper.ManagedServiceFactoryTracker +org.apache.felix.cm.impl.helper.ManagedServiceTracker +org.apache.felix.cm.impl.helper.TargetedPID +org.apache.felix.cm.impl.Log +org.apache.felix.cm.impl.persistence.CachingPersistenceManagerProxy +org.apache.felix.cm.impl.persistence.ExtPersistenceManager +org.apache.felix.cm.impl.persistence.MemoryPersistenceManager +org.apache.felix.cm.impl.persistence.PersistenceManagerProxy +org.apache.felix.cm.impl.persistence.PersistenceManagerTracker +org.apache.felix.cm.impl.RankingComparator +org.apache.felix.cm.impl.RequiredConfigurationPluginTracker +org.apache.felix.cm.impl.SimpleFilter +org.apache.felix.cm.impl.UpdateThread +org.apache.felix.cm.NotCachablePersistenceManager +org.apache.felix.cm.package-info +org.apache.felix.cm.PersistenceManager +org.apache.felix.cm.file.ConfigurationHandlerTest +org.apache.felix.cm.file.FilePersistenceManagerConstructorTest +org.apache.felix.cm.file.FilePersistenceManagerTest +org.apache.felix.cm.impl.CaseInsensitiveDictionaryTest +org.apache.felix.cm.impl.ConfigurationAdapterTest +org.apache.felix.cm.impl.ConfigurationAdminStarterTest +org.apache.felix.cm.impl.ConfigurationImplTest +org.apache.felix.cm.impl.ConfigurationManagerTest +org.apache.felix.cm.impl.DynamicBindingsTest +org.apache.felix.cm.impl.helper.ConfigurationMapTest +org.apache.felix.cm.impl.helper.TargetedPidTest +org.apache.felix.cm.impl.persistence.CachingPersistenceManagerProxyTest +org.apache.felix.cm.impl.persistence.MemoryPersistenceManagerTest +org.apache.felix.cm.impl.persistence.PersistenceManagerProxyTest +org.apache.felix.cm.impl.RankingComparatorTest +org.apache.felix.cm.impl.RequiredConfigurationPluginTrackerTest +org.apache.felix.cm.impl.UpdateThreadTest +org.apache.felix.cm.integration.ActivationTest +org.apache.felix.cm.integration.ConfigAdminSecurityTest +org.apache.felix.cm.integration.ConfigUpdateStressTest +org.apache.felix.cm.integration.ConfigurationAdminUpdateStressTest +org.apache.felix.cm.integration.ConfigurationBaseTest +org.apache.felix.cm.integration.ConfigurationBindingTest +org.apache.felix.cm.integration.ConfigurationListenerTest +org.apache.felix.cm.integration.ConfigurationTestBase +org.apache.felix.cm.integration.FELIX2813_ConfigurationAdminStartupTest +org.apache.felix.cm.integration.FELIX4385_StressTest +org.apache.felix.cm.integration.helper.BaseTestActivator +org.apache.felix.cm.integration.helper.ConfigurationListenerTestActivator +org.apache.felix.cm.integration.helper.ConfigureThread +org.apache.felix.cm.integration.helper.Log +org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator +org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator2 +org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator3 +org.apache.felix.cm.integration.helper.ManagedServiceFactoryTestActivator4 +org.apache.felix.cm.integration.helper.ManagedServiceFactoryThread +org.apache.felix.cm.integration.helper.ManagedServiceTestActivator +org.apache.felix.cm.integration.helper.ManagedServiceTestActivator2 +org.apache.felix.cm.integration.helper.ManagedServiceThread +org.apache.felix.cm.integration.helper.MultiManagedServiceFactoryTestActivator +org.apache.felix.cm.integration.helper.MultiManagedServiceTestActivator +org.apache.felix.cm.integration.helper.NestedURLStreamHandler +org.apache.felix.cm.integration.helper.SynchronousTestListener +org.apache.felix.cm.integration.helper.TestListener +org.apache.felix.cm.integration.helper.TestThread +org.apache.felix.cm.integration.helper.UpdateThreadSignalTask +org.apache.felix.cm.integration.MultiServiceFactoryPIDTest +org.apache.felix.cm.integration.MultiServicePIDTest +org.apache.felix.cm.integration.MultiValuePIDTest +org.apache.felix.cm.integration.TargetedPidTest +org.apache.felix.cm.MockBundle +org.apache.felix.cm.MockBundleContext +org.apache.felix.cm.MockLogService +org.apache.felix.cm.MockNotCachablePersistenceManager +org.apache.felix.cm.MockPersistenceManager +org.apache.felix.cm.MockServiceReference +org.apache.felix.configadmin.plugin.interpolation.Activator +org.apache.felix.configadmin.plugin.interpolation.InterpolationConfigurationPlugin +org.apache.felix.configadmin.plugin.interpolation.Interpolator +org.apache.felix.configadmin.plugin.interpolation.StandaloneInterpolator +org.apache.felix.configadmin.plugin.interpolation.ActivatorTest +org.apache.felix.configadmin.plugin.interpolation.InterpolationConfigurationPluginTest +org.apache.felix.configadmin.plugin.interpolation.InterpolatorTest +org.apache.felix.configadmin.plugin.interpolation.StandaloneInterpolatorTest +org.apache.felix.configurator.impl.Activator +org.apache.felix.configurator.impl.Configurator +org.apache.felix.configurator.impl.ConfigUtil +org.apache.felix.configurator.impl.CoordinatorUtil +org.apache.felix.configurator.impl.json.BinaryManager +org.apache.felix.configurator.impl.json.BinUtil +org.apache.felix.configurator.impl.json.JSONUtil +org.apache.felix.configurator.impl.logger.InternalLogger +org.apache.felix.configurator.impl.logger.LogServiceEnabledLogger +org.apache.felix.configurator.impl.logger.LogServiceSupport +org.apache.felix.configurator.impl.logger.R6LogServiceLogger +org.apache.felix.configurator.impl.logger.StdOutLogger +org.apache.felix.configurator.impl.logger.SystemLogger +org.apache.felix.configurator.impl.model.AbstractState +org.apache.felix.configurator.impl.model.BundleState +org.apache.felix.configurator.impl.model.Config +org.apache.felix.configurator.impl.model.ConfigList +org.apache.felix.configurator.impl.model.ConfigPolicy +org.apache.felix.configurator.impl.model.ConfigState +org.apache.felix.configurator.impl.model.ConfigurationFile +org.apache.felix.configurator.impl.model.State +org.apache.felix.configurator.impl.ServicesListener +org.apache.felix.configurator.impl.Util +org.apache.felix.configurator.impl.WorkerQueue +org.apache.felix.configurator.impl.ConfiguratorTest +org.apache.felix.configurator.impl.ConfigUtilTest +org.apache.felix.configurator.impl.model.BundleStateTest +org.apache.felix.configurator.impl.model.ConfigListTest +org.apache.felix.configurator.impl.model.ConfigTest +org.apache.felix.configurator.impl.model.StateTest +org.apache.felix.connect.BundleAware +org.apache.felix.connect.DirRevision +org.apache.felix.connect.EntriesEnumeration +org.apache.felix.connect.EntryFilterEnumeration +org.apache.felix.connect.ExportedPackageImpl +org.apache.felix.connect.felix.framework.capabilityset.CapabilitySet +org.apache.felix.connect.felix.framework.capabilityset.SimpleFilter +org.apache.felix.connect.felix.framework.DTOFactory +org.apache.felix.connect.felix.framework.HookRegistry +org.apache.felix.connect.felix.framework.ServiceRegistrationImpl +org.apache.felix.connect.felix.framework.ServiceRegistry +org.apache.felix.connect.felix.framework.util.EventDispatcher +org.apache.felix.connect.felix.framework.util.ListenerInfo +org.apache.felix.connect.felix.framework.util.MapToDictionary +org.apache.felix.connect.felix.framework.util.ShrinkableCollection +org.apache.felix.connect.felix.framework.util.ShrinkableList +org.apache.felix.connect.felix.framework.util.ShrinkableMap +org.apache.felix.connect.felix.framework.util.StringComparator +org.apache.felix.connect.felix.framework.util.StringMap +org.apache.felix.connect.felix.framework.util.Util +org.apache.felix.connect.felix.framework.util.VersionRange +org.apache.felix.connect.felix.framework.wiring.BundleCapabilityImpl +org.apache.felix.connect.FileEntriesEnumeration +org.apache.felix.connect.JarRevision +org.apache.felix.connect.launch.BundleDescriptor +org.apache.felix.connect.launch.ClasspathScanner +org.apache.felix.connect.launch.PojoServiceRegistry +org.apache.felix.connect.launch.PojoServiceRegistryFactory +org.apache.felix.connect.PojoServiceRegistryFactoryImpl +org.apache.felix.connect.PojoSR +org.apache.felix.connect.PojoSRBundle +org.apache.felix.connect.PojoSRBundleContext +org.apache.felix.connect.RequiredBundleImpl +org.apache.felix.connect.Revision +org.apache.felix.connect.URLRevision +org.apache.felix.connect.VFSRevision +org.osgi.util.converter.AbstractCollectionDelegate +org.osgi.util.converter.AbstractSpecifying +org.osgi.util.converter.ArrayDelegate +org.osgi.util.converter.CollectionDelegate +org.osgi.util.converter.CollectionSetDelegate +org.osgi.util.converter.ConversionException +org.osgi.util.converter.Converter +org.osgi.util.converter.ConverterBuilder +org.osgi.util.converter.ConverterBuilderImpl +org.osgi.util.converter.ConverterFunction +org.osgi.util.converter.ConverterImpl +org.osgi.util.converter.Converters +org.osgi.util.converter.Converting +org.osgi.util.converter.ConvertingImpl +org.osgi.util.converter.CustomConverterImpl +org.osgi.util.converter.DTOUtil +org.osgi.util.converter.DynamicMapLikeFacade +org.osgi.util.converter.Functioning +org.osgi.util.converter.FunctioningImpl +org.osgi.util.converter.InternalConverter +org.osgi.util.converter.InternalConverting +org.osgi.util.converter.ListDelegate +org.osgi.util.converter.MapDelegate +org.osgi.util.converter.package-info +org.osgi.util.converter.Rule +org.osgi.util.converter.SetDelegate +org.osgi.util.converter.Specifying +org.osgi.util.converter.TargetRule +org.osgi.util.converter.TypeReference +org.osgi.util.converter.TypeRule +org.osgi.util.converter.Util +org.osgi.util.converter.ConverterBuilderTest +org.osgi.util.converter.ConverterCollectionsTest +org.osgi.util.converter.ConverterFunctionTest +org.osgi.util.converter.ConverterMapTest +org.osgi.util.converter.ConverterTest +org.osgi.util.converter.InterfaceWithDefaultMethod +org.osgi.util.converter.MyBean +org.osgi.util.converter.MyDefaultCtorDTOAlike +org.osgi.util.converter.MyDTO +org.osgi.util.converter.MyDTO2 +org.osgi.util.converter.MyDTO3 +org.osgi.util.converter.MyDTO4 +org.osgi.util.converter.MyDTO5 +org.osgi.util.converter.MyDTO6 +org.osgi.util.converter.MyDTO7 +org.osgi.util.converter.MyDTO8 +org.osgi.util.converter.MyDTO9 +org.osgi.util.converter.MyDTOWithMethods +org.osgi.util.converter.MyEmbeddedDTO +org.osgi.util.converter.MyGenericDTOWithVariables +org.osgi.util.converter.MyGenericInterface +org.osgi.util.converter.MyGenericInterfaceWithVariables +org.osgi.util.converter.MySubDTO +org.osgi.util.converter.PrefixAnnotation +org.osgi.util.converter.PrefixDTO +org.osgi.util.converter.PrefixEnumAnnotation +org.osgi.util.converter.PrefixInterface +org.osgi.util.converter.PrefixMarkerAnnotation +org.osgi.util.converter.sub1.TestAnn1 +org.osgi.util.converter.sub2.TestAnn2 +org.osgi.util.converter.TestDictionary +org.osgi.util.converter.UtilTest +org.apache.felix.schematizer.AsDTO +org.apache.felix.schematizer.impl.Activator +org.apache.felix.schematizer.impl.CollectionNode +org.apache.felix.schematizer.impl.NodeImpl +org.apache.felix.schematizer.impl.SchemaBasedConverter +org.apache.felix.schematizer.impl.SchemaImpl +org.apache.felix.schematizer.impl.SchematizerImpl +org.apache.felix.schematizer.impl.SchematizerImplOLD +org.apache.felix.schematizer.impl.Util +org.apache.felix.schematizer.Node +org.apache.felix.schematizer.NodeVisitor +org.apache.felix.schematizer.package-info +org.apache.felix.schematizer.Schema +org.apache.felix.schematizer.Schematizer +org.apache.felix.schematizer.StandardSchematizer +org.apache.felix.schematizer.impl.CollectionType +org.apache.felix.schematizer.impl.MyBean +org.apache.felix.schematizer.impl.MyDTO +org.apache.felix.schematizer.impl.MyDTO2 +org.apache.felix.schematizer.impl.MyDTO3 +org.apache.felix.schematizer.impl.MyDTO4 +org.apache.felix.schematizer.impl.MyEmbeddedDTO +org.apache.felix.schematizer.impl.MyEmbeddedDTO2 +org.apache.felix.schematizer.impl.MySubDTO +org.apache.felix.schematizer.impl.SchemaTest +org.apache.felix.schematizer.impl.SchematizerServiceTest +org.apache.felix.serializer.impl.json.JsonDeserializationTest +org.apache.felix.serializer.impl.json.MyDTO +org.apache.felix.serializer.impl.json.MyDTO2 +org.apache.felix.serializer.impl.json.MyEmbeddedDTO +org.apache.felix.serializer.impl.json.MyEmbeddedDTO2 +org.apache.felix.serializer.Deserializing +org.apache.felix.serializer.impl.AbstractSpecifying +org.apache.felix.serializer.impl.Activator +org.apache.felix.serializer.impl.json.DebugJsonWriter +org.apache.felix.serializer.impl.json.DefaultJsonParser +org.apache.felix.serializer.impl.json.DefaultJsonWriter +org.apache.felix.serializer.impl.json.JsonDeserializingImpl +org.apache.felix.serializer.impl.json.JsonSerializerImpl +org.apache.felix.serializer.impl.json.JsonSerializingImpl +org.apache.felix.serializer.impl.json.JsonWriterFactory +org.apache.felix.serializer.impl.PrototypeWriterFactory +org.apache.felix.serializer.impl.Util +org.apache.felix.serializer.impl.yaml.DefaultYamlParser +org.apache.felix.serializer.impl.yaml.DefaultYamlWriter +org.apache.felix.serializer.impl.yaml.YamlDeserializingImpl +org.apache.felix.serializer.impl.yaml.YamlSerializerImpl +org.apache.felix.serializer.impl.yaml.YamlSerializingImpl +org.apache.felix.serializer.impl.yaml.YamlWriterFactory +org.apache.felix.serializer.package-info +org.apache.felix.serializer.Parser +org.apache.felix.serializer.Serializer +org.apache.felix.serializer.Serializing +org.apache.felix.serializer.Writer +org.apache.felix.serializer.WriterFactory +org.apache.felix.serializer.impl.json.JsonBackingObjectSerializationTest +org.apache.felix.serializer.impl.json.JsonParserTest +org.apache.felix.serializer.impl.json.JsonSerializationTest +org.apache.felix.serializer.impl.json.JsonSerializerTest +org.apache.felix.serializer.impl.json.MyDTO +org.apache.felix.serializer.impl.json.MyEmbeddedDTO +org.apache.felix.serializer.impl.yaml.YamlSerializationTest +org.apache.felix.serializer.impl.yaml.YamlSerializerTest +org.apache.felix.coordinator.impl.Activator +org.apache.felix.coordinator.impl.CoordinationHolder +org.apache.felix.coordinator.impl.CoordinationImpl +org.apache.felix.coordinator.impl.CoordinationMgr +org.apache.felix.coordinator.impl.CoordinatorImpl +org.apache.felix.coordinator.impl.LogWrapper +org.apache.felix.coordinator.impl.CoordinatorImplTest +org.apache.felix.deployment.rp.autoconf.Activator +org.apache.felix.deployment.rp.autoconf.AttributeDefinitionImpl +org.apache.felix.deployment.rp.autoconf.AutoConfResource +org.apache.felix.deployment.rp.autoconf.AutoConfResourceProcessor +org.apache.felix.deployment.rp.autoconf.MetaTypeUtil +org.apache.felix.deployment.rp.autoconf.ObjectClassDefinitionImpl +org.apache.felix.deployment.rp.autoconf.PersistencyManager +org.apache.felix.deployment.rp.autoconf.AutoConfResourceProcessorTest +org.apache.felix.deployment.rp.autoconf.DefaultNullObject +org.apache.felix.deployment.rp.autoconf.PersistencyManagerTest +org.apache.felix.deployment.rp.autoconf.Utils +org.apache.felix.deploymentadmin.AbstractDeploymentPackage +org.apache.felix.deploymentadmin.AbstractInfo +org.apache.felix.deploymentadmin.Activator +org.apache.felix.deploymentadmin.BundleInfoImpl +org.apache.felix.deploymentadmin.Constants +org.apache.felix.deploymentadmin.ContentCopyingJarInputStream +org.apache.felix.deploymentadmin.DeploymentAdminConfig +org.apache.felix.deploymentadmin.DeploymentAdminImpl +org.apache.felix.deploymentadmin.DeploymentPackageManifest +org.apache.felix.deploymentadmin.FileDeploymentPackage +org.apache.felix.deploymentadmin.NonCloseableStream +org.apache.felix.deploymentadmin.ResourceInfoImpl +org.apache.felix.deploymentadmin.Semaphore +org.apache.felix.deploymentadmin.spi.AbstractAction +org.apache.felix.deploymentadmin.spi.Command +org.apache.felix.deploymentadmin.spi.CommitResourceCommand +org.apache.felix.deploymentadmin.spi.DeploymentSessionImpl +org.apache.felix.deploymentadmin.spi.DropAllBundlesCommand +org.apache.felix.deploymentadmin.spi.DropAllResourcesCommand +org.apache.felix.deploymentadmin.spi.DropBundleCommand +org.apache.felix.deploymentadmin.spi.DropResourceCommand +org.apache.felix.deploymentadmin.spi.GetStorageAreaCommand +org.apache.felix.deploymentadmin.spi.ProcessResourceCommand +org.apache.felix.deploymentadmin.spi.SnapshotCommand +org.apache.felix.deploymentadmin.spi.StartBundleCommand +org.apache.felix.deploymentadmin.spi.StartCustomizerCommand +org.apache.felix.deploymentadmin.spi.StopBundleCommand +org.apache.felix.deploymentadmin.spi.UpdateCommand +org.apache.felix.deploymentadmin.StreamDeploymentPackage +org.apache.felix.deploymentadmin.Utils +org.apache.felix.deploymentadmin.VersionRange +org.apache.felix.deploymentadmin.ContentCopyingJarInputStreamTest +org.apache.felix.deploymentadmin.DeploymentAdminConfigTest +org.apache.felix.deploymentadmin.spi.SnapshotCommandTest +org.apache.felix.deploymentadmin.itest.BaseIntegrationTest +org.apache.felix.deploymentadmin.itest.CustomizerTest +org.apache.felix.deploymentadmin.itest.DeploymentAdminEventTest +org.apache.felix.deploymentadmin.itest.DeploymentAdminTest +org.apache.felix.deploymentadmin.itest.DeploymentPackageBuilderTest +org.apache.felix.deploymentadmin.itest.DPSignerTest +org.apache.felix.deploymentadmin.itest.InstallDeploymentPackageTest +org.apache.felix.deploymentadmin.itest.InstallFixPackageTest +org.apache.felix.deploymentadmin.itest.ResourceSharingTest +org.apache.felix.deploymentadmin.itest.UninstallDeploymentPackageTest +org.apache.felix.deploymentadmin.itest.util.ArtifactData +org.apache.felix.deploymentadmin.itest.util.ArtifactDataBuilder +org.apache.felix.deploymentadmin.itest.util.BundleDataBuilder +org.apache.felix.deploymentadmin.itest.util.CertificateUtil +org.apache.felix.deploymentadmin.itest.util.DeploymentPackageBuilder +org.apache.felix.deploymentadmin.itest.util.DPSigner +org.apache.felix.deploymentadmin.itest.util.LocalizationResourceDataBuilder +org.apache.felix.deploymentadmin.itest.util.ResourceDataBuilder +org.apache.felix.deploymentadmin.itest.util.ResourceFilter +org.apache.felix.deploymentadmin.itest.util.ResourceProcessorDataBuilder +org.apache.felix.deploymentadmin.test.bundle1.impl.Activator +org.apache.felix.deploymentadmin.test.bundle1.impl.TestServiceImpl +org.apache.felix.deploymentadmin.test.bundle1.TestService +org.apache.felix.deploymentadmin.test.bundle2.impl.Activator +org.apache.felix.deploymentadmin.test.bundle3.impl.Activator +org.apache.felix.deploymentadmin.test.bundleapi.MyService +org.apache.felix.deploymentadmin.test.bundleapi.MyService +org.apache.felix.deploymentadmin.test.bundleimpl.MyServiceImpl +org.apache.felix.deploymentadmin.test.bundleimpl.MyServiceImpl +org.apache.felix.deploymentadmin.test.fragment1.impl.Pojo +org.apache.felix.deploymentadmin.test.rp1.impl.Activator +org.apache.felix.deploymentadmin.test.rp2.impl.Activator +org.apache.felix.das.Activator +org.apache.felix.das.DeviceManager +org.apache.felix.das.DriverAttributes +org.apache.felix.das.Log +org.apache.felix.das.NamedThreadFactory +org.apache.felix.das.util.DeviceAnalyzer +org.apache.felix.das.util.DriverAnalyzer +org.apache.felix.das.util.DriverLoader +org.apache.felix.das.util.DriverMatcher +org.apache.felix.das.util.Util +org.apache.felix.das.DeviceManagerTest +org.apache.felix.das.DriverAttributesTest +org.apache.felix.das.OSGiMock +org.apache.felix.das.util.DriverAnalyzerTest +org.apache.felix.das.util.DriverLoaderTest +org.apache.felix.das.util.DriverMatcherTest +org.apache.felix.das.util.UtilTest +org.apache.felix.das.Utils +org.apache.felix.eventadmin.bridge.configuration.impl.Activator +org.apache.felix.eventadmin.bridge.configuration.impl.ConfigurationEventToEventAdminBridge +org.apache.felix.eventadmin.bridge.upnp.Activator +org.apache.felix.eventadmin.bridge.upnp.UPnPEventToEventAdminBridge +org.apache.felix.eventadmin.bridge.useradmin.Activator +org.apache.felix.eventadmin.bridge.useradmin.UserAdminEventToEventAdminBridge +org.apache.felix.eventadmin.bridge.wireadmin.Activator +org.apache.felix.eventadmin.bridge.wireadmin.WireAdminEventToEventAdminBridge +org.apache.felix.eventadmin.impl.Activator +org.apache.felix.eventadmin.impl.adapter.AbstractAdapter +org.apache.felix.eventadmin.impl.adapter.BundleEventAdapter +org.apache.felix.eventadmin.impl.adapter.FrameworkEventAdapter +org.apache.felix.eventadmin.impl.adapter.LogEventAdapter +org.apache.felix.eventadmin.impl.adapter.ServiceEventAdapter +org.apache.felix.eventadmin.impl.Configuration +org.apache.felix.eventadmin.impl.handler.EventAdminImpl +org.apache.felix.eventadmin.impl.handler.EventHandlerProxy +org.apache.felix.eventadmin.impl.handler.EventHandlerTracker +org.apache.felix.eventadmin.impl.MetaTypeProviderImpl +org.apache.felix.eventadmin.impl.security.EventAdminSecurityDecorator +org.apache.felix.eventadmin.impl.security.PermissionsUtil +org.apache.felix.eventadmin.impl.security.SecureEventAdminFactory +org.apache.felix.eventadmin.impl.tasks.AsyncDeliverTasks +org.apache.felix.eventadmin.impl.tasks.DefaultThreadPool +org.apache.felix.eventadmin.impl.tasks.DenylistLatch +org.apache.felix.eventadmin.impl.tasks.HandlerTask +org.apache.felix.eventadmin.impl.tasks.SyncDeliverTasks +org.apache.felix.eventadmin.impl.tasks.SyncThread +org.apache.felix.eventadmin.impl.util.LogWrapper +org.apache.felix.eventadmin.impl.util.Matchers +org.apache.felix.eventadmin.impl.util.MatchersTest +org.apache.felix.eventadmin.ittests.AbstractTest +org.apache.felix.eventadmin.ittests.Listener +org.apache.felix.eventadmin.ittests.StressTestIT +org.apache.felix.eventadmin.perftests.PerformanceTestIT +org.apache.felix.examples.dictionaryclient.Activator +org.apache.felix.examples.dictionaryclient2.Activator +org.apache.felix.examples.dictionaryservice.DictionaryService +org.apache.felix.examples.dictionaryservice.impl.Activator +org.apache.felix.examples.dictionaryservice.itest.Activator +org.apache.felix.examples.eventlistener.Activator +org.apache.felix.example.extenderbased.circle.Circle +org.apache.felix.example.extenderbased.host.Activator +org.apache.felix.example.extenderbased.host.DefaultShape +org.apache.felix.example.extenderbased.host.DrawingFrame +org.apache.felix.example.extenderbased.host.extension.SimpleShape +org.apache.felix.example.extenderbased.host.launch.Application +org.apache.felix.example.extenderbased.host.launch.ConfigUtil +org.apache.felix.example.extenderbased.host.ShapeBundleTracker +org.apache.felix.example.extenderbased.host.ShapeComponent +org.apache.felix.example.extenderbased.square.Square +org.apache.felix.example.extenderbased.triangle.Triangle +org.apache.felix.examples.frenchdictionary.Activator +org.apache.felix.example.jaas.app.internal.BootClasspathDemoServlet +org.apache.felix.example.jaas.app.internal.FactoryDemoServlet +org.apache.felix.example.jaas.app.internal.GlobalConfigDemoServlet +org.apache.felix.example.jaas.app.internal.ServletRequestCallbackHandler +org.apache.felix.example.jaas.app.internal.TCCLDemoServlet +org.apache.felix.example.jaas.jdbc.internal.H2Activator +org.apache.felix.example.jaas.config.internal.SampleConfigLoginModule +org.apache.felix.example.jaas.config.internal.SamplePrincipal +org.apache.felix.example.jaas.jdbc.JdbcLoginModule +org.apache.felix.example.jaas.jdbc.JdbcLoginModuleFactory +org.apache.felix.example.jaas.jdbc.RolePrincipal +org.apache.felix.example.jaas.jdbc.UserPrincipal +org.apache.felix.example.servicebased.circle.Activator +org.apache.felix.example.servicebased.host.Activator +org.apache.felix.example.servicebased.host.DefaultShape +org.apache.felix.example.servicebased.host.DrawingFrame +org.apache.felix.example.servicebased.host.launch.Application +org.apache.felix.example.servicebased.host.launch.ConfigUtil +org.apache.felix.example.servicebased.host.service.SimpleShape +org.apache.felix.example.servicebased.host.ShapeComponent +org.apache.felix.example.servicebased.host.ShapeTracker +org.apache.felix.example.servicebased.square.Activator +org.apache.felix.example.servicebased.trapezoid.Activator +org.apache.felix.example.servicebased.triangle.Activator +..examples.simple.src.org.apache.felix.examples.simple.Activator +..examples.simple.src.org.apache.felix.examples.simple.embedded.Embedded +org.apache.felix.examples.spellcheckbinder.Activator +org.apache.felix.examples.spellcheckbinder.SpellCheckServiceImpl +org.apache.felix.examples.spellcheckclient.Activator +org.apache.felix.examples.spellcheckscr.SpellCheckServiceImpl +org.apache.felix.examples.spellcheckservice.impl.Activator +org.apache.felix.examples.spellcheckservice.SpellCheckService +org.apache.felix.fileinstall.ArtifactInstaller +org.apache.felix.fileinstall.ArtifactListener +org.apache.felix.fileinstall.ArtifactTransformer +org.apache.felix.fileinstall.ArtifactUrlTransformer +org.apache.felix.fileinstall.internal.Artifact +org.apache.felix.fileinstall.internal.BundleTransformer +org.apache.felix.fileinstall.internal.ConfigInstaller +org.apache.felix.fileinstall.internal.DirectoryWatcher +org.apache.felix.fileinstall.internal.FileInstall +org.apache.felix.fileinstall.internal.JarDirUrlHandler +org.apache.felix.fileinstall.internal.Scanner +org.apache.felix.fileinstall.internal.Util +org.apache.felix.fileinstall.internal.Watcher +org.apache.felix.fileinstall.internal.WatcherScanner +org.apache.felix.fileinstall.internal.BundleTransformerTest +org.apache.felix.fileinstall.internal.ConfigInstallerTest +org.apache.felix.fileinstall.internal.DirectoryWatcherTest +org.apache.felix.fileinstall.internal.ScannerSubDirTest +org.apache.felix.fileinstall.plugins.installer.Artifact +org.apache.felix.fileinstall.plugins.installer.FrameworkInstaller +org.apache.felix.fileinstall.plugins.installer.Hash +org.apache.felix.fileinstall.plugins.installer.impl.ArtifactImpl +org.apache.felix.fileinstall.plugins.installer.impl.Constants +org.apache.felix.fileinstall.plugins.installer.impl.DeploymentInstaller +org.apache.felix.fileinstall.plugins.installer.impl.FrameworkInstallerComponent +org.apache.felix.fileinstall.plugins.installer.impl.InstallableUnitImpl +org.apache.felix.fileinstall.plugins.installer.impl.Job +org.apache.felix.fileinstall.plugins.installer.impl.Locking +org.apache.felix.fileinstall.plugins.installer.impl.RequirementParser +org.apache.felix.fileinstall.plugins.installer.InstallableListener +org.apache.felix.fileinstall.plugins.installer.InstallableManager +org.apache.felix.fileinstall.plugins.installer.InstallableUnit +org.apache.felix.fileinstall.plugins.installer.InstallableUnitEvent +org.apache.felix.fileinstall.plugins.installer.package-info +org.apache.felix.fileinstall.plugins.installer.State +org.apache.felix.fileinstall.plugins.installer.impl.CompositeBundleInstallerTest +org.apache.felix.fileinstall.plugins.installer.impl.RequirementParserTest +org.apache.felix.fileinstall.plugins.installer.impl.TestLogService +org.apache.felix.fileinstall.plugins.installer.test.InstallerIntegrationTest +org.apache.felix.fileinstall.plugins.resolver.impl.BasicRegistry +org.apache.felix.fileinstall.plugins.resolver.impl.CapabilityImpl +org.apache.felix.fileinstall.plugins.resolver.impl.CapReqBase +org.apache.felix.fileinstall.plugins.resolver.impl.JarURLConnector +org.apache.felix.fileinstall.plugins.resolver.impl.Path +org.apache.felix.fileinstall.plugins.resolver.impl.PluginResolveContext +org.apache.felix.fileinstall.plugins.resolver.impl.PluginResolverComponent +org.apache.felix.fileinstall.plugins.resolver.impl.RequirementImpl +org.apache.felix.fileinstall.plugins.resolver.impl.ResolveResultImpl +org.apache.felix.fileinstall.plugins.resolver.impl.ResourceImpl +org.apache.felix.fileinstall.plugins.resolver.impl.URIUtils +org.apache.felix.fileinstall.plugins.resolver.package-info +org.apache.felix.fileinstall.plugins.resolver.PluginResolver +org.apache.felix.fileinstall.plugins.resolver.ResolveRequest +org.apache.felix.fileinstall.plugins.resolver.ResolveResult +org.apache.felix.fileinstall.plugins.resolver.impl.PluginResolveContextTest +org.apache.felix.fileinstall.plugins.resolver.impl.URIUtilsTest +org.apache.felix.framework.BundleContextImpl +org.apache.felix.framework.BundleImpl +org.apache.felix.framework.BundleProtectionDomain +org.apache.felix.framework.BundleRevisionDependencies +org.apache.felix.framework.BundleRevisionImpl +org.apache.felix.framework.BundleWiringImpl +org.apache.felix.framework.cache.BundleArchive +org.apache.felix.framework.cache.BundleArchiveRevision +org.apache.felix.framework.cache.BundleCache +org.apache.felix.framework.cache.ConnectContentContent +org.apache.felix.framework.cache.ConnectRevision +org.apache.felix.framework.cache.Content +org.apache.felix.framework.cache.ContentDirectoryContent +org.apache.felix.framework.cache.DirectoryContent +org.apache.felix.framework.cache.DirectoryRevision +org.apache.felix.framework.cache.JarContent +org.apache.felix.framework.cache.JarRevision +org.apache.felix.framework.capabilityset.CapabilitySet +org.apache.felix.framework.capabilityset.SimpleFilter +org.apache.felix.framework.DTOFactory +org.apache.felix.framework.EntryFilterEnumeration +org.apache.felix.framework.EventDispatcher +org.apache.felix.framework.ExportedPackageImpl +org.apache.felix.framework.ext.ClassPathExtenderFactory +org.apache.felix.framework.ext.SecurityProvider +org.apache.felix.framework.ExtensionManager +org.apache.felix.framework.FakeURLStreamHandler +org.apache.felix.framework.Felix +org.apache.felix.framework.FilterImpl +org.apache.felix.framework.FrameworkFactory +org.apache.felix.framework.FrameworkStartLevelImpl +org.apache.felix.framework.FrameworkWiringImpl +org.apache.felix.framework.HookRegistry +org.apache.felix.framework.Logger +org.apache.felix.framework.PackageAdminImpl +org.apache.felix.framework.RequiredBundleImpl +org.apache.felix.framework.ResolveContextImpl +org.apache.felix.framework.resolver.CandidateComparator +org.apache.felix.framework.resolver.ResolveException +org.apache.felix.framework.resolver.ResourceNotFoundException +org.apache.felix.framework.ServiceRegistrationImpl +org.apache.felix.framework.ServiceRegistry +org.apache.felix.framework.StartLevelImpl +org.apache.felix.framework.StatefulResolver +org.apache.felix.framework.URLHandlers +org.apache.felix.framework.URLHandlersActivator +org.apache.felix.framework.URLHandlersBundleStreamHandler +org.apache.felix.framework.URLHandlersBundleURLConnection +org.apache.felix.framework.URLHandlersContentHandlerProxy +org.apache.felix.framework.URLHandlersStreamHandlerProxy +org.apache.felix.framework.util.ClassFileVisitor +org.apache.felix.framework.util.ClassParser +org.apache.felix.framework.util.CompoundEnumeration +org.apache.felix.framework.util.FelixConstants +org.apache.felix.framework.util.ListenerInfo +org.apache.felix.framework.util.manifestparser.ManifestParser +org.apache.felix.framework.util.manifestparser.NativeLibrary +org.apache.felix.framework.util.manifestparser.NativeLibraryClause +org.apache.felix.framework.util.manifestparser.ParsedHeaderClause +org.apache.felix.framework.util.MapToDictionary +org.apache.felix.framework.util.MultiReleaseContent +org.apache.felix.framework.util.SecureAction +org.apache.felix.framework.util.SecurityManagerEx +org.apache.felix.framework.util.ShrinkableCollection +org.apache.felix.framework.util.ShrinkableMap +org.apache.felix.framework.util.StringComparator +org.apache.felix.framework.util.StringMap +org.apache.felix.framework.util.ThreadGate +org.apache.felix.framework.util.Util +org.apache.felix.framework.util.WeakZipFileFactory +org.apache.felix.framework.wiring.BundleCapabilityImpl +org.apache.felix.framework.wiring.BundleRequirementImpl +org.apache.felix.framework.wiring.BundleWireImpl +org.apache.felix.framework.WovenClassImpl +org.osgi.dto.DTO +org.osgi.dto.package-info +org.osgi.framework.AdaptPermission +org.osgi.framework.AdminPermission +org.osgi.framework.AllServiceListener +org.osgi.framework.Bundle +org.osgi.framework.BundleActivator +org.osgi.framework.BundleContext +org.osgi.framework.BundleEvent +org.osgi.framework.BundleException +org.osgi.framework.BundleListener +org.osgi.framework.BundlePermission +org.osgi.framework.BundleReference +org.osgi.framework.CapabilityPermission +org.osgi.framework.Configurable +org.osgi.framework.connect.ConnectContent +org.osgi.framework.connect.ConnectFrameworkFactory +org.osgi.framework.connect.ConnectModule +org.osgi.framework.connect.FrameworkUtilHelper +org.osgi.framework.connect.ModuleConnector +org.osgi.framework.connect.package-info +org.osgi.framework.Constants +org.osgi.framework.dto.BundleDTO +org.osgi.framework.dto.FrameworkDTO +org.osgi.framework.dto.package-info +org.osgi.framework.dto.ServiceReferenceDTO +org.osgi.framework.Filter +org.osgi.framework.FilterImpl +org.osgi.framework.FrameworkEvent +org.osgi.framework.FrameworkListener +org.osgi.framework.FrameworkUtil +org.osgi.framework.hooks.bundle.CollisionHook +org.osgi.framework.hooks.bundle.EventHook +org.osgi.framework.hooks.bundle.FindHook +org.osgi.framework.hooks.bundle.package-info +org.osgi.framework.hooks.resolver.package-info +org.osgi.framework.hooks.resolver.ResolverHook +org.osgi.framework.hooks.resolver.ResolverHookFactory +org.osgi.framework.hooks.service.EventHook +org.osgi.framework.hooks.service.EventListenerHook +org.osgi.framework.hooks.service.FindHook +org.osgi.framework.hooks.service.ListenerHook +org.osgi.framework.hooks.service.package-info +org.osgi.framework.hooks.weaving.package-info +org.osgi.framework.hooks.weaving.WeavingException +org.osgi.framework.hooks.weaving.WeavingHook +org.osgi.framework.hooks.weaving.WovenClass +org.osgi.framework.hooks.weaving.WovenClassListener +org.osgi.framework.InvalidSyntaxException +org.osgi.framework.launch.Framework +org.osgi.framework.launch.FrameworkFactory +org.osgi.framework.launch.package-info +org.osgi.framework.namespace.AbstractWiringNamespace +org.osgi.framework.namespace.BundleNamespace +org.osgi.framework.namespace.ExecutionEnvironmentNamespace +org.osgi.framework.namespace.HostNamespace +org.osgi.framework.namespace.IdentityNamespace +org.osgi.framework.namespace.NativeNamespace +org.osgi.framework.namespace.package-info +org.osgi.framework.namespace.PackageNamespace +org.osgi.framework.package-info +org.osgi.framework.PackagePermission +org.osgi.framework.PrototypeServiceFactory +org.osgi.framework.ServiceEvent +org.osgi.framework.ServiceException +org.osgi.framework.ServiceFactory +org.osgi.framework.ServiceListener +org.osgi.framework.ServiceObjects +org.osgi.framework.ServicePermission +org.osgi.framework.ServiceReference +org.osgi.framework.ServiceRegistration +org.osgi.framework.SignerProperty +org.osgi.framework.startlevel.BundleStartLevel +org.osgi.framework.startlevel.dto.BundleStartLevelDTO +org.osgi.framework.startlevel.dto.FrameworkStartLevelDTO +org.osgi.framework.startlevel.dto.package-info +org.osgi.framework.startlevel.FrameworkStartLevel +org.osgi.framework.startlevel.package-info +org.osgi.framework.SynchronousBundleListener +org.osgi.framework.UnfilteredServiceListener +org.osgi.framework.Version +org.osgi.framework.VersionRange +org.osgi.framework.wiring.BundleCapability +org.osgi.framework.wiring.BundleRequirement +org.osgi.framework.wiring.BundleRevision +org.osgi.framework.wiring.BundleRevisions +org.osgi.framework.wiring.BundleWire +org.osgi.framework.wiring.BundleWiring +org.osgi.framework.wiring.dto.BundleRevisionDTO +org.osgi.framework.wiring.dto.BundleWireDTO +org.osgi.framework.wiring.dto.BundleWiringDTO +org.osgi.framework.wiring.dto.FrameworkWiringDTO +org.osgi.framework.wiring.dto.package-info +org.osgi.framework.wiring.FrameworkWiring +org.osgi.framework.wiring.package-info +org.osgi.resource.Capability +org.osgi.resource.dto.CapabilityDTO +org.osgi.resource.dto.CapabilityRefDTO +org.osgi.resource.dto.package-info +org.osgi.resource.dto.RequirementDTO +org.osgi.resource.dto.RequirementRefDTO +org.osgi.resource.dto.ResourceDTO +org.osgi.resource.dto.WireDTO +org.osgi.resource.dto.WiringDTO +org.osgi.resource.Namespace +org.osgi.resource.package-info +org.osgi.resource.Requirement +org.osgi.resource.Resource +org.osgi.resource.Wire +org.osgi.resource.Wiring +org.osgi.service.condition.Condition +org.osgi.service.condition.package-info +org.osgi.service.packageadmin.ExportedPackage +org.osgi.service.packageadmin.package-info +org.osgi.service.packageadmin.PackageAdmin +org.osgi.service.packageadmin.RequiredBundle +org.osgi.service.startlevel.package-info +org.osgi.service.startlevel.StartLevel +org.osgi.service.url.AbstractURLStreamHandlerService +org.osgi.service.url.package-info +org.osgi.service.url.URLConstants +org.osgi.service.url.URLStreamHandlerService +org.osgi.service.url.URLStreamHandlerSetter +org.osgi.util.tracker.AbstractTracked +org.osgi.util.tracker.BundleTracker +org.osgi.util.tracker.BundleTrackerCustomizer +org.osgi.util.tracker.package-info +org.osgi.util.tracker.ServiceTracker +org.osgi.util.tracker.ServiceTrackerCustomizer +org.apache.felix.framework.BootLoaderTest +org.apache.felix.framework.BundleRevisionImplTest +org.apache.felix.framework.BundleWiringImplTest +org.apache.felix.framework.cache.BundleCacheTest +org.apache.felix.framework.capabilityset.SimpleFilterTest +org.apache.felix.framework.CollisionHookTest +org.apache.felix.framework.ConcurrencyTest +org.apache.felix.framework.ConcurrentBundleUpdateTest +org.apache.felix.framework.ConcurrentClassLoaderTest +org.apache.felix.framework.ConnectTest +org.apache.felix.framework.CycleDetectionWithWovenClassTest +org.apache.felix.framework.DTOFactoryTest +org.apache.felix.framework.EventDispatcherTest +org.apache.felix.framework.ExtensionManagerTest +org.apache.felix.framework.FilterTest +org.apache.felix.framework.FrameworkVersionTest +org.apache.felix.framework.ImplicitBootDelegationTest +org.apache.felix.framework.LaunchTest +org.apache.felix.framework.MultiReleaseVersionTest +org.apache.felix.framework.PackageAdminImplTest +org.apache.felix.framework.RequirementsCapabilitiesTest +org.apache.felix.framework.ResolveTest +org.apache.felix.framework.ResourceLoadingTest +org.apache.felix.framework.ServiceRegistrationImplTest +org.apache.felix.framework.ServiceRegistryTest +org.apache.felix.framework.StartBundleTest +org.apache.felix.framework.StartStopBundleTest +org.apache.felix.framework.UninstallBundleTest +org.apache.felix.framework.URLHandlersTest +org.apache.felix.framework.util.manifestparser.ManifestParserTest +org.apache.felix.framework.util.manifestparser.NativeLibraryClauseTest +org.apache.felix.framework.util.UtilTest +org.apache.felix.framework.util.WeakZipFileTest +org.osgi.util.tracker.BundleTrackerTest +org.osgi.util.tracker.ServiceTrackerTest +org.apache.felix.framework.FakeBundle +org.apache.felix.framework.security.condpermadmin.ConditionalPermissionAdminImpl +org.apache.felix.framework.security.condpermadmin.ConditionalPermissionInfoImpl +org.apache.felix.framework.security.condpermadmin.DomainGripper +org.apache.felix.framework.security.permissionadmin.PermissionAdminImpl +org.apache.felix.framework.security.SecurityConstants +org.apache.felix.framework.security.util.BundleInputStream +org.apache.felix.framework.security.util.Conditions +org.apache.felix.framework.security.util.LocalPermissions +org.apache.felix.framework.security.util.Permissions +org.apache.felix.framework.security.util.PropertiesCache +org.apache.felix.framework.security.util.TrustManager +org.apache.felix.framework.security.verifier.BundleDNParser +org.apache.felix.framework.SecurityActivator +org.apache.felix.framework.SecurityProviderImpl +org.apache.felix.gogo.command.Activator +org.apache.felix.gogo.command.Basic +org.apache.felix.gogo.command.Files +org.apache.felix.gogo.command.Inspect +org.apache.felix.gogo.command.package-info +org.apache.felix.gogo.command.Util +org.apache.felix.gogo.command.UtilTest +org.apache.felix.gogo.itest.jline.package-info +org.apache.felix.gogo.itest.shell.package-info +org.apache.felix.gogo.jline.Activator +org.apache.felix.gogo.jline.BaseConverters +org.apache.felix.gogo.jline.Builtin +org.apache.felix.gogo.jline.Converters +org.apache.felix.gogo.jline.Expander +org.apache.felix.gogo.jline.Highlighter +org.apache.felix.gogo.jline.Main +org.apache.felix.gogo.jline.package-info +org.apache.felix.gogo.jline.ParsedLineImpl +org.apache.felix.gogo.jline.Parser +org.apache.felix.gogo.jline.Posix +org.apache.felix.gogo.jline.Procedural +org.apache.felix.gogo.jline.Shell +org.apache.felix.gogo.jline.SingleServiceTracker +org.apache.felix.gogo.jline.AbstractParserTest +org.apache.felix.gogo.jline.BaseConvertersTest +org.apache.felix.gogo.jline.Context +org.apache.felix.gogo.jline.ParserTest +org.apache.felix.gogo.jline.PosixTest +org.apache.felix.gogo.jline.ShellTest +org.apache.felix.gogo.jline.ssh.ShellCommand +org.apache.felix.gogo.jline.ssh.ShellCommandFactory +org.apache.felix.gogo.jline.ssh.ShellFactoryImpl +org.apache.felix.gogo.jline.ssh.Ssh +org.apache.felix.gogo.jline.telnet.BootException +org.apache.felix.gogo.jline.telnet.Connection +org.apache.felix.gogo.jline.telnet.ConnectionData +org.apache.felix.gogo.jline.telnet.ConnectionEvent +org.apache.felix.gogo.jline.telnet.ConnectionFilter +org.apache.felix.gogo.jline.telnet.ConnectionListener +org.apache.felix.gogo.jline.telnet.ConnectionManager +org.apache.felix.gogo.jline.telnet.PortListener +org.apache.felix.gogo.jline.telnet.Telnet +org.apache.felix.gogo.jline.telnet.TelnetIO +org.apache.felix.gogo.runtime.activator.Activator +org.apache.felix.gogo.runtime.activator.EventAdminListener +org.apache.felix.gogo.runtime.ArgList +org.apache.felix.gogo.runtime.BaseTokenizer +org.apache.felix.gogo.runtime.Closure +org.apache.felix.gogo.runtime.CommandNotFoundException +org.apache.felix.gogo.runtime.CommandProcessorImpl +org.apache.felix.gogo.runtime.CommandProxy +org.apache.felix.gogo.runtime.CommandSessionImpl +org.apache.felix.gogo.runtime.EOFError +org.apache.felix.gogo.runtime.Evaluate +org.apache.felix.gogo.runtime.Expander +org.apache.felix.gogo.runtime.Expression +org.apache.felix.gogo.runtime.GlobPathMatcher +org.apache.felix.gogo.runtime.package-info +org.apache.felix.gogo.runtime.Parser +org.apache.felix.gogo.runtime.Pipe +org.apache.felix.gogo.runtime.Reflective +org.apache.felix.gogo.runtime.SyntaxError +org.apache.felix.gogo.runtime.threadio.Marker +org.apache.felix.gogo.runtime.threadio.ThreadInputStream +org.apache.felix.gogo.runtime.threadio.ThreadIOImpl +org.apache.felix.gogo.runtime.threadio.ThreadPrintStream +org.apache.felix.gogo.runtime.ThreadUtils +org.apache.felix.gogo.runtime.Token +org.apache.felix.gogo.runtime.Tokenizer +org.apache.felix.gogo.runtime.util.function.BiFunction +org.apache.felix.gogo.runtime.util.function.Function +org.apache.felix.service.command.annotations.GogoCommand +org.apache.felix.service.command.annotations.package-info +org.apache.felix.service.command.annotations.RequireGogo +org.apache.felix.service.command.annotations.RequireGogoWhiteboard +org.apache.felix.service.command.CommandProcessor +org.apache.felix.service.command.CommandSession +org.apache.felix.service.command.CommandSessionListener +org.apache.felix.service.command.Converter +org.apache.felix.service.command.Descriptor +org.apache.felix.service.command.Function +org.apache.felix.service.command.Job +org.apache.felix.service.command.JobListener +org.apache.felix.service.command.package-info +org.apache.felix.service.command.Parameter +org.apache.felix.service.command.Process +org.apache.felix.service.command.Result +org.apache.felix.service.threadio.package-info +org.apache.felix.service.threadio.ThreadIO +org.apache.felix.gogo.runtime.AbstractParserTest +org.apache.felix.gogo.runtime.ClosureTest +org.apache.felix.gogo.runtime.Context +org.apache.felix.gogo.runtime.ExpanderTest +org.apache.felix.gogo.runtime.expr.ExpressionTest +org.apache.felix.gogo.runtime.GlobPathMatcherTest +org.apache.felix.gogo.runtime.ReflectiveTest +org.apache.felix.gogo.runtime.TestCoercion +org.apache.felix.gogo.runtime.TestEvaluate +org.apache.felix.gogo.runtime.TestParser +org.apache.felix.gogo.runtime.TestParser2 +org.apache.felix.gogo.runtime.TestParser3 +org.apache.felix.gogo.runtime.TestParser4 +org.apache.felix.gogo.runtime.TestTokenizer +org.apache.felix.gogo.runtime.threadio.TestThreadIO +org.apache.felix.gogo.options.Option +org.apache.felix.gogo.options.Options +org.apache.felix.gogo.shell.Activator +org.apache.felix.gogo.shell.Builtin +org.apache.felix.gogo.shell.Console +org.apache.felix.gogo.shell.Converters +org.apache.felix.gogo.shell.History +org.apache.felix.gogo.shell.package-info +org.apache.felix.gogo.shell.Posix +org.apache.felix.gogo.shell.Procedural +org.apache.felix.gogo.shell.Shell +org.apache.felix.gogo.shell.Telnet +org.apache.felix.gogo.shell.HistoryTest +org.apache.felix.hc.annotation.Async +org.apache.felix.hc.annotation.HealthCheckMBean +org.apache.felix.hc.annotation.HealthCheckService +org.apache.felix.hc.annotation.package-info +org.apache.felix.hc.annotation.ResultTTL +org.apache.felix.hc.annotation.Sticky +org.apache.felix.hc.api.condition.Healthy +org.apache.felix.hc.api.condition.package-info +org.apache.felix.hc.api.condition.SystemReady +org.apache.felix.hc.api.condition.Unhealthy +org.apache.felix.hc.api.execution.HealthCheckExecutionOptions +org.apache.felix.hc.api.execution.HealthCheckExecutionResult +org.apache.felix.hc.api.execution.HealthCheckExecutor +org.apache.felix.hc.api.execution.HealthCheckMetadata +org.apache.felix.hc.api.execution.HealthCheckSelector +org.apache.felix.hc.api.execution.package-info +org.apache.felix.hc.api.FormattingResultLog +org.apache.felix.hc.api.HealthCheck +org.apache.felix.hc.api.package-info +org.apache.felix.hc.api.Result +org.apache.felix.hc.api.ResultLog +org.apache.felix.hc.api.execution.HealthCheckMetadataTest +org.apache.felix.hc.api.FormattingResultLogTest +org.apache.felix.hc.api.ResultLogTest +org.apache.felix.hc.api.ResultTest +org.apache.felix.hc.core.impl.commands.HealthCheckExecCommand +org.apache.felix.hc.core.impl.commands.HealthCheckListCommand +org.apache.felix.hc.core.impl.CompositeHealthCheck +org.apache.felix.hc.core.impl.CompositeHealthCheckConfiguration +org.apache.felix.hc.core.impl.CompositeResult +org.apache.felix.hc.core.impl.executor.async.AsyncHealthCheckExecutor +org.apache.felix.hc.core.impl.executor.CombinedExecutionResult +org.apache.felix.hc.core.impl.executor.ExecutionResult +org.apache.felix.hc.core.impl.executor.ExtendedHealthCheckExecutor +org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImpl +org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImplConfiguration +org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPool +org.apache.felix.hc.core.impl.executor.HealthCheckExecutorThreadPoolConfiguration +org.apache.felix.hc.core.impl.executor.HealthCheckFuture +org.apache.felix.hc.core.impl.executor.HealthCheckResultCache +org.apache.felix.hc.core.impl.executor.TempUnavailableGracePeriodEvaluator +org.apache.felix.hc.core.impl.filter.AdhocResultDuringRequestProcessingFilter +org.apache.felix.hc.core.impl.filter.ServiceUnavailableFilter +org.apache.felix.hc.core.impl.JmxAdjustableStatusHealthCheck +org.apache.felix.hc.core.impl.monitor.HealthCheckMonitor +org.apache.felix.hc.core.impl.monitor.HealthState +org.apache.felix.hc.core.impl.scheduling.AsyncIntervalJob +org.apache.felix.hc.core.impl.scheduling.AsyncJob +org.apache.felix.hc.core.impl.scheduling.cron.embedded.AsyncEmbeddedCronJob +org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronParser +org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronScheduler +org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerProvider +org.apache.felix.hc.core.impl.scheduling.cron.quartz.AsyncQuartzCronJob +org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronScheduler +org.apache.felix.hc.core.impl.scheduling.cron.quartz.QuartzCronSchedulerProvider +org.apache.felix.hc.core.impl.scheduling.CronJobFactory +org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServlet +org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServletConfiguration +org.apache.felix.hc.core.impl.servlet.ResultHtmlSerializer +org.apache.felix.hc.core.impl.servlet.ResultHtmlSerializerConfiguration +org.apache.felix.hc.core.impl.servlet.ResultJsonSerializer +org.apache.felix.hc.core.impl.servlet.ResultTxtSerializer +org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializer +org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializerConfiguration +org.apache.felix.hc.core.impl.util.AdhocStatusHealthCheck +org.apache.felix.hc.core.impl.util.HealthCheckFilter +org.apache.felix.hc.core.impl.util.lang.StringUtils +org.apache.felix.hc.jmx.impl.HealthCheckMBean +org.apache.felix.hc.jmx.impl.HealthCheckMBeanCreator +org.apache.felix.hc.core.impl.CompositeHealthCheckTest +org.apache.felix.hc.core.impl.executor.HealthCheckExecutorImplTest +org.apache.felix.hc.core.impl.executor.HealthCheckResultCacheTest +org.apache.felix.hc.core.impl.executor.TempUnavailableGracePeriodEvaluatorTest +org.apache.felix.hc.core.impl.filter.ServiceUnavailableFilterTest +org.apache.felix.hc.core.impl.monitor.HealthCheckMonitorTest +org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronParserTest +org.apache.felix.hc.core.impl.scheduling.cron.embedded.EmbeddedCronSchedulerTest +org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServletTest +org.apache.felix.hc.core.impl.servlet.ResultJsonSerializerTest +org.apache.felix.hc.core.impl.servlet.ResultTxtVerboseSerializerTest +org.apache.felix.hc.core.impl.util.HealthCheckFilterTest +org.apache.felix.hc.core.impl.util.StringUtilsTest +org.apache.felix.hc.core.it.AsyncHealthCheckIT +org.apache.felix.hc.core.it.ExtendedHealthCheckExecutorIT +org.apache.felix.hc.core.it.HealthCheckExecutorSelectionIT +org.apache.felix.hc.core.it.HealthCheckFilterIT +org.apache.felix.hc.core.it.HealthCheckServletIT +org.apache.felix.hc.core.it.JmxAdjustableStatusHealthCheckIT +org.apache.felix.hc.core.it.MockHttpService +org.apache.felix.hc.core.it.U +org.apache.felix.hc.jmx.impl.HealthCheckMBeanTest +org.apache.felix.hc.generalchecks.BundlesStartedCheck +org.apache.felix.hc.generalchecks.CpuCheck +org.apache.felix.hc.generalchecks.DiskSpaceCheck +org.apache.felix.hc.generalchecks.DsComponentsCheck +org.apache.felix.hc.generalchecks.FrameworkStartCheck +org.apache.felix.hc.generalchecks.HttpRequestsCheck +org.apache.felix.hc.generalchecks.JmxAttributeCheck +org.apache.felix.hc.generalchecks.MemoryCheck +org.apache.felix.hc.generalchecks.ScriptedHealthCheck +org.apache.felix.hc.generalchecks.scrutil.DsRootCauseAdapter +org.apache.felix.hc.generalchecks.scrutil.DsRootCauseAnalyzer +org.apache.felix.hc.generalchecks.ServicesCheck +org.apache.felix.hc.generalchecks.ThreadUsageCheck +org.apache.felix.hc.generalchecks.util.package-info +org.apache.felix.hc.generalchecks.util.ScriptEnginesTracker +org.apache.felix.hc.generalchecks.util.ScriptHelper +org.apache.felix.hc.generalchecks.util.SimpleConstraintChecker +org.apache.felix.hc.generalchecks.HttpRequestsCheckTest +org.apache.felix.hc.generalchecks.JmxAttributeHealthCheckTest +org.apache.felix.hc.generalchecks.ScriptedHealthCheckTest +org.apache.felix.hc.generalchecks.util.SimpleConstraintCheckerTest +org.apache.felix.hc.webconsole.impl.HealthCheckWebconsolePlugin +org.apache.felix.hc.webconsole.impl.WebConsoleHelper +org.apache.felix.hc.webconsole.impl.HealthCheckWebconsolePluginTest +org.apache.felix.http.base.internal.AbstractActivator +org.apache.felix.http.base.internal.AbstractHttpActivator +org.apache.felix.http.base.internal.console.HttpServicePlugin +org.apache.felix.http.base.internal.context.ExtServletContext +org.apache.felix.http.base.internal.context.ExtServletContextWrapper +org.apache.felix.http.base.internal.dispatch.Dispatcher +org.apache.felix.http.base.internal.dispatch.DispatcherServlet +org.apache.felix.http.base.internal.dispatch.InvocationChain +org.apache.felix.http.base.internal.dispatch.MultipartConfig +org.apache.felix.http.base.internal.dispatch.RequestDispatcherImpl +org.apache.felix.http.base.internal.dispatch.RequestInfo +org.apache.felix.http.base.internal.dispatch.ServletRequestWrapper +org.apache.felix.http.base.internal.dispatch.ServletResponseWrapper +org.apache.felix.http.base.internal.EventDispatcher +org.apache.felix.http.base.internal.handler.FilterConfigImpl +org.apache.felix.http.base.internal.handler.FilterHandler +org.apache.felix.http.base.internal.handler.HttpServiceFilterHandler +org.apache.felix.http.base.internal.handler.HttpServiceServletHandler +org.apache.felix.http.base.internal.handler.HttpSessionWrapper +org.apache.felix.http.base.internal.handler.ListenerHandler +org.apache.felix.http.base.internal.handler.PreprocessorHandler +org.apache.felix.http.base.internal.handler.ServletConfigImpl +org.apache.felix.http.base.internal.handler.ServletHandler +org.apache.felix.http.base.internal.handler.WhiteboardFilterHandler +org.apache.felix.http.base.internal.handler.WhiteboardListenerHandler +org.apache.felix.http.base.internal.handler.WhiteboardServletHandler +org.apache.felix.http.base.internal.HttpConfig +org.apache.felix.http.base.internal.HttpServiceController +org.apache.felix.http.base.internal.logger.ConsoleLogger +org.apache.felix.http.base.internal.logger.InternalLogger +org.apache.felix.http.base.internal.logger.JDK14Logger +org.apache.felix.http.base.internal.logger.LogServiceEnabledLogger +org.apache.felix.http.base.internal.logger.LogServiceSupport +org.apache.felix.http.base.internal.logger.R6LogServiceLogger +org.apache.felix.http.base.internal.logger.SystemLogger +org.apache.felix.http.base.internal.registry.ErrorPageRegistry +org.apache.felix.http.base.internal.registry.EventListenerRegistry +org.apache.felix.http.base.internal.registry.FilterRegistry +org.apache.felix.http.base.internal.registry.HandlerRegistry +org.apache.felix.http.base.internal.registry.ListenerMap +org.apache.felix.http.base.internal.registry.PathResolution +org.apache.felix.http.base.internal.registry.PathResolver +org.apache.felix.http.base.internal.registry.PathResolverFactory +org.apache.felix.http.base.internal.registry.PerContextHandlerRegistry +org.apache.felix.http.base.internal.registry.ServletRegistry +org.apache.felix.http.base.internal.registry.ServletResolution +org.apache.felix.http.base.internal.runtime.AbstractInfo +org.apache.felix.http.base.internal.runtime.dto.BaseServletDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.BuilderConstants +org.apache.felix.http.base.internal.runtime.dto.ErrorPageDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.FailedDTOHolder +org.apache.felix.http.base.internal.runtime.dto.FilterDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.ListenerDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.PreprocessorDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.RegistryRuntime +org.apache.felix.http.base.internal.runtime.dto.RequestInfoDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.ResourceDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.RuntimeDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.ServletContextDTOBuilder +org.apache.felix.http.base.internal.runtime.dto.ServletDTOBuilder +org.apache.felix.http.base.internal.runtime.FilterInfo +org.apache.felix.http.base.internal.runtime.ListenerInfo +org.apache.felix.http.base.internal.runtime.PreprocessorInfo +org.apache.felix.http.base.internal.runtime.ResourceInfo +org.apache.felix.http.base.internal.runtime.ServletContextHelperInfo +org.apache.felix.http.base.internal.runtime.ServletInfo +org.apache.felix.http.base.internal.runtime.WhiteboardServiceInfo +org.apache.felix.http.base.internal.service.DefaultHttpContext +org.apache.felix.http.base.internal.service.HttpServiceFactory +org.apache.felix.http.base.internal.service.HttpServiceRuntimeImpl +org.apache.felix.http.base.internal.service.PerBundleHttpServiceImpl +org.apache.felix.http.base.internal.service.ResourceServlet +org.apache.felix.http.base.internal.service.ServletContextImpl +org.apache.felix.http.base.internal.service.ServletContextManager +org.apache.felix.http.base.internal.service.SharedHttpServiceImpl +org.apache.felix.http.base.internal.util.InternalIdFactory +org.apache.felix.http.base.internal.util.MimeTypes +org.apache.felix.http.base.internal.util.PatternUtil +org.apache.felix.http.base.internal.util.ServiceUtils +org.apache.felix.http.base.internal.util.UriUtils +org.apache.felix.http.base.internal.whiteboard.FailureStateHandler +org.apache.felix.http.base.internal.whiteboard.HttpServiceContextHandler +org.apache.felix.http.base.internal.whiteboard.PerBundleServletContextImpl +org.apache.felix.http.base.internal.whiteboard.RegistrationFailureException +org.apache.felix.http.base.internal.whiteboard.SharedServletContextImpl +org.apache.felix.http.base.internal.whiteboard.tracker.FilterTracker +org.apache.felix.http.base.internal.whiteboard.tracker.ListenersTracker +org.apache.felix.http.base.internal.whiteboard.tracker.PreprocessorTracker +org.apache.felix.http.base.internal.whiteboard.tracker.ResourceTracker +org.apache.felix.http.base.internal.whiteboard.tracker.ServletContextHelperTracker +org.apache.felix.http.base.internal.whiteboard.tracker.ServletTracker +org.apache.felix.http.base.internal.whiteboard.tracker.WhiteboardServiceTracker +org.apache.felix.http.base.internal.whiteboard.WhiteboardContextHandler +org.apache.felix.http.base.internal.whiteboard.WhiteboardManager +org.apache.felix.http.base.internal.context.ServletContextImplTest +org.apache.felix.http.base.internal.context.ServletContextManagerTest +org.apache.felix.http.base.internal.handler.FilterConfigImplTest +org.apache.felix.http.base.internal.handler.FilterHandlerTest +org.apache.felix.http.base.internal.handler.HttpServiceServletHandlerTest +org.apache.felix.http.base.internal.handler.HttpSessionWrapperTest +org.apache.felix.http.base.internal.handler.ServletConfigImplTest +org.apache.felix.http.base.internal.registry.ErrorPageRegistryTest +org.apache.felix.http.base.internal.registry.EventListenerRegistryTest +org.apache.felix.http.base.internal.registry.FilterRegistryTest +org.apache.felix.http.base.internal.registry.HandlerRegistryTest +org.apache.felix.http.base.internal.registry.PathResolverFactoryTest +org.apache.felix.http.base.internal.registry.PerContextHandlerRegistryTest +org.apache.felix.http.base.internal.registry.ServletRegistryTest +org.apache.felix.http.base.internal.runtime.AbstractInfoOrderingTest +org.apache.felix.http.base.internal.runtime.dto.RuntimeDTOBuilderTest +org.apache.felix.http.base.internal.runtime.ListenerInfoTest +org.apache.felix.http.base.internal.service.HttpServiceFactoryTest +org.apache.felix.http.base.internal.util.MimeTypesTest +org.apache.felix.http.base.internal.util.PatternUtilTest +org.apache.felix.http.base.internal.util.UriUtilsTest +org.apache.felix.http.base.internal.whiteboard.FailureStateHandlerTest +org.apache.felix.http.bridge.internal.BridgeActivator +org.apache.felix.http.bundle.internal.CombinedActivator +org.apache.felix.http.cometd.CometdService +org.apache.felix.http.cometd.internal.CometdActivator +org.apache.felix.http.cometd.internal.CometdConfig +org.apache.felix.http.cometd.internal.CometdServiceImpl +org.apache.felix.http.itest.AsyncTest +org.apache.felix.http.itest.BaseIntegrationTest +org.apache.felix.http.itest.ErrorPageTest +org.apache.felix.http.itest.EventListenerTest +org.apache.felix.http.itest.HttpJettyConnectorTest +org.apache.felix.http.itest.HttpJettyTest +org.apache.felix.http.itest.HttpServiceRuntimeTest +org.apache.felix.http.itest.HttpServiceTest +org.apache.felix.http.itest.HttpWhiteboardTargetTest +org.apache.felix.http.itest.RequestDispatchTest +org.apache.felix.http.itest.ResourceTest +org.apache.felix.http.itest.ServletPatternTest +org.apache.felix.http.itest.SessionHandlingTest +org.apache.felix.http.itest.UploadTest +org.apache.felix.http.jetty.ConnectorFactory +org.apache.felix.http.jetty.internal.ConfigMetaTypeProvider +org.apache.felix.http.jetty.internal.ConnectorFactoryTracker +org.apache.felix.http.jetty.internal.CustomizerWrapper +org.apache.felix.http.jetty.internal.FileRequestLog +org.apache.felix.http.jetty.internal.ForwardedRequestCustomizerFactory +org.apache.felix.http.jetty.internal.JettyActivator +org.apache.felix.http.jetty.internal.JettyConfig +org.apache.felix.http.jetty.internal.JettyLogger +org.apache.felix.http.jetty.internal.JettyManagedService +org.apache.felix.http.jetty.internal.JettyManagedServiceFactory +org.apache.felix.http.jetty.internal.JettyService +org.apache.felix.http.jetty.internal.JettyServiceStarter +org.apache.felix.http.jetty.internal.LoadBalancerCustomizerFactoryTracker +org.apache.felix.http.jetty.internal.LogServiceRequestLog +org.apache.felix.http.jetty.internal.MBeanServerTracker +org.apache.felix.http.jetty.internal.RequestLogTracker +org.apache.felix.http.jetty.internal.webapp.WebAppBundleContext +org.apache.felix.http.jetty.internal.webapp.WebAppBundleTracker +org.apache.felix.http.jetty.internal.webapp.WebEvent +org.apache.felix.http.jetty.LoadBalancerCustomizerFactory +org.apache.felix.http.jetty.package-info +org.apache.felix.http.jetty.internal.JettyConfigTest +org.apache.felix.http.jetty.internal.JettyServiceTest +org.apache.felix.http.jetty.internal.LoadBalancerCustomizerFactoryTrackerTest +org.apache.felix.http.jetty.internal.RequestLogTrackerTest +org.apache.felix.http.proxy.AbstractProxyListener +org.apache.felix.http.proxy.AbstractProxyServlet +org.apache.felix.http.proxy.DispatcherTracker +org.apache.felix.http.proxy.impl.BridgeServiceTracker +org.apache.felix.http.proxy.impl.EventDispatcherTracker +org.apache.felix.http.proxy.impl.ProxyServletContextListener +org.apache.felix.http.proxy.package-info +org.apache.felix.http.proxy.ProxyListener +org.apache.felix.http.proxy.ProxyServlet +org.apache.felix.http.samples.bridge.FrameworkService +org.apache.felix.http.samples.bridge.ProvisionActivator +org.apache.felix.http.samples.bridge.StartupListener +org.apache.felix.http.samples.cometd.Activator +org.apache.felix.http.samples.cometd.TimeServlet +org.apache.felix.http.samples.whiteboard.Activator +org.apache.felix.http.samples.whiteboard.TestFilter +org.apache.felix.http.samples.whiteboard.TestServlet +org.apache.felix.http.samples.whiteboard.TestServletContext +org.apache.felix.http.sslfilter.internal.LogServiceTracker +org.apache.felix.http.sslfilter.internal.SslFilter +org.apache.felix.http.sslfilter.internal.SslFilterActivator +org.apache.felix.http.sslfilter.internal.SslFilterConstants +org.apache.felix.http.sslfilter.internal.SslFilterRequest +org.apache.felix.http.sslfilter.internal.SslFilterResponse +org.apache.felix.http.sslfilter.internal.SystemLogger +org.apache.felix.http.sslfilter.internal.SslFilterJettyTest +org.apache.felix.http.sslfilter.internal.SslFilterRequestTest +org.apache.felix.http.sslfilter.internal.SslFilterResponseTest +org.apache.felix.http.whiteboard.HttpWhiteboardConstants +org.apache.felix.http.whiteboard.internal.manager.ExtenderManager +org.apache.felix.http.whiteboard.internal.tracker.AbstractTracker +org.apache.felix.http.whiteboard.internal.tracker.FilterTracker +org.apache.felix.http.whiteboard.internal.tracker.HttpContextTracker +org.apache.felix.http.whiteboard.internal.tracker.ListenersTracker +org.apache.felix.http.whiteboard.internal.tracker.ServletTracker +org.apache.felix.http.whiteboard.internal.WhiteboardActivator +org.apache.felix.http.whiteboard.package-info +org.apache.felix.httplite.osgi.Activator +org.apache.felix.httplite.osgi.DefaultContextImpl +org.apache.felix.httplite.osgi.HttpServiceFactoryImpl +org.apache.felix.httplite.osgi.HttpServiceImpl +org.apache.felix.httplite.osgi.Logger +org.apache.felix.httplite.osgi.ServiceRegistration +org.apache.felix.httplite.osgi.ServiceRegistrationHandler +org.apache.felix.httplite.osgi.ServiceRegistrationResolver +org.apache.felix.httplite.server.Connection +org.apache.felix.httplite.server.ResourceHandler +org.apache.felix.httplite.server.Server +org.apache.felix.httplite.server.ServletHandler +org.apache.felix.httplite.server.ThreadGate +org.apache.felix.httplite.server.ThreadPool +org.apache.felix.httplite.servlet.ConcreteServletInputStream +org.apache.felix.httplite.servlet.HttpConstants +org.apache.felix.httplite.servlet.HttpServletRequestImpl +org.apache.felix.httplite.servlet.HttpServletResponseImpl +org.apache.felix.httplite.servlet.ServletConfigImpl +org.apache.felix.httplite.servlet.ServletContextImpl +org.apache.felix.httplite.servlet.ServletOutputStreamImpl +org.apache.felix.httplite.servlet.UnimplementedAPIException +org.apache.felix.httplite.osgi.test.AbstractHttpliteTestCase +org.apache.felix.httplite.osgi.test.AbstractPojoSRTestCase +org.apache.felix.httplite.osgi.test.BasicTestingServlet +org.apache.felix.httplite.osgi.test.cases.TestCookies +org.apache.felix.httplite.osgi.test.cases.TestOSGiService +org.apache.felix.httplite.osgi.test.cases.TestParameters +org.apache.felix.httplite.osgi.test.cases.TestQueryString +org.apache.felix.httplite.osgi.test.cases.TestRequestPath +org.apache.felix.httplite.osgi.test.cases.TestResources +org.apache.felix.httplite.osgi.test.cases.TestServletContainer +org.apache.felix.inventory.Format +org.apache.felix.inventory.impl.AbstractWebConsolePlugin +org.apache.felix.inventory.impl.Activator +org.apache.felix.inventory.impl.DefaultWebConsolePlugin +org.apache.felix.inventory.impl.helper.ConfigurationWriter +org.apache.felix.inventory.impl.helper.HtmlConfigurationWriter +org.apache.felix.inventory.impl.helper.JSONConfigurationWriter +org.apache.felix.inventory.impl.helper.PlainTextConfigurationWriter +org.apache.felix.inventory.impl.helper.SimpleJson +org.apache.felix.inventory.impl.helper.ZipConfigurationWriter +org.apache.felix.inventory.impl.InventoryPrinterAdapter +org.apache.felix.inventory.impl.InventoryPrinterDescription +org.apache.felix.inventory.impl.InventoryPrinterHandler +org.apache.felix.inventory.impl.InventoryPrinterManagerImpl +org.apache.felix.inventory.impl.webconsole.ConfigurationPrinterAdapter +org.apache.felix.inventory.impl.webconsole.ConsoleConstants +org.apache.felix.inventory.impl.webconsole.ResourceBundleManager +org.apache.felix.inventory.impl.webconsole.WebConsoleAdapter +org.apache.felix.inventory.impl.WebConsolePlugin +org.apache.felix.inventory.InventoryPrinter +org.apache.felix.inventory.package-info +org.apache.felix.inventory.ZipAttachmentProvider +org.apache.felix.inventory.FormatTest +org.apache.felix.inventory.impl.helper.JSONConfigurationWriterTest +org.apache.felix.io.Activator +org.apache.felix.io.ConnectorServiceImpl +org.apache.felix.io.ConnectionFactoryMock +org.apache.felix.io.ConnectorServiceTest +org.apache.felix.ipojo.arch.ArchCommandImpl +org.apache.felix.ipojo.foo.FooService +org.apache.felix.ipojo.foo.impl.FooServiceImpl +org.apache.felix.ipojo.log.handler.Log +org.apache.felix.ipojo.log.handler.LogHandler +org.apache.felix.ipojo.log.handler.example.SimpleComponent +org.apache.felix.ipojo.handler.properties.Properties +org.apache.felix.ipojo.handler.properties.PropertiesHandler +org.apache.felix.ipojo.handler.properties.example.PropertiesTester +org.apache.felix.ipojo.handlers.event.EventUtil +org.apache.felix.ipojo.handlers.event.publisher.EventAdminPublisherHandler +org.apache.felix.ipojo.handlers.event.publisher.EventAdminPublisherHandlerDescription +org.apache.felix.ipojo.handlers.event.publisher.EventAdminPublisherMetadata +org.apache.felix.ipojo.handlers.event.publisher.Publisher +org.apache.felix.ipojo.handlers.event.publisher.PublisherDescription +org.apache.felix.ipojo.handlers.event.publisher.PublisherImpl +org.apache.felix.ipojo.handlers.event.subscriber.EventAdminSubscriberHandler +org.apache.felix.ipojo.handlers.event.subscriber.EventAdminSubscriberHandlerDescription +org.apache.felix.ipojo.handlers.event.subscriber.EventAdminSubscriberMetadata +org.apache.felix.ipojo.handler.eventadmin.test.donut.AsyncEventProviderImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.Donut +org.apache.felix.ipojo.handler.eventadmin.test.donut.DonutConsumer +org.apache.felix.ipojo.handler.eventadmin.test.donut.DonutConsumerImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.DonutEventProviderImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.DonutProvider +org.apache.felix.ipojo.handler.eventadmin.test.donut.DonutProviderImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.EventConsumerImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.EventTracker +org.apache.felix.ipojo.handler.eventadmin.test.donut.EventTrackerImpl +org.apache.felix.ipojo.handler.eventadmin.test.donut.SyncEventProviderImpl +org.apache.felix.ipojo.handler.eventadmin.test.Common +org.apache.felix.ipojo.handler.eventadmin.test.EahTestUtils +org.apache.felix.ipojo.handler.eventadmin.test.TestBadConfigurations +org.apache.felix.ipojo.handler.eventadmin.test.TestEventAdminHandler +org.apache.felix.ipojo.handler.eventadmin.test.TestEventAdminHandlerWithNewAttributes +org.apache.felix.ipojo.handler.eventadmin.test.TestPublisher +org.apache.felix.ipojo.handler.extender.BundleTracker +org.apache.felix.ipojo.handler.extender.ExtenderManager +org.apache.felix.ipojo.handler.extender.ExtenderModelHandler +org.apache.felix.ipojo.handlers.jmx.DynamicMBeanImpl +org.apache.felix.ipojo.handlers.jmx.DynamicMBeanWRegisterImpl +org.apache.felix.ipojo.handlers.jmx.JmxConfigFieldMap +org.apache.felix.ipojo.handlers.jmx.JMXHandlerDescription +org.apache.felix.ipojo.handlers.jmx.MBeanHandler +org.apache.felix.ipojo.handlers.jmx.MethodField +org.apache.felix.ipojo.handlers.jmx.NotificationField +org.apache.felix.ipojo.handlers.jmx.PropertyField +org.apache.felix.ipojo.handler.jmx.components.SimpleManagedComponent +org.apache.felix.ipojo.handler.jmx.components.SimpleManagedComponentAnnotated +org.apache.felix.ipojo.handler.jmx.components.SimpleManagedComponentAnnotatedDeprecated +org.apache.felix.ipojo.handler.jmx.test.Common +org.apache.felix.ipojo.handler.jmx.test.TestMBean +org.apache.felix.ipojo.handler.temporal.ProxyGenerator +org.apache.felix.ipojo.handler.temporal.ServiceCollection +org.apache.felix.ipojo.handler.temporal.ServiceUsage +org.apache.felix.ipojo.handler.temporal.TemporalDependency +org.apache.felix.ipojo.handler.temporal.TemporalHandler +org.apache.felix.ipojo.handler.temporal.components.CheckServiceProvider +org.apache.felix.ipojo.handler.temporal.components.CollectionCheckServiceProvider +org.apache.felix.ipojo.handler.temporal.components.FooProvider +org.apache.felix.ipojo.handler.temporal.components.MultipleCheckServiceProvider +org.apache.felix.ipojo.handler.temporal.components.NullableFooProvider +org.apache.felix.ipojo.handler.temporal.components.proxy.CheckServiceProviderHelper +org.apache.felix.ipojo.handler.temporal.components.proxy.CollectionCheckServiceProviderHelper +org.apache.felix.ipojo.handler.temporal.components.proxy.HelpedCheckServiceProvider +org.apache.felix.ipojo.handler.temporal.components.proxy.HelpedCollectionCheckServiceProvider +org.apache.felix.ipojo.handler.temporal.services.BarService +org.apache.felix.ipojo.handler.temporal.services.CheckService +org.apache.felix.ipojo.handler.temporal.services.ChildInterface +org.apache.felix.ipojo.handler.temporal.services.FooService +org.apache.felix.ipojo.handler.temporal.services.ParentInterface1 +org.apache.felix.ipojo.handler.temporal.services.ParentInterface2 +org.apache.felix.ipojo.handler.temporal.services.ParentParentInterface +org.apache.felix.ipojo.handler.temporal.test.Common +org.apache.felix.ipojo.handler.temporal.test.DefaultImplementationTest +org.apache.felix.ipojo.handler.temporal.test.DelayedProvider +org.apache.felix.ipojo.handler.temporal.test.DelayTest +org.apache.felix.ipojo.handler.temporal.test.EmptyTest +org.apache.felix.ipojo.handler.temporal.test.FilterTest +org.apache.felix.ipojo.handler.temporal.test.NoDelayTest +org.apache.felix.ipojo.handler.temporal.test.NullableTest +org.apache.felix.ipojo.handler.temporal.test.NullTest +org.apache.felix.ipojo.handler.temporal.test.TemporalTest +org.apache.felix.ipojo.transaction.TransactionalMethod +org.apache.felix.ipojo.transaction.TransactionHandler +org.apache.felix.ipojo.handler.transaction.components.ComponentUsingAnnotations +org.apache.felix.ipojo.handler.transaction.components.FooDelegator +org.apache.felix.ipojo.handler.transaction.components.FooImpl +org.apache.felix.ipojo.handler.transaction.services.CheckService +org.apache.felix.ipojo.handler.transaction.services.Foo +org.apache.felix.ipojo.handler.transaction.test.Common +org.apache.felix.ipojo.handler.transaction.test.TestAnnotations +org.apache.felix.ipojo.handler.transaction.test.TestInstallation +org.apache.felix.ipojo.handler.transaction.test.TestInvalidation +org.apache.felix.ipojo.handler.transaction.test.TestMandatory +org.apache.felix.ipojo.handler.transaction.test.TestNever +org.apache.felix.ipojo.handler.transaction.test.TestNotSupported +org.apache.felix.ipojo.handler.transaction.test.TestRequires +org.apache.felix.ipojo.handler.transaction.test.TestRequiresNew +org.apache.felix.ipojo.handler.transaction.test.TestSupported +org.apache.felix.ipojo.handler.wbp.WhiteBoardManager +org.apache.felix.ipojo.handler.wbp.WhiteBoardPatternHandler +org.apache.felix.ipojo.handler.whiteboard.components.FooProvider +org.apache.felix.ipojo.handler.whiteboard.components.FooWhiteBoardPattern +org.apache.felix.ipojo.handler.whiteboard.services.FooService +org.apache.felix.ipojo.handler.whiteboard.services.Observable +org.apache.felix.ipojo.handler.whiteboard.test.Common +org.apache.felix.ipojo.handler.whiteboard.test.TestWhiteboardPatternHandler +org.apache.felix.ipojo.junit4osgi.command.JunitCommand +org.apache.felix.ipojo.junit4osgi.command.ImmediateRunner +org.apache.felix.ipojo.junit4osgi.Helper +org.apache.felix.ipojo.junit4osgi.helpers.IPOJOHelper +org.apache.felix.ipojo.junit4osgi.impl.JunitExtender +org.apache.felix.ipojo.junit4osgi.impl.LogServiceImpl +org.apache.felix.ipojo.junit4osgi.impl.ResultPrinter +org.apache.felix.ipojo.junit4osgi.OSGiJunitRunner +org.apache.felix.ipojo.junit4osgi.OSGiTestCase +org.apache.felix.ipojo.junit4osgi.OSGiTestSuite +org.apache.felix.ipojo.junit4osgi.test.TestOSGiTestCase +org.apache.felix.ipojo.junit4osgi.test.TestOSGiTestSuite +org.apache.felix.ipojo.junit4osgi.test.TestTestCase +org.apache.felix.ipojo.junit4osgi.test.TestTestSuite +org.apache.felix.ipojo.junit4osgi.plugin.Installer +org.apache.felix.ipojo.junit4osgi.plugin.Junit4osgiPlugin +org.apache.felix.ipojo.junit4osgi.plugin.log.LogServiceImpl +org.apache.felix.ipojo.junit4osgi.plugin.Report +org.apache.felix.ipojo.junit4osgi.plugin.ReportPrintStream +org.apache.felix.ipojo.junit4osgi.plugin.StringOutputStream +org.apache.felix.ipojo.junit4osgi.plugin.XMLReport +org.apache.felix.ipojo.junit4osgi.command.ResultCellRenderer +org.apache.felix.ipojo.junit4osgi.command.ResultTableModel +org.apache.felix.ipojo.junit4osgi.command.SwingRunner +org.apache.felix.ipojo.junit4osgi.command.TestListModel +org.apache.felix.ipojo.annotations.Bind +org.apache.felix.ipojo.annotations.BindingPolicy +org.apache.felix.ipojo.annotations.Component +org.apache.felix.ipojo.annotations.Context +org.apache.felix.ipojo.annotations.Controller +org.apache.felix.ipojo.annotations.Handler +org.apache.felix.ipojo.annotations.HandlerBinding +org.apache.felix.ipojo.annotations.HandlerDeclaration +org.apache.felix.ipojo.annotations.Ignore +org.apache.felix.ipojo.annotations.Instantiate +org.apache.felix.ipojo.annotations.Invalidate +org.apache.felix.ipojo.annotations.Modified +org.apache.felix.ipojo.annotations.PostRegistration +org.apache.felix.ipojo.annotations.PostUnregistration +org.apache.felix.ipojo.annotations.Property +org.apache.felix.ipojo.annotations.Provides +org.apache.felix.ipojo.annotations.Requires +org.apache.felix.ipojo.annotations.ServiceController +org.apache.felix.ipojo.annotations.ServiceProperty +org.apache.felix.ipojo.annotations.StaticServiceProperty +org.apache.felix.ipojo.annotations.Stereotype +org.apache.felix.ipojo.annotations.Unbind +org.apache.felix.ipojo.annotations.Updated +org.apache.felix.ipojo.annotations.Validate +org.apache.felix.ipojo.extender.Extender +org.apache.felix.ipojo.handler.temporal.Requires +org.apache.felix.ipojo.handler.temporal.Temporal +org.apache.felix.ipojo.handlers.event.Publisher +org.apache.felix.ipojo.handlers.event.Publishes +org.apache.felix.ipojo.handlers.event.Subscriber +org.apache.felix.ipojo.handlers.jmx.Config +org.apache.felix.ipojo.handlers.jmx.JMXBean +org.apache.felix.ipojo.handlers.jmx.JMXMethod +org.apache.felix.ipojo.handlers.jmx.JMXProperty +org.apache.felix.ipojo.handlers.jmx.Method +org.apache.felix.ipojo.handlers.jmx.Property +org.apache.felix.ipojo.transaction.Transaction +org.apache.felix.ipojo.transaction.Transactional +org.apache.felix.ipojo.whiteboard.Wbp +org.apache.felix.ipojo.whiteboard.Whiteboards +org.apache.felix.ipojo.bnd.BndJarResourceStore +org.apache.felix.ipojo.bnd.BndReporter +org.apache.felix.ipojo.bnd.ByteArrayResource +org.apache.felix.ipojo.bnd.Manifests +org.apache.felix.ipojo.bnd.PojoizationPlugin +org.apache.felix.ipojo.bnd.ResourceMetadataProvider +org.apache.felix.ipojo.bnd.BndJarResourceStoreTestCase +org.apache.felix.ipojo.bnd.EmptyComponent +org.apache.felix.ipojo.bnd.PojoizationPluginTestCase +org.apache.felix.ipojo.task.AntReporter +org.apache.felix.ipojo.task.IPojoc +org.apache.felix.ipojo.task.IPojoTask +org.apache.felix.ipojo.manipulation.ClassChecker +org.apache.felix.ipojo.manipulation.ClassLoaderAwareClassWriter +org.apache.felix.ipojo.manipulation.ClassManipulator +org.apache.felix.ipojo.manipulation.ConstructorCodeAdapter +org.apache.felix.ipojo.manipulation.InnerClassAdapter +org.apache.felix.ipojo.manipulation.InnerClassChecker +org.apache.felix.ipojo.manipulation.ManipulationProperty +org.apache.felix.ipojo.manipulation.Manipulator +org.apache.felix.ipojo.manipulation.MethodCodeAdapter +org.apache.felix.ipojo.manipulation.MethodDescriptor +org.apache.felix.ipojo.manipulator.manifest.DirectManifestProvider +org.apache.felix.ipojo.manipulator.manifest.FileManifestProvider +org.apache.felix.ipojo.manipulator.ManifestAttributeFilter +org.apache.felix.ipojo.manipulator.ManifestProvider +org.apache.felix.ipojo.manipulator.ManipulationEngine +org.apache.felix.ipojo.manipulator.ManipulationResultVisitor +org.apache.felix.ipojo.manipulator.ManipulationUnit +org.apache.felix.ipojo.manipulator.ManipulationVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.ClassMetadataCollector +org.apache.felix.ipojo.manipulator.metadata.annotation.ComponentWorkbench +org.apache.felix.ipojo.manipulator.metadata.annotation.FieldMetadataCollector +org.apache.felix.ipojo.manipulator.metadata.annotation.MethodMetadataCollector +org.apache.felix.ipojo.manipulator.metadata.annotation.model.AnnotationDiscovery +org.apache.felix.ipojo.manipulator.metadata.annotation.model.AnnotationType +org.apache.felix.ipojo.manipulator.metadata.annotation.model.discovery.ChainedAnnotationDiscovery +org.apache.felix.ipojo.manipulator.metadata.annotation.model.discovery.HandlerBindingDiscovery +org.apache.felix.ipojo.manipulator.metadata.annotation.model.discovery.IgnoredDiscovery +org.apache.felix.ipojo.manipulator.metadata.annotation.model.discovery.StereotypeDiscovery +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.AnnotationPlayback +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.AnnotationParser +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.AnnotationTypeVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.AnnotationRecorder +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.AnnotationVisitorPlayback +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.Replay +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.Visit +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.VisitAnnotation +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.VisitArray +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.VisitEnd +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.VisitEnum +org.apache.felix.ipojo.manipulator.metadata.annotation.model.Playback +org.apache.felix.ipojo.manipulator.metadata.annotation.module.DefaultBindingModule +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.Binding +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.BindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.CompletableBindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.DefaultBindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.IgnoreAllBindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.LegacyGenericBindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.MetaAnnotationBindingRegistry +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.Selection +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.bind.AbstractBindVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.bind.Action +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.bind.MethodBindVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.bind.ParameterBindVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ComponentVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ControllerVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.FieldPropertyVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.FieldGenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.GenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.GenericVisitorFactory +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.MethodGenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.ParameterGenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.RootGenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.SubArrayVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.generic.TypeGenericVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.HandlerDeclarationVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.HandlerVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ignore.NullBinding +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ignore.NullVisitorFactory +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.InstantiateVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.LifecycleVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.MethodPropertyVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ParameterPropertyVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.PostRegistrationVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.PostUnregistrationVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ProvidesVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.RequiresVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ServiceControllerVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.stereotype.FieldStereotypeVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.stereotype.MethodStereotypeVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.stereotype.ParameterStereotypeVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.stereotype.StereotypeVisitorFactory +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.stereotype.TypeStereotypeVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.UpdatedVisitor +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.util.Bindings +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.util.Elements +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.util.Names +org.apache.felix.ipojo.manipulator.metadata.AnnotationMetadataProvider +org.apache.felix.ipojo.manipulator.metadata.CacheableMetadataProvider +org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProvider +org.apache.felix.ipojo.manipulator.metadata.EmptyMetadataProvider +org.apache.felix.ipojo.manipulator.metadata.FileMetadataProvider +org.apache.felix.ipojo.manipulator.metadata.StreamMetadataProvider +org.apache.felix.ipojo.manipulator.MetadataProvider +org.apache.felix.ipojo.manipulator.Pojoization +org.apache.felix.ipojo.manipulator.QuotedTokenizer +org.apache.felix.ipojo.manipulator.render.ManipulatedMetadataFilter +org.apache.felix.ipojo.manipulator.render.MetadataFilter +org.apache.felix.ipojo.manipulator.render.MetadataRenderer +org.apache.felix.ipojo.manipulator.reporter.EmptyReporter +org.apache.felix.ipojo.manipulator.reporter.SystemReporter +org.apache.felix.ipojo.manipulator.Reporter +org.apache.felix.ipojo.manipulator.ResourceStore +org.apache.felix.ipojo.manipulator.ResourceVisitor +org.apache.felix.ipojo.manipulator.spi.AbsBindingModule +org.apache.felix.ipojo.manipulator.spi.AnnotationLiteral +org.apache.felix.ipojo.manipulator.spi.AnnotationVisitorFactory +org.apache.felix.ipojo.manipulator.spi.BindingContext +org.apache.felix.ipojo.manipulator.spi.helper.Predicates +org.apache.felix.ipojo.manipulator.spi.Module +org.apache.felix.ipojo.manipulator.spi.ModuleProvider +org.apache.felix.ipojo.manipulator.spi.Predicate +org.apache.felix.ipojo.manipulator.spi.provider.CompositeModuleProvider +org.apache.felix.ipojo.manipulator.spi.provider.CoreModuleProvider +org.apache.felix.ipojo.manipulator.spi.provider.DefaultModuleProvider +org.apache.felix.ipojo.manipulator.spi.provider.ServiceLoaderModuleProvider +org.apache.felix.ipojo.manipulator.store.builder.DefaultManifestBuilder +org.apache.felix.ipojo.manipulator.store.DirectoryResourceStore +org.apache.felix.ipojo.manipulator.store.JarFileResourceStore +org.apache.felix.ipojo.manipulator.store.ManifestBuilder +org.apache.felix.ipojo.manipulator.store.mapper.FileSystemResourceMapper +org.apache.felix.ipojo.manipulator.store.mapper.IdentityResourceMapper +org.apache.felix.ipojo.manipulator.store.mapper.WABResourceMapper +org.apache.felix.ipojo.manipulator.store.ResourceMapper +org.apache.felix.ipojo.manipulator.util.ChainedAnnotationVisitor +org.apache.felix.ipojo.manipulator.util.Classpath +org.apache.felix.ipojo.manipulator.util.Collections5 +org.apache.felix.ipojo.manipulator.util.Constants +org.apache.felix.ipojo.manipulator.util.IsolatedClassLoader +org.apache.felix.ipojo.manipulator.util.Metadatas +org.apache.felix.ipojo.manipulator.util.Streams +org.apache.felix.ipojo.manipulator.util.Strings +org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyResultVisitor +org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyVisitor +org.apache.felix.ipojo.manipulator.visitor.ManipulationAdapter +org.apache.felix.ipojo.manipulator.visitor.ManipulationResultAdapter +org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriter +org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResultWriter +org.apache.felix.ipojo.xml.parser.ParseException +org.apache.felix.ipojo.xml.parser.SchemaResolver +org.apache.felix.ipojo.xml.parser.XMLMetadataParser +org.apache.felix.ipojo.ComponentInstance +org.apache.felix.ipojo.InstanceManager +org.apache.felix.ipojo.manipulation.ClassCheckerTestCase +org.apache.felix.ipojo.manipulation.DirManipulationTest +org.apache.felix.ipojo.manipulation.InnerClassAdapterTest +org.apache.felix.ipojo.manipulation.ManipulatedClassLoader +org.apache.felix.ipojo.manipulation.ManipulatorTest +org.apache.felix.ipojo.manipulation.PojoizationTest +org.apache.felix.ipojo.manipulation.RemanipulationTest +org.apache.felix.ipojo.manipulator.manifest.DirectManifestProviderTestCase +org.apache.felix.ipojo.manipulator.manifest.FileManifestProviderTestCase +org.apache.felix.ipojo.manipulator.ManipulationEngineTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.ComponentWorkbenchTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.AnnotationPlaybackTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.AnnotationAnnotation +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.ArrayAnnotation +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.EnumAnnotation +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.InnerAnnotation +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.Mode +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.SimpleTypes +org.apache.felix.ipojo.manipulator.metadata.annotation.model.literal.types.Support +org.apache.felix.ipojo.manipulator.metadata.annotation.model.parser.replay.AnnotationRecorderTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.CompletableBindingRegistryTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.DefaultBindingRegistryTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.IgnoreAllBindingRegistryTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.LegacyGenericBindingRegistryTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.MetaAnnotationBindingRegistryTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.registry.SelectionTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.bind.MethodBindVisitorTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.ComponentVisitorTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.HandlerDeclarationVisitorTestCase +org.apache.felix.ipojo.manipulator.metadata.annotation.visitor.util.NamesTestCase +org.apache.felix.ipojo.manipulator.metadata.AnnotationMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.metadata.CacheableMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.metadata.CompositeMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.metadata.EmptyMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.metadata.FileMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.metadata.StreamMetadataProviderTestCase +org.apache.felix.ipojo.manipulator.render.ManipulatedMetadataFilterTestCase +org.apache.felix.ipojo.manipulator.render.MetadataRendererTestCase +org.apache.felix.ipojo.manipulator.spi.AbsBindingModuleTestCase +org.apache.felix.ipojo.manipulator.spi.AnnotationLiteralTestCase +org.apache.felix.ipojo.manipulator.store.builder.DefaultManifestBuilderTestCase +org.apache.felix.ipojo.manipulator.store.DirectoryResourceStoreTestCase +org.apache.felix.ipojo.manipulator.store.JarFileResourceStoreTestCase +org.apache.felix.ipojo.manipulator.store.mapper.FileSystemResourceMapperTestCase +org.apache.felix.ipojo.manipulator.store.mapper.IdentityResourceMapperTestCase +org.apache.felix.ipojo.manipulator.store.mapper.WABResourceMapperTestCase +org.apache.felix.ipojo.manipulator.util.StreamsTestCase +org.apache.felix.ipojo.manipulator.util.StringsTestCase +org.apache.felix.ipojo.manipulator.visitor.check.CheckFieldConsistencyResultVisitorTestCase +org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResourcesWriterTestCase +org.apache.felix.ipojo.manipulator.visitor.writer.ManipulatedResultWriterTestCase +org.apache.felix.ipojo.Pojo +test.AnnotatedComponent +test.Child +test.ClusterDaemon +test.ConstructorCheck +test.DoubleArray +test.FakeAnnotation +test.frames.CryptoServiceSingleton +test.frames.Hash +test.inner.ComponentWithInnerClasses +test.inner.Computation +test.inner.Example +test.ipojo.ExternalHandler +test.NonSunClass +test.NoValidConstructor +test.Parent +test.PlentyOfAnnotations +test.PojoWithInner +test.SimplePojo +org.apache.felix.ipojo.runtime.core.components.ASimpleParentClass +org.apache.felix.ipojo.runtime.core.components.CallSuperConstructor +org.apache.felix.ipojo.runtime.core.components.CallSuperConstructorWithBC +org.apache.felix.ipojo.runtime.core.components.CallSuperConstructorWithNew +org.apache.felix.ipojo.runtime.core.components.CallSuperSuperConstructorWithNew +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.NoEmptyConstructor +org.apache.felix.ipojo.runtime.core.components.NoEmptyConstructorWithParentClass +org.apache.felix.ipojo.runtime.core.components.ParentClass +org.apache.felix.ipojo.runtime.core.components.ParentClass2 +org.apache.felix.ipojo.runtime.core.components.ParentClassWithBC +org.apache.felix.ipojo.runtime.core.components.SeveralConstructors +org.apache.felix.ipojo.runtime.core.components.SuperParentClass +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Plop +org.apache.felix.ipojo.runtime.core.services.PrimitiveManipulationTestService +org.apache.felix.ipojo.runtime.core.TestPOJOCreation +org.apache.felix.ipojo.runtime.core.TestSeveralConstructor +org.apache.felix.ipojo.runtime.core.components.Child +org.apache.felix.ipojo.runtime.core.components.ComponentWithInnerClasses +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.Manipulation23Tester +org.apache.felix.ipojo.runtime.core.components.Multiconstructor +org.apache.felix.ipojo.runtime.core.components.MultipleCheckService +org.apache.felix.ipojo.runtime.core.components.Parent +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Plop +org.apache.felix.ipojo.runtime.core.services.PrimitiveManipulationTestService +org.apache.felix.ipojo.runtime.core.TestManipulationMetadata +org.apache.felix.ipojo.runtime.core.TestManipulationMetadataAPI +org.apache.felix.ipojo.runtime.core.components.A123.Manipulation23Tester +org.apache.felix.ipojo.runtime.core.components.Annotation +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooServiceImpl +org.apache.felix.ipojo.runtime.core.components.HandlerBindingTestComponent +org.apache.felix.ipojo.runtime.core.components.ImmediateComponent +org.apache.felix.ipojo.runtime.core.components.InnerClasses +org.apache.felix.ipojo.runtime.core.components.Invisible +org.apache.felix.ipojo.runtime.core.components.Manipulation23Tester +org.apache.felix.ipojo.runtime.core.components.Marker +org.apache.felix.ipojo.runtime.core.components.MultiBind +org.apache.felix.ipojo.runtime.core.components.nativ.NativeComponent +org.apache.felix.ipojo.runtime.core.components.PlopImpl +org.apache.felix.ipojo.runtime.core.components.StereotypedBazComponent +org.apache.felix.ipojo.runtime.core.components.StereotypedMultiBind +org.apache.felix.ipojo.runtime.core.components.SubMarker +org.apache.felix.ipojo.runtime.core.components.Switches +org.apache.felix.ipojo.runtime.core.components.TestTypedList +org.apache.felix.ipojo.runtime.core.handlers.Foo +org.apache.felix.ipojo.runtime.core.handlers.FooHandler +org.apache.felix.ipojo.runtime.core.handlers.IgnoredFoo +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.BazService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.Color +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.HandlerBindingTestService +org.apache.felix.ipojo.runtime.core.services.Job +org.apache.felix.ipojo.runtime.core.services.Plop +org.apache.felix.ipojo.runtime.core.services.PrimitiveManipulationTestService +org.apache.felix.ipojo.runtime.core.TestAnnotationProcessing +org.apache.felix.ipojo.runtime.core.TestDuplicateMethods +org.apache.felix.ipojo.runtime.core.TestExceptionHandling +org.apache.felix.ipojo.runtime.core.TestGenericList +org.apache.felix.ipojo.runtime.core.TestGetComponentInstance +org.apache.felix.ipojo.runtime.core.TestHandlerBindingAndIgnoreAnnotation +org.apache.felix.ipojo.runtime.core.TestNativeMethod +org.apache.felix.ipojo.runtime.core.TestNestedClasses +org.apache.felix.ipojo.runtime.core.TestPrimitiveTypes +org.apache.felix.ipojo.runtime.core.TestPrimitiveTypesWithNumberInNames +org.apache.felix.ipojo.runtime.core.TestStereotypeAnnotation +org.apache.felix.ipojo.runtime.core.TestSwitches +org.apache.felix.ipojo.runtime.core.TestTypeBoxing +org.apache.felix.ipojo.test.online.components.Consumer +org.apache.felix.ipojo.test.online.components.FrenchHelloService +org.apache.felix.ipojo.test.online.components.GermanHelloService +org.apache.felix.ipojo.test.online.components.MyProvider +org.apache.felix.ipojo.test.online.components.MyProviderWithAnnotations +org.apache.felix.ipojo.test.online.module.Activator +org.apache.felix.ipojo.test.online.module.Type +org.apache.felix.ipojo.test.online.module.Type2 +org.apache.felix.ipojo.test.online.module.TypeModule +org.apache.felix.ipojo.test.online.services.Hello +org.apache.felix.ipojo.test.online.OnlineManipulatorTest +org.apache.felix.ipojo.plugin.ManipulatorMojo +org.apache.felix.ipojo.plugin.MavenReporter +HelloComponent +org.apache.felix.ipojo.online.manipulator.BridgeClassLoader +org.apache.felix.ipojo.online.manipulator.BundleAwareJarFileResourceStore +org.apache.felix.ipojo.online.manipulator.Files +org.apache.felix.ipojo.online.manipulator.IPOJOURLHandler +org.apache.felix.ipojo.online.manipulator.SystemLogService +org.apache.felix.ipojo.metadata.Attribute +org.apache.felix.ipojo.metadata.Element +org.apache.felix.ipojo.api.ComponentType +org.apache.felix.ipojo.api.composite.CompositeComponentType +org.apache.felix.ipojo.api.composite.ExportedService +org.apache.felix.ipojo.api.composite.ImportedService +org.apache.felix.ipojo.api.composite.Instance +org.apache.felix.ipojo.api.composite.InstantiatedService +org.apache.felix.ipojo.api.composite.ProvidedService +org.apache.felix.ipojo.api.Dependency +org.apache.felix.ipojo.api.HandlerConfiguration +org.apache.felix.ipojo.api.PrimitiveComponentType +org.apache.felix.ipojo.api.Property +org.apache.felix.ipojo.api.Service +org.apache.felix.ipojo.api.ServiceProperty +org.apache.felix.ipojo.api.SingletonComponentType +org.apache.felix.ipojo.api.TemporalDependency +org.apache.felix.ipojo.api.composite.ExportedServiceTest +org.apache.felix.ipojo.api.composite.ImportedServiceTest +org.apache.felix.ipojo.api.composite.InstanceTest +org.apache.felix.ipojo.api.composite.InstantiatedServiceTest +org.apache.felix.ipojo.api.DependencyTest +org.apache.felix.ipojo.api.PropertyTest +org.apache.felix.ipojo.api.ServiceTest +org.apache.felix.ipojo.composite.architecture.ArchitectureHandler +org.apache.felix.ipojo.composite.CompositeFactory +org.apache.felix.ipojo.composite.CompositeHandler +org.apache.felix.ipojo.composite.CompositeInstanceDescription +org.apache.felix.ipojo.composite.CompositeManager +org.apache.felix.ipojo.composite.CompositeServiceContext +org.apache.felix.ipojo.composite.FactoryProxy +org.apache.felix.ipojo.composite.instance.InstanceHandler +org.apache.felix.ipojo.composite.instance.InstanceHandlerDescription +org.apache.felix.ipojo.composite.service.instantiator.ServiceDependencyHandler +org.apache.felix.ipojo.composite.service.instantiator.ServiceImporter +org.apache.felix.ipojo.composite.service.instantiator.ServiceInstantiatorDescription +org.apache.felix.ipojo.composite.service.instantiator.SvcInstance +org.apache.felix.ipojo.composite.service.provides.CompositionException +org.apache.felix.ipojo.composite.service.provides.CompositionMetadata +org.apache.felix.ipojo.composite.service.provides.FieldMetadata +org.apache.felix.ipojo.composite.service.provides.MethodMetadata +org.apache.felix.ipojo.composite.service.provides.POJOWriter +org.apache.felix.ipojo.composite.service.provides.ProvidedService +org.apache.felix.ipojo.composite.service.provides.ProvidedServiceHandler +org.apache.felix.ipojo.composite.service.provides.ProvidedServiceHandlerDescription +org.apache.felix.ipojo.composite.service.provides.ServiceExporter +org.apache.felix.ipojo.composite.service.provides.SpecificationMetadata +org.apache.felix.ipojo.composite.util.SourceManager +org.apache.felix.ipojo.runtime.core.components.Baz2CheckProvider +org.apache.felix.ipojo.runtime.core.components.BazProviderType1 +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.TataProvider +org.apache.felix.ipojo.runtime.core.components.TotoProvider +org.apache.felix.ipojo.runtime.core.components.TotoProviderGlue +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.BazService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Tata +org.apache.felix.ipojo.runtime.core.services.Tota +org.apache.felix.ipojo.runtime.core.services.Toto +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.exporter.TestFilteredExport +org.apache.felix.ipojo.runtime.core.exporter.TestMultipleExport +org.apache.felix.ipojo.runtime.core.exporter.TestOptionalExport +org.apache.felix.ipojo.runtime.core.exporter.TestOptionalMultipleExport +org.apache.felix.ipojo.runtime.core.exporter.TestSimpleExport +org.apache.felix.ipojo.runtime.core.importer.TestDelayedFilteredImport +org.apache.felix.ipojo.runtime.core.importer.TestDelayedMultipleImport +org.apache.felix.ipojo.runtime.core.importer.TestDelayedOptionalImport +org.apache.felix.ipojo.runtime.core.importer.TestDelayedOptionalMultipleImport +org.apache.felix.ipojo.runtime.core.importer.TestDelayedSimpleImport +org.apache.felix.ipojo.runtime.core.importer.TestFilteredImport +org.apache.felix.ipojo.runtime.core.importer.TestMultipleImport +org.apache.felix.ipojo.runtime.core.importer.TestOptionalImport +org.apache.felix.ipojo.runtime.core.importer.TestOptionalMultipleImport +org.apache.felix.ipojo.runtime.core.importer.TestSimpleImport +org.apache.felix.ipojo.runtime.core.components.Baz2CheckProvider +org.apache.felix.ipojo.runtime.core.components.BazProviderType1 +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.ServiceConsumer +org.apache.felix.ipojo.runtime.core.components.ServiceProvider +org.apache.felix.ipojo.runtime.core.components.TataProvider +org.apache.felix.ipojo.runtime.core.components.TotoProvider +org.apache.felix.ipojo.runtime.core.components.TotoProviderGlue +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.BazService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Service +org.apache.felix.ipojo.runtime.core.services.Tata +org.apache.felix.ipojo.runtime.core.services.Tota +org.apache.felix.ipojo.runtime.core.services.Toto +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.instance.TestInstanceScope +org.apache.felix.ipojo.runtime.core.instance.TestSimpleInstance +org.apache.felix.ipojo.runtime.core.instantiator.TestConfigurableInstantiation +org.apache.felix.ipojo.runtime.core.instantiator.TestConfiguration +org.apache.felix.ipojo.runtime.core.instantiator.TestMultipleInstantiation +org.apache.felix.ipojo.runtime.core.instantiator.TestOptionalInstantiation +org.apache.felix.ipojo.runtime.core.instantiator.TestOptionalMultipleInstantiation +org.apache.felix.ipojo.runtime.core.instantiator.TestSimpleInstantiation +org.apache.felix.ipojo.runtime.core.components.Baz2CheckProvider +org.apache.felix.ipojo.runtime.core.components.BazProviderType1 +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.TataProvider +org.apache.felix.ipojo.runtime.core.components.TotoProvider +org.apache.felix.ipojo.runtime.core.components.TotoProviderGlue +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.BazService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Tata +org.apache.felix.ipojo.runtime.core.services.Tota +org.apache.felix.ipojo.runtime.core.services.Toto +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.EmptyCompositeTest +org.apache.felix.ipojo.runtime.core.FactoryManagementTest +org.apache.felix.ipojo.runtime.core.ServiceRangeTest +org.apache.felix.ipojo.runtime.core.ServiceRegistryTest +org.apache.felix.ipojo.runtime.core.components.Baz2CheckProvider +org.apache.felix.ipojo.runtime.core.components.BazProviderType1 +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.ServiceConsumer +org.apache.felix.ipojo.runtime.core.components.ServiceProvider +org.apache.felix.ipojo.runtime.core.components.TataProvider +org.apache.felix.ipojo.runtime.core.components.TotoProvider +org.apache.felix.ipojo.runtime.core.components.TotoProviderGlue +org.apache.felix.ipojo.runtime.core.services.A123.CheckService2 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.BazService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.Service +org.apache.felix.ipojo.runtime.core.services.Tata +org.apache.felix.ipojo.runtime.core.services.Tota +org.apache.felix.ipojo.runtime.core.services.Toto +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.providing.TestComp0 +org.apache.felix.ipojo.runtime.core.providing.TestComp1 +org.apache.felix.ipojo.runtime.core.providing.TestComp2 +org.apache.felix.ipojo.runtime.core.providing.TestComp3 +org.apache.felix.ipojo.runtime.core.providing.TestComp4 +org.apache.felix.ipojo.runtime.core.providing.TestComp5 +org.apache.felix.ipojo.runtime.core.providing.TestComp6 +org.apache.felix.ipojo.runtime.core.providing.TestComp7 +org.apache.felix.ipojo.runtime.core.providing.TestComp8 +org.apache.felix.ipojo.runtime.core.TestCompositeAPI +org.apache.felix.ipojo.architecture.Architecture +org.apache.felix.ipojo.architecture.ComponentTypeDescription +org.apache.felix.ipojo.architecture.CustomHandlerInfo +org.apache.felix.ipojo.architecture.HandlerDescription +org.apache.felix.ipojo.architecture.InstanceDescription +org.apache.felix.ipojo.architecture.PropertyDescription +org.apache.felix.ipojo.ComponentFactory +org.apache.felix.ipojo.ComponentInstance +org.apache.felix.ipojo.configuration.Configuration +org.apache.felix.ipojo.configuration.Instance +org.apache.felix.ipojo.ConfigurationException +org.apache.felix.ipojo.ConfigurationTracker +org.apache.felix.ipojo.ConstructorInjector +org.apache.felix.ipojo.context.ServiceReferenceImpl +org.apache.felix.ipojo.context.ServiceRegistrationImpl +org.apache.felix.ipojo.context.ServiceRegistry +org.apache.felix.ipojo.context.StringMap +org.apache.felix.ipojo.ContextListener +org.apache.felix.ipojo.ContextSource +org.apache.felix.ipojo.dependency.impl.ComparatorBasedServiceRankingInterceptor +org.apache.felix.ipojo.dependency.impl.DependencyProperties +org.apache.felix.ipojo.dependency.impl.EmptyBasedServiceRankingInterceptor +org.apache.felix.ipojo.dependency.impl.FilterBasedServiceTrackingInterceptor +org.apache.felix.ipojo.dependency.impl.ServiceReferenceManager +org.apache.felix.ipojo.dependency.impl.ServiceReferenceUtils +org.apache.felix.ipojo.dependency.impl.TransformedServiceReferenceImpl +org.apache.felix.ipojo.dependency.interceptors.DefaultDependencyInterceptor +org.apache.felix.ipojo.dependency.interceptors.DefaultServiceRankingInterceptor +org.apache.felix.ipojo.dependency.interceptors.DefaultServiceTrackingInterceptor +org.apache.felix.ipojo.dependency.interceptors.DependencyInterceptor +org.apache.felix.ipojo.dependency.interceptors.ServiceBindingInterceptor +org.apache.felix.ipojo.dependency.interceptors.ServiceRankingInterceptor +org.apache.felix.ipojo.dependency.interceptors.ServiceTrackingInterceptor +org.apache.felix.ipojo.dependency.interceptors.TransformedServiceReference +org.apache.felix.ipojo.ErrorHandler +org.apache.felix.ipojo.EventDispatcher +org.apache.felix.ipojo.extender.builder.FactoryBuilder +org.apache.felix.ipojo.extender.builder.FactoryBuilderException +org.apache.felix.ipojo.extender.ConfigurationBuilder +org.apache.felix.ipojo.extender.Declaration +org.apache.felix.ipojo.extender.DeclarationBuilderService +org.apache.felix.ipojo.extender.DeclarationHandle +org.apache.felix.ipojo.extender.ExtensionDeclaration +org.apache.felix.ipojo.extender.InstanceBuilder +org.apache.felix.ipojo.extender.InstanceDeclaration +org.apache.felix.ipojo.extender.internal.AbstractService +org.apache.felix.ipojo.extender.internal.builder.ReflectiveFactoryBuilder +org.apache.felix.ipojo.extender.internal.BundleProcessor +org.apache.felix.ipojo.extender.internal.declaration.AbstractDeclaration +org.apache.felix.ipojo.extender.internal.declaration.DefaultExtensionDeclaration +org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclaration +org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclaration +org.apache.felix.ipojo.extender.internal.declaration.service.DeclarationServiceFactory +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultConfigurationBuilder +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultDeclarationBuilderService +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultInstanceBuilder +org.apache.felix.ipojo.extender.internal.DefaultJob +org.apache.felix.ipojo.extender.internal.Extender +org.apache.felix.ipojo.extender.internal.Lifecycle +org.apache.felix.ipojo.extender.internal.LifecycleQueueService +org.apache.felix.ipojo.extender.internal.linker.DeclarationLinker +org.apache.felix.ipojo.extender.internal.linker.InstanceBundleContextAware +org.apache.felix.ipojo.extender.internal.linker.ManagedType +org.apache.felix.ipojo.extender.internal.processor.ChainedBundleProcessor +org.apache.felix.ipojo.extender.internal.processor.ComponentsBundleProcessor +org.apache.felix.ipojo.extender.internal.processor.ConfigurationAnnotationScanner +org.apache.felix.ipojo.extender.internal.processor.ConfigurationProcessor +org.apache.felix.ipojo.extender.internal.processor.ExtensionBundleProcessor +org.apache.felix.ipojo.extender.internal.processor.ForwardingBundleProcessor +org.apache.felix.ipojo.extender.internal.processor.QueuingActivationProcessor +org.apache.felix.ipojo.extender.internal.processor.ReverseBundleProcessor +org.apache.felix.ipojo.extender.internal.queue.AbstractQueueService +org.apache.felix.ipojo.extender.internal.queue.debug.ReplayQueueEventProxy +org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueService +org.apache.felix.ipojo.extender.internal.queue.GroupThreadFactory +org.apache.felix.ipojo.extender.internal.queue.JobInfoCallable +org.apache.felix.ipojo.extender.internal.queue.NamingThreadFactory +org.apache.felix.ipojo.extender.internal.queue.pref.enforce.EnforcedQueueService +org.apache.felix.ipojo.extender.internal.queue.pref.enforce.ForwardingQueueService +org.apache.felix.ipojo.extender.internal.queue.pref.HeaderPreferenceSelection +org.apache.felix.ipojo.extender.internal.queue.pref.Preference +org.apache.felix.ipojo.extender.internal.queue.pref.PreferenceQueueService +org.apache.felix.ipojo.extender.internal.queue.pref.PreferenceSelection +org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactory +org.apache.felix.ipojo.extender.internal.queue.QueueNotifier +org.apache.felix.ipojo.extender.internal.queue.Statistic +org.apache.felix.ipojo.extender.internal.queue.SynchronousQueueService +org.apache.felix.ipojo.extender.queue.Callback +org.apache.felix.ipojo.extender.queue.debug.QueueEventProxy +org.apache.felix.ipojo.extender.queue.Job +org.apache.felix.ipojo.extender.queue.JobInfo +org.apache.felix.ipojo.extender.queue.QueueListener +org.apache.felix.ipojo.extender.queue.QueueService +org.apache.felix.ipojo.extender.Status +org.apache.felix.ipojo.extender.TypeDeclaration +org.apache.felix.ipojo.Factory +org.apache.felix.ipojo.FactoryClassloader +org.apache.felix.ipojo.FactoryStateListener +org.apache.felix.ipojo.FieldInterceptor +org.apache.felix.ipojo.Handler +org.apache.felix.ipojo.HandlerFactory +org.apache.felix.ipojo.HandlerManager +org.apache.felix.ipojo.HandlerManagerFactory +org.apache.felix.ipojo.handlers.architecture.ArchitectureHandler +org.apache.felix.ipojo.handlers.configuration.ConfigurationHandler +org.apache.felix.ipojo.handlers.configuration.ConfigurationHandlerDescription +org.apache.felix.ipojo.handlers.configuration.ConfigurationListener +org.apache.felix.ipojo.handlers.context.BundleContextHandler +org.apache.felix.ipojo.handlers.dependency.AggregateDependencyInjectionType +org.apache.felix.ipojo.handlers.dependency.Dependency +org.apache.felix.ipojo.handlers.dependency.DependencyCallback +org.apache.felix.ipojo.handlers.dependency.DependencyConfigurationChecker +org.apache.felix.ipojo.handlers.dependency.DependencyDescription +org.apache.felix.ipojo.handlers.dependency.DependencyHandler +org.apache.felix.ipojo.handlers.dependency.DependencyHandlerDescription +org.apache.felix.ipojo.handlers.dependency.NullableObject +org.apache.felix.ipojo.handlers.dependency.ProxyGenerator +org.apache.felix.ipojo.handlers.dependency.ServiceList +org.apache.felix.ipojo.handlers.dependency.ServiceSet +org.apache.felix.ipojo.handlers.dependency.ServiceUsage +org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallback +org.apache.felix.ipojo.handlers.lifecycle.callback.LifecycleCallbackHandler +org.apache.felix.ipojo.handlers.lifecycle.controller.ControllerHandler +org.apache.felix.ipojo.handlers.providedservice.CreationStrategy +org.apache.felix.ipojo.handlers.providedservice.ProvidedService +org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceDescription +org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandler +org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandlerDescription +org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceListener +org.apache.felix.ipojo.handlers.providedservice.strategy.ConfigurableCreationStrategy +org.apache.felix.ipojo.handlers.providedservice.strategy.ErrorPrintingServiceFactoryProxy +org.apache.felix.ipojo.handlers.providedservice.strategy.ServiceObjectFactory +org.apache.felix.ipojo.InstanceManager +org.apache.felix.ipojo.InstanceStateListener +org.apache.felix.ipojo.IPojoContext +org.apache.felix.ipojo.IPojoFactory +org.apache.felix.ipojo.IPOJOServiceFactory +org.apache.felix.ipojo.MethodInterceptor +org.apache.felix.ipojo.MissingHandlerException +org.apache.felix.ipojo.Nullable +org.apache.felix.ipojo.parser.FieldMetadata +org.apache.felix.ipojo.parser.ManifestMetadataParser +org.apache.felix.ipojo.parser.MethodMetadata +org.apache.felix.ipojo.parser.ParseException +org.apache.felix.ipojo.parser.ParseUtils +org.apache.felix.ipojo.parser.PojoMetadata +org.apache.felix.ipojo.Pojo +org.apache.felix.ipojo.PolicyServiceContext +org.apache.felix.ipojo.PrimitiveHandler +org.apache.felix.ipojo.PrimitiveInstanceDescription +org.apache.felix.ipojo.PrimitiveTypeDescription +org.apache.felix.ipojo.ServiceContext +org.apache.felix.ipojo.UnacceptableConfiguration +org.apache.felix.ipojo.util.Callback +org.apache.felix.ipojo.util.ContextSourceManager +org.apache.felix.ipojo.util.DependencyMetadataHelper +org.apache.felix.ipojo.util.DependencyModel +org.apache.felix.ipojo.util.DependencyModelListener +org.apache.felix.ipojo.util.DependencyStateListener +org.apache.felix.ipojo.util.Fields +org.apache.felix.ipojo.util.InstanceConfigurationSource +org.apache.felix.ipojo.util.InvocationResult +org.apache.felix.ipojo.util.Log +org.apache.felix.ipojo.util.Logger +org.apache.felix.ipojo.util.Methods +org.apache.felix.ipojo.util.Property +org.apache.felix.ipojo.util.Reflection +org.apache.felix.ipojo.util.SecurityHelper +org.apache.felix.ipojo.util.ServiceLocator +org.apache.felix.ipojo.util.ServiceReferenceRankingComparator +org.apache.felix.ipojo.util.StreamUtils +org.apache.felix.ipojo.util.SystemPropertiesSource +org.apache.felix.ipojo.util.Tracker +org.apache.felix.ipojo.util.TrackerCustomizer +org.apache.felix.ipojo.configuration.InstanceDSLTest +org.apache.felix.ipojo.dependency.impl.DependencyPropertiesTest +org.apache.felix.ipojo.dependency.interceptors.TransformedServiceReferenceTest +org.apache.felix.ipojo.extender.internal.declaration.DefaultExtensionDeclarationTestCase +org.apache.felix.ipojo.extender.internal.declaration.DefaultInstanceDeclarationTestCase +org.apache.felix.ipojo.extender.internal.declaration.DefaultTypeDeclarationTestCase +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultConfigurationBuilderTestCase +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultDeclarationBuilderServiceTestCase +org.apache.felix.ipojo.extender.internal.declaration.service.DefaultInstanceBuilderTestCase +org.apache.felix.ipojo.extender.internal.linker.DeclarationLinkerTestCase +org.apache.felix.ipojo.extender.internal.linker.ManagedTypeTestCase +org.apache.felix.ipojo.extender.internal.processor.ChainedBundleProcessorTestCase +org.apache.felix.ipojo.extender.internal.processor.ExtensionBundleProcessorTestCase +org.apache.felix.ipojo.extender.internal.processor.ReverseBundleProcessorTestCase +org.apache.felix.ipojo.extender.internal.queue.AbstractQueueServiceTestCase +org.apache.felix.ipojo.extender.internal.queue.callable.EmptyJob +org.apache.felix.ipojo.extender.internal.queue.callable.ExceptionCallable +org.apache.felix.ipojo.extender.internal.queue.callable.SleepingCallable +org.apache.felix.ipojo.extender.internal.queue.callable.StringCallable +org.apache.felix.ipojo.extender.internal.queue.debug.ReplayQueueEventProxyTestCase +org.apache.felix.ipojo.extender.internal.queue.ExecutorQueueServiceTestCase +org.apache.felix.ipojo.extender.internal.queue.GroupThreadFactoryTestCase +org.apache.felix.ipojo.extender.internal.queue.JobInfoCallableTestCase +org.apache.felix.ipojo.extender.internal.queue.NamingThreadFactoryTestCase +org.apache.felix.ipojo.extender.internal.queue.pref.enforce.EnforcedQueueServiceTestCase +org.apache.felix.ipojo.extender.internal.queue.pref.HeaderPreferenceSelectionTestCase +org.apache.felix.ipojo.extender.internal.queue.PrefixedThreadFactoryTestCase +org.apache.felix.ipojo.extender.internal.queue.SynchronousQueueServiceTestCase +org.apache.felix.ipojo.handlers.dependency.DependencyIdentifierTest +org.apache.felix.ipojo.handlers.dependency.NullableTest +org.apache.felix.ipojo.handlers.dependency.SmartProxyTest +org.apache.felix.ipojo.handlers.dependency.TestSpecification +org.apache.felix.ipojo.handlers.providedservice.ComponentTestWithAnotherSuperClass +org.apache.felix.ipojo.handlers.providedservice.ComponentTestWithSuperClass +org.apache.felix.ipojo.handlers.providedservice.ProvidedServiceHandlerTest +org.apache.felix.ipojo.InstanceManagerTest +org.apache.felix.ipojo.IPojoFactoryTestCase +org.apache.felix.ipojo.parser.ManipulationMetadataTest +org.apache.felix.ipojo.parser.MethodMetadataTest +org.apache.felix.ipojo.parser.PojoMetadataTest +org.apache.felix.ipojo.test.MockBundle +org.apache.felix.ipojo.util.ContextSourceManagerTest +org.apache.felix.ipojo.util.InstanceConfigurationSourceTest +org.apache.felix.ipojo.util.ManifestMetadataParserTest +org.apache.felix.ipojo.util.SystemPropertiesSourceTest +org.apache.felix.ipojo.runtime.core.api.components.FooImpl +org.apache.felix.ipojo.runtime.core.api.components.HostImpl +org.apache.felix.ipojo.runtime.core.api.components.MyComponentImpl +org.apache.felix.ipojo.runtime.core.api.components.MyServiceImpl +org.apache.felix.ipojo.runtime.core.api.components.PlainHelloImpl +org.apache.felix.ipojo.runtime.core.api.services.BarService +org.apache.felix.ipojo.runtime.core.api.services.Foo +org.apache.felix.ipojo.runtime.core.api.services.MyService +org.apache.felix.ipojo.runtime.core.api.services.PlainHello +org.apache.felix.ipojo.runtime.core.api.Common +org.apache.felix.ipojo.runtime.core.api.CompositeTest +org.apache.felix.ipojo.runtime.core.api.ExternalHandlerTest +org.apache.felix.ipojo.runtime.core.api.PrimitiveComponentTest +org.apache.felix.ipojo.runtime.core.api.SingletonComponentTest +org.apache.felix.ipojo.runtime.core.api.Whiteboard +org.apache.felix.ipojo.test.compatibility.ipojo.HelloServiceConsumer +org.apache.felix.ipojo.test.compatibility.ipojo.HelloServiceProvider +org.apache.felix.ipojo.test.compatibility.scr.HelloServiceConsumer +org.apache.felix.ipojo.test.compatibility.scr.HelloServiceProvider +org.apache.felix.ipojo.test.compatibility.service.CheckService +org.apache.felix.ipojo.test.compatibility.service.HelloService +org.apache.felix.ipojo.test.compatibility.Common +org.apache.felix.ipojo.test.compatibility.TestBeingConsumedByAriesBlueprint1_1_0 +org.apache.felix.ipojo.test.compatibility.TestBeingConsumedByEquinoxSCR1_4_100 +org.apache.felix.ipojo.test.compatibility.TestBeingConsumedByFelixSCR1_6_2 +org.apache.felix.ipojo.test.compatibility.TestBeingConsumedByKnopflerfishSCR4_0_2 +org.apache.felix.ipojo.test.compatibility.TestConsumingProviderUsingAriesBlueprint1_1_0 +org.apache.felix.ipojo.test.compatibility.TestConsumingProviderUsingEquinoxSCR1_4_100 +org.apache.felix.ipojo.test.compatibility.TestConsumingProviderUsingFelixSCR1_6_2 +org.apache.felix.ipojo.test.compatibility.TestConsumingProviderUsingKnopflerfishSCR4_0_2 +org.apache.felix.ipojo.test.compatibility.TestiPOJOOnly +foo.Foo +foo.ipojo.IPOJOFoo +foo.RGB +org.apache.felix.ipojo.runtime.core.test.components.AggregateDependency +org.apache.felix.ipojo.runtime.core.test.components.Arch +org.apache.felix.ipojo.runtime.core.test.components.components.ComponentWithProperties +org.apache.felix.ipojo.runtime.core.test.components.components.InstantiatedComponent +org.apache.felix.ipojo.runtime.core.test.components.ComponentTypeVersion +org.apache.felix.ipojo.runtime.core.test.components.context.ComponentUsingContext +org.apache.felix.ipojo.runtime.core.test.components.CustomAnnotationWithEnum +org.apache.felix.ipojo.runtime.core.test.components.DefaultImplementationDependency +org.apache.felix.ipojo.runtime.core.test.components.Dependency +org.apache.felix.ipojo.runtime.core.test.components.DependencyUsingSpecification +org.apache.felix.ipojo.runtime.core.test.components.error.AbstractClass +org.apache.felix.ipojo.runtime.core.test.components.event.PubSub +org.apache.felix.ipojo.runtime.core.test.components.event.PubSubDeprecated +org.apache.felix.ipojo.runtime.core.test.components.event.PubSubWithPublishes +org.apache.felix.ipojo.runtime.core.test.components.extender.Extender +org.apache.felix.ipojo.runtime.core.test.components.Factory +org.apache.felix.ipojo.runtime.core.test.components.FactoryDeprecated +org.apache.felix.ipojo.runtime.core.test.components.FactoryMethod +org.apache.felix.ipojo.runtime.core.test.components.FactoryMethodDeprecated +org.apache.felix.ipojo.runtime.core.test.components.FilteredDependency +org.apache.felix.ipojo.runtime.core.test.components.FromDependency +org.apache.felix.ipojo.runtime.core.test.components.Immediate +org.apache.felix.ipojo.runtime.core.test.components.InstantiateSimple +org.apache.felix.ipojo.runtime.core.test.components.InstantiateWithName +org.apache.felix.ipojo.runtime.core.test.components.jmx.JMXDeprecated +org.apache.felix.ipojo.runtime.core.test.components.jmx.JMXSimple +org.apache.felix.ipojo.runtime.core.test.components.Lifecycle +org.apache.felix.ipojo.runtime.core.test.components.ManagedServicePID +org.apache.felix.ipojo.runtime.core.test.components.MyComparator +org.apache.felix.ipojo.runtime.core.test.components.NoAnnotation +org.apache.felix.ipojo.runtime.core.test.components.NoArch +org.apache.felix.ipojo.runtime.core.test.components.NoFactory +org.apache.felix.ipojo.runtime.core.test.components.NoImmediate +org.apache.felix.ipojo.runtime.core.test.components.NoPropagation +org.apache.felix.ipojo.runtime.core.test.components.NullableDependency +org.apache.felix.ipojo.runtime.core.test.components.OnlyFoo +org.apache.felix.ipojo.runtime.core.test.components.OnlyiPOJOFoo +org.apache.felix.ipojo.runtime.core.test.components.OptionalDependency +org.apache.felix.ipojo.runtime.core.test.components.PIDandPropagation +org.apache.felix.ipojo.runtime.core.test.components.PolicyDependency +org.apache.felix.ipojo.runtime.core.test.components.Propagation +org.apache.felix.ipojo.runtime.core.test.components.PropagationandPID +org.apache.felix.ipojo.runtime.core.test.components.Properties +org.apache.felix.ipojo.runtime.core.test.components.ProvidesDouble +org.apache.felix.ipojo.runtime.core.test.components.ProvidesProperties +org.apache.felix.ipojo.runtime.core.test.components.ProvidesQuatro +org.apache.felix.ipojo.runtime.core.test.components.ProvidesSimple +org.apache.felix.ipojo.runtime.core.test.components.ProvidesStaticProperties +org.apache.felix.ipojo.runtime.core.test.components.ProvidesTriple +org.apache.felix.ipojo.runtime.core.test.components.PSServiceController +org.apache.felix.ipojo.runtime.core.test.components.PSServiceControllerSpec +org.apache.felix.ipojo.runtime.core.test.components.RankedDependency +org.apache.felix.ipojo.runtime.core.test.components.temporal.Temporal +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalCollection +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalSimple +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithDI +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithEmptyArray +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithFilter +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithNull +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithNullable +org.apache.felix.ipojo.runtime.core.test.components.temporal.TemporalWithTimeout +org.apache.felix.ipojo.runtime.core.test.components.UpdatedWithManagedService +org.apache.felix.ipojo.runtime.core.test.components.UpdatedWithProperties +org.apache.felix.ipojo.runtime.core.test.components.whiteboard.WhiteBoards +org.apache.felix.ipojo.runtime.core.test.components.whiteboard.WhiteBoardWIModification +org.apache.felix.ipojo.runtime.core.test.components.whiteboard.WhiteBoardWOModification +org.apache.felix.ipojo.runtime.core.test.services.BarService +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.ChildInterface +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.services.ParentInterface1 +org.apache.felix.ipojo.runtime.core.test.services.ParentInterface2 +org.apache.felix.ipojo.runtime.core.test.services.ParentParentInterface +org.apache.felix.ipojo.runtime.core.test.annotations.Common +org.apache.felix.ipojo.runtime.core.test.annotations.TestAggregateDependency +org.apache.felix.ipojo.runtime.core.test.annotations.TestArchitecture +org.apache.felix.ipojo.runtime.core.test.annotations.TestContext +org.apache.felix.ipojo.runtime.core.test.annotations.TestCustomAnnotations +org.apache.felix.ipojo.runtime.core.test.annotations.TestDependency +org.apache.felix.ipojo.runtime.core.test.annotations.TestDependencyPolicy +org.apache.felix.ipojo.runtime.core.test.annotations.TestEventAdmin +org.apache.felix.ipojo.runtime.core.test.annotations.TestExtender +org.apache.felix.ipojo.runtime.core.test.annotations.TestFactory +org.apache.felix.ipojo.runtime.core.test.annotations.TestFilteredDependency +org.apache.felix.ipojo.runtime.core.test.annotations.TestInstantiate +org.apache.felix.ipojo.runtime.core.test.annotations.TestInstantiatedComponent +org.apache.felix.ipojo.runtime.core.test.annotations.TestJMX +org.apache.felix.ipojo.runtime.core.test.annotations.TestLifecycleCallbacks +org.apache.felix.ipojo.runtime.core.test.annotations.TestLifecycleController +org.apache.felix.ipojo.runtime.core.test.annotations.TestOptionalDependency +org.apache.felix.ipojo.runtime.core.test.annotations.TestProperties +org.apache.felix.ipojo.runtime.core.test.annotations.TestRankedDependency +org.apache.felix.ipojo.runtime.core.test.annotations.TestServiceProviding +org.apache.felix.ipojo.runtime.core.test.annotations.TestTemporalDependencies +org.apache.felix.ipojo.runtime.core.test.annotations.TestWhiteBoard +org.apache.felix.ipojo.runtime.bad.components.BadConstructors +org.apache.felix.ipojo.runtime.bad.components.CallbackCheckService +org.apache.felix.ipojo.runtime.bad.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.bad.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.bad.components.LifecycleControllerTest +org.apache.felix.ipojo.runtime.bad.components.ParentClass +org.apache.felix.ipojo.runtime.bad.services.BarService +org.apache.felix.ipojo.runtime.bad.services.CheckService +org.apache.felix.ipojo.runtime.bad.services.FooService +org.apache.felix.ipojo.runtime.bad.test.Common +org.apache.felix.ipojo.runtime.bad.test.TestBadFactories +org.apache.felix.ipojo.runtime.bad.test.TestBadLFCCallback +org.apache.felix.ipojo.runtime.bad.test.TestBadLFCController +org.apache.felix.ipojo.runtime.bad.test.TestBadServiceDependencies +org.apache.felix.ipojo.runtime.core.components.ConfigurableFooProvider +org.apache.felix.ipojo.runtime.core.components.ConfigurableFooProviderWithPropagation +org.apache.felix.ipojo.runtime.core.components.ImmediateConfigurableFooProvider +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestManagedServiceFactoryTestForImmediate +org.apache.felix.ipojo.runtime.core.TestManagedServiceFactoryTestForServices +org.apache.felix.ipojo.runtime.core.TestManagedServiceTestForImmediate +org.apache.felix.ipojo.runtime.core.TestManagedServiceTestForService +org.apache.felix.ipojo.runtime.core.TestPropagation +org.apache.felix.ipojo.runtime.core.TestUpdated +org.apache.felix.ipojo.runtime.core.components.Bean +org.apache.felix.ipojo.runtime.core.components.configuration.MyComplexConfiguration +org.apache.felix.ipojo.runtime.core.components.configuration.MyConfiguration +org.apache.felix.ipojo.runtime.core.components.ConfigureAnotherInstance +org.apache.felix.ipojo.runtime.core.components.ConfigureNothing +org.apache.felix.ipojo.runtime.core.components.ConfigureOneInstance +org.apache.felix.ipojo.runtime.core.components.ConfigureThreeInstancesUsingMethods +org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstances +org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstancesWithInheritance +org.apache.felix.ipojo.runtime.core.components.ConfigureTwoInstancesWithOverridding +org.apache.felix.ipojo.runtime.core.components.MyComplexComponent +org.apache.felix.ipojo.runtime.core.components.MyComponent +org.apache.felix.ipojo.runtime.core.components.ParentConfiguration +org.apache.felix.ipojo.runtime.core.components.SimpleConfiguration +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestComplexConfigurations +org.apache.felix.ipojo.runtime.core.TestConfigurationInAnotherBundle +org.apache.felix.ipojo.runtime.core.TestConfigurationOfMyComponent +org.apache.felix.ipojo.runtime.core.TestConfigurationOfThreeInstancesUsingMethods +org.apache.felix.ipojo.runtime.core.TestConfigurationOfTwoInstances +org.apache.felix.ipojo.runtime.core.TestConfigurationWithInheritedInstance +org.apache.felix.ipojo.runtime.core.TestConfigurationWithOverriddenInstance +org.apache.felix.ipojo.runtime.core.TestEmptyConfiguration +org.apache.felix.ipojo.runtime.core.TestSeveralConfigurations +org.apache.felix.ipojo.runtime.core.TestSimpleConfiguration +org.apache.felix.ipojo.runtime.core.components.arch.MyComponentToIntrospect +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.ComplexConfiguration +org.apache.felix.ipojo.runtime.core.components.ConfigurableCheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.constructor.CheckServiceProviderWithBundleContextAndProperty +org.apache.felix.ipojo.runtime.core.components.constructor.CheckServiceProviderWithDefaultValueProperty +org.apache.felix.ipojo.runtime.core.components.constructor.CheckServiceProviderWithNamedProperty +org.apache.felix.ipojo.runtime.core.components.constructor.CheckServiceProviderWithTwoProperties +org.apache.felix.ipojo.runtime.core.components.constructor.CheckServiceProviderWithUnnamedProperty +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType2 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.ParentClass +org.apache.felix.ipojo.runtime.core.components.ParentConfigurableCheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.PropertyModifier +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestArchitecture +org.apache.felix.ipojo.runtime.core.TestBothProperties +org.apache.felix.ipojo.runtime.core.TestComplexProperties +org.apache.felix.ipojo.runtime.core.TestConstructorInjectionOfProperties +org.apache.felix.ipojo.runtime.core.TestDynamicallyConfigurablePropertiesUsingConfigAdmin +org.apache.felix.ipojo.runtime.core.TestFieldProperties +org.apache.felix.ipojo.runtime.core.TestListeners +org.apache.felix.ipojo.runtime.core.TestManagedServiceConfigurableProperties +org.apache.felix.ipojo.runtime.core.TestMethodProperties +org.apache.felix.ipojo.runtime.core.TestPropertyModifier +org.apache.felix.ipojo.runtime.core.TestSimpleProperties +org.apache.felix.ipojo.runtime.core.TestSuperMethodProperties +org.apache.felix.ipojo.runtime.core.TestUpdatedMethod +org.apache.felix.ipojo.runtime.core.TestUpdatedMethodAndConfigAdmin +org.apache.felix.ipojo.runtime.core.TestUpdatedMethodAndManagedService +org.apache.felix.ipojo.runtime.core.TestUpdatedNoArgMethodAndConfigAdmin +org.apache.felix.ipojo.runtime.core.TestUpdatedNoArgMethodAndManagedService +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentBundleContextInjectionInConstructor +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentBundleContextInjectionInField +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentBundleContextInjectionInMethod +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentWithThreeConstructorParams +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentWithTwoConstructorParams +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentWithTwoFields +org.apache.felix.ipojo.runtime.core.components.annotations.ComponentWithTwoSetters +org.apache.felix.ipojo.runtime.core.components.annotations.InstanceBundleContextInjectionInConstructor +org.apache.felix.ipojo.runtime.core.components.annotations.InstanceBundleContextInjectionInField +org.apache.felix.ipojo.runtime.core.components.annotations.InstanceBundleContextInjectionInMethod +org.apache.felix.ipojo.runtime.core.components.ComponentUsingConstructor +org.apache.felix.ipojo.runtime.core.components.ComponentUsingField +org.apache.felix.ipojo.runtime.core.components.ComponentUsingMethod +org.apache.felix.ipojo.runtime.core.components.configuration.MyConfiguration +org.apache.felix.ipojo.runtime.core.components.mix.MixWithProperties1 +org.apache.felix.ipojo.runtime.core.components.mix.MixWithProperties2 +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestContextInjectionFromAnnotations +org.apache.felix.ipojo.runtime.core.TestContextInjectionFromXML +org.apache.felix.ipojo.runtime.core.TestInjectingComponentAndInstanceContext +org.apache.felix.ipojo.runtime.core.TestInjectingContextAndProperties +org.apache.felix.ipojo.runtime.core.test.components.EnglishHelloService +org.apache.felix.ipojo.runtime.core.test.components.EnglishHelloService2 +org.apache.felix.ipojo.runtime.core.test.components.FrenchHelloService +org.apache.felix.ipojo.runtime.core.test.components.GermanHelloService +org.apache.felix.ipojo.runtime.core.test.services.HelloService +org.apache.felix.ipojo.runtime.core.test.declaration.Common +org.apache.felix.ipojo.runtime.core.test.declaration.TestDeclarationBuilderService +org.apache.felix.ipojo.runtime.externalhandlers.components.CheckServiceAndFieldInterceptorHandler +org.apache.felix.ipojo.runtime.externalhandlers.components.CheckServiceHandler +org.apache.felix.ipojo.runtime.externalhandlers.components.FooProviderType1 +org.apache.felix.ipojo.runtime.externalhandlers.services.CheckService +org.apache.felix.ipojo.runtime.externalhandlers.services.CheckServiceHandlerDescription +org.apache.felix.ipojo.runtime.externalhandlers.services.FooService +org.apache.felix.ipojo.runtime.externalhandlers.test.AutoHandlerTest +org.apache.felix.ipojo.runtime.externalhandlers.test.Common +org.apache.felix.ipojo.runtime.externalhandlers.test.HandlerTest +org.apache.felix.ipojo.runtime.externalhandlers.test.HandlerWithFieldInterceptorTest +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.FiveInstances +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.NoInstances +org.apache.felix.ipojo.runtime.core.components.OneDuplicateInstance +org.apache.felix.ipojo.runtime.core.components.ReconfigurableSimpleType +org.apache.felix.ipojo.runtime.core.components.SimpleType +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.NoService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestComponentDesc +org.apache.felix.ipojo.runtime.core.TestConfigAdmin +org.apache.felix.ipojo.runtime.core.TestFactoryProperties +org.apache.felix.ipojo.runtime.core.TestInstances +org.apache.felix.ipojo.runtime.core.TestObedience +org.apache.felix.ipojo.runtime.core.TestReconfiguration +org.apache.felix.ipojo.runtime.core.TestUnacceptableConfiguration +org.apache.felix.ipojo.core.tests.components.MyComponent +org.apache.felix.ipojo.core.tests.components.MyCons +org.apache.felix.ipojo.core.tests.services.MyService +org.apache.felix.ipojo.tests.core.Common +org.apache.felix.ipojo.tests.core.FactoryVersionTest +org.apache.felix.ipojo.tests.core.VersionConflictTest +org.apache.felix.ipojo.runtime.core.components.DummyImpl +org.apache.felix.ipojo.runtime.core.components.MyComponent +org.apache.felix.ipojo.runtime.core.handlers.DummyHandler +org.apache.felix.ipojo.runtime.core.handlers.EmptyHandler +org.apache.felix.ipojo.runtime.core.services.Dummy +org.apache.felix.ipojo.runtime.core.services.MyService +org.apache.felix.ipojo.runtime.core.services.User +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.DummyHandlerTest +org.apache.felix.ipojo.runtime.core.IgnoreCaseHandlerSelectionTest +org.apache.felix.ipojo.runtime.core.Tools +org.apache.felix.ipojo.runtime.core.components.CallbackWithErrorCheckService +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.LifecycleControllerTest +org.apache.felix.ipojo.runtime.core.components.ParentClass +org.apache.felix.ipojo.runtime.core.services.CallbackCheckService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestCallback +org.apache.felix.ipojo.runtime.core.TestErrorCallback +org.apache.felix.ipojo.runtime.core.TestImmediateCallback +org.apache.felix.ipojo.runtime.core.TestImmediateCallbackSeveralFactories +org.apache.felix.ipojo.runtime.core.TestImmediateCallbackSingletonFactory +org.apache.felix.ipojo.runtime.core.TestImmediateLifeCycleController +org.apache.felix.ipojo.runtime.core.TestParentCallback +org.apache.felix.ipojo.runtime.core.components.ConfigurableLifecycleControllerTest +org.apache.felix.ipojo.runtime.core.components.LifecycleControllerTest +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestConfigurableLifeCycleController +org.apache.felix.ipojo.runtime.core.TestImmediateLifeCycleController +org.apache.felix.ipojo.runtime.core.TestLifeCycleController +org.apache.felix.ipojo.runtime.core.components.MyComponent +org.apache.felix.ipojo.runtime.core.components.MyErroneousComponent +org.apache.felix.ipojo.runtime.core.services.MyService +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.TestBNDManifestLoggerInfo +org.apache.felix.ipojo.runtime.core.TestErrorHandler +org.apache.felix.ipojo.runtime.core.TestManifestLoggerInfo +org.apache.felix.ipojo.runtime.core.TestSystemLoggerInfo +org.apache.felix.ipojo.runtime.core.TestSystemLoggerWarning +org.apache.felix.ipojo.runtime.core.test.components.AdvancedFooConsumer +org.apache.felix.ipojo.runtime.core.test.components.FooConsumer +org.apache.felix.ipojo.runtime.core.test.components.FooProvider +org.apache.felix.ipojo.runtime.core.test.interceptors.AddLocationTrackingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.AdvancedTrackerAndRankerInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.EnhancingBindingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.FilterRankingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.GradeComparator +org.apache.felix.ipojo.runtime.core.test.interceptors.HidingTrackingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.PropertyTrackingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.ProxyBindingInterceptor +org.apache.felix.ipojo.runtime.core.test.interceptors.TrackerAndRankerInterceptor +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.Enhanced +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.services.Setter +org.apache.felix.ipojo.runtime.core.test.dependencies.Common +org.apache.felix.ipojo.runtime.core.test.dependencies.TestBindingInterceptors +org.apache.felix.ipojo.runtime.core.test.dependencies.TestCombinationOfInterceptors +org.apache.felix.ipojo.runtime.core.test.dependencies.TestHidingServices +org.apache.felix.ipojo.runtime.core.test.dependencies.TestInterceptorsOnSeveralDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestRankingServices +org.apache.felix.ipojo.runtime.core.test.dependencies.TestTransformingServices +org.apache.felix.ipojo.runtime.core.test.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.CollectionCheckService +org.apache.felix.ipojo.runtime.core.test.components.comparator.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradeComparator +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradedFooServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.exceptions.ExceptionAwareCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.exceptions.NoServiceException +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.filter.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType2 +org.apache.felix.ipojo.runtime.core.test.components.FooServiceDefaultImpl +org.apache.felix.ipojo.runtime.core.test.components.inner.C1 +org.apache.felix.ipojo.runtime.core.test.components.inner.C2 +org.apache.felix.ipojo.runtime.core.test.components.inner.C3 +org.apache.felix.ipojo.runtime.core.test.components.inner.MyFilter +org.apache.felix.ipojo.runtime.core.test.components.ListCheckService +org.apache.felix.ipojo.runtime.core.test.components.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.optional.MyComponent +org.apache.felix.ipojo.runtime.core.test.components.ParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceDelegator +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceGetAndDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceNoDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceUsingStringService +org.apache.felix.ipojo.runtime.core.test.components.proxy.Helper +org.apache.felix.ipojo.runtime.core.test.components.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.SetCheckService +org.apache.felix.ipojo.runtime.core.test.components.VectorCheckService +org.apache.felix.ipojo.runtime.core.test.services.Call +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.dependencies.Common +org.apache.felix.ipojo.runtime.core.test.dependencies.di.TestDelayedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.di.TestMethodDelayedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.di.TestMethodOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.di.TestOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.exceptions.TestDependenciesWithExceptions +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestDelayedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestDelayedOptionalMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestMethodDelayedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestMethodDelayedOptionalMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestMethodOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestMethodOptionalMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestNullableTransitiveClassloading +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestOptionalMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestOptionalNoNullableDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestProxiedDelayedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestProxiedDelayedOptionalMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.optional.TestProxiedOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.CollectionCheckService +org.apache.felix.ipojo.runtime.core.test.components.comparator.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradeComparator +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradedFooServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.filter.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType2 +org.apache.felix.ipojo.runtime.core.test.components.FooServiceDefaultImpl +org.apache.felix.ipojo.runtime.core.test.components.inner.C1 +org.apache.felix.ipojo.runtime.core.test.components.inner.C2 +org.apache.felix.ipojo.runtime.core.test.components.inner.C3 +org.apache.felix.ipojo.runtime.core.test.components.inner.MyFilter +org.apache.felix.ipojo.runtime.core.test.components.ListCheckService +org.apache.felix.ipojo.runtime.core.test.components.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.optional.MyComponent +org.apache.felix.ipojo.runtime.core.test.components.ParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceDelegator +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceGetAndDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceNoDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceUsingStringService +org.apache.felix.ipojo.runtime.core.test.components.proxy.Helper +org.apache.felix.ipojo.runtime.core.test.components.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.SetCheckService +org.apache.felix.ipojo.runtime.core.test.components.VectorCheckService +org.apache.felix.ipojo.runtime.core.test.services.Call +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.dependencies.Common +org.apache.felix.ipojo.runtime.core.test.dependencies.comparator.TestComparator +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestDynamicPriority +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestStaticMethodOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestStaticMethodSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestStaticMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestStaticOptionalDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.policies.TestStaticSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.CollectionCheckService +org.apache.felix.ipojo.runtime.core.test.components.comparator.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradeComparator +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradedFooServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.filter.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType2 +org.apache.felix.ipojo.runtime.core.test.components.FooServiceDefaultImpl +org.apache.felix.ipojo.runtime.core.test.components.inner.C1 +org.apache.felix.ipojo.runtime.core.test.components.inner.C2 +org.apache.felix.ipojo.runtime.core.test.components.inner.C3 +org.apache.felix.ipojo.runtime.core.test.components.inner.MyFilter +org.apache.felix.ipojo.runtime.core.test.components.ListCheckService +org.apache.felix.ipojo.runtime.core.test.components.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.optional.MyComponent +org.apache.felix.ipojo.runtime.core.test.components.ParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceDelegator +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceGetAndDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceNoDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceUsingStringService +org.apache.felix.ipojo.runtime.core.test.components.proxy.Helper +org.apache.felix.ipojo.runtime.core.test.components.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.SetCheckService +org.apache.felix.ipojo.runtime.core.test.components.VectorCheckService +org.apache.felix.ipojo.runtime.core.test.services.Call +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.dependencies.Common +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedCollectionMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedDelayedMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedDelayedSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedListMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedSetMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxiedSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.proxies.TestProxyTest +org.apache.felix.ipojo.runtime.core.test.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.CollectionCheckService +org.apache.felix.ipojo.runtime.core.test.components.comparator.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradeComparator +org.apache.felix.ipojo.runtime.core.test.components.comparator.GradedFooServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.context.ContextualFilterConsumer +org.apache.felix.ipojo.runtime.core.test.components.context.Provider1 +org.apache.felix.ipojo.runtime.core.test.components.context.Provider2 +org.apache.felix.ipojo.runtime.core.test.components.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.error.NullPointerExceptionBinder +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckProvider +org.apache.felix.ipojo.runtime.core.test.components.filter.FilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.filter.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.FooProviderType2 +org.apache.felix.ipojo.runtime.core.test.components.FooServiceDefaultImpl +org.apache.felix.ipojo.runtime.core.test.components.identification.ComponentWithCustomMethods +org.apache.felix.ipojo.runtime.core.test.components.inner.C1 +org.apache.felix.ipojo.runtime.core.test.components.inner.C2 +org.apache.felix.ipojo.runtime.core.test.components.inner.C3 +org.apache.felix.ipojo.runtime.core.test.components.inner.MyFilter +org.apache.felix.ipojo.runtime.core.test.components.leak.DefaultHelloService +org.apache.felix.ipojo.runtime.core.test.components.leak.DefaultLeakingService +org.apache.felix.ipojo.runtime.core.test.components.ListCheckService +org.apache.felix.ipojo.runtime.core.test.components.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.MultipleFilterCheckSubscriber +org.apache.felix.ipojo.runtime.core.test.components.optional.MyComponent +org.apache.felix.ipojo.runtime.core.test.components.ParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.test.components.policies.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.DynCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodCheckServiceProvider +org.apache.felix.ipojo.runtime.core.test.components.policies.MethodMultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.MultipleCheckService +org.apache.felix.ipojo.runtime.core.test.components.policies.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceDelegator +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceGetAndDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceNoDelegate +org.apache.felix.ipojo.runtime.core.test.components.proxy.CheckServiceUsingStringService +org.apache.felix.ipojo.runtime.core.test.components.proxy.Helper +org.apache.felix.ipojo.runtime.core.test.components.RankedFooProviderType1 +org.apache.felix.ipojo.runtime.core.test.components.SetCheckService +org.apache.felix.ipojo.runtime.core.test.components.VectorCheckService +org.apache.felix.ipojo.runtime.core.test.services.Call +org.apache.felix.ipojo.runtime.core.test.services.CheckService +org.apache.felix.ipojo.runtime.core.test.services.FooService +org.apache.felix.ipojo.runtime.core.test.services.HelloService +org.apache.felix.ipojo.runtime.core.test.services.LeakingService +org.apache.felix.ipojo.runtime.core.test.services.Listener +org.apache.felix.ipojo.runtime.core.test.dependencies.Common +org.apache.felix.ipojo.runtime.core.test.dependencies.context.TestContextualFilters +org.apache.felix.ipojo.runtime.core.test.dependencies.context.TestContextualFiltersAndExternalSources +org.apache.felix.ipojo.runtime.core.test.dependencies.error.TestNPEInBindMethod +org.apache.felix.ipojo.runtime.core.test.dependencies.filter.TestFromDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.filter.TestMultipleFilterDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.filter.TestOptionalMultipleFilterDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.filter.TestOptionalSimpleFilterDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.filter.TestSimpleFilterDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.identification.TestDependencyIdDetection +org.apache.felix.ipojo.runtime.core.test.dependencies.inner.TestInnerProxyMix +org.apache.felix.ipojo.runtime.core.test.dependencies.leak.ListenerStyleLeakTest +org.apache.felix.ipojo.runtime.core.test.dependencies.TestCollectionMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestDelayedMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestDelayedSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestDependencyArchitecture +org.apache.felix.ipojo.runtime.core.test.dependencies.TestListMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestMethodDelayedMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestMethodDelayedSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestMethodMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestMethodSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestModifyDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestNonProxiedNotInterfaceDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestSetMultipleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestSimpleDependencies +org.apache.felix.ipojo.runtime.core.test.dependencies.TestVectorMultipleDependencies +org.apache.felix.ipojo.runtime.test.dependencies.timeout.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.components.CollectionCheckServiceProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.components.FooProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.components.MultipleCheckServiceProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.components.NullableFooProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.services.CheckService +org.apache.felix.ipojo.runtime.test.dependencies.timeout.services.FooService +org.apache.felix.ipojo.runtime.test.dependencies.timeout.Common +org.apache.felix.ipojo.runtime.test.dependencies.timeout.DefaultImplementationTest +org.apache.felix.ipojo.runtime.test.dependencies.timeout.DelayedProvider +org.apache.felix.ipojo.runtime.test.dependencies.timeout.DelayTest +org.apache.felix.ipojo.runtime.test.dependencies.timeout.EmptyTest +org.apache.felix.ipojo.runtime.test.dependencies.timeout.NullableTest +org.apache.felix.ipojo.runtime.test.dependencies.timeout.NullTest +org.apache.felix.ipojo.runtime.test.dependencies.timeout.TimeoutTest +org.apache.felix.ipojo.runtime.core.components.callbacks.CallbacksCheckService +org.apache.felix.ipojo.runtime.core.components.CheckProviderParentClass +org.apache.felix.ipojo.runtime.core.components.CheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.controller.ControllerCheckService +org.apache.felix.ipojo.runtime.core.components.controller.DoubleControllerCheckService +org.apache.felix.ipojo.runtime.core.components.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderType1 +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn +org.apache.felix.ipojo.runtime.core.components.FooProviderTypeDyn2 +org.apache.felix.ipojo.runtime.core.components.FooProviderWithAnonymousClass +org.apache.felix.ipojo.runtime.core.components.inheritance.a.IA +org.apache.felix.ipojo.runtime.core.components.inheritance.b.IB +org.apache.felix.ipojo.runtime.core.components.inheritance.c.C +org.apache.felix.ipojo.runtime.core.components.inheritance.d.D +org.apache.felix.ipojo.runtime.core.components.inherited.ProcessImplementation1 +org.apache.felix.ipojo.runtime.core.components.inherited.ProcessImplementation2 +org.apache.felix.ipojo.runtime.core.components.inherited.ProcessImplementation3 +org.apache.felix.ipojo.runtime.core.components.inherited.ProcessImplementation4 +org.apache.felix.ipojo.runtime.core.components.inherited.ProcessParentImplementation +org.apache.felix.ipojo.runtime.core.components.NullCheckServiceProvider +org.apache.felix.ipojo.runtime.core.components.SimpleClass +org.apache.felix.ipojo.runtime.core.components.strategies.BarConsumer +org.apache.felix.ipojo.runtime.core.components.strategies.Consumer +org.apache.felix.ipojo.runtime.core.components.strategies.DummyCreationStrategy +org.apache.felix.ipojo.runtime.core.components.strategies.DummyCreationStrategy2 +org.apache.felix.ipojo.runtime.core.components.strategies.DummyServiceFactory +org.apache.felix.ipojo.runtime.core.components.strategies.FooBarProviderType1 +org.apache.felix.ipojo.runtime.core.components.strategies.FooProviderType1 +org.apache.felix.ipojo.runtime.core.services.BarService +org.apache.felix.ipojo.runtime.core.services.CheckService +org.apache.felix.ipojo.runtime.core.services.ChildInterface +org.apache.felix.ipojo.runtime.core.services.FooService +org.apache.felix.ipojo.runtime.core.services.ParentInterface1 +org.apache.felix.ipojo.runtime.core.services.ParentInterface2 +org.apache.felix.ipojo.runtime.core.services.ParentParentInterface +org.apache.felix.ipojo.runtime.core.Common +org.apache.felix.ipojo.runtime.core.inheritence.InheritanceTest +org.apache.felix.ipojo.runtime.core.strategies.CustomStrategy2Test +org.apache.felix.ipojo.runtime.core.strategies.CustomStrategyTest +org.apache.felix.ipojo.runtime.core.strategies.PerInstanceStrategyTest +org.apache.felix.ipojo.runtime.core.TestCallbacks +org.apache.felix.ipojo.runtime.core.TestClass +org.apache.felix.ipojo.runtime.core.TestDynamicProps +org.apache.felix.ipojo.runtime.core.TestDynamicPropsReconfiguration +org.apache.felix.ipojo.runtime.core.TestExposition +org.apache.felix.ipojo.runtime.core.TestInheritedClasses +org.apache.felix.ipojo.runtime.core.TestListeners +org.apache.felix.ipojo.runtime.core.TestNullCheck +org.apache.felix.ipojo.runtime.core.TestOSGiProperties +org.apache.felix.ipojo.runtime.core.TestPropertiesInAnonymousClass +org.apache.felix.ipojo.runtime.core.TestProvidedServiceArchitecture +org.apache.felix.ipojo.runtime.core.TestServiceController +org.apache.felix.ipojo.runtime.core.TestSimplePS +org.apache.felix.ipojo.runtime.core.TestStaticProps +org.apache.felix.ipojo.runtime.core.TestStaticPropsReconfiguration +ipojo.example.hello.client.HelloClient +ipojo.example.hello.client.MyDummyHello +ipojo.example.hello.impl.HelloImpl +ipojo.example.hello.Hello +ipojo.example.hello.client.HelloClient +ipojo.example.hello.impl.HelloImpl +org.apache.felix.ipojo.arch.gogo.Arch +org.apache.felix.ipojo.webconsole.IPOJOPlugin +org.apache.felix.ipojo.webconsole.StateUtils +org.apache.felix.jaas.boot.package-info +org.apache.felix.jaas.boot.ProxyLoginModule +org.apache.felix.jaas.internal.Activator +org.apache.felix.jaas.internal.BundleLoginModuleCreator +org.apache.felix.jaas.internal.ConfigLoginModuleProvider +org.apache.felix.jaas.internal.ConfigSpiOsgi +org.apache.felix.jaas.internal.ControlFlag +org.apache.felix.jaas.internal.JaasConfigFactory +org.apache.felix.jaas.internal.JaasWebConsolePlugin +org.apache.felix.jaas.internal.Logger +org.apache.felix.jaas.internal.LoginModuleCreator +org.apache.felix.jaas.internal.LoginModuleProvider +org.apache.felix.jaas.internal.OsgiLoginModuleProvider +org.apache.felix.jaas.internal.Util +org.apache.felix.jaas.LoginContextFactory +org.apache.felix.jaas.LoginModuleFactory +org.apache.felix.jaas.package-info +org.apache.felix.jaas.integration.common.SimpleCallbackHandler +org.apache.felix.jaas.integration.common.SimplePrincipal +org.apache.felix.jaas.integration.ITJaasWebConsolePlugin +org.apache.felix.jaas.integration.ITJaasWithBootClasspath +org.apache.felix.jaas.integration.ITJaasWithConfigBasedLoginModule +org.apache.felix.jaas.integration.ITJaasWithGlobalConfig +org.apache.felix.jaas.integration.JaasTestBase +org.apache.felix.jaas.integration.sample1.ConfigLoginModule +org.apache.felix.jaas.internal.ITConcurrentLoginModuleFactoryTest +org.apache.felix.jaas.internal.UtilTest +org.apache.felix.jmood.Activator +org.apache.felix.jmood.AgentConstants +org.apache.felix.jmood.AgentContext +org.apache.felix.jmood.compendium.ConfigAdminManager +org.apache.felix.jmood.compendium.ConfigAdminManagerMBean +org.apache.felix.jmood.compendium.ConfigurationDelegate +org.apache.felix.jmood.compendium.ConfigurationDelegateMBean +org.apache.felix.jmood.compendium.LogManager +org.apache.felix.jmood.compendium.LogManagerMBean +org.apache.felix.jmood.compendium.UserManager +org.apache.felix.jmood.compendium.UserManagerMBean +org.apache.felix.jmood.CompendiumHandler +org.apache.felix.jmood.core.BundleNotAvailableException +org.apache.felix.jmood.core.CoreController +org.apache.felix.jmood.core.CoreControllerMBean +org.apache.felix.jmood.core.Framework +org.apache.felix.jmood.core.FrameworkMBean +org.apache.felix.jmood.core.instrumentation.BundleInfo +org.apache.felix.jmood.core.instrumentation.FrameworkSnapshot +org.apache.felix.jmood.core.instrumentation.PackageInfo +org.apache.felix.jmood.core.instrumentation.ServiceInfo +org.apache.felix.jmood.core.ManagedBundle +org.apache.felix.jmood.core.ManagedBundleMBean +org.apache.felix.jmood.core.ManagedPackage +org.apache.felix.jmood.core.ManagedPackageMBean +org.apache.felix.jmood.core.ManagedService +org.apache.felix.jmood.core.ManagedServiceMBean +org.apache.felix.jmood.core.NotImplementedException +org.apache.felix.jmood.core.ServiceNotAvailableException +org.apache.felix.jmood.RMIHandler +org.apache.felix.jmood.SecurityManagerHandler +org.apache.felix.jmood.utils.CompositeDataItemNames +org.apache.felix.jmood.utils.InstrumentationSupport +org.apache.felix.jmood.utils.ObjectNames +org.apache.felix.jmood.utils.OSGi2JMXCodec +org.apache.felix.jmood.utils.OSGiTypes +org.apache.felix.jmood.core.BundleMBeanTestCase +org.apache.felix.jmood.core.CoreTestCase +org.apache.felix.jmood.core.TestHarness +org.apache.felix.jmood.FelixLauncher +org.apache.felix.jmxintrospector.JmxAsmHelper +org.apache.felix.jmxintrospector.MBean +org.apache.felix.jmxintrospector.MBeanProxyFactory +org.apache.felix.jmxintrospector.MBeanProxyManager +org.apache.felix.jmxintrospector.Server +org.apache.felix.log.Activator +org.apache.felix.log.ConfigurationListenerImpl +org.apache.felix.log.FormatterLoggerImpl +org.apache.felix.log.Log +org.apache.felix.log.LogEntryImpl +org.apache.felix.log.LogException +org.apache.felix.log.LoggerAdminImpl +org.apache.felix.log.LoggerContextImpl +org.apache.felix.log.LoggerImpl +org.apache.felix.log.LogListenerThread +org.apache.felix.log.LogNode +org.apache.felix.log.LogNodeEnumeration +org.apache.felix.log.LogReaderServiceFactory +org.apache.felix.log.LogReaderServiceImpl +org.apache.felix.log.LogServiceFactory +org.apache.felix.log.LogServiceImpl +org.apache.felix.log.RootLoggerContextImpl +org.apache.felix.log.extension.Activator +org.apache.felix.logback.test.LogServiceTest +org.apache.felix.logback.test.LogServiceTest +org.apache.felix.logback.test.JBLTest +org.apache.felix.logback.test.JCLTest +org.apache.felix.logback.test.JULTest +org.apache.felix.logback.test.JULTest +org.apache.felix.logback.test.Log4j1Test +org.apache.felix.logback.test.Log4j2Test +org.apache.felix.logback.test.SLF4JTest +org.apache.felix.logback.test.LogServiceTest +org.apache.felix.logback.test.LogServiceTest +org.apache.felix.logback.test.JBLTest +org.apache.felix.logback.test.JCLTest +org.apache.felix.logback.test.JULTest +org.apache.felix.logback.test.Log4j1Test +org.apache.felix.logback.test.Activator +org.apache.felix.logback.test.Log4j2Test +org.apache.felix.logback.test.SLF4JTest +org.apache.felix.logback.test.helper.LogTestHelper +org.apache.felix.logback.test.helper.ls.LogServiceHelper +org.apache.felix.logback.internal.Activator +org.apache.felix.logback.internal.EquinoxHookSupport +org.apache.felix.logback.internal.LogbackLogListener +org.apache.felix.main.AutoProcessor +org.apache.felix.main.Main +org.apache.felix.metatype.AD +org.apache.felix.metatype.ADValidator +org.apache.felix.metatype.Attribute +org.apache.felix.metatype.DefaultMetaTypeProvider +org.apache.felix.metatype.Designate +org.apache.felix.metatype.DesignateObject +org.apache.felix.metatype.internal.Activator +org.apache.felix.metatype.internal.BaseProviderHolder +org.apache.felix.metatype.internal.l10n.BundleResources +org.apache.felix.metatype.internal.l10n.Resources +org.apache.felix.metatype.internal.LocalizedAttributeDefinition +org.apache.felix.metatype.internal.LocalizedBase +org.apache.felix.metatype.internal.LocalizedObjectClassDefinition +org.apache.felix.metatype.internal.ManagedServiceHolder +org.apache.felix.metatype.internal.ManagedServiceTracker +org.apache.felix.metatype.internal.MetaTypeInformationImpl +org.apache.felix.metatype.internal.MetaTypeProviderHolder +org.apache.felix.metatype.internal.MetaTypeProviderTracker +org.apache.felix.metatype.internal.MetaTypeServiceImpl +org.apache.felix.metatype.internal.ServiceMetaTypeInformation +org.apache.felix.metatype.MetaData +org.apache.felix.metatype.MetaDataReader +org.apache.felix.metatype.OCD +org.apache.felix.metatype.OptionalAttributes +org.apache.felix.metatype.package-info +org.apache.felix.metatype.ADTest +org.apache.felix.metatype.ADValidatorTest +org.apache.felix.metatype.AttributeTest +org.apache.felix.metatype.internal.MetaTypeServiceImplTest +org.apache.felix.metatype.MetaDataReaderTest +org.apache.felix.metatype.MockBundle +org.apache.felix.metatype.MockBundleContext +org.apache.felix.metrics.osgi.BundleStartDuration +org.apache.felix.metrics.osgi.impl.Activator +org.apache.felix.metrics.osgi.impl.BundleStartTimeCalculator +org.apache.felix.metrics.osgi.impl.Log +org.apache.felix.metrics.osgi.impl.ServiceRestartCountCalculator +org.apache.felix.metrics.osgi.impl.ServiceTrackerCustomizerAdapter +org.apache.felix.metrics.osgi.impl.StartupTimeCalculator +org.apache.felix.metrics.osgi.package-info +org.apache.felix.metrics.osgi.ServiceRestartCounter +org.apache.felix.metrics.osgi.StartupMetrics +org.apache.felix.metrics.osgi.StartupMetricsListener +org.apache.felix.metrics.osgi.impl.AbstractIT +org.apache.felix.metrics.osgi.impl.BundleStartTimeCalculatorTest +org.apache.felix.metrics.osgi.impl.HealthCheckSmokeIT +org.apache.felix.metrics.osgi.impl.ServiceRegistrationsTrackerTest +org.apache.felix.metrics.osgi.impl.ServiceRestartCountCalculatorTest +org.apache.felix.metrics.osgi.impl.SystemReadySmokeIT +org.apache.felix.metrics.osgi.impl.WaitForResultsStartupMetricsListener +org.apache.felix.metrics.osgi.consumers.impl.dropwizard.DropwizardMetricsListener +org.apache.felix.metrics.osgi.consumers.impl.json.JsonWritingMetricsListener +org.apache.felix.metrics.osgi.consumers.impl.log.LoggingMetricsListener +org.apache.felix.metrics.osgi.consumers.impl.json.JsonWritingMetricsListenerTest +org.apache.felix.mishell.Activator +org.apache.felix.mishell.console.Command +org.apache.felix.mishell.console.Commander +org.apache.felix.mishell.console.CommandNotFoundException +org.apache.felix.mishell.console.Console +org.apache.felix.mishell.EngineNotFoundException +org.apache.felix.mishell.JMoodProxyManager +org.apache.felix.mishell.JMXEngineContext +org.apache.felix.mishell.OSGiScriptEngine +org.apache.felix.mishell.OSGiScriptEngineFactory +org.apache.felix.mishell.OSGiScriptEngineManager +org.apache.felix.mishell.OSGiScriptEngineFinder +org.apache.felix.mishell.TypeTest +org.apache.felix.mosgi.console.component.Activator +org.apache.felix.mosgi.console.component.JtableCellRenderer +org.apache.felix.mosgi.console.component.JtreeCellRenderer +org.apache.felix.mosgi.console.component.MyTree +org.apache.felix.mosgi.console.component.RemoteLogger_jtable +org.apache.felix.mosgi.console.component.RemoteLogger_jtree +org.apache.felix.mosgi.console.gui.Activator +org.apache.felix.mosgi.console.gui.CommonPanel +org.apache.felix.mosgi.console.gui.Gateway +org.apache.felix.mosgi.console.gui.NodeCellRenderer +org.apache.felix.mosgi.console.gui.NodePanel +org.apache.felix.mosgi.console.gui.NodesTree +org.apache.felix.mosgi.console.ifc.CommonPlugin +org.apache.felix.mosgi.console.ifc.Plugin +org.apache.felix.mosgi.console.ifc.TabIfc +org.apache.felix.mosgi.jmx.agent.AgentActivator +org.apache.felix.mosgi.jmx.agent.Constants +org.apache.felix.mosgi.jmx.agent.mx4j.AbstractDynamicMBean +org.apache.felix.mosgi.jmx.agent.mx4j.ImplementationException +org.apache.felix.mosgi.jmx.agent.mx4j.loading.ClassLoaderObjectInputStream +org.apache.felix.mosgi.jmx.agent.mx4j.loading.MLetParseException +org.apache.felix.mosgi.jmx.agent.mx4j.loading.MLetParser +org.apache.felix.mosgi.jmx.agent.mx4j.loading.MLetTag +org.apache.felix.mosgi.jmx.agent.mx4j.log.Log +org.apache.felix.mosgi.jmx.agent.mx4j.log.Logger +org.apache.felix.mosgi.jmx.agent.mx4j.MBeanDescription +org.apache.felix.mosgi.jmx.agent.mx4j.MBeanDescriptionAdapter +org.apache.felix.mosgi.jmx.agent.mx4j.MX4JSystemKeys +org.apache.felix.mosgi.jmx.agent.mx4j.server.DefaultClassLoaderRepository +org.apache.felix.mosgi.jmx.agent.mx4j.server.DefaultMBeanRepository +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.ContextClassLoaderMBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.DefaultMBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.DefaultMBeanServerInterceptorMBean +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.InvokerMBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.InvokerMBeanServerInterceptorMBean +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.MBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.MBeanServerInterceptorConfigurator +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.MBeanServerInterceptorConfiguratorMBean +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.NotificationListenerMBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.SecurityMBeanServerInterceptor +org.apache.felix.mosgi.jmx.agent.mx4j.server.interceptor.SecurityMBeanServerInterceptorMBean +org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanIntrospector +org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanInvoker +org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanMetaData +org.apache.felix.mosgi.jmx.agent.mx4j.server.MBeanRepository +org.apache.felix.mosgi.jmx.agent.mx4j.server.ModifiableClassLoaderRepository +org.apache.felix.mosgi.jmx.agent.mx4j.server.MX4JMBeanServer +org.apache.felix.mosgi.jmx.agent.mx4j.server.MX4JMBeanServerBuilder +org.apache.felix.mosgi.jmx.agent.mx4j.server.MX4JMBeanServerDelegate +org.apache.felix.mosgi.jmx.agent.mx4j.server.ReflectedMBeanInvoker +org.apache.felix.mosgi.jmx.agent.mx4j.util.Base64Codec +org.apache.felix.mosgi.jmx.agent.mx4j.util.MethodTernaryTree +org.apache.felix.mosgi.jmx.agent.mx4j.util.Utils +org.apache.felix.mosgi.jmx.httpconnector.HttpConnectorActivator +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.AdaptorServerSocketFactory +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.CommandProcessorUtil +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ConstructorsCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.CreateMBeanCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.DefaultProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.DeleteMBeanCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.EmptyCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.GetAttributeCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpAdaptor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpAdaptorMBean +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpCommandProcessorAdaptor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpConstants +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpException +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpInputStream +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpOutputStream +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.HttpUtil +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.InvokeOperationCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.MBeanCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ProcessorMBean +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ServerByDomainCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.ServerCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.SetAttributeCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.SetAttributesCommandProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.XSLTProcessor +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.http.XSLTProcessorMBean +org.apache.felix.mosgi.jmx.httpconnector.mx4j.tools.adaptor.PlainAdaptorServerSocketFactory +org.apache.felix.mosgi.jmx.registry.mx4j.tools.naming.NamingService +org.apache.felix.mosgi.jmx.registry.mx4j.tools.naming.NamingServiceIfc +org.apache.felix.mosgi.jmx.registry.mx4j.tools.naming.NamingServiceMBean +org.apache.felix.mosgi.jmx.remotelogger.Logger +org.apache.felix.mosgi.jmx.remotelogger.LoggerMBean +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.AbstractHeartBeat +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ClientProxy +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ConnectionNotificationEmitter +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ConnectionResolver +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.DefaultRemoteNotificationServerHandler +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.HeartBeat +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteConstants +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.MX4JRemoteUtils +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.NotificationTuple +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.rmi.ServerProvider +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.RMIClientProvider +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.provider.RMIServerProvider +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ProviderFactory +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.ProviderHelper +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.RemoteNotificationClientHandler +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.RemoteNotificationServerHandler +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.resolver.rmi.RMIResolver +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.ClientExceptionCatcher +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.ClientInvoker +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.ClientUnmarshaller +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIConnectionInvoker +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIConnectionProxy +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIConnectionSubjectInvoker +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIHeartBeat +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIMarshaller +org.apache.felix.mosgi.jmx.rmiconnector.mx4j.remote.rmi.RMIRemoteNotificationServerHandler +org.apache.felix.mosgi.jmx.rmiconnector.RmiConnectorActivator +org.apache.felix.mosgi.managedelements.bundlesprobes.BundlesProbes +org.apache.felix.mosgi.managedelements.bundlesprobes.BundlesProbesMBean +org.apache.felix.mosgi.managedelements.bundlesprobes.tab.BundlesProbesModelTabUI +org.apache.felix.mosgi.managedelements.bundlesprobes.tab.BundlesProbesTabUI +org.apache.felix.mosgi.managedelements.memoryprobe.MemoryProbe +org.apache.felix.mosgi.managedelements.memoryprobe.MemoryProbeMBean +org.apache.felix.mosgi.managedelements.obrprobe.ObrProbe +org.apache.felix.mosgi.managedelements.obrprobe.ObrProbeMBean +org.apache.felix.mosgi.managedelements.obrprobe.tab.ObrProbeTabUI +org.apache.felix.mosgi.managedelements.osgiprobes.OsgiProbes +org.apache.felix.mosgi.managedelements.osgiprobes.OsgiProbesMBean +org.apache.felix.mosgi.managedelements.osgiprobes.tab.OsgiProbesTabUI +org.apache.felix.daemon.FelixLayout +org.apache.felix.daemon.Service +org.osgi.service.obr.Capability +org.osgi.service.obr.CapabilityProvider +org.osgi.service.obr.InterruptedResolutionException +org.osgi.service.obr.Repository +org.osgi.service.obr.RepositoryAdmin +org.osgi.service.obr.RepositoryPermission +org.osgi.service.obr.Requirement +org.osgi.service.obr.Resolver +org.osgi.service.obr.Resource +org.apache.felix.prefs.BackingStore +org.apache.felix.prefs.BackingStoreManager +org.apache.felix.prefs.ChangeSet +org.apache.felix.prefs.impl.DataFileBackingStoreImpl +org.apache.felix.prefs.impl.PreferencesManager +org.apache.felix.prefs.impl.PreferencesServiceImpl +org.apache.felix.prefs.impl.StreamBackingStoreImpl +org.apache.felix.prefs.package-info +org.apache.felix.prefs.PreferencesDescription +org.apache.felix.prefs.PreferencesImpl +org.apache.felix.prefs.PreferencesImplTest +org.apache.felix.resolver.Activator +org.apache.felix.resolver.Candidates +org.apache.felix.resolver.Logger +org.apache.felix.resolver.reason.ReasonException +org.apache.felix.resolver.ResolutionError +org.apache.felix.resolver.ResolverImpl +org.apache.felix.resolver.SimpleHostedCapability +org.apache.felix.resolver.util.ArrayMap +org.apache.felix.resolver.util.CandidateSelector +org.apache.felix.resolver.util.CopyOnWriteSet +org.apache.felix.resolver.util.OpenHashMap +org.apache.felix.resolver.util.OpenHashMapList +org.apache.felix.resolver.util.OpenHashMapSet +org.apache.felix.resolver.util.ShadowList +org.apache.felix.resolver.Util +org.apache.felix.resolver.WireImpl +org.apache.felix.resolver.WrappedCapability +org.apache.felix.resolver.WrappedRequirement +org.apache.felix.resolver.WrappedResource +org.osgi.service.resolver.HostedCapability +org.osgi.service.resolver.package-info +org.osgi.service.resolver.ResolutionException +org.osgi.service.resolver.ResolveContext +org.osgi.service.resolver.Resolver +org.apache.felix.resolver.test.BigResolutionTest +org.apache.felix.resolver.test.FELIX_4914_Test +org.apache.felix.resolver.test.Main +org.apache.felix.resolver.test.ResolverTest +org.apache.felix.resolver.test.util.BundleCapability +org.apache.felix.resolver.test.util.BundleRequirement +org.apache.felix.resolver.test.util.CandidateComparator +org.apache.felix.resolver.test.util.CapabilitySet +org.apache.felix.resolver.test.util.ClauseParser +org.apache.felix.resolver.test.util.GenericCapability +org.apache.felix.resolver.test.util.GenericRequirement +org.apache.felix.resolver.test.util.IdentityCapability +org.apache.felix.resolver.test.util.IterativeResolver +org.apache.felix.resolver.test.util.JsonReader +org.apache.felix.resolver.test.util.PackageCapability +org.apache.felix.resolver.test.util.PackageRequirement +org.apache.felix.resolver.test.util.ResolveContextImpl +org.apache.felix.resolver.test.util.ResourceImpl +org.apache.felix.resolver.test.util.SimpleFilter +org.apache.felix.rootcause.DSComp +org.apache.felix.rootcause.DSRef +org.apache.felix.rootcause.DSRootCause +org.apache.felix.rootcause.package-info +org.apache.felix.rootcause.RootCauseCommand +org.apache.felix.rootcause.RootCausePrinter +org.apache.felix.rootcause.DSRootCauseTest +org.apache.felix.rootcause.examples.CompWithCyclicRef +org.apache.felix.rootcause.examples.CompWithMissingConfig +org.apache.felix.rootcause.examples.CompWithMissingRef +org.apache.felix.rootcause.examples.CompWithMissingRef2 +org.apache.felix.rootcause.examples.CompWithoutService +org.apache.felix.rootcause.util.BaseTest +org.apache.felix.rootcause.util.BndDSOptions +org.apache.felix.scr.component.ExtComponentContext +org.apache.felix.scr.component.ExtFactoryComponentInstance +org.apache.felix.scr.impl.AbstractExtender +org.apache.felix.scr.impl.Activator +org.apache.felix.scr.impl.BundleComponentActivator +org.apache.felix.scr.impl.ComponentActorThread +org.apache.felix.scr.impl.ComponentCommands +org.apache.felix.scr.impl.ComponentCommandsScrInfo +org.apache.felix.scr.impl.ComponentRegistry +org.apache.felix.scr.impl.ComponentRegistryKey +org.apache.felix.scr.impl.config.ScrConfigurationImpl +org.apache.felix.scr.impl.config.ScrManagedService +org.apache.felix.scr.impl.config.ScrManagedServiceServiceFactory +org.apache.felix.scr.impl.config.ScrMetaTypeProvider +org.apache.felix.scr.impl.config.ScrMetaTypeProviderServiceFactory +org.apache.felix.scr.impl.helper.Coercions +org.apache.felix.scr.impl.helper.ComponentServiceObjectsHelper +org.apache.felix.scr.impl.helper.ConfigAdminTracker +org.apache.felix.scr.impl.helper.ReadOnlyDictionary +org.apache.felix.scr.impl.inject.ActivatorParameter +org.apache.felix.scr.impl.inject.BaseParameter +org.apache.felix.scr.impl.inject.BindParameters +org.apache.felix.scr.impl.inject.ComponentConstructor +org.apache.felix.scr.impl.inject.ComponentMethods +org.apache.felix.scr.impl.inject.field.FieldHandler +org.apache.felix.scr.impl.inject.field.FieldMethods +org.apache.felix.scr.impl.inject.field.FieldUtils +org.apache.felix.scr.impl.inject.InitReferenceMethod +org.apache.felix.scr.impl.inject.internal.Annotations +org.apache.felix.scr.impl.inject.internal.ClassUtils +org.apache.felix.scr.impl.inject.internal.ComponentConstructorImpl +org.apache.felix.scr.impl.inject.internal.ComponentMethodsImpl +org.apache.felix.scr.impl.inject.internal.DuplexReferenceMethods +org.apache.felix.scr.impl.inject.LifecycleMethod +org.apache.felix.scr.impl.inject.MethodResult +org.apache.felix.scr.impl.inject.methods.ActivateMethod +org.apache.felix.scr.impl.inject.methods.BaseMethod +org.apache.felix.scr.impl.inject.methods.BindMethod +org.apache.felix.scr.impl.inject.methods.BindMethods +org.apache.felix.scr.impl.inject.methods.DeactivateMethod +org.apache.felix.scr.impl.inject.methods.ModifiedMethod +org.apache.felix.scr.impl.inject.methods.SuitableMethodNotAccessibleException +org.apache.felix.scr.impl.inject.methods.UnbindMethod +org.apache.felix.scr.impl.inject.methods.UpdatedMethod +org.apache.felix.scr.impl.inject.OpenStatus +org.apache.felix.scr.impl.inject.ReferenceMethod +org.apache.felix.scr.impl.inject.ReferenceMethods +org.apache.felix.scr.impl.inject.RefPair +org.apache.felix.scr.impl.inject.ScrComponentContext +org.apache.felix.scr.impl.inject.ValueUtils +org.apache.felix.scr.impl.logger.BundleLogger +org.apache.felix.scr.impl.logger.ComponentLogger +org.apache.felix.scr.impl.logger.ExtLogManager +org.apache.felix.scr.impl.logger.InternalLogger +org.apache.felix.scr.impl.logger.LogConfiguration +org.apache.felix.scr.impl.logger.LogManager +org.apache.felix.scr.impl.logger.NoOpLogger +org.apache.felix.scr.impl.logger.ScrLogger +org.apache.felix.scr.impl.logger.ScrLoggerFactory +org.apache.felix.scr.impl.logger.ScrLogManager +org.apache.felix.scr.impl.manager.AbstractComponentManager +org.apache.felix.scr.impl.manager.AbstractPrototypeRefPair +org.apache.felix.scr.impl.manager.ComponentActivator +org.apache.felix.scr.impl.manager.ComponentContainer +org.apache.felix.scr.impl.manager.ComponentContextImpl +org.apache.felix.scr.impl.manager.ComponentFactoryImpl +org.apache.felix.scr.impl.manager.ComponentHolder +org.apache.felix.scr.impl.manager.ComponentManager +org.apache.felix.scr.impl.manager.ConfigurableComponentHolder +org.apache.felix.scr.impl.manager.DependencyManager +org.apache.felix.scr.impl.manager.EdgeInfo +org.apache.felix.scr.impl.manager.ExtendedServiceEvent +org.apache.felix.scr.impl.manager.ExtendedServiceListener +org.apache.felix.scr.impl.manager.ExtendedServiceListenerContext +org.apache.felix.scr.impl.manager.MultiplePrototypeRefPair +org.apache.felix.scr.impl.manager.PrototypeServiceFactoryComponentManager +org.apache.felix.scr.impl.manager.ReferenceManager +org.apache.felix.scr.impl.manager.RegionConfigurationSupport +org.apache.felix.scr.impl.manager.RegistrationManager +org.apache.felix.scr.impl.manager.ScrConfiguration +org.apache.felix.scr.impl.manager.ServiceFactoryComponentManager +org.apache.felix.scr.impl.manager.ServiceTracker +org.apache.felix.scr.impl.manager.ServiceTrackerCustomizer +org.apache.felix.scr.impl.manager.SingleComponentManager +org.apache.felix.scr.impl.manager.SinglePrototypeRefPair +org.apache.felix.scr.impl.manager.SingleRefPair +org.apache.felix.scr.impl.manager.ThreadDump +org.apache.felix.scr.impl.metadata.ComponentMetadata +org.apache.felix.scr.impl.metadata.DSVersion +org.apache.felix.scr.impl.metadata.MetadataStoreHelper +org.apache.felix.scr.impl.metadata.PropertyMetadata +org.apache.felix.scr.impl.metadata.ReferenceMetadata +org.apache.felix.scr.impl.metadata.ServiceMetadata +org.apache.felix.scr.impl.metadata.TargetedPID +org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImpl +org.apache.felix.scr.impl.xml.XmlConstants +org.apache.felix.scr.impl.xml.XmlHandler +org.apache.felix.scr.info.ScrInfo +DefaultPackageClass +org.apache.felix.scr.impl.BundleComponentActivatorTest +org.apache.felix.scr.impl.ComponentRegistryKeyTest +org.apache.felix.scr.impl.inject.internal.AnnotationTest +org.apache.felix.scr.impl.inject.methods.ActivateMethodTest +org.apache.felix.scr.impl.inject.methods.BindMethodTest +org.apache.felix.scr.impl.logger.LoggerTest +org.apache.felix.scr.impl.logger.LogService +org.apache.felix.scr.impl.logger.MockBundleLogger +org.apache.felix.scr.impl.logger.MockComponentLogger +org.apache.felix.scr.impl.logger.MockScrLogger +org.apache.felix.scr.impl.manager.AbstractComponentManagerTest +org.apache.felix.scr.impl.manager.components.FakeService +org.apache.felix.scr.impl.manager.components.SuperFakeService +org.apache.felix.scr.impl.manager.components.T1 +org.apache.felix.scr.impl.manager.components.T1a +org.apache.felix.scr.impl.manager.components.T1MapSR +org.apache.felix.scr.impl.manager.components.T3 +org.apache.felix.scr.impl.manager.components2.T2 +org.apache.felix.scr.impl.manager.ConfigurationSupportTest +org.apache.felix.scr.impl.manager.ConfiguredComponentHolderTest +org.apache.felix.scr.impl.manager.RegistrationManagerTest +org.apache.felix.scr.impl.manager.SingleComponentManagerTest +org.apache.felix.scr.impl.metadata.ComponentBase +org.apache.felix.scr.impl.metadata.ComponentMetaDataCacheTest +org.apache.felix.scr.impl.metadata.ComponentMetadataTest +org.apache.felix.scr.impl.metadata.instances.AcceptMethod +org.apache.felix.scr.impl.metadata.instances.BaseObject +org.apache.felix.scr.impl.metadata.instances.Level1Object +org.apache.felix.scr.impl.metadata.instances.Level3Object +org.apache.felix.scr.impl.metadata.instances2.Level2Object +org.apache.felix.scr.impl.metadata.XmlHandlerTest +org.apache.felix.scr.impl.MockBundle +org.apache.felix.scr.impl.MockBundleContext +org.apache.felix.scr.impl.PackageIsolationTest +org.apache.felix.scr.impl.runtime.ServiceComponentRuntimeImplTest +org.apache.felix.scr.impl.xml.XmlHandlerTest +org.apache.felix.scr.integration.ActivateSignatureTest +org.apache.felix.scr.integration.AnnoConfigTest +org.apache.felix.scr.integration.CircularFactoryTest +org.apache.felix.scr.integration.CircularReferenceTest +org.apache.felix.scr.integration.ComponentActivationTest +org.apache.felix.scr.integration.ComponentConcurrencyTest +org.apache.felix.scr.integration.ComponentConfigurationPidTest +org.apache.felix.scr.integration.ComponentConfigurationTest +org.apache.felix.scr.integration.ComponentConstructorTest +org.apache.felix.scr.integration.ComponentDisposeTest +org.apache.felix.scr.integration.ComponentEnableTest +org.apache.felix.scr.integration.ComponentFactoryTest +org.apache.felix.scr.integration.ComponentFieldActivationTest +org.apache.felix.scr.integration.ComponentMetaDataCacheIntegrationTest +org.apache.felix.scr.integration.components.activatesignature.AbstractActivateSignatureTestComponent +org.apache.felix.scr.integration.components.activatesignature.Signature_Package_BundleContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Package_ComponentContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Package_Map +org.apache.felix.scr.integration.components.activatesignature.Signature_Private_BundleContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Private_ComponentContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Private_Map +org.apache.felix.scr.integration.components.activatesignature.Signature_Protected_BundleContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Protected_ComponentContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Protected_Map +org.apache.felix.scr.integration.components.activatesignature.Signature_Public_BundleContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Public_ComponentContext +org.apache.felix.scr.integration.components.activatesignature.Signature_Public_Map +org.apache.felix.scr.integration.components.ActivatorComponent +org.apache.felix.scr.integration.components.annoconfig.AnnoComponent +org.apache.felix.scr.integration.components.annoconfig.NestedAnnoComponent +org.apache.felix.scr.integration.components.circular.A +org.apache.felix.scr.integration.components.circular.B +org.apache.felix.scr.integration.components.circularFactory.FactoryClient +org.apache.felix.scr.integration.components.circularFactory.FactoryInstance +org.apache.felix.scr.integration.components.concurrency.A +org.apache.felix.scr.integration.components.concurrency.AFactory +org.apache.felix.scr.integration.components.concurrency.B +org.apache.felix.scr.integration.components.concurrency.C +org.apache.felix.scr.integration.components.concurrency.CFactory +org.apache.felix.scr.integration.components.ConstructorComponent +org.apache.felix.scr.integration.components.ConstructorMultiReference +org.apache.felix.scr.integration.components.ConstructorSingleReference +org.apache.felix.scr.integration.components.deadlock.Consumer +org.apache.felix.scr.integration.components.deadlock.TestComponent +org.apache.felix.scr.integration.components.EnableComponent +org.apache.felix.scr.integration.components.felix3680.A +org.apache.felix.scr.integration.components.felix3680.B +org.apache.felix.scr.integration.components.felix3680.C +org.apache.felix.scr.integration.components.felix3680.D +org.apache.felix.scr.integration.components.felix3680.E +org.apache.felix.scr.integration.components.felix3680.F +org.apache.felix.scr.integration.components.felix3680.G +org.apache.felix.scr.integration.components.felix3680.H +org.apache.felix.scr.integration.components.felix3680.I +org.apache.felix.scr.integration.components.felix3680.J +org.apache.felix.scr.integration.components.felix3680.K +org.apache.felix.scr.integration.components.felix3680.Main +org.apache.felix.scr.integration.components.felix3680_2.A +org.apache.felix.scr.integration.components.felix3680_2.B +org.apache.felix.scr.integration.components.felix3680_2.C +org.apache.felix.scr.integration.components.felix3680_2.D +org.apache.felix.scr.integration.components.felix3680_2.E +org.apache.felix.scr.integration.components.felix3680_2.F +org.apache.felix.scr.integration.components.felix3680_2.G +org.apache.felix.scr.integration.components.felix3680_2.H +org.apache.felix.scr.integration.components.felix3680_2.I +org.apache.felix.scr.integration.components.felix3680_2.J +org.apache.felix.scr.integration.components.felix3680_2.K +org.apache.felix.scr.integration.components.felix3680_2.Main +org.apache.felix.scr.integration.components.felix4188.Felix4188Component +org.apache.felix.scr.integration.components.Felix4188Component +org.apache.felix.scr.integration.components.Felix4350Component +org.apache.felix.scr.integration.components.felix4984.A +org.apache.felix.scr.integration.components.felix4984.B +org.apache.felix.scr.integration.components.felix5248.Component +org.apache.felix.scr.integration.components.felix5276.A +org.apache.felix.scr.integration.components.felix5276.B +org.apache.felix.scr.integration.components.felix5276.C +org.apache.felix.scr.integration.components.felix6274_1.Component +org.apache.felix.scr.integration.components.felix6274_2.Component +org.apache.felix.scr.integration.components.felix6274_hook.Activator +org.apache.felix.scr.integration.components.FieldActivatorComponent +org.apache.felix.scr.integration.components.metadata.cache.Activator +org.apache.felix.scr.integration.components.metadata.cache.SimpleService +org.apache.felix.scr.integration.components.metadata.cache.SimpleServiceImpl +org.apache.felix.scr.integration.components.MutatingService +org.apache.felix.scr.integration.components.MutatingServiceConsumer +org.apache.felix.scr.integration.components.MutatingServiceImpl +org.apache.felix.scr.integration.components.SimpleComponent +org.apache.felix.scr.integration.components.SimpleComponent2 +org.apache.felix.scr.integration.components.SimpleService +org.apache.felix.scr.integration.components.SimpleService2 +org.apache.felix.scr.integration.components.SimpleService2Impl +org.apache.felix.scr.integration.components.SimpleServiceImpl +org.apache.felix.scr.integration.ComponentTestBase +org.apache.felix.scr.integration.ConfigurationChangeTest +org.apache.felix.scr.integration.ConfigurationComponentFactoryTest +org.apache.felix.scr.integration.ExtenderTest +org.apache.felix.scr.integration.Felix3680Test +org.apache.felix.scr.integration.Felix3680_2Test +org.apache.felix.scr.integration.Felix4188Test +org.apache.felix.scr.integration.Felix4350Test +org.apache.felix.scr.integration.Felix4984Test +org.apache.felix.scr.integration.Felix5248Test +org.apache.felix.scr.integration.Felix5276Test +org.apache.felix.scr.integration.Felix5356Test +org.apache.felix.scr.integration.Felix6161Test +org.apache.felix.scr.integration.Felix6274Test +org.apache.felix.scr.integration.Felix6325OptionalLogTest +org.apache.felix.scr.integration.LocateTest +org.apache.felix.scr.integration.LocationTest +org.apache.felix.scr.integration.MinimumCardinalityTest +org.apache.felix.scr.integration.MutablePropertiesTest +org.apache.felix.scr.integration.PersistentComponentFactoryTest +org.apache.felix.scr.integration.ServiceBindGreedyTest +org.apache.felix.scr.integration.ServiceBindTest +org.apache.felix.scr.integration.ServiceChangedTest +org.apache.felix.scr.integration.ServiceComponentTest +org.apache.felix.scr.integration.TargetedPIDTest +org.apache.felix.scr.integration.TargetPropertyTest +org.apache.felix.scr.Component +org.apache.felix.scr.impl.compat.Activator +org.apache.felix.scr.impl.compat.ScrCommand +org.apache.felix.scr.impl.compat.ScrServiceImpl +org.apache.felix.scr.Reference +org.apache.felix.scr.ScrInfo +org.apache.felix.scr.ScrService +org.apache.felix.scr.ext.annotation.DSExt +org.apache.felix.scr.ext.annotation.package-info +org.apache.felix.shell.CdCommand +org.apache.felix.shell.Command +org.apache.felix.shell.impl.Activator +org.apache.felix.shell.impl.BundleLevelCommandImpl +org.apache.felix.shell.impl.CdCommandImpl +org.apache.felix.shell.impl.FindCommandImpl +org.apache.felix.shell.impl.HeadersCommandImpl +org.apache.felix.shell.impl.HelpCommandImpl +org.apache.felix.shell.impl.InspectCommandImpl +org.apache.felix.shell.impl.InstallCommandImpl +org.apache.felix.shell.impl.LogCommandImpl +org.apache.felix.shell.impl.LogOptions +org.apache.felix.shell.impl.PsCommandImpl +org.apache.felix.shell.impl.RefreshCommandImpl +org.apache.felix.shell.impl.ResolveCommandImpl +org.apache.felix.shell.impl.ShutdownCommandImpl +org.apache.felix.shell.impl.StartCommandImpl +org.apache.felix.shell.impl.StartLevelCommandImpl +org.apache.felix.shell.impl.StopCommandImpl +org.apache.felix.shell.impl.SystemPropertiesCommandImpl +org.apache.felix.shell.impl.UninstallCommandImpl +org.apache.felix.shell.impl.UpdateCommandImpl +org.apache.felix.shell.impl.Util +org.apache.felix.shell.impl.VersionCommandImpl +org.apache.felix.shell.ShellService +org.ungoverned.osgi.service.shell.CdCommand +org.ungoverned.osgi.service.shell.Command +org.ungoverned.osgi.service.shell.ShellService +org.apache.felix.shell.impl.LogOptionsTest +org.apache.felix.shell.impl.SystemPropertiesTest +org.apache.felix.shell.gui.impl.Activator +org.apache.felix.shell.gui.impl.ShellPanel +org.apache.felix.shell.gui.Plugin +org.apache.felix.shell.gui.plugin.Activator +org.apache.felix.shell.gui.plugin.BundleListPlugin +org.apache.felix.shell.gui.plugin.OBRPlugin +org.apache.felix.shell.gui.plugin.OutputAreaStream +org.apache.felix.shell.gui.plugin.ScrollableOutputArea +org.apache.felix.shell.gui.plugin.ShellPlugin +org.apache.felix.shell.remote.Activator +org.apache.felix.shell.remote.AtomicInteger +org.apache.felix.shell.remote.Listener +org.apache.felix.shell.remote.ReentrantLock +org.apache.felix.shell.remote.ServiceMediator +org.apache.felix.shell.remote.Shell +org.apache.felix.shell.remote.TerminalPrintStream +org.apache.felix.shell.remote.TerminalReader +org.apache.felix.shell.tui.Activator +org.apache.felix.systemready.CheckStatus +org.apache.felix.systemready.impl.ComponentsCheck +org.apache.felix.systemready.impl.FrameworkStartCheck +org.apache.felix.systemready.impl.ServicesCheck +org.apache.felix.systemready.impl.servlet.StatusReporter +org.apache.felix.systemready.impl.servlet.StatusWriterJson +org.apache.felix.systemready.impl.servlet.SystemAliveServlet +org.apache.felix.systemready.impl.servlet.SystemReadyServlet +org.apache.felix.systemready.impl.SystemReadyMonitorImpl +org.apache.felix.systemready.impl.Tracker +org.apache.felix.systemready.package-info +org.apache.felix.systemready.StateType +org.apache.felix.systemready.SystemReady +org.apache.felix.systemready.SystemReadyCheck +org.apache.felix.systemready.SystemReadyMonitor +org.apache.felix.systemready.SystemStatus +org.apache.felix.systemready.osgi.ComponentsCheckTest +org.apache.felix.systemready.osgi.examples.CompWithoutService +org.apache.felix.systemready.osgi.examples.CompWithoutService2 +org.apache.felix.systemready.osgi.examples.TestSystemReadyCheck +org.apache.felix.systemready.osgi.FrameworkStartTestGreen +org.apache.felix.systemready.osgi.FrameworkStartTestYellow +org.apache.felix.systemready.osgi.ServicesCheckTest +org.apache.felix.systemready.osgi.ServletTest +org.apache.felix.systemready.osgi.SystemReadyMonitorTest +org.apache.felix.systemready.osgi.util.BaseTest +org.apache.felix.systemready.osgi.util.BndDSOptions +org.apache.felix.systemready.StateTest +org.apache.felix.threaddump.internal.Jdk14ThreadDumper +org.apache.felix.threaddump.internal.jdk5.Jdk15ThreadDumper +org.apache.felix.threaddump.internal.jdk5.ThreadStateConverter +org.apache.felix.threaddump.internal.jdk6.Jdk16ThreadDumper +org.apache.felix.threaddump.internal.ThreadDumpActivator +org.apache.felix.threaddump.internal.ThreadDumper +org.apache.felix.threaddump.internal.ThreadDumpInventoryPrinter +org.apache.felix.threaddump.internal.ThreadWriter +org.apache.felix.test.Dummy +org.apache.felix.bundleplugin.AbstractDependencyFilter +org.apache.felix.bundleplugin.AntPlugin +org.apache.felix.bundleplugin.baseline.AbstractBaselinePlugin +org.apache.felix.bundleplugin.baseline.BaselinePlugin +org.apache.felix.bundleplugin.baseline.BaselineReport +org.apache.felix.bundleplugin.baseline.DiffMessage +org.apache.felix.bundleplugin.baseline.InfoComparator +org.apache.felix.bundleplugin.BlueprintPlugin +org.apache.felix.bundleplugin.BundleAllPlugin +org.apache.felix.bundleplugin.BundleInfo +org.apache.felix.bundleplugin.BundlePlugin +org.apache.felix.bundleplugin.DependencyEmbedder +org.apache.felix.bundleplugin.DependencyExcluder +org.apache.felix.bundleplugin.InstructionsPlugin +org.apache.felix.bundleplugin.JarPluginConfiguration +org.apache.felix.bundleplugin.JpaPlugin +org.apache.felix.bundleplugin.ManifestPlugin +org.apache.felix.bundleplugin.ManifestWriter +org.apache.felix.bundleplugin.RelativizePath +org.apache.felix.bundleplugin.ScrPlugin +org.apache.felix.bundleplugin.VerifyBundlePlugin +org.apache.felix.bundleplugin.VersionCleanerPlugin +org.apache.felix.bundleplugin.WrapPlugin +org.apache.felix.obrplugin.AbstractFileMojo +org.apache.felix.obrplugin.Config +org.apache.felix.obrplugin.ObrCleanRepo +org.apache.felix.obrplugin.ObrDeploy +org.apache.felix.obrplugin.ObrDeployFile +org.apache.felix.obrplugin.ObrIndex +org.apache.felix.obrplugin.ObrInstall +org.apache.felix.obrplugin.ObrInstallFile +org.apache.felix.obrplugin.ObrRemoteClean +org.apache.felix.obrplugin.ObrUpdate +org.apache.felix.obrplugin.ObrUtils +org.apache.felix.obrplugin.PomHelper +org.apache.felix.obrplugin.RemoteFileManager +org.apache.felix.obrplugin.XmlHelper +org.apache.maven.shared.osgi.DefaultMaven2OsgiConverter +org.apache.maven.shared.osgi.ManifestReadingException +org.apache.maven.shared.osgi.Maven2OsgiConverter +org.apache.felix.bundleplugin.AbstractBundlePluginTest +org.apache.felix.bundleplugin.ArtifactStubFactory +org.apache.felix.bundleplugin.BlueprintComponentTest +org.apache.felix.bundleplugin.BundleAllPluginTest +org.apache.felix.bundleplugin.BundlePluginTest +org.apache.felix.bundleplugin.JpaPluginTest +org.apache.felix.bundleplugin.ManifestWriterTest +org.apache.maven.shared.osgi.Maven2OsgiConverterTest +org.apache.felix.scr.SimpleDSComponent +org.apache.felix.scr.SimpleDSComponent +org.apache.felix.scrplugin.mojo.MavenLog +org.apache.felix.scrplugin.mojo.MavenProjectScanner +org.apache.felix.scrplugin.mojo.SCRDescriptorMojo +org.apache.felix.scr.annotations.Activate +org.apache.felix.scr.annotations.AutoDetect +org.apache.felix.scr.annotations.Component +org.apache.felix.scr.annotations.ConfigurationPolicy +org.apache.felix.scr.annotations.Deactivate +org.apache.felix.scr.annotations.Modified +org.apache.felix.scr.annotations.package-info +org.apache.felix.scr.annotations.Properties +org.apache.felix.scr.annotations.Property +org.apache.felix.scr.annotations.PropertyOption +org.apache.felix.scr.annotations.PropertyUnbounded +org.apache.felix.scr.annotations.Reference +org.apache.felix.scr.annotations.ReferenceCardinality +org.apache.felix.scr.annotations.ReferencePolicy +org.apache.felix.scr.annotations.ReferencePolicyOption +org.apache.felix.scr.annotations.References +org.apache.felix.scr.annotations.ReferenceStrategy +org.apache.felix.scr.annotations.Service +org.apache.felix.scr.annotations.Services +org.apache.felix.scr.annotations.sling.SlingFilter +org.apache.felix.scr.annotations.sling.SlingFilterScope +org.apache.felix.scr.annotations.sling.SlingServlet +org.apache.felix.scrplugin.processing.SCRAnnotationProcessor +org.apache.felix.scrplugin.processing.SlingAnnotationProcessor +org.apache.felix.scrplugin.ant.AntLog +org.apache.felix.scrplugin.ant.SCRDescriptorTask +org.apache.felix.scrplugin.bnd.BndLog +org.apache.felix.scrplugin.bnd.SCRDescriptorBndPlugin +org.apache.felix.scrplugin.ds.DSAnnotationProcessor +org.apache.felix.scrplugin.annotations.AnnotationProcessor +org.apache.felix.scrplugin.annotations.ClassAnnotation +org.apache.felix.scrplugin.annotations.FieldAnnotation +org.apache.felix.scrplugin.annotations.MethodAnnotation +org.apache.felix.scrplugin.annotations.ScannedAnnotation +org.apache.felix.scrplugin.annotations.ScannedClass +org.apache.felix.scrplugin.description.AbstractDescription +org.apache.felix.scrplugin.description.ClassDescription +org.apache.felix.scrplugin.description.ComponentConfigurationPolicy +org.apache.felix.scrplugin.description.ComponentDescription +org.apache.felix.scrplugin.description.PropertyDescription +org.apache.felix.scrplugin.description.PropertyType +org.apache.felix.scrplugin.description.PropertyUnbounded +org.apache.felix.scrplugin.description.ReferenceCardinality +org.apache.felix.scrplugin.description.ReferenceDescription +org.apache.felix.scrplugin.description.ReferencePolicy +org.apache.felix.scrplugin.description.ReferencePolicyOption +org.apache.felix.scrplugin.description.ReferenceStrategy +org.apache.felix.scrplugin.description.ServiceDescription +org.apache.felix.scrplugin.helper.AnnotationProcessorManager +org.apache.felix.scrplugin.helper.ClassModifier +org.apache.felix.scrplugin.helper.ClassScanner +org.apache.felix.scrplugin.helper.ComponentContainer +org.apache.felix.scrplugin.helper.ComponentContainerUtil +org.apache.felix.scrplugin.helper.DescriptionContainer +org.apache.felix.scrplugin.helper.IssueLog +org.apache.felix.scrplugin.helper.MetatypeAttributeDefinition +org.apache.felix.scrplugin.helper.MetatypeContainer +org.apache.felix.scrplugin.helper.StringUtils +org.apache.felix.scrplugin.helper.Validator +org.apache.felix.scrplugin.Log +org.apache.felix.scrplugin.Options +org.apache.felix.scrplugin.Project +org.apache.felix.scrplugin.Result +org.apache.felix.scrplugin.SCRDescriptorException +org.apache.felix.scrplugin.SCRDescriptorFailureException +org.apache.felix.scrplugin.SCRDescriptorGenerator +org.apache.felix.scrplugin.Source +org.apache.felix.scrplugin.SpecVersion +org.apache.felix.scrplugin.xml.ComponentDescriptorIO +org.apache.felix.scrplugin.xml.IOUtils +org.apache.felix.scrplugin.xml.MetaTypeIO +org.apache.felix.scrplugin.SCRDescriptorGeneratorTest +org.apache.felix.scrplugin.SourceImpl +testComponents.ComponentWithClassReferenceAndMissingInterface +testComponents.SimpleComponent +org.apache.felix.maven.osgicheck.impl.Check +org.apache.felix.maven.osgicheck.impl.CheckContext +org.apache.felix.maven.osgicheck.impl.CheckMojo +org.apache.felix.maven.osgicheck.impl.checks.ConsumerProviderTypeCheck +org.apache.felix.maven.osgicheck.impl.checks.ImportExportCheck +org.apache.felix.maven.osgicheck.impl.checks.SCRCheck +org.apache.felix.maven.osgicheck.impl.featureutil.ManifestUtil +org.apache.felix.maven.osgicheck.impl.featureutil.PackageInfo +org.apache.felix.maven.osgicheck.impl.mddocgen.AbstractMarkdownMojo +org.apache.felix.maven.osgicheck.impl.mddocgen.ClassName +org.apache.felix.maven.osgicheck.impl.mddocgen.MavenScrLogger +org.apache.felix.maven.osgicheck.impl.mddocgen.MetatypeMarkdownGeneratorMojo +org.apache.felix.maven.osgicheck.impl.mddocgen.ServicesMarkdownGeneratorMojo +org.apache.felix.maven.osgicheck.impl.mddocgen.SyntheticBundle +org.apache.felix.scr.impl.metadata.Faker +org.apache.felix.transaction.itests.Test +org.apache.felix.transaction.internal.Activator +org.apache.felix.transaction.internal.GeronimoPlatformTransactionManager +org.apache.felix.transaction.internal.TransactionManagerService +org.apache.felix.upnp.basedriver.Activator +org.apache.felix.upnp.basedriver.controller.DevicesInfo +org.apache.felix.upnp.basedriver.controller.DriverController +org.apache.felix.upnp.basedriver.controller.impl.DriverControllerImpl +org.apache.felix.upnp.basedriver.export.ActionDispatcher +org.apache.felix.upnp.basedriver.export.BuildDevice +org.apache.felix.upnp.basedriver.export.DeviceNode +org.apache.felix.upnp.basedriver.export.ExporterUPnPEventListener +org.apache.felix.upnp.basedriver.export.GeneralActionListener +org.apache.felix.upnp.basedriver.export.RootDeviceExportingQueue +org.apache.felix.upnp.basedriver.export.RootDeviceListener +org.apache.felix.upnp.basedriver.export.ThreadExporter +org.apache.felix.upnp.basedriver.importer.core.event.message.FirstMessage +org.apache.felix.upnp.basedriver.importer.core.event.message.ListenerModified +org.apache.felix.upnp.basedriver.importer.core.event.message.ListenerUnRegistration +org.apache.felix.upnp.basedriver.importer.core.event.message.SidExipired +org.apache.felix.upnp.basedriver.importer.core.event.message.StateChanged +org.apache.felix.upnp.basedriver.importer.core.event.structs.Listener2Sids +org.apache.felix.upnp.basedriver.importer.core.event.structs.Monitor +org.apache.felix.upnp.basedriver.importer.core.event.structs.NotifierQueue +org.apache.felix.upnp.basedriver.importer.core.event.structs.Sid2Listeners +org.apache.felix.upnp.basedriver.importer.core.event.structs.SidRenewer +org.apache.felix.upnp.basedriver.importer.core.event.structs.SidsListenersMaps +org.apache.felix.upnp.basedriver.importer.core.event.structs.StateVarsToNotify +org.apache.felix.upnp.basedriver.importer.core.event.structs.SubscriptionQueue +org.apache.felix.upnp.basedriver.importer.core.event.thread.Notifier +org.apache.felix.upnp.basedriver.importer.core.event.thread.Renewer +org.apache.felix.upnp.basedriver.importer.core.event.thread.SubScriber +org.apache.felix.upnp.basedriver.importer.core.MyCtrlPoint +org.apache.felix.upnp.basedriver.importer.core.OSGiDeviceInfo +org.apache.felix.upnp.basedriver.importer.core.upnp.UPnPActionImpl +org.apache.felix.upnp.basedriver.importer.core.upnp.UPnPDeviceImpl +org.apache.felix.upnp.basedriver.importer.core.upnp.UPnPIconImpl +org.apache.felix.upnp.basedriver.importer.core.upnp.UPnPServiceImpl +org.apache.felix.upnp.basedriver.importer.core.upnp.UPnPStateVariableImpl +org.apache.felix.upnp.basedriver.importer.util.DeviceSetup +org.apache.felix.upnp.basedriver.importer.util.HTTPRequestForIcon +org.apache.felix.upnp.basedriver.importer.util.ParseLocation +org.apache.felix.upnp.basedriver.importer.util.ParseUSN +org.apache.felix.upnp.basedriver.importer.util.StringSplitter +org.apache.felix.upnp.basedriver.tool.Logger +org.apache.felix.upnp.basedriver.util.Constants +org.apache.felix.upnp.basedriver.util.Converter +org.apache.xerces.impl.dv.util.Base64 +org.apache.xerces.impl.dv.util.HexBin +org.apache.felix.upnp.extra.util.EventSource +org.apache.felix.upnp.extra.util.UPnPEventNotifier +org.apache.felix.upnp.extra.util.UPnPSubscriber +org.apache.felix.upnp.sample.binaryLight.actions.GetStatusAction +org.apache.felix.upnp.sample.binaryLight.actions.GetTargetAction +org.apache.felix.upnp.sample.binaryLight.actions.SetTargetAction +org.apache.felix.upnp.sample.binaryLight.Activator +org.apache.felix.upnp.sample.binaryLight.devices.LightDevice +org.apache.felix.upnp.sample.binaryLight.icons.LightIcon +org.apache.felix.upnp.sample.binaryLight.LightModel +org.apache.felix.upnp.sample.binaryLight.LightUI +org.apache.felix.upnp.sample.binaryLight.PresentationServlet +org.apache.felix.upnp.sample.binaryLight.services.PowerSwitchService +org.apache.felix.upnp.sample.binaryLight.statevariables.StatusStateVariable +org.apache.felix.upnp.sample.binaryLight.statevariables.TargetStateVariable +org.apache.felix.upnp.sample.clock.Activator +org.apache.felix.upnp.sample.clock.Clock +org.apache.felix.upnp.sample.clock.ClockDevice +org.apache.felix.upnp.sample.clock.ClockFrame +org.apache.felix.upnp.sample.clock.ClockIcon +org.apache.felix.upnp.sample.clock.ClockPane +org.apache.felix.upnp.sample.clock.GetTimeAction +org.apache.felix.upnp.sample.clock.ResultStateVariable +org.apache.felix.upnp.sample.clock.SetTimeAction +org.apache.felix.upnp.sample.clock.TimerService +org.apache.felix.upnp.sample.clock.TimeStateVariable +org.apache.felix.upnp.sample.tv.Activator +org.apache.felix.upnp.sample.tv.GetPowerAction +org.apache.felix.upnp.sample.tv.PowerService +org.apache.felix.upnp.sample.tv.PowerStateVariable +org.apache.felix.upnp.sample.tv.ResultStateVariable +org.apache.felix.upnp.sample.tv.SetPowerAction +org.apache.felix.upnp.sample.tv.TvDevice +org.apache.felix.upnp.sample.tv.TvFrame +org.apache.felix.upnp.sample.tv.TvIcon +org.apache.felix.upnp.sample.tv.TvPane +org.apache.felix.upnp.sample.tv.UPnPSubscriber +org.apache.felix.upnp.tester.Activator +org.apache.felix.upnp.tester.ControlPoint +org.apache.felix.upnp.tester.discovery.DeviceChangeListener +org.apache.felix.upnp.tester.discovery.DeviceNode +org.apache.felix.upnp.tester.discovery.DeviceNodeListener +org.apache.felix.upnp.tester.discovery.DevicesList +org.apache.felix.upnp.tester.discovery.DriverProxy +org.apache.felix.upnp.tester.discovery.RootDeviceListener +org.apache.felix.upnp.tester.gui.ActionPanel +org.apache.felix.upnp.tester.gui.LogPanel +org.apache.felix.upnp.tester.gui.PropertiesViewer +org.apache.felix.upnp.tester.gui.SubscriptionPanel +org.apache.felix.upnp.tester.gui.TreeViewer +org.apache.felix.upnp.tester.gui.UPnPDeviceTreeNode +org.apache.felix.upnp.tester.gui.Util +org.apache.felix.upnp.tester.Mediator +org.apache.felix.upnp.tester.test +org.apache.felix.upnp.tester.UPnPSubscriber +org.apache.felix.useradmin.filestore.osgi.Activator +org.apache.felix.useradmin.filestore.ResettableTimer +org.apache.felix.useradmin.filestore.RoleRepositoryFileStore +org.apache.felix.useradmin.filestore.RoleRepositoryMemoryStore +org.apache.felix.useradmin.filestore.RoleRepositorySerializer +org.apache.felix.useradmin.filestore.StubGroupImpl +org.apache.felix.useradmin.filestore.ResettableTimerTest +org.apache.felix.useradmin.filestore.RoleRepositoryFileStorePerformanceTest +org.apache.felix.useradmin.filestore.RoleRepositoryFileStoreTest +org.apache.felix.useradmin.filestore.RoleRepositorySerializerTest +org.apache.felix.useradmin.filestore.StubGroupImplTest +org.apache.felix.useradmin.itest.BaseIntegrationTest +org.apache.felix.useradmin.itest.FileStoreInitializationTest +org.apache.felix.useradmin.itest.MongoDBStoreTest +org.apache.felix.useradmin.itest.UserAdminIntegrationTest +org.apache.felix.useradmin.mongodb.KeyCodec +org.apache.felix.useradmin.mongodb.MongoDB +org.apache.felix.useradmin.mongodb.MongoDBStore +org.apache.felix.useradmin.mongodb.MongoSerializerHelper +org.apache.felix.useradmin.mongodb.osgi.Activator +org.apache.felix.useradmin.mongodb.osgi.LogServiceHelper +org.apache.felix.useradmin.mongodb.RoleProvider +org.apache.felix.useradmin.mongodb.KeyCodecTest +org.apache.felix.useradmin.BackendException +org.apache.felix.useradmin.impl.AuthorizationImpl +org.apache.felix.useradmin.impl.EventDispatcher +org.apache.felix.useradmin.impl.role.GroupImpl +org.apache.felix.useradmin.impl.role.ObservableDictionary +org.apache.felix.useradmin.impl.role.ObservableGroup +org.apache.felix.useradmin.impl.role.ObservableProperties +org.apache.felix.useradmin.impl.role.ObservableRole +org.apache.felix.useradmin.impl.role.ObservableUser +org.apache.felix.useradmin.impl.role.RoleImpl +org.apache.felix.useradmin.impl.role.UserImpl +org.apache.felix.useradmin.impl.RoleChangeListener +org.apache.felix.useradmin.impl.RoleChecker +org.apache.felix.useradmin.impl.RoleRepository +org.apache.felix.useradmin.impl.UserAdminImpl +org.apache.felix.useradmin.impl.UserAdminListenerList +org.apache.felix.useradmin.osgi.Activator +org.apache.felix.useradmin.osgi.EventAdminHelper +org.apache.felix.useradmin.osgi.RoleRepositoryStoreHelper +org.apache.felix.useradmin.osgi.ServiceContext +org.apache.felix.useradmin.osgi.UserAdminListenerListHelper +org.apache.felix.useradmin.RoleFactory +org.apache.felix.useradmin.RoleRepositoryStore +org.apache.felix.useradmin.impl.AuthorizationImplTest +org.apache.felix.useradmin.impl.CustomRoleImplTest +org.apache.felix.useradmin.impl.EventDispatcherTest +org.apache.felix.useradmin.impl.MemoryRoleRepositoryStore +org.apache.felix.useradmin.impl.role.GroupImplSecurityTest +org.apache.felix.useradmin.impl.role.GroupImplTest +org.apache.felix.useradmin.impl.role.ObservableDictionarySecurityTest +org.apache.felix.useradmin.impl.role.ObservableDictionaryTest +org.apache.felix.useradmin.impl.role.ObservablePropertiesTest +org.apache.felix.useradmin.impl.role.RoleImplTest +org.apache.felix.useradmin.impl.role.UserImplTest +org.apache.felix.useradmin.impl.RoleCheckerTest +org.apache.felix.useradmin.impl.RoleRepositorySecurityTest +org.apache.felix.useradmin.impl.RoleRepositoryTest +org.apache.felix.useradmin.impl.UserAdminImplTest +org.apache.felix.utils.collections.DictionaryAsMap +org.apache.felix.utils.collections.IteratorToEnumeration +org.apache.felix.utils.collections.MapToDictionary +org.apache.felix.utils.collections.StringArrayMap +org.apache.felix.utils.extender.AbstractExtender +org.apache.felix.utils.extender.Extension +org.apache.felix.utils.extender.SimpleExtension +org.apache.felix.utils.filter.FilterImpl +org.apache.felix.utils.json.JSONParser +org.apache.felix.utils.json.JSONWriter +org.apache.felix.utils.log.Logger +org.apache.felix.utils.manifest.Attribute +org.apache.felix.utils.manifest.Clause +org.apache.felix.utils.manifest.Directive +org.apache.felix.utils.manifest.Parser +org.apache.felix.utils.properties.ConfigurationHandler +org.apache.felix.utils.properties.InterpolationHelper +org.apache.felix.utils.properties.Properties +org.apache.felix.utils.properties.TypedProperties +org.apache.felix.utils.repository.AggregateRepository +org.apache.felix.utils.repository.BaseRepository +org.apache.felix.utils.repository.JsonRepository +org.apache.felix.utils.repository.StaxParser +org.apache.felix.utils.repository.UrlLoader +org.apache.felix.utils.repository.XmlRepository +org.apache.felix.utils.resource.AbstractCapabilityRequirement +org.apache.felix.utils.resource.CapabilityImpl +org.apache.felix.utils.resource.CapabilitySet +org.apache.felix.utils.resource.RequirementImpl +org.apache.felix.utils.resource.ResourceBuilder +org.apache.felix.utils.resource.ResourceImpl +org.apache.felix.utils.resource.ResourceUtils +org.apache.felix.utils.resource.SimpleFilter +org.apache.felix.utils.service.BaseManagedServiceFactory +org.apache.felix.utils.version.VersionCleaner +org.apache.felix.utils.version.VersionRange +org.apache.felix.utils.version.VersionTable +org.apache.felix.utils.collections.StringArrayMapTest +org.apache.felix.utils.filter.FilterImplTest +org.apache.felix.utils.json.JSONParserTest +org.apache.felix.utils.manifest.ParserTest +org.apache.felix.utils.properties.ConfigurationHandlerTest +org.apache.felix.utils.properties.InterpolationHelperTest +org.apache.felix.utils.properties.MockBundleContext +org.apache.felix.utils.properties.PropertiesTest +org.apache.felix.utils.properties.TypedPropertiesTest +org.apache.felix.utils.repository.RepositoryTest +org.apache.felix.utils.resource.CapabilityImplTest +org.apache.felix.utils.resource.CapabilitySetTest +org.apache.felix.utils.resource.RequirementImplTest +org.apache.felix.utils.resource.ResourceBuilderTest +org.apache.felix.utils.resource.SimpleFilterTest +org.apache.felix.utils.version.VersionCleanerTest +org.apache.felix.utils.version.VersionRangeTest +org.apache.felix.webconsole.AbstractWebConsolePlugin +org.apache.felix.webconsole.AttachmentProvider +org.apache.felix.webconsole.BrandingPlugin +org.apache.felix.webconsole.bundleinfo.BundleInfo +org.apache.felix.webconsole.bundleinfo.BundleInfoProvider +org.apache.felix.webconsole.bundleinfo.BundleInfoType +org.apache.felix.webconsole.bundleinfo.package-info +org.apache.felix.webconsole.ConfigurationPrinter +org.apache.felix.webconsole.DefaultBrandingPlugin +org.apache.felix.webconsole.DefaultVariableResolver +org.apache.felix.webconsole.i18n.LocalizationHelper +org.apache.felix.webconsole.i18n.package-info +org.apache.felix.webconsole.internal.AbstractConfigurationPrinter +org.apache.felix.webconsole.internal.compendium.LogServlet +org.apache.felix.webconsole.internal.compendium.PreferencesConfigurationPrinter +org.apache.felix.webconsole.internal.compendium.WireAdminConfigurationPrinter +org.apache.felix.webconsole.internal.configuration.ConfigAdminSupport +org.apache.felix.webconsole.internal.configuration.ConfigManager +org.apache.felix.webconsole.internal.configuration.ConfigurationAdminConfigurationPrinter +org.apache.felix.webconsole.internal.configuration.MetatypePropertyDescriptor +org.apache.felix.webconsole.internal.configuration.MetaTypeServiceSupport +org.apache.felix.webconsole.internal.configuration.MetaTypeSupport +org.apache.felix.webconsole.internal.configuration.PropertyDescriptor +org.apache.felix.webconsole.internal.core.BaseUpdateInstallHelper +org.apache.felix.webconsole.internal.core.BundleContextUtil +org.apache.felix.webconsole.internal.core.BundlesConfigurationPrinter +org.apache.felix.webconsole.internal.core.BundlesServlet +org.apache.felix.webconsole.internal.core.InstallHelper +org.apache.felix.webconsole.internal.core.PermissionsConfigurationPrinter +org.apache.felix.webconsole.internal.core.ServicesConfigurationPrinter +org.apache.felix.webconsole.internal.core.ServicesServlet +org.apache.felix.webconsole.internal.core.ServicesUsedInfoProvider +org.apache.felix.webconsole.internal.core.UpdateHelper +org.apache.felix.webconsole.internal.filter.FilteringResponseWrapper +org.apache.felix.webconsole.internal.filter.ResourceFilteringWriter +org.apache.felix.webconsole.internal.i18n.CombinedEnumeration +org.apache.felix.webconsole.internal.i18n.CombinedResourceBundle +org.apache.felix.webconsole.internal.i18n.ConsolePropertyResourceBundle +org.apache.felix.webconsole.internal.i18n.ResourceBundleCache +org.apache.felix.webconsole.internal.i18n.ResourceBundleManager +org.apache.felix.webconsole.internal.misc.ConfigurationRender +org.apache.felix.webconsole.internal.misc.LicenseServlet +org.apache.felix.webconsole.internal.misc.SystemPropertiesPrinter +org.apache.felix.webconsole.internal.misc.ThreadDumper +org.apache.felix.webconsole.internal.misc.ThreadPrinter +org.apache.felix.webconsole.internal.OsgiManagerActivator +org.apache.felix.webconsole.internal.OsgiManagerPlugin +org.apache.felix.webconsole.internal.servlet.Base64 +org.apache.felix.webconsole.internal.servlet.BasicWebConsoleSecurityProvider +org.apache.felix.webconsole.internal.servlet.ConfigurationMetatypeSupport +org.apache.felix.webconsole.internal.servlet.ConfigurationSupport +org.apache.felix.webconsole.internal.servlet.ConfigurationUtil +org.apache.felix.webconsole.internal.servlet.OsgiManager +org.apache.felix.webconsole.internal.servlet.OsgiManagerHttpContext +org.apache.felix.webconsole.internal.servlet.Password +org.apache.felix.webconsole.internal.servlet.PluginHolder +org.apache.felix.webconsole.internal.system.VMStatPlugin +org.apache.felix.webconsole.internal.Util +org.apache.felix.webconsole.internal.WebConsolePluginAdapter +org.apache.felix.webconsole.ModeAwareConfigurationPrinter +org.apache.felix.webconsole.package-info +org.apache.felix.webconsole.SimpleWebConsolePlugin +org.apache.felix.webconsole.User +org.apache.felix.webconsole.VariableResolver +org.apache.felix.webconsole.WebConsoleConstants +org.apache.felix.webconsole.WebConsoleSecurityProvider +org.apache.felix.webconsole.WebConsoleSecurityProvider2 +org.apache.felix.webconsole.WebConsoleSecurityProvider3 +org.apache.felix.webconsole.WebConsoleUtil +org.apache.felix.webconsole.AbstractWebConsolePluginTest +org.apache.felix.webconsole.internal.misc.LicenseServletTest +org.apache.felix.webconsole.internal.servlet.OsgiManagerHttpContextTest +org.apache.felix.webconsole.internal.servlet.OsgiManagerTest +org.apache.felix.webconsole.internal.servlet.PasswordTest +org.apache.felix.webconsole.plugins.deppack.internal.Activator +org.apache.felix.webconsole.plugins.deppack.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.ds.internal.Activator +org.apache.felix.webconsole.plugins.ds.internal.ComponentConfigurationPrinter +org.apache.felix.webconsole.plugins.ds.internal.ConfigurationAdminSupport +org.apache.felix.webconsole.plugins.ds.internal.ConfigurationSupport +org.apache.felix.webconsole.plugins.ds.internal.InfoProvider +org.apache.felix.webconsole.plugins.ds.internal.MetatypeSupport +org.apache.felix.webconsole.plugins.ds.internal.Util +org.apache.felix.webconsole.plugins.ds.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.event.internal.Activator +org.apache.felix.webconsole.plugins.event.internal.ConfigurationListener +org.apache.felix.webconsole.plugins.event.internal.converter.BundleEventConverter +org.apache.felix.webconsole.plugins.event.internal.converter.ConfigurationEventConverter +org.apache.felix.webconsole.plugins.event.internal.converter.FrameworkEventConverter +org.apache.felix.webconsole.plugins.event.internal.converter.ServiceEventConverter +org.apache.felix.webconsole.plugins.event.internal.EventCollector +org.apache.felix.webconsole.plugins.event.internal.EventHandler +org.apache.felix.webconsole.plugins.event.internal.EventInfo +org.apache.felix.webconsole.plugins.event.internal.EventListener +org.apache.felix.webconsole.plugins.event.internal.OptionalFeaturesHandler +org.apache.felix.webconsole.plugins.event.internal.OsgiUtil +org.apache.felix.webconsole.plugins.event.internal.PluginServlet +org.apache.felix.webconsole.plugins.event.internal.PropertiesEditorSupport +org.apache.felix.webconsole.plugins.gogo.impl.Activator +org.apache.felix.webconsole.plugins.gogo.impl.Console +org.apache.felix.webconsole.plugins.gogo.impl.GogoPlugin +org.apache.felix.webconsole.plugins.gogo.impl.SessionTerminal +org.apache.felix.webconsole.plugins.gogo.impl.SessionTerminalManager +org.apache.felix.webconsole.plugins.gogo.impl.Terminal +org.apache.felix.webconsole.plugins.memoryusage.internal.Activator +org.apache.felix.webconsole.plugins.memoryusage.internal.MemoryUsageCommand +org.apache.felix.webconsole.plugins.memoryusage.internal.MemoryUsageConfigurator +org.apache.felix.webconsole.plugins.memoryusage.internal.MemoryUsageConstants +org.apache.felix.webconsole.plugins.memoryusage.internal.MemoryUsagePanel +org.apache.felix.webconsole.plugins.memoryusage.internal.MemoryUsageSupport +org.apache.felix.webconsole.plugins.metatype.internal.Activator +org.apache.felix.webconsole.plugins.metatype.internal.MetatypeInventoryPrinter +org.apache.felix.webconsole.plugins.obr.internal.AbstractBundleRepositoryRenderHelper +org.apache.felix.webconsole.plugins.obr.internal.Activator +org.apache.felix.webconsole.plugins.obr.internal.FelixBundleRepositoryRenderHelper +org.apache.felix.webconsole.plugins.obr.internal.FelixDeployer +org.apache.felix.webconsole.plugins.obr.internal.OsgiBundleRepositoryRenderHelper +org.apache.felix.webconsole.plugins.obr.internal.OsgiDeployer +org.apache.felix.webconsole.plugins.obr.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.packageadmin.internal.Activator +org.apache.felix.webconsole.plugins.packageadmin.internal.ExportedPackageComparator +org.apache.felix.webconsole.plugins.packageadmin.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.packageadmin.internal.WebConsolePrinter +org.apache.felix.webconsole.plugins.scriptconsole.internal.Activator +org.apache.felix.webconsole.plugins.scriptconsole.internal.Logger +org.apache.felix.webconsole.plugins.scriptconsole.internal.LogWriter +org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptConsolePlugin +org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptEngineManager +org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptHelper +org.apache.felix.webconsole.plugins.scriptconsole.integration.ITScriptConsolePlugin +org.apache.felix.webconsole.plugins.scriptconsole.integration.ServerConfiguration +org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptEngineManagerTest +org.apache.felix.webconsole.plugins.shell.internal.Activator +org.apache.felix.webconsole.plugins.shell.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.subsystem.internal.Activator +org.apache.felix.webconsole.plugins.subsystem.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.upnp.internal.Activator +org.apache.felix.webconsole.plugins.upnp.internal.Base64 +org.apache.felix.webconsole.plugins.upnp.internal.ConfigurationPrinterImpl +org.apache.felix.webconsole.plugins.upnp.internal.ControlServlet +org.apache.felix.webconsole.plugins.upnp.internal.Hex +org.apache.felix.webconsole.plugins.upnp.internal.Serializer +org.apache.felix.webconsole.plugins.upnp.internal.SessionObject +org.apache.felix.webconsole.plugins.upnp.internal.WebConsolePlugin +org.apache.felix.webconsole.plugins.useradmin.internal.Activator +org.apache.felix.webconsole.plugins.useradmin.internal.WebConsolePlugin +org.apache.felix.wireadmin.Activator +org.apache.felix.wireadmin.EventManager +org.apache.felix.wireadmin.WireAdminImpl +org.apache.felix.wireadmin.WireImpl diff --git a/logback-classic/src/test/input/gaffer/asyncAppender.groovy b/logback-classic/src/test/input/gaffer/asyncAppender.groovy index ba6383bd51..2f08236525 100644 --- a/logback-classic/src/test/input/gaffer/asyncAppender.groovy +++ b/logback-classic/src/test/input/gaffer/asyncAppender.groovy @@ -16,6 +16,8 @@ import ch.qos.logback.classic.PatternLayout import ch.qos.logback.core.ConsoleAppender import ch.qos.logback.core.encoder.LayoutWrappingEncoder +def p = "HELLO" + appender("STDOUT", ConsoleAppender) { encoder(LayoutWrappingEncoder) { layout(PatternLayout) { diff --git a/logback-classic/src/test/input/issue/gh_issues_450.xml b/logback-classic/src/test/input/issue/gh_issues_450.xml new file mode 100644 index 0000000000..6526d66f22 --- /dev/null +++ b/logback-classic/src/test/input/issue/gh_issues_450.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + %d{HH:mm:ss.SSS} [%t] **[%X]** %-5level %logger{36} - %msg%n + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/issue/logback-1159.xml b/logback-classic/src/test/input/issue/logback-1159.xml index 40bf975e3d..cbd44fe5dc 100755 --- a/logback-classic/src/test/input/issue/logback-1159.xml +++ b/logback-classic/src/test/input/issue/logback-1159.xml @@ -2,7 +2,7 @@ + class="ch.qos.logback.classic.util.LogbackListener1159" /> diff --git a/logback-classic/src/test/input/issue/logback-1754.xml b/logback-classic/src/test/input/issue/logback-1754.xml new file mode 100644 index 0000000000..0d57944d1c --- /dev/null +++ b/logback-classic/src/test/input/issue/logback-1754.xml @@ -0,0 +1,32 @@ + + + + + + + + + ${logback_1754_targetDirectory}/test-%d{yyyy-MM-dd}.log + 120 + + + %date{HH:mm:ss.SSS} [%level] %logger{0} [%thread] [%class{3}:%line] : %msg%n + + true + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/appenderRefBeforeAppender.xml b/logback-classic/src/test/input/joran/appenderRefBeforeAppender.xml new file mode 100755 index 0000000000..be9b33a702 --- /dev/null +++ b/logback-classic/src/test/input/joran/appenderRefBeforeAppender.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/appenderRefByProperty.xml b/logback-classic/src/test/input/joran/appenderRefByProperty.xml index 8e9d0b9d8a..677fc525d0 100644 --- a/logback-classic/src/test/input/joran/appenderRefByProperty.xml +++ b/logback-classic/src/test/input/joran/appenderRefByProperty.xml @@ -4,7 +4,6 @@ - diff --git a/logback-classic/src/test/input/joran/appenderRefByPropertyDefault.xml b/logback-classic/src/test/input/joran/appenderRefByPropertyDefault.xml new file mode 100644 index 0000000000..4a13e44095 --- /dev/null +++ b/logback-classic/src/test/input/joran/appenderRefByPropertyDefault.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/async/logback_1614.xml b/logback-classic/src/test/input/joran/async/logback_1614.xml new file mode 100644 index 0000000000..c67ffe69f1 --- /dev/null +++ b/logback-classic/src/test/input/joran/async/logback_1614.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + %level %logger - %msg%n + + + + + + + + target/test-output/logback_1614/test.log + false + + %d [%thread] %-5level %logger{36}: - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/asyncAppender_list_after.xml b/logback-classic/src/test/input/joran/asyncAppender_list_after.xml new file mode 100755 index 0000000000..d983986564 --- /dev/null +++ b/logback-classic/src/test/input/joran/asyncAppender_list_after.xml @@ -0,0 +1,16 @@ + + + + 256 + false + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/asyncAppender_list_first.xml b/logback-classic/src/test/input/joran/asyncAppender_list_first.xml new file mode 100755 index 0000000000..e1f2381033 --- /dev/null +++ b/logback-classic/src/test/input/joran/asyncAppender_list_first.xml @@ -0,0 +1,20 @@ + + + + + + + + + + 256 + false + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/basicEventEvaluator.xml b/logback-classic/src/test/input/joran/basicEventEvaluator.xml new file mode 100644 index 0000000000..52177d7b53 --- /dev/null +++ b/logback-classic/src/test/input/joran/basicEventEvaluator.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + message.contains("billing") + + NEUTRAL + DENY + + + %-4relative [%thread] %-5level %logger - %msg%n + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/boolex/exceptionEvaluator.xml b/logback-classic/src/test/input/joran/boolex/exceptionEvaluator.xml new file mode 100644 index 0000000000..9cf95ff18f --- /dev/null +++ b/logback-classic/src/test/input/joran/boolex/exceptionEvaluator.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + java.lang.RuntimeException + + DENY + NEUTRAL + + + + %-4relative [%thread] %-5level %logger -%kvp -%msg%n + + + + + + + diff --git a/logback-classic/src/test/input/joran/callerData.xml b/logback-classic/src/test/input/joran/callerData.xml index 1c499f8acc..560c5f4b94 100644 --- a/logback-classic/src/test/input/joran/callerData.xml +++ b/logback-classic/src/test/input/joran/callerData.xml @@ -13,7 +13,7 @@ + class="ch.qos.logback.core.testUtil.StringListAppender"> %caller{4, helloEval}%d %level - %m%n diff --git a/logback-classic/src/test/input/joran/collision/conditionalRepeat.xml b/logback-classic/src/test/input/joran/collision/conditionalRepeat.xml new file mode 100644 index 0000000000..aca515e274 --- /dev/null +++ b/logback-classic/src/test/input/joran/collision/conditionalRepeat.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + + + + undefinedTestCollisionVar + + + + + + ${outputTargetKey} + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + ${outputTargetKey} + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/collision/repeatFile.xml b/logback-classic/src/test/input/joran/collision/repeatFile.xml new file mode 100644 index 0000000000..6a7643c332 --- /dev/null +++ b/logback-classic/src/test/input/joran/collision/repeatFile.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + ${outputTargetKey} + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + ${outputTargetKey} + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/collision/repeatMixedFileAndRolling.xml b/logback-classic/src/test/input/joran/collision/repeatMixedFileAndRolling.xml new file mode 100644 index 0000000000..1159c5304a --- /dev/null +++ b/logback-classic/src/test/input/joran/collision/repeatMixedFileAndRolling.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + ${outputTargetKey} + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + ${outputTargetKey} + + ${fileNamePatternKey} + 30 + 3GB + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFile.xml b/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFile.xml new file mode 100644 index 0000000000..b638cd1e6d --- /dev/null +++ b/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFile.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + ${outputTargetKey} + + ${fileNamePatternKey} + 30 + 3GB + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + ${outputTargetKey} + + DISTINCT${fileNamePatternKey} + 30 + 3GB + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFilePattern.xml b/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFilePattern.xml new file mode 100644 index 0000000000..bff0f3c113 --- /dev/null +++ b/logback-classic/src/test/input/joran/collision/repeatRollingFileAppenderByFilePattern.xml @@ -0,0 +1,59 @@ + + + + + + + + + + + + + ${outputTargetKey} + + ${fileNamePatternKey} + 30 + 3GB + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + DISTICNT${outputTargetKey} + + ${fileNamePatternKey} + 30 + 3GB + + + %-4relative [%thread] %-5level %logger{35} -%kvp- %msg%n + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/conditional/conditionalIncludeExistingFile.xml b/logback-classic/src/test/input/joran/conditional/conditionalIncludeExistingFile.xml deleted file mode 100644 index f39b5f5de8..0000000000 --- a/logback-classic/src/test/input/joran/conditional/conditionalIncludeExistingFile.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - src/test/input/joran/conditional/includedFile.xml - - - - - - - - \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/consoleCharset.xml b/logback-classic/src/test/input/joran/consoleCharset.xml new file mode 100644 index 0000000000..3dca617016 --- /dev/null +++ b/logback-classic/src/test/input/joran/consoleCharset.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + ${consoleCharset} + TEST %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/conversionRule/conversionRuleAtEnd.xml b/logback-classic/src/test/input/joran/conversionRule/conversionRuleAtEnd.xml new file mode 100644 index 0000000000..4e06dac0de --- /dev/null +++ b/logback-classic/src/test/input/joran/conversionRule/conversionRuleAtEnd.xml @@ -0,0 +1,16 @@ + + + + + + %sample - %msg + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/conversionRule/conversionRuleIncluded.xml b/logback-classic/src/test/input/joran/conversionRule/conversionRuleIncluded.xml new file mode 100644 index 0000000000..a28eba2e08 --- /dev/null +++ b/logback-classic/src/test/input/joran/conversionRule/conversionRuleIncluded.xml @@ -0,0 +1,16 @@ + + + + + + %sample - %msg + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/conversionRule/conversionRuleTop0.xml b/logback-classic/src/test/input/joran/conversionRule/conversionRuleTop0.xml new file mode 100644 index 0000000000..3ed44138ac --- /dev/null +++ b/logback-classic/src/test/input/joran/conversionRule/conversionRuleTop0.xml @@ -0,0 +1,15 @@ + + + + + + %sample - %msg + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/conversionRule/htmlLayout0.xml b/logback-classic/src/test/input/joran/conversionRule/htmlLayout0.xml index 302af87cf0..88895d57a7 100644 --- a/logback-classic/src/test/input/joran/conversionRule/htmlLayout0.xml +++ b/logback-classic/src/test/input/joran/conversionRule/htmlLayout0.xml @@ -1,7 +1,7 @@ + converterClass="ch.qos.logback.classic.pattern.SampleConverter" /> diff --git a/logback-classic/src/test/input/joran/conversionRule/patternLayout0.xml b/logback-classic/src/test/input/joran/conversionRule/patternLayout0.xml index 1468bbb22c..2e6d3d84d5 100644 --- a/logback-classic/src/test/input/joran/conversionRule/patternLayout0.xml +++ b/logback-classic/src/test/input/joran/conversionRule/patternLayout0.xml @@ -1,7 +1,7 @@ + converterClass="ch.qos.logback.classic.pattern.SampleConverter" /> diff --git a/logback-classic/src/test/input/joran/dateWithLocale.xml b/logback-classic/src/test/input/joran/dateWithLocale.xml new file mode 100644 index 0000000000..57f848a533 --- /dev/null +++ b/logback-classic/src/test/input/joran/dateWithLocale.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + %d{yyyy-MM-dd_HH_mm_ss, Australia/Perth, en-AU} - %m%n + + + + + + + diff --git a/logback-classic/src/test/input/joran/encoderCharset.xml b/logback-classic/src/test/input/joran/encoderCharset.xml index 2ffe366b4c..7f1f69adff 100644 --- a/logback-classic/src/test/input/joran/encoderCharset.xml +++ b/logback-classic/src/test/input/joran/encoderCharset.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/fauxListener.xml b/logback-classic/src/test/input/joran/fauxListener.xml new file mode 100644 index 0000000000..83d164ef35 --- /dev/null +++ b/logback-classic/src/test/input/joran/fauxListener.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/ignore.xml b/logback-classic/src/test/input/joran/ignore.xml index 1bd2cd7891..858b1bd227 100644 --- a/logback-classic/src/test/input/joran/ignore.xml +++ b/logback-classic/src/test/input/joran/ignore.xml @@ -2,6 +2,7 @@ + (marker.contains("IGNORE")) diff --git a/logback-classic/src/test/input/joran/include/included0.xml b/logback-classic/src/test/input/joran/include/included0.xml new file mode 100644 index 0000000000..8da6d4cd12 --- /dev/null +++ b/logback-classic/src/test/input/joran/include/included0.xml @@ -0,0 +1,3 @@ + + + diff --git a/logback-classic/src/test/input/joran/include/topLevel0.xml b/logback-classic/src/test/input/joran/include/topLevel0.xml new file mode 100644 index 0000000000..6c5c23f2a8 --- /dev/null +++ b/logback-classic/src/test/input/joran/include/topLevel0.xml @@ -0,0 +1,22 @@ + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/issues/logback1572.xml b/logback-classic/src/test/input/joran/issues/logback1572.xml new file mode 100644 index 0000000000..c405f3cb08 --- /dev/null +++ b/logback-classic/src/test/input/joran/issues/logback1572.xml @@ -0,0 +1,21 @@ + + + + + + + %date %level [%thread] %logger(%file:%line\) + + + + + + + smtp.gmail.com + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/issues/logback_1162.xml b/logback-classic/src/test/input/joran/issues/logback_1162.xml index c4218a6cad..030e809686 100755 --- a/logback-classic/src/test/input/joran/issues/logback_1162.xml +++ b/logback-classic/src/test/input/joran/issues/logback_1162.xml @@ -1,5 +1,4 @@ - - + ${output_dir}/info.log diff --git a/logback-classic/src/test/input/joran/issues/logback_1672.xml b/logback-classic/src/test/input/joran/issues/logback_1672.xml new file mode 100644 index 0000000000..b4de01d7bf --- /dev/null +++ b/logback-classic/src/test/input/joran/issues/logback_1672.xml @@ -0,0 +1,12 @@ + + + 10 + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/issues/logback_1674.xml b/logback-classic/src/test/input/joran/issues/logback_1674.xml new file mode 100644 index 0000000000..a4274b44b4 --- /dev/null +++ b/logback-classic/src/test/input/joran/issues/logback_1674.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/issues/logback_1678_shutdown.xml b/logback-classic/src/test/input/joran/issues/logback_1678_shutdown.xml new file mode 100644 index 0000000000..6f20c93d3d --- /dev/null +++ b/logback-classic/src/test/input/joran/issues/logback_1678_shutdown.xml @@ -0,0 +1,13 @@ + + + + 10 + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/json/jsonEncoder.xml b/logback-classic/src/test/input/joran/json/jsonEncoder.xml new file mode 100644 index 0000000000..ffef3d87d9 --- /dev/null +++ b/logback-classic/src/test/input/joran/json/jsonEncoder.xml @@ -0,0 +1,30 @@ + + + + + + + + target/test-output/json/test-${diff}.json + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml b/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml new file mode 100644 index 0000000000..58c88a75aa --- /dev/null +++ b/logback-classic/src/test/input/joran/json/jsonEncoderAndEnabledFormattedMessage.xml @@ -0,0 +1,36 @@ + + + + + + + + target/test-output/json/test-${diff}.json + + true + false + false + false + false + false + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/levelFromAProperty.xml b/logback-classic/src/test/input/joran/levelFromAProperty.xml new file mode 100644 index 0000000000..89ad6cced3 --- /dev/null +++ b/logback-classic/src/test/input/joran/levelFromAProperty.xml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/loggerLevelByProperty.xml b/logback-classic/src/test/input/joran/loggerLevelByProperty.xml index eb6e92b685..f29c189368 100644 --- a/logback-classic/src/test/input/joran/loggerLevelByProperty.xml +++ b/logback-classic/src/test/input/joran/loggerLevelByProperty.xml @@ -3,12 +3,12 @@ - + - + - - - + + + diff --git a/logback-classic/src/test/input/joran/missingProperty.xml b/logback-classic/src/test/input/joran/missingProperty.xml new file mode 100644 index 0000000000..c30b36cd0b --- /dev/null +++ b/logback-classic/src/test/input/joran/missingProperty.xml @@ -0,0 +1,14 @@ + + + + + + + Hello + + + + + + + diff --git a/logback-classic/src/test/input/joran/model/minimal.xml b/logback-classic/src/test/input/joran/model/minimal.xml new file mode 100644 index 0000000000..945e4c8924 --- /dev/null +++ b/logback-classic/src/test/input/joran/model/minimal.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-46697.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-46697.xml new file mode 100644 index 0000000000..6758e5be31 --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-46697.xml @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-47093.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47093.xml new file mode 100644 index 0000000000..c8a4972f9d --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47093.xml @@ -0,0 +1,4 @@ + + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis.xml new file mode 100644 index 0000000000..a90bdfd542 --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis.xml @@ -0,0 +1,3 @@ + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis2.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis2.xml new file mode 100644 index 0000000000..de2570dfdc --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117-bis2.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117.xml new file mode 100644 index 0000000000..8c2d2e763a --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47117.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/fuzz-47293.xml b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47293.xml new file mode 100644 index 0000000000..1095b60437 --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/fuzz-47293.xml @@ -0,0 +1,3 @@ + + + diff --git a/logback-classic/src/test/input/joran/ossfuzz/nestedComplexWithNoKnownClass.xml b/logback-classic/src/test/input/joran/ossfuzz/nestedComplexWithNoKnownClass.xml new file mode 100644 index 0000000000..23e2c89c2a --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/nestedComplexWithNoKnownClass.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/ossfuzz/noConfig.xml b/logback-classic/src/test/input/joran/ossfuzz/noConfig.xml new file mode 100644 index 0000000000..754e210138 --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/noConfig.xml @@ -0,0 +1,4 @@ + + + +a \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/ossfuzz/unknownProperty.xml b/logback-classic/src/test/input/joran/ossfuzz/unknownProperty.xml new file mode 100644 index 0000000000..943aa6979f --- /dev/null +++ b/logback-classic/src/test/input/joran/ossfuzz/unknownProperty.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/packagingDataDisabled.xml b/logback-classic/src/test/input/joran/packagingDataDisabled.xml index ce44147346..0e19c6b9e1 100644 --- a/logback-classic/src/test/input/joran/packagingDataDisabled.xml +++ b/logback-classic/src/test/input/joran/packagingDataDisabled.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/packagingDataEnabled.xml b/logback-classic/src/test/input/joran/packagingDataEnabled.xml index c7218b3294..b5ff1bb184 100644 --- a/logback-classic/src/test/input/joran/packagingDataEnabled.xml +++ b/logback-classic/src/test/input/joran/packagingDataEnabled.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/pattern/kvp.xml b/logback-classic/src/test/input/joran/pattern/kvp.xml new file mode 100644 index 0000000000..585b35a858 --- /dev/null +++ b/logback-classic/src/test/input/joran/pattern/kvp.xml @@ -0,0 +1,12 @@ + + + + + %kvp %msg + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/pattern/replace0.xml b/logback-classic/src/test/input/joran/pattern/replace0.xml index dbdfd4a13f..0c5e7d4b1e 100644 --- a/logback-classic/src/test/input/joran/pattern/replace0.xml +++ b/logback-classic/src/test/input/joran/pattern/replace0.xml @@ -1,7 +1,7 @@ + converterClass="ch.qos.logback.classic.pattern.SampleConverter" /> diff --git a/logback-classic/src/test/input/joran/pattern/replaceNewline.xml b/logback-classic/src/test/input/joran/pattern/replaceNewline.xml index f3cee11f96..d6d2d303ec 100755 --- a/logback-classic/src/test/input/joran/pattern/replaceNewline.xml +++ b/logback-classic/src/test/input/joran/pattern/replaceNewline.xml @@ -1,7 +1,7 @@ + converterClass="ch.qos.logback.classic.pattern.SampleConverter" /> diff --git a/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.properties b/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.properties new file mode 100644 index 0000000000..f201483db6 --- /dev/null +++ b/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.properties @@ -0,0 +1,15 @@ +# +# Logback: the reliable, generic, fast and flexible logging framework. +# Copyright (C) 1999-2024, QOS.ch. All rights reserved. +# +# This program and the accompanying materials are dual-licensed under +# either the terms of the Eclipse Public License v1.0 as published by +# the Eclipse Foundation +# +# or (per the licensee's choosing) +# +# under the terms of the GNU Lesser General Public License version 2.1 +# as published by the Free Software Foundation. +# + +logback.logger.com.toto = WARN \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.xml b/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.xml new file mode 100644 index 0000000000..28b588885a --- /dev/null +++ b/logback-classic/src/test/input/joran/propertiesConfigurator/smoke.xml @@ -0,0 +1,30 @@ + + + + + + + %msg + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/propsMissingRightCurlyBrace.xml b/logback-classic/src/test/input/joran/propsMissingRightCurlyBrace.xml new file mode 100644 index 0000000000..7da5d9e487 --- /dev/null +++ b/logback-classic/src/test/input/joran/propsMissingRightCurlyBrace.xml @@ -0,0 +1,17 @@ + + + + + + + + %d ${ab - %m%n + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/refToUndefinedAppender.xml b/logback-classic/src/test/input/joran/refToUndefinedAppender.xml new file mode 100644 index 0000000000..d2debe389c --- /dev/null +++ b/logback-classic/src/test/input/joran/refToUndefinedAppender.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/refViaDefaultSubstitution.xml b/logback-classic/src/test/input/joran/refViaDefaultSubstitution.xml new file mode 100644 index 0000000000..bec20a7cd2 --- /dev/null +++ b/logback-classic/src/test/input/joran/refViaDefaultSubstitution.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/roct/inclusion/topByResource.xml b/logback-classic/src/test/input/joran/roct/inclusion/topByResource.xml deleted file mode 100644 index ad450cf6d0..0000000000 --- a/logback-classic/src/test/input/joran/roct/inclusion/topByResource.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/logback-classic/src/test/input/joran/roct/inclusion/topLevel0.xml b/logback-classic/src/test/input/joran/roct/inclusion/topLevel0.xml deleted file mode 100644 index 77ebde0433..0000000000 --- a/logback-classic/src/test/input/joran/roct/inclusion/topLevel0.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/roct/scan_logback_474.xml b/logback-classic/src/test/input/joran/roct/scan_logback_474.xml deleted file mode 100755 index 9f9700d4db..0000000000 --- a/logback-classic/src/test/input/joran/roct/scan_logback_474.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/rolling/depratedSizeAndTimeBasedFNATPWarning.xml b/logback-classic/src/test/input/joran/rolling/depratedSizeAndTimeBasedFNATPWarning.xml index 2f7662f9d9..5004b57966 100755 --- a/logback-classic/src/test/input/joran/rolling/depratedSizeAndTimeBasedFNATPWarning.xml +++ b/logback-classic/src/test/input/joran/rolling/depratedSizeAndTimeBasedFNATPWarning.xml @@ -9,7 +9,7 @@ ${randomOutputDir}${testId}-%d{yyyy-MM-dd_HH_mm_ss}.%i + class="ch.qos.logback.core.rolling.SizeAndTimeBasedFileNamingAndTriggeringPolicy"> 100 diff --git a/logback-classic/src/test/input/joran/rolling/timeAndSize.xml b/logback-classic/src/test/input/joran/rolling/timeAndSize.xml index 24816993e9..f84b99e5fa 100644 --- a/logback-classic/src/test/input/joran/rolling/timeAndSize.xml +++ b/logback-classic/src/test/input/joran/rolling/timeAndSize.xml @@ -8,6 +8,7 @@ ${randomOutputDir}${testId}-%d{yyyy-MM-dd_HH_mm_ss}.%i + ${sizeThreshold} diff --git a/logback-classic/src/test/input/joran/rolling/timeAndSizeWithoutMaxFileSize.xml b/logback-classic/src/test/input/joran/rolling/timeAndSizeWithoutMaxFileSize.xml index 67c2f75262..80dfe15b37 100755 --- a/logback-classic/src/test/input/joran/rolling/timeAndSizeWithoutMaxFileSize.xml +++ b/logback-classic/src/test/input/joran/rolling/timeAndSizeWithoutMaxFileSize.xml @@ -7,10 +7,9 @@ %d [%thread] %-5level %logger{36} - %msg%n - c:/tmp/logs/rolling-test-%d{yyyy-MM-dd}.%i.log - + c:/tmp/logs/rolling-test-%d{yyyy-MM-dd}.%i.log + class="ch.qos.logback.core.rolling.SizeAndTimeBasedFileNamingAndTriggeringPolicy" /> 30 true diff --git a/logback-classic/src/test/input/joran/rolling/totalSizeCapSmallerThanMaxFileSize.xml b/logback-classic/src/test/input/joran/rolling/totalSizeCapSmallerThanMaxFileSize.xml index 682ca7c418..a59a522376 100755 --- a/logback-classic/src/test/input/joran/rolling/totalSizeCapSmallerThanMaxFileSize.xml +++ b/logback-classic/src/test/input/joran/rolling/totalSizeCapSmallerThanMaxFileSize.xml @@ -5,7 +5,7 @@ ${randomOutputDir}z${testId} ${randomOutputDir}${testId}-%d{yyyy-MM-dd_HH_mm_ss}.%i - 100 + 250 4 10 diff --git a/logback-classic/src/test/input/joran/sequenceNumberGenerator-missingClass.xml b/logback-classic/src/test/input/joran/sequenceNumberGenerator-missingClass.xml new file mode 100644 index 0000000000..0290134fab --- /dev/null +++ b/logback-classic/src/test/input/joran/sequenceNumberGenerator-missingClass.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/sequenceNumberGenerator.xml b/logback-classic/src/test/input/joran/sequenceNumberGenerator.xml new file mode 100644 index 0000000000..5c14432880 --- /dev/null +++ b/logback-classic/src/test/input/joran/sequenceNumberGenerator.xml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/sift/completeCycle.xml b/logback-classic/src/test/input/joran/sift/completeCycle.xml index 622582f42b..4a08a0b995 100644 --- a/logback-classic/src/test/input/joran/sift/completeCycle.xml +++ b/logback-classic/src/test/input/joran/sift/completeCycle.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/compositeProperty.xml b/logback-classic/src/test/input/joran/sift/compositeProperty.xml index 37ed96bd51..4796bc712c 100644 --- a/logback-classic/src/test/input/joran/sift/compositeProperty.xml +++ b/logback-classic/src/test/input/joran/sift/compositeProperty.xml @@ -1,5 +1,5 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/defaultLayoutRule.xml b/logback-classic/src/test/input/joran/sift/defaultLayoutRule.xml index b8753ea78a..59ddaf63b0 100644 --- a/logback-classic/src/test/input/joran/sift/defaultLayoutRule.xml +++ b/logback-classic/src/test/input/joran/sift/defaultLayoutRule.xml @@ -1,4 +1,4 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/fileAppender.xml b/logback-classic/src/test/input/joran/sift/fileAppender.xml index 79bffd0810..862bd91c4e 100755 --- a/logback-classic/src/test/input/joran/sift/fileAppender.xml +++ b/logback-classic/src/test/input/joran/sift/fileAppender.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/hoard0.xml b/logback-classic/src/test/input/joran/sift/hoard0.xml index d1673bf921..b2b3f8d570 100644 --- a/logback-classic/src/test/input/joran/sift/hoard0.xml +++ b/logback-classic/src/test/input/joran/sift/hoard0.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/lingering.xml b/logback-classic/src/test/input/joran/sift/lingering.xml index 5c53496e15..1a312b76ee 100644 --- a/logback-classic/src/test/input/joran/sift/lingering.xml +++ b/logback-classic/src/test/input/joran/sift/lingering.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/maxAppenderCount.xml b/logback-classic/src/test/input/joran/sift/maxAppenderCount.xml index 90c5957d4d..bf00fae38e 100644 --- a/logback-classic/src/test/input/joran/sift/maxAppenderCount.xml +++ b/logback-classic/src/test/input/joran/sift/maxAppenderCount.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/multipleNesting.xml b/logback-classic/src/test/input/joran/sift/multipleNesting.xml index 40d572a0ab..3336f6cf0d 100644 --- a/logback-classic/src/test/input/joran/sift/multipleNesting.xml +++ b/logback-classic/src/test/input/joran/sift/multipleNesting.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/propertyDefinedInSiftElement.xml b/logback-classic/src/test/input/joran/sift/propertyDefinedInSiftElement.xml index 6a08c63a11..23cb3c3560 100644 --- a/logback-classic/src/test/input/joran/sift/propertyDefinedInSiftElement.xml +++ b/logback-classic/src/test/input/joran/sift/propertyDefinedInSiftElement.xml @@ -1,4 +1,4 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/propertyPropagation.xml b/logback-classic/src/test/input/joran/sift/propertyPropagation.xml index ab7ff7dae4..467bae6ec8 100644 --- a/logback-classic/src/test/input/joran/sift/propertyPropagation.xml +++ b/logback-classic/src/test/input/joran/sift/propertyPropagation.xml @@ -1,4 +1,4 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/smoke.xml b/logback-classic/src/test/input/joran/sift/smoke.xml index 846fd14a08..6509e496a5 100644 --- a/logback-classic/src/test/input/joran/sift/smoke.xml +++ b/logback-classic/src/test/input/joran/sift/smoke.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/timeout.xml b/logback-classic/src/test/input/joran/sift/timeout.xml index 854a1b92b7..a2e048c298 100644 --- a/logback-classic/src/test/input/joran/sift/timeout.xml +++ b/logback-classic/src/test/input/joran/sift/timeout.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/unsetDefaultValueProperty.xml b/logback-classic/src/test/input/joran/sift/unsetDefaultValueProperty.xml index 95615cbdd7..c9170d8d07 100644 --- a/logback-classic/src/test/input/joran/sift/unsetDefaultValueProperty.xml +++ b/logback-classic/src/test/input/joran/sift/unsetDefaultValueProperty.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/sift/zeroNesting.xml b/logback-classic/src/test/input/joran/sift/zeroNesting.xml index 3c51cc6ef7..e05ced3039 100644 --- a/logback-classic/src/test/input/joran/sift/zeroNesting.xml +++ b/logback-classic/src/test/input/joran/sift/zeroNesting.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/simpleList.xml b/logback-classic/src/test/input/joran/simpleList.xml index 97239950f0..0d06f40000 100644 --- a/logback-classic/src/test/input/joran/simpleList.xml +++ b/logback-classic/src/test/input/joran/simpleList.xml @@ -1,7 +1,7 @@ - + diff --git a/logback-classic/src/test/input/joran/simpleListWithImports.xml b/logback-classic/src/test/input/joran/simpleListWithImports.xml new file mode 100644 index 0000000000..dd3cf153c4 --- /dev/null +++ b/logback-classic/src/test/input/joran/simpleListWithImports.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/spi/contextListener.xml b/logback-classic/src/test/input/joran/spi/contextListener.xml new file mode 100644 index 0000000000..de5b5bb444 --- /dev/null +++ b/logback-classic/src/test/input/joran/spi/contextListener.xml @@ -0,0 +1,4 @@ + + + + diff --git a/logback-classic/src/test/input/joran/spi/contextListenerWithImports.xml b/logback-classic/src/test/input/joran/spi/contextListenerWithImports.xml new file mode 100644 index 0000000000..d6ab6940ed --- /dev/null +++ b/logback-classic/src/test/input/joran/spi/contextListenerWithImports.xml @@ -0,0 +1,6 @@ + + + + + + diff --git a/logback-classic/src/test/input/joran/statusListener.xml b/logback-classic/src/test/input/joran/statusListener.xml index bc9c7202ae..92c73b3e4d 100644 --- a/logback-classic/src/test/input/joran/statusListener.xml +++ b/logback-classic/src/test/input/joran/statusListener.xml @@ -4,7 +4,7 @@ - + diff --git a/logback-classic/src/test/input/joran/statusListenerWithImports.xml b/logback-classic/src/test/input/joran/statusListenerWithImports.xml new file mode 100644 index 0000000000..9914fddc00 --- /dev/null +++ b/logback-classic/src/test/input/joran/statusListenerWithImports.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/twoAppenders.xml b/logback-classic/src/test/input/joran/twoAppenders.xml new file mode 100644 index 0000000000..0a17024947 --- /dev/null +++ b/logback-classic/src/test/input/joran/twoAppenders.xml @@ -0,0 +1,47 @@ + + + infoLog.log + + infoLogs.%d{yyyy-MM-dd}.log + + 30 + 3GB + + + + INFO + + + + %-4relative [%thread] %-5level %logger{35} - %msg%n + + + + + + errorLog.log + + errorLogs.%d{yyyy-MM-dd}.log + + 30 + 3GB + + + + ERROR + + + + %-4relative [%thread] %-5level %logger{35} - %msg%n + + + + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/input/joran/unreferencedAppender1.xml b/logback-classic/src/test/input/joran/unreferencedAppender1.xml new file mode 100755 index 0000000000..e64a96b30f --- /dev/null +++ b/logback-classic/src/test/input/joran/unreferencedAppender1.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/logback-classic/src/test/input/joran/valueOfConvention.xml b/logback-classic/src/test/input/joran/valueOfConvention.xml index f672df3938..4c47aec103 100755 --- a/logback-classic/src/test/input/joran/valueOfConvention.xml +++ b/logback-classic/src/test/input/joran/valueOfConvention.xml @@ -1,4 +1,5 @@ - + + target/test-output/valueOfConvention_${diff}/infoLog.log diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java deleted file mode 100644 index 0d7047aa3d..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/AllClassicTest.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ org.slf4j.impl.PackageTest.class, ch.qos.logback.classic.PackageTest.class, ch.qos.logback.classic.util.PackageTest.class, - ch.qos.logback.classic.control.PackageTest.class, ch.qos.logback.classic.joran.PackageTest.class, ch.qos.logback.classic.rolling.PackageTest.class, - ch.qos.logback.classic.jmx.PackageTest.class, ch.qos.logback.classic.boolex.PackageTest.class, ch.qos.logback.classic.selector.PackageTest.class, - ch.qos.logback.classic.html.PackageTest.class, ch.qos.logback.classic.net.PackageTest.class, ch.qos.logback.classic.pattern.PackageTest.class, - ch.qos.logback.classic.encoder.PackageTest.class, ch.qos.logback.classic.db.PackageTest.class, ch.qos.logback.classic.spi.PackageTest.class, - ch.qos.logback.classic.turbo.PackageTest.class, ch.qos.logback.classic.sift.PackageTest.class, ch.qos.logback.classic.jul.PackageTest.class, - ch.qos.logback.classic.issue.PackageTest.class }) -public class AllClassicTest { - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/AsyncAppenderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/AsyncAppenderTest.java index 2711724155..da6767d253 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/AsyncAppenderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/AsyncAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,15 +15,18 @@ import ch.qos.logback.classic.net.testObjectBuilders.LoggingEventBuilderInContext; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.UnsynchronizedAppenderBase; import ch.qos.logback.core.read.ListAppender; import ch.qos.logback.core.status.OnConsoleStatusListener; import ch.qos.logback.core.testUtil.RandomUtil; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.MDC; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Ceki Gülcü @@ -32,21 +35,24 @@ public class AsyncAppenderTest { String thisClassName = this.getClass().getName(); - LoggerContext context = new LoggerContext(); + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); AsyncAppender asyncAppender = new AsyncAppender(); ListAppender listAppender = new ListAppender(); OnConsoleStatusListener onConsoleStatusListener = new OnConsoleStatusListener(); - LoggingEventBuilderInContext builder = new LoggingEventBuilderInContext(context, thisClassName, UnsynchronizedAppenderBase.class.getName()); + LoggingEventBuilderInContext builder = new LoggingEventBuilderInContext(loggerContext, thisClassName, + UnsynchronizedAppenderBase.class.getName()); int diff = RandomUtil.getPositiveInt(); - @Before + @BeforeEach public void setUp() { - onConsoleStatusListener.setContext(context); - context.getStatusManager().add(onConsoleStatusListener); + loggerContext.setMDCAdapter(logbackMDCAdapter); + onConsoleStatusListener.setContext(loggerContext); + loggerContext.getStatusManager().add(onConsoleStatusListener); onConsoleStatusListener.start(); - asyncAppender.setContext(context); - listAppender.setContext(context); + asyncAppender.setContext(loggerContext); + listAppender.setContext(loggerContext); listAppender.setName("list"); listAppender.start(); } @@ -57,7 +63,7 @@ public void eventWasPreparedForDeferredProcessing() { asyncAppender.start(); String k = "k" + diff; - MDC.put(k, "v"); + logbackMDCAdapter.put(k, "v"); asyncAppender.doAppend(builder.build(diff)); MDC.clear(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/ClassicTestConstants.java b/logback-classic/src/test/java/ch/qos/logback/classic/ClassicTestConstants.java index 6f6e210ab8..0f1836d859 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/ClassicTestConstants.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/ClassicTestConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/FluentAPILocationExtractionTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/FluentAPILocationExtractionTest.java new file mode 100644 index 0000000000..32b72d7fc9 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/FluentAPILocationExtractionTest.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic; + + +import java.util.ArrayList; +import java.util.List; + +import org.junit.jupiter.api.BeforeEach; + +import ch.qos.logback.classic.pattern.ConverterTest; +import ch.qos.logback.classic.pattern.LineOfCallerConverter; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.pattern.DynamicConverter; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class FluentAPILocationExtractionTest { + static public class WithLocationInfoListAppender extends AppenderBase { + + DynamicConverter converter = new LineOfCallerConverter(); + public List list = new ArrayList<>(); + + protected void append(ILoggingEvent e) { + String val = converter.convert(e); + list.add(val); + } + } + + LoggerContext lc = new LoggerContext(); + Logger logger = lc.getLogger(ConverterTest.class); + WithLocationInfoListAppender wlila = new WithLocationInfoListAppender(); + + @BeforeEach + public void setUp() { + wlila.setContext(lc); + wlila.start(); + + logger.addAppender(wlila); + } + + @Test + public void smoke() { + logger.addAppender(wlila); + // line number to retain is the next line's number + logger.atInfo().log("smoke"); + + assertEquals(1, wlila.list.size()); + String result = wlila.list.get(0); + assertEquals("59", result); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/Foo.java b/logback-classic/src/test/java/ch/qos/logback/classic/Foo.java index 4f2ca09c8c..2b397cf072 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/Foo.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/Foo.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/HLogger.java b/logback-classic/src/test/java/ch/qos/logback/classic/HLogger.java index 3786d5fd67..c071edcc23 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/HLogger.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/HLogger.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -43,8 +43,8 @@ public class HLogger extends MarkerIgnoringBase { private Level effectiveLevel; /** - * The parent of this category. All categories have at least one ancestor - * which is the root category. + * The parent of this category. All categories have at least one ancestor which + * is the root category. */ HLogger parent; @@ -59,12 +59,12 @@ public class HLogger extends MarkerIgnoringBase { private ArrayList> appenderList; /** - * Additivity is set to true by default, that is children inherit the - * appenders of their ancestors by default. If this variable is set to - * false then the appenders located in the ancestors of this - * logger will not be used. However, the children of this logger will inherit - * its appenders, unless the children have their additivity flag set to - * false too. See the user manual for more details. + * Additivity is set to true by default, that is children inherit the appenders + * of their ancestors by default. If this variable is set to false + * then the appenders located in the ancestors of this logger will not be used. + * However, the children of this logger will inherit its appenders, unless the + * children have their additivity flag set to false too. See the + * user manual for more details. */ protected boolean additive = true; @@ -95,7 +95,7 @@ private boolean isRootLogger() { * Get a child by its suffix. * *

- * IMPORTANT: Calls to this method must be within a syncronized block on this + * IMPORTANT: Calls to this method must be within a synchronized block on this * logger! * * @param suffix @@ -151,8 +151,9 @@ private synchronized void handleParentLevelChange(Level newParentLevel) { } /** - * Remove all previously added appenders from this logger instance.

This - * is useful when re-reading configuration information. + * Remove all previously added appenders from this logger instance. + *

+ * This is useful when re-reading configuration information. */ public synchronized void removeAllAppenders() { if (appenderList != null) { @@ -169,11 +170,10 @@ public synchronized void removeAllAppenders() { /** * Invoke all the appenders of this logger. * - * @param event - * The event to log + * @param event The event to log */ public void callAppenders(ILoggingEvent event) { - ///int writes = 0; + /// int writes = 0; for (HLogger l = this; l != null; l = l.parent) { // Protected against simultaneous call to addAppender, removeAppender,... @@ -225,19 +225,19 @@ public synchronized void removeAppender(Appender appender) { * lastPart is "z", then the created child logger will be named "x.y.z". * *

- * IMPORTANT: Calls to this method must be within a syncronized block on this + * IMPORTANT: Calls to this method must be within a synchronized block on this * logger. * - * @param lastPart - * the suffix (i.e. last part) of the child logger name. This - * parameter may not include dots, i.e. the logger separator - * character. + * @param lastPart the suffix (i.e. last part) of the child logger name. This + * parameter may not include dots, i.e. the logger separator + * character. * @return */ HLogger createChildByLastNamePart(final String lastPart) { int i_index = lastPart.indexOf(CoreConstants.DOT); if (i_index != -1) { - throw new IllegalArgumentException("Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]"); + throw new IllegalArgumentException( + "Child name [" + lastPart + " passed as parameter, may not include [" + CoreConstants.DOT + "]"); } if (childrenMap == null) { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/HLoggerContext.java b/logback-classic/src/test/java/ch/qos/logback/classic/HLoggerContext.java index 5e0937b130..11a034a8fc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/HLoggerContext.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/HLoggerContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,6 @@ */ package ch.qos.logback.classic; -import ch.qos.logback.classic.Level; - /** * @author ceki */ @@ -78,8 +76,8 @@ int size() { } /** - * Check if the named logger exists in the hierarchy. If so return - * its reference, otherwise returns null. + * Check if the named logger exists in the hierarchy. If so return its + * reference, otherwise returns null. * * @param name the name of the logger to search for. */ diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LevelTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LevelTest.java index f2ecc39970..1bd3566595 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LevelTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LevelTest.java @@ -1,24 +1,36 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class LevelTest { - - @Test - public void smoke( ) { + public void smoke() { assertEquals(Level.TRACE, Level.toLevel("TRACE")); assertEquals(Level.DEBUG, Level.toLevel("DEBUG")); assertEquals(Level.INFO, Level.toLevel("INFO")); assertEquals(Level.WARN, Level.toLevel("WARN")); assertEquals(Level.ERROR, Level.toLevel("ERROR")); } - + @Test - public void withSpaceSuffix( ) { + public void withSpaceSuffix() { assertEquals(Level.INFO, Level.toLevel("INFO ")); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/Logback1551.java b/logback-classic/src/test/java/ch/qos/logback/classic/Logback1551.java new file mode 100644 index 0000000000..951ff64e65 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/Logback1551.java @@ -0,0 +1,81 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic; + +import ch.qos.logback.core.util.Duration; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public class Logback1551 { + LoggerContext lc; + + @BeforeEach + public void setUp() throws Exception { + lc = new LoggerContext(); + lc.setName("x"); + } + @Test + public void testConcurrentModificationScheduledTasks() { + ScheduledExecutorService scheduledExecutorService = lc.getScheduledExecutorService(); + Duration duration = Duration.buildByMilliseconds(10); + + Runnable runnable = new Runnable() { + public void run() { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }; + ScheduledFuture scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(runnable, + duration.getMilliseconds(), duration.getMilliseconds(), TimeUnit.MILLISECONDS); + + lc.addScheduledFuture(scheduledFuture); + int THREAD_COUNT = 20; + Thread[] threads = new Thread[THREAD_COUNT]; + + for (int i = 0; i < THREAD_COUNT; i++) { + threads[i] = new Thread(new CancelRunnable(lc)); + threads[i].start(); + } + + Arrays.stream(threads).forEach(t-> { + try { + t.join(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + }); + + } + + private class CancelRunnable implements Runnable { + LoggerContext lc; + public CancelRunnable(LoggerContext lc) { + this.lc = lc; + } + + @Override + public void run() { + lc.cancelScheduledTasks(); + } + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextConcurrentResetTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextConcurrentResetTest.java index 097cee8808..df5b3b9363 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextConcurrentResetTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextConcurrentResetTest.java @@ -1,20 +1,36 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic; import java.util.concurrent.CyclicBarrier; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.contention.AbstractMultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import org.junit.jupiter.api.Timeout; -@Ignore +@Disabled public class LoggerContextConcurrentResetTest { static int CONCURRENT_RESET_THREAD_COUNT = 10; // see http://jira.qos.ch/browse/LOGBACK-397 - @Test(timeout = 1000) + @Test + @Timeout(value = 1) public void concurrentReset() throws InterruptedException { LoggerContext loggerContext = new LoggerContext(); CyclicBarrier cyclicBarrier = new CyclicBarrier(CONCURRENT_RESET_THREAD_COUNT); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextDeadlockTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextDeadlockTest.java index ef79216b97..96026f30d6 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextDeadlockTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextDeadlockTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,35 +14,43 @@ package ch.qos.logback.classic; import java.io.ByteArrayInputStream; +import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; +import org.junit.jupiter.api.Timeout; public class LoggerContextDeadlockTest { LoggerContext loggerContext = new LoggerContext(); - JoranConfigurator jc = new JoranConfigurator(); + GetLoggerThread getLoggerThread = new GetLoggerThread(loggerContext); - @Before + @BeforeEach public void setUp() throws Exception { - jc.setContext(loggerContext); + } - @After + @AfterEach public void tearDown() throws Exception { } - @Test(timeout = 20000) - public void testLBCLASSIC_81() throws JoranException { + // LBCLASSIC_81 + // LOGBACK-394 + @Test + @Timeout(value = 20, unit= TimeUnit.SECONDS) + public void test_LOGBACK_394() throws JoranException { getLoggerThread.start(); for (int i = 0; i < 500; i++) { - ByteArrayInputStream baos = new ByteArrayInputStream("".getBytes()); + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + ByteArrayInputStream baos = new ByteArrayInputStream( + "".getBytes()); jc.doConfigure(baos); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextPerfTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextPerfTest.java index da4f387128..a747807925 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextPerfTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextPerfTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,15 @@ */ package ch.qos.logback.classic; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.corpus.CorpusModel; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.contention.ThreadedThroughputCalculator; +@Disabled public class LoggerContextPerfTest { static int THREAD_COUNT = 10000; @@ -32,7 +34,7 @@ public class LoggerContextPerfTest { CorpusModel corpusMaker; - @Before + @BeforeEach public void setUp() throws Exception { } @@ -68,7 +70,7 @@ private RunnableWithCounterAndDone[] buildRunnableArray() { @Test public void computeResults() throws InterruptedException { harness.execute(runnableArray); - harness.printThroughput("getLogger performance: ", true); + harness.printThroughput(runnableArray,"getLogger performance: ", true); } private class GetLoggerRunnable extends RunnableWithCounterAndDone { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextTest.java index 43b383b139..620782f057 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerContextTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,28 +13,18 @@ */ package ch.qos.logback.classic; -import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.Map; - -import org.junit.Before; -import org.junit.Test; - import ch.qos.logback.classic.turbo.NOPTurboFilter; import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.rolling.helper.FileNamePattern; import ch.qos.logback.core.status.StatusManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; public class LoggerContextTest { LoggerContext lc; - @Before + @BeforeEach public void setUp() throws Exception { lc = new LoggerContext(); lc.setName("x"); @@ -166,7 +156,7 @@ public void testStatusWithUnconfiguredContext() { } StatusManager sm = lc.getStatusManager(); - assertTrue("StatusManager has recieved too many messages", sm.getCount() == 1); + assertTrue(sm.getCount() == 1, "StatusManager has recieved too many messages"); } @Test @@ -218,7 +208,7 @@ public void loggerNameEndingInDotOrDollarShouldWork() { Logger logger = lc.getLogger(loggerName); assertEquals(loggerName, logger.getName()); } - + { String loggerName = "toto.x$"; Logger logger = lc.getLogger(loggerName); @@ -242,20 +232,6 @@ public void evaluatorMapPostReset() { assertNotNull(lc.getObject(CoreConstants.EVALUATOR_MAP)); } - @SuppressWarnings("unchecked") - @Test - public void collisionMapsPostReset() { - lc.reset(); - - Map fileCollisions = (Map) lc.getObject(FA_FILENAME_COLLISION_MAP); - assertNotNull(fileCollisions); - assertTrue(fileCollisions.isEmpty()); - - Map filenamePatternCollisionMap = (Map) lc.getObject(CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP); - assertNotNull(filenamePatternCollisionMap); - assertTrue(filenamePatternCollisionMap.isEmpty()); - } - // http://jira.qos.ch/browse/LOGBACK-142 @Test public void concurrentModification() { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerMessageFormattingTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerMessageFormattingTest.java index d4506b811a..ced1d772d7 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerMessageFormattingTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerMessageFormattingTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,20 +13,21 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class LoggerMessageFormattingTest { LoggerContext lc; ListAppender listAppender; - @Before + @BeforeEach public void setUp() { lc = new LoggerContext(); Logger logger = lc.getLogger(Logger.ROOT_LOGGER_NAME); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerPerfTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerPerfTest.java index 9529fa87a0..4bc29ec658 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerPerfTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerPerfTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,9 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.turbo.NOPTurboFilter; @@ -25,7 +23,9 @@ import ch.qos.logback.core.helpers.NOPAppender; import ch.qos.logback.core.testUtil.EnvUtilForTests; -@Ignore +import static org.junit.jupiter.api.Assertions.assertTrue; + +@Disabled public class LoggerPerfTest { static final long NANOS_IN_ONE_SEC = 1000 * 1000 * 1000L; @@ -36,7 +36,7 @@ public class LoggerPerfTest { Logger lbLogger = lc.getLogger(this.getClass()); org.slf4j.Logger logger = lbLogger; - @Before + @BeforeEach public void setUp() throws Exception { } @@ -48,7 +48,7 @@ public void durationOfDisabledLogsWith_1_NOPFilter() { @SuppressWarnings("unused") long referencePerf = 60; - //BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); } double computeDurationOfDisabledLogsWith_1_NOPFilter(int numOfFilters, long len) { @@ -74,7 +74,7 @@ public void durationOfIsDebugEnabled() { @SuppressWarnings("unused") long referencePerf = 15; - //BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); } double computedurationOfIsDebugEnabled(final long len) { @@ -99,7 +99,7 @@ public void durationOfDisabledLog_NoParameters() { @SuppressWarnings("unused") long referencePerf = 18; - //BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); } double computeDurationOfDisabledLog_NoParameters(final long len) { @@ -126,7 +126,8 @@ public void durationOfDisabledLog_1_Parameter() { @SuppressWarnings("unused") long referencePerf = 30; - //BogoPerf.assertDuration(avgDuration, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(avgDuration, referencePerf, + // CoreConstants.REFERENCE_BIPS); } double computeDurationOfDisabledLog_1_Parameter(long len) { @@ -156,7 +157,8 @@ public void durationOfEnabledLog() { @SuppressWarnings("unused") long referencePerf = 800; - // BogoPerf.assertDuration(avgDuration, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(avgDuration, referencePerf, + // CoreConstants.REFERENCE_BIPS); } double computeDurationOfEnabledLog(long len) { @@ -211,8 +213,9 @@ public void testThreadedLogging() throws InterruptedException { double max = ((((double) NANOS_PER_CALL) / NANOS_IN_ONE_SEC) * iterCount) * tolerance; double serialized = (((double) NANOS_PER_CALL) / NANOS_IN_ONE_SEC) * iterCount * threadCount; double actual = ((double) (end - start)) / NANOS_IN_ONE_SEC; - System.out.printf("Sleep duration: %,.4f seconds. Max expected: %,.4f seconds, Serialized: %,.4f\n", actual, max, serialized); - assertTrue("Exceeded maximum expected time.", actual < max); + System.out.printf("Sleep duration: %,.4f seconds. Max expected: %,.4f seconds, Serialized: %,.4f\n", actual, + max, serialized); + assertTrue(actual < max, "Exceeded maximum expected time."); } // ============================================================ diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerSerializationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerSerializationTest.java index 84ec422c64..8690c1c66a 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerSerializationTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerSerializationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,6 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FileInputStream; @@ -24,20 +21,23 @@ import java.util.ArrayList; import java.util.List; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream; import ch.qos.logback.core.net.HardenedObjectInputStream; import ch.qos.logback.core.testUtil.CoreTestConstants; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class LoggerSerializationTest { static final String SERIALIZATION_PREFIX = CoreTestConstants.TEST_INPUT_PREFIX + "/serialization/"; - // force SLF4J initialization for subsequent Logger readResolce ooperaiton + // force SLF4J initialization for subsequent Logger readResolve operation org.slf4j.Logger unused = LoggerFactory.getLogger(this.getClass()); LoggerContext lc; Logger logger; @@ -46,8 +46,8 @@ public class LoggerSerializationTest { ObjectOutputStream oos; HardenedLoggingEventInputStream hardenedLoggingEventInputStream; List whitelist = new ArrayList(); - - @Before + + @BeforeEach public void setUp() throws Exception { lc = new LoggerContext(); lc.setName("testContext"); @@ -58,7 +58,7 @@ public void setUp() throws Exception { whitelist.add(Foo.class.getName()); } - @After + @AfterEach public void tearDown() throws Exception { lc = null; logger = null; @@ -113,15 +113,16 @@ public void deepTreeSerialization() throws IOException { oos.close(); int sizeB = bos.size(); - assertTrue("serialized logger should be less than 100 bytes", sizeA < 100); + assertTrue(sizeA < 100, "serialized logger should be less than 100 bytes"); // logger tree should not influnce serialization - assertTrue("serialized loggers should be nearly the same size a:" + sizeA + ", sizeB:" + sizeB, (sizeA - sizeB) < 10); + assertTrue((sizeA - sizeB) < 10, + "serialized loggers should be nearly the same size a:" + sizeA + ", sizeB:" + sizeB); } private Foo writeAndRead(Foo foo) throws IOException, ClassNotFoundException { writeObject(oos, foo); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); - hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(bis, whitelist); + hardenedLoggingEventInputStream = new HardenedLoggingEventInputStream(bis, whitelist); Foo fooBack = readFooObject(hardenedLoggingEventInputStream); hardenedLoggingEventInputStream.close(); return fooBack; @@ -144,19 +145,23 @@ private void writeObject(ObjectOutputStream oos, Object o) throws IOException { @Test public void testCompatibilityWith_v1_0_11() throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.11.ser"); - HardenedObjectInputStream ois = new HardenedLoggingEventInputStream(fis); // new String[] {Logger.class.getName(), LoggerRemoteView.class.getName()}); + HardenedObjectInputStream ois = new HardenedLoggingEventInputStream(fis); // new String[] + // {Logger.class.getName(), + // LoggerRemoteView.class.getName()}); Logger a = (Logger) ois.readObject(); ois.close(); assertEquals("a", a.getName()); } - // interestingly enough, logback 1.0.11 and earlier can also read loggers serialized by 1.0.12. - // fields not serialized are set to their default values and since the fields are not + // interestingly enough, logback 1.0.11 and earlier can also read loggers + // serialized by 1.0.12. + // fields not serialized are set to their default values and since the fields + // are not // used, it works out nicely @Test public void testCompatibilityWith_v1_0_12() throws IOException, ClassNotFoundException { FileInputStream fis = new FileInputStream(SERIALIZATION_PREFIX + "logger_v1.0.12.ser"); - HardenedObjectInputStream ois = new HardenedObjectInputStream(fis, new String[] {Logger.class.getName()}); + HardenedObjectInputStream ois = new HardenedObjectInputStream(fis, new String[]{Logger.class.getName()}); Logger a = (Logger) ois.readObject(); ois.close(); assertEquals("a", a.getName()); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTest.java index 4b016cbd4e..9a9b9d6d22 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,20 +13,23 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; import ch.qos.logback.core.status.Status; +import org.slf4j.spi.LoggingEventBuilder; +import org.slf4j.spi.NOPLoggingEventBuilder; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; public class LoggerTest { @@ -77,9 +80,9 @@ public void testRootLogger() { Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); LoggerContext lc = logger.getLoggerContext(); - assertNotNull("Returned logger is null", logger); - assertEquals("Return logger isn't named root", logger.getName(), Logger.ROOT_LOGGER_NAME); - assertTrue("logger instances should be indentical", logger == lc.root); + assertNotNull(logger, "Returned logger is null"); + assertEquals(logger.getName(), Logger.ROOT_LOGGER_NAME, "Return logger isn't named root"); + assertTrue(logger == lc.root, "logger instances should be indentical"); } @Test @@ -162,6 +165,13 @@ public void testEnabled_All() throws Exception { checkLevelThreshold(loggerTest, Level.ALL); } + @Test + public void fluentAPIAtDisabledDebugLevelShouldReturnNOPLoggingEventBuilder() throws Exception { + root.setLevel(Level.INFO); + LoggingEventBuilder leb = loggerTest.atLevel(org.slf4j.event.Level.DEBUG); + assertEquals(NOPLoggingEventBuilder.class, leb.getClass()); + } + @Test public void testEnabled_Debug() throws Exception { root.setLevel(Level.DEBUG); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTestHelper.java b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTestHelper.java index b46a06143b..caeb96b273 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTestHelper.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/LoggerTestHelper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,12 @@ */ package ch.qos.logback.classic; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import junit.framework.*; -public class LoggerTestHelper extends TestCase { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class LoggerTestHelper { static void assertNameEquals(Logger logger, String name) { assertNotNull(logger); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/MDCTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/MDCTest.java index ae45371978..9f0f7e9698 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/MDCTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/MDCTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,22 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - import java.util.HashMap; -import org.junit.Test; +import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.MDC; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + public class MDCTest { + int diff = RandomUtil.getPositiveInt(); + + @Test public void test() throws InterruptedException { MDCTestThread threadA = new MDCTestThread("a"); @@ -49,4 +55,41 @@ public void testLBCLASSIC_98() { MDC.setContextMap(new HashMap()); } + + // this test shows the + @Disabled + @Test + public void closableTestA() { + String key = "key-" + diff; + String val = "val-" + diff; + + try (MDC.MDCCloseable closeable = MDC.putCloseable(key, val)) { + if (1 == 1) + throw new IllegalStateException("x"); + } catch (IllegalStateException e) { + assertNotNull(MDC.get(key)); + assertEquals(val, MDC.get(key)); + } finally { + } + assertNull(MDC.get(key)); + } + + @Test + public void closableTest() { + String key = "key-" + diff; + String val = "val-" + diff; + MDC.MDCCloseable closeable = MDC.putCloseable(key, val); + + try { + if (1 == 1) + throw new IllegalStateException("x"); + } catch (IllegalStateException e) { + assertNotNull(MDC.get(key)); + assertEquals(val, MDC.get(key)); + } finally { + closeable.close(); + } + assertNull(MDC.get(key)); + } + } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/MDCTestThread.java b/logback-classic/src/test/java/ch/qos/logback/classic/MDCTestThread.java index 335111446b..81b72c855d 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/MDCTestThread.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/MDCTestThread.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/PackageTest.java deleted file mode 100755 index a2e8a1f980..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ LoggerContextTest.class, LoggerContextConcurrentResetTest.class, LoggerPerfTest.class, ScenarioBasedLoggerContextTest.class, - PatternLayoutTest.class, LoggerTest.class, LoggerSerializationTest.class, LoggerMessageFormattingTest.class, MDCTest.class, - TurboFilteringInLoggerTest.class, AsyncAppenderTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/PatternLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/PatternLayoutTest.java index 5a13512f0d..35f2dbc4d2 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/PatternLayoutTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/PatternLayoutTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,56 +15,75 @@ import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.pattern.ConverterTest; +import ch.qos.logback.classic.pattern.ExceptionalConverter2; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.testUtil.SampleConverter; +import ch.qos.logback.classic.pattern.SampleConverter; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.Context; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.pattern.PatternLayoutBase; import ch.qos.logback.core.pattern.parser.test.AbstractPatternLayoutBaseTest; +import ch.qos.logback.core.spi.ScanException; +import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.testUtil.StringListAppender; import ch.qos.logback.core.util.OptionHelper; import ch.qos.logback.core.util.StatusPrinter; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.MDC; import static ch.qos.logback.classic.ClassicTestConstants.ISO_REGEX; import static ch.qos.logback.classic.ClassicTestConstants.MAIN_REGEX; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.time.Instant; public class PatternLayoutTest extends AbstractPatternLayoutBaseTest { private PatternLayout pl = new PatternLayout(); - private LoggerContext lc = new LoggerContext(); - Logger logger = lc.getLogger(ConverterTest.class); - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); + private LoggerContext loggerContext = new LoggerContext(); + + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger logger = loggerContext.getLogger(ConverterTest.class); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + + int diff = RandomUtil.getPositiveInt(); String aMessage = "Some message"; - - ILoggingEvent le; - public PatternLayoutTest() { - super(); - Exception ex = new Exception("Bogus exception"); - le = makeLoggingEvent(aMessage, ex); - } + Exception ex = new Exception("Bogus exception"); + - @Before + + @BeforeEach public void setUp() { - pl.setContext(lc); + loggerContext.setMDCAdapter(logbackMDCAdapter); + pl.setContext(loggerContext); + //le = makeLoggingEvent(aMessage, ex); } - ILoggingEvent makeLoggingEvent(String msg, Exception ex) { - return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO, msg, ex, null); + /** + * Circumvent JMPS issue: java.lang.NoClassDefFoundError: ch/qos/logback/core/pattern/ExceptionalConverter + * Is logback-clasic not open to logback-core? + * @return + */ + protected String getExceptionalConverterClassName() { + return ExceptionalConverter2.class.getName(); } + LoggingEvent makeLoggingEvent(String msg, Exception ex) { + return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO, + msg, ex, null); + } public ILoggingEvent getEventObject() { return makeLoggingEvent("Some message", null); } - public PatternLayoutBase getPatternLayoutBase() { + public PatternLayoutBase getPatternLayoutBase() { return new PatternLayout(); } @@ -75,17 +94,18 @@ public void testOK() { String val = pl.doLayout(getEventObject()); // 2006-02-01 22:38:06,212 INFO [main] c.q.l.pattern.ConverterTest - Some // message - // 2010-12-29 19:04:26,137 INFO [pool-1-thread-47] c.q.l.c.pattern.ConverterTest - Some message + // 2010-12-29 19:04:26,137 INFO [pool-1-thread-47] c.q.l.c.pattern.ConverterTest + // - Some message String regex = ISO_REGEX + " INFO " + MAIN_REGEX + " c.q.l.c.pattern.ConverterTest - Some message\\s*"; - assertTrue("val=" + val, val.matches(regex)); + assertTrue( val.matches(regex), "val=" + val); } @Test public void testNoExeptionHandler() { pl.setPattern("%m%n"); pl.start(); - String val = pl.doLayout(le); + String val = pl.doLayout(makeLoggingEvent(aMessage, ex)); assertTrue(val.contains("java.lang.Exception: Bogus exception")); } @@ -103,7 +123,7 @@ public void testCompositePattern() { public void contextProperty() { pl.setPattern("%property{a}"); pl.start(); - lc.putProperty("a", "b"); + loggerContext.putProperty("a", "b"); String val = pl.doLayout(getEventObject()); assertEquals("b", val); @@ -113,7 +133,7 @@ public void contextProperty() { public void testNopExeptionHandler() { pl.setPattern("%nopex %m%n"); pl.start(); - String val = pl.doLayout(le); + String val = pl.doLayout(makeLoggingEvent(aMessage, ex)); assertTrue(!val.contains("java.lang.Exception: Bogus exception")); } @@ -121,7 +141,7 @@ public void testNopExeptionHandler() { public void testWithParenthesis() { pl.setPattern("\\(%msg:%msg\\) %msg"); pl.start(); - le = makeLoggingEvent(aMessage, null); + LoggingEvent le = makeLoggingEvent(aMessage, null); String val = pl.doLayout(le); assertEquals("(Some message:Some message) Some message", val); } @@ -134,28 +154,32 @@ public void testWithLettersComingFromLog4j() { String val = pl.doLayout(getEventObject()); // 2006-02-01 22:38:06,212 INFO [main] c.q.l.pattern.ConverterTest - Some // message - String regex = ClassicTestConstants.ISO_REGEX + " INFO " + MAIN_REGEX + " c.q.l.c.pattern.ConverterTest - Some message\\s*"; + String regex = ClassicTestConstants.ISO_REGEX + " INFO " + MAIN_REGEX + + " c.q.l.c.pattern.ConverterTest - Some message\\s*"; assertTrue(val.matches(regex)); } @Test - public void mdcWithDefaultValue() { - String pattern = "%msg %mdc{foo} %mdc{bar:-[null]}"; - pl.setPattern(OptionHelper.substVars(pattern, lc)); + public void mdcWithDefaultValue() throws ScanException { + String pattern = "%msg %mdc{foo1} %mdc{bar:-[null]}"; + pl.setPattern(OptionHelper.substVars(pattern, loggerContext)); pl.start(); - MDC.put("foo", "foo"); + + String key = "foo1"; + + logbackMDCAdapter.put(key, key); try { String val = pl.doLayout(getEventObject()); - assertEquals("Some message foo [null]", val); + assertEquals("Some message foo1 [null]", val); } finally { - MDC.remove("foo"); + logbackMDCAdapter.remove(key); } } @Test public void contextNameTest() { pl.setPattern("%contextName"); - lc.setName("aValue"); + loggerContext.setName("aValue"); pl.start(); String val = pl.doLayout(getEventObject()); assertEquals("aValue", val); @@ -164,20 +188,62 @@ public void contextNameTest() { @Test public void cnTest() { pl.setPattern("%cn"); - lc.setName("aValue"); + loggerContext.setName("aValue"); pl.start(); String val = pl.doLayout(getEventObject()); assertEquals("aValue", val); } + @Test + public void micros() { + verifyMicros(122_891_479, "2011-12-03 10:15:30.122 891 Some message"); + verifyMicros(122_091_479, "2011-12-03 10:15:30.122 091 Some message"); + verifyMicros(122_001_479, "2011-12-03 10:15:30.122 001 Some message"); + } + + void verifyMicros(int nanos, String expected) { + Instant instant = Instant.parse("2011-12-03T10:15:30Z"); + instant = instant.plusNanos(nanos); + LoggingEvent le = makeLoggingEvent(aMessage, null); + le.setInstant(instant); + + pl.setPattern("%date{yyyy-MM-dd HH:mm:ss.SSS, UTC} %micros %message%nopex"); + pl.start(); + + String val = pl.doLayout(le); + assertEquals(expected, val); + } + + @Test + public void epoch() { + verifyEpoch(123, false, "2026-01-15 10:15:30.123 1768472130123 Some message"); + verifyEpoch(456, false, "2026-01-15 10:15:30.456 1768472130456 Some message"); + verifyEpoch(123, true, "2026-01-15 10:15:30.123 1768472130 Some message"); + verifyEpoch(456, true, "2026-01-15 10:15:30.456 1768472130 Some message"); + } + + void verifyEpoch(int millis, boolean secondsNotMillis, String expected) { + Instant instant = Instant.parse("2026-01-15T10:15:30Z"); + instant = instant.plusMillis(millis); + LoggingEvent le = makeLoggingEvent(aMessage, null); + le.setInstant(instant); + + String option = secondsNotMillis ? "{seconds}" : ""; + pl.setPattern("%date{yyyy-MM-dd HH:mm:ss.SSS, UTC} %epoch"+option+" %message%nopex"); + pl.start(); + + String val = pl.doLayout(le); + assertEquals(expected, val); + } + @Override public Context getContext() { - return lc; + return loggerContext; } void configure(String file) throws JoranException { JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(lc); + jc.setContext(loggerContext); jc.doConfigure(file); } @@ -193,31 +259,55 @@ public void testConversionRuleSupportInPatternLayout() throws JoranException { assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0)); } + @Test + public void testConversionRuleAtEnd() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/conversionRuleAtEnd.xml"); + root.getAppender("LIST"); + String msg = "testConversionRuleAtEnd"; + logger.debug(msg); + StringListAppender sla = (StringListAppender) root.getAppender("LIST"); + assertNotNull(sla); + assertEquals(1, sla.strList.size()); + assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0)); + } + + @Test + public void testConversionRuleInIncluded() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "conversionRule/conversionRuleTop0.xml"); + StatusPrinter.print(loggerContext); + root.getAppender("LIST"); + String msg = "testConversionRuleInIncluded"; + logger.debug(msg); + StringListAppender sla = (StringListAppender) root.getAppender("LIST"); + assertNotNull(sla); + assertEquals(1, sla.strList.size()); + assertEquals(SampleConverter.SAMPLE_STR + " - " + msg, sla.strList.get(0)); + } + @Test public void smokeReplace() { pl.setPattern("%replace(a1234b){'\\d{4}', 'XXXX'}"); pl.start(); - StatusPrinter.print(lc); String val = pl.doLayout(getEventObject()); assertEquals("aXXXXb", val); } @Test - public void replaceNewline() { + public void replaceNewline() throws ScanException { String pattern = "%replace(A\nB){'\n', '\n\t'}"; - String substPattern = OptionHelper.substVars(pattern, null, lc); + String substPattern = OptionHelper.substVars(pattern, null, loggerContext); assertEquals(pattern, substPattern); pl.setPattern(substPattern); pl.start(); - StatusPrinter.print(lc); + //StatusPrinter.print(lc); String val = pl.doLayout(makeLoggingEvent("", null)); assertEquals("A\n\tB", val); } - + @Test public void replaceWithJoran() throws JoranException { configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "pattern/replace0.xml"); - StatusPrinter.print(lc); + //StatusPrinter.print(lc); root.getAppender("LIST"); String msg = "And the number is 4111111111110000, expiring on 12/2010"; logger.debug(msg); @@ -229,9 +319,9 @@ public void replaceWithJoran() throws JoranException { @Test public void replaceWithJoran_NEWLINE() throws JoranException { - lc.putProperty("TAB", "\t"); + loggerContext.putProperty("TAB", "\t"); configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "pattern/replaceNewline.xml"); - StatusPrinter.print(lc); + //StatusPrinter.print(lc); root.getAppender("LIST"); String msg = "A\nC"; logger.debug(msg); @@ -240,4 +330,55 @@ public void replaceWithJoran_NEWLINE() throws JoranException { assertEquals(1, sla.strList.size()); assertEquals("A\n\tC", sla.strList.get(0)); } + + @Test + public void prefixConverterSmoke() { + String pattern = "%prefix(%logger) %message"; + pl.setPattern(pattern); + pl.start(); + String val = pl.doLayout(makeLoggingEvent("hello", null)); + assertEquals("logger=" + logger.getName() + " hello", val); + } + + @Test + public void prefixConverterWithMDC() { + String mdcKey = "boo"; + String mdcVal = "moo"; + + String pattern = "%prefix(%level %logger %X{" + mdcKey + "}) %message"; + pl.setPattern(pattern); + pl.start(); + logbackMDCAdapter.put(mdcKey, mdcVal); + try { + String val = pl.doLayout(makeLoggingEvent("hello", null)); + + assertEquals("level=" + "INFO logger=" + logger.getName() + " " + mdcKey + "=" + mdcVal + " hello", val); + + } finally { + MDC.remove(mdcKey); + } + } + + @Test + public void prefixConverterWithProperty() { + + try { + String propertyKey = "px1953"; + String propertyVal = "pxVal"; + + System.setProperty(propertyKey, propertyVal); + + String pattern = "%prefix(%logger %property{" + propertyKey + "}) %message"; + pl.setPattern(pattern); + pl.start(); + + String val = pl.doLayout(makeLoggingEvent("hello", null)); + + assertEquals("logger=" + logger.getName() + " " + propertyKey + "=" + propertyVal + " hello", val); + + } finally { + System.clearProperty("px"); + } + } + } diff --git a/logback-classic/src/test/java/org/slf4j/impl/RecursiveLBAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/RecursiveLBAppender.java similarity index 87% rename from logback-classic/src/test/java/org/slf4j/impl/RecursiveLBAppender.java rename to logback-classic/src/test/java/ch/qos/logback/classic/RecursiveLBAppender.java index 2096618cda..8629a27619 100644 --- a/logback-classic/src/test/java/org/slf4j/impl/RecursiveLBAppender.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/RecursiveLBAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package org.slf4j.impl; +package ch.qos.logback.classic; import java.util.ArrayList; import java.util.List; @@ -19,7 +19,6 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.testUtil.RandomUtil; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/ScenarioBasedLoggerContextTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/ScenarioBasedLoggerContextTest.java index 4a515c649c..3c4a79fbed 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/ScenarioBasedLoggerContextTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/ScenarioBasedLoggerContextTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,11 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - import java.util.List; import java.util.Map; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.control.ControlLogger; import ch.qos.logback.classic.control.ControlLoggerContext; @@ -30,6 +27,9 @@ import ch.qos.logback.classic.control.SetLevel; import ch.qos.logback.classic.control.Scenario; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + public class ScenarioBasedLoggerContextTest { LoggerContext lc; @@ -49,7 +49,7 @@ public void testLength_20000() { } @Test - @Ignore + @Disabled public void testLengthLong() { doScenarioedTest(100 * 1000); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java index 775245de76..4575f8788e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/TurboFilteringInLoggerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,45 +13,74 @@ */ package ch.qos.logback.classic; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.turbo.MDCFilter; import ch.qos.logback.classic.turbo.MarkerFilter; import ch.qos.logback.classic.turbo.TurboFilter; +import ch.qos.logback.core.read.ListAppender; import ch.qos.logback.core.spi.FilterReply; +import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TurboFilteringInLoggerTest { static final String BLUE = "BLUE"; - LoggerContext context; + LoggerContext loggerContext; Logger logger; Marker blueMarker = MarkerFactory.getMarker(BLUE); - @Before + int diff = RandomUtil.getPositiveInt(); + String key = "tfiolKey" + diff; + String value = "val" + diff; + + ListAppender listAppender = new ListAppender<>(); + + + @BeforeEach public void setUp() throws Exception { - context = new LoggerContext(); - context.setName("test"); - context.start(); - logger = context.getLogger(TurboFilteringInLoggerTest.class); + loggerContext = new LoggerContext(); + loggerContext.setName("test"); + loggerContext.start(); + logger = loggerContext.getLogger(TurboFilteringInLoggerTest.class); + + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + root.setLevel(Level.ERROR); + listAppender.start(); + root.addAppender(listAppender); + } - private void addYesFilter() { + private CountingMDCFilter addMDCFilter() { + CountingMDCFilter countingMDCFilter = new CountingMDCFilter(); + countingMDCFilter.setOnMatch("ACCEPT"); + countingMDCFilter.setOnMismatch("DENY"); + countingMDCFilter.setMDCKey(key); + countingMDCFilter.setValue(value); + countingMDCFilter.start(); + loggerContext.addTurboFilter(countingMDCFilter); + return countingMDCFilter; + } + private YesFilter addYesFilter() { YesFilter filter = new YesFilter(); filter.start(); - context.addTurboFilter(filter); + loggerContext.addTurboFilter(filter); + return filter; } - private void addNoFilter() { + private NoFilter addNoFilter() { NoFilter filter = new NoFilter(); filter.start(); - context.addTurboFilter(filter); + loggerContext.addTurboFilter(filter); + return filter; } private void addAcceptBLUEFilter() { @@ -59,7 +88,7 @@ private void addAcceptBLUEFilter() { filter.setMarker(BLUE); filter.setOnMatch("ACCEPT"); filter.start(); - context.addTurboFilter(filter); + loggerContext.addTurboFilter(filter); } private void addDenyBLUEFilter() { @@ -67,7 +96,7 @@ private void addDenyBLUEFilter() { filter.setMarker(BLUE); filter.setOnMatch("DENY"); filter.start(); - context.addTurboFilter(filter); + loggerContext.addTurboFilter(filter); } @Test @@ -79,16 +108,22 @@ public void testIsDebugEnabledWithYesFilter() { @Test public void testIsInfoEnabledWithYesFilter() { - addYesFilter(); + YesFilter filter = addYesFilter(); logger.setLevel(Level.WARN); - assertTrue(logger.isInfoEnabled()); + assertTrue(logger.isInfoEnabled()); // count+=1 + logger.info("testIsInfoEnabledWithYesFilter1"); // count+=1 + logger.atInfo().log("testIsInfoEnabledWithYesFilter2"); // count+=2 + assertEquals(2, listAppender.list.size()); + assertEquals(4, filter.count); } @Test public void testIsWarnEnabledWithYesFilter() { - addYesFilter(); + YesFilter filter = addYesFilter(); logger.setLevel(Level.ERROR); - assertTrue(logger.isWarnEnabled()); + assertTrue(logger.isWarnEnabled()); // count+=1 + assertEquals(1, filter.count); + } @Test @@ -157,23 +192,48 @@ public void testIsErrorEnabledWithDenyBlueFilter() { @Test public void testLoggingContextReset() { addYesFilter(); - assertNotNull(context.getTurboFilterList().get(0)); - context.reset(); - assertEquals(0, context.getTurboFilterList().size()); + assertNotNull(loggerContext.getTurboFilterList().get(0)); + loggerContext.reset(); + assertEquals(0, loggerContext.getTurboFilterList().size()); } + @Test + public void fluentAPI() { + CountingMDCFilter countingMDCFilter = addMDCFilter(); + Logger logger = loggerContext.getLogger(this.getClass()); + logger.atDebug().log("hello 1"); // count+=1 + assertEquals(0, listAppender.list.size()); + MDC.put(key, value); + logger.atDebug().log("hello 2"); // count+=2 + assertEquals(1, listAppender.list.size()); + assertEquals(3, countingMDCFilter.count); + } } class YesFilter extends TurboFilter { + int count = 0; @Override public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { + count++; return FilterReply.ACCEPT; } } class NoFilter extends TurboFilter { + int count = 0; @Override public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { + count++; return FilterReply.DENY; } +} + + +class CountingMDCFilter extends MDCFilter { + int count = 0; + @Override + public FilterReply decide(Marker marker, Logger logger, Level level, String format, Object[] params, Throwable t) { + count++; + return super.decide(marker, logger, level, format, params, t); + } } \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/blackbox/util/EnvUtilTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/blackbox/util/EnvUtilTest.java new file mode 100644 index 0000000000..8afb87487c --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/blackbox/util/EnvUtilTest.java @@ -0,0 +1,68 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.blackbox.util; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.util.ClassicEnvUtil; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.VersionUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests in this class are run during the regular build process. + * + *

It follows that {@link VersionUtil} class is present. Moreover, logback-core and + * logback-classic versions are the same. + *

+ */ +public class EnvUtilTest { + + // Beware: ---------------------------------------- + // Beware: needs to be updated upon version change + // Beware: ---------------------------------------- + static final String EXPECTED_VERSION = "1.5"; + + + @BeforeEach + public void setUp() throws Exception { + + } + + // this test runs fine if run from logback-classic but fails when + // run from logback-core. This is due to the fact that package information + // is added when creating the jar. + @Test + public void versionTest() { + String versionStr = VersionUtil.getVersionOfArtifact(ClassicConstants.class); + assertNotNull(versionStr); + assertTrue(versionStr.startsWith(EXPECTED_VERSION)); + } + + @Test + public void versionCompare() { + String coreVersionStr = VersionUtil.getVersionOfArtifact(CoreConstants.class); + String versionOfLogbackClassic = VersionUtil.getVersionOfArtifact(ClassicConstants.class); + assertNotNull(coreVersionStr); + assertNotNull(versionOfLogbackClassic); + + assertEquals(coreVersionStr, versionOfLogbackClassic); + } + + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/ConditionalWithoutJanino.java b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/ConditionalWithoutJanino.java deleted file mode 100755 index f6165846de..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/ConditionalWithoutJanino.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.boolex; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.joran.conditional.IfAction; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * @author Ceki Gülcü - */ -public class ConditionalWithoutJanino { - - LoggerContext loggerContext = new LoggerContext(); - Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - - void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(file); - } - - // assume that janino.jar ia NOT on the classpath - @Test - public void conditionalWithoutJanino() throws JoranException { - String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/withoutJanino.xml"; - String currentDir = System.getProperty("user.dir"); - if (!currentDir.contains("logback-classic")) { - configFile = "logback-classic/" + configFile; - } - configure(configFile); - StatusPrinter.print(loggerContext); - StatusChecker checker = new StatusChecker(loggerContext); - checker.assertContainsMatch(IfAction.MISSING_JANINO_MSG); - - assertSame(Level.WARN, loggerContext.getLogger("a").getLevel()); - assertSame(Level.WARN, root.getLevel()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/GEventEvaluatorTest.DISABLEDjava b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/GEventEvaluatorTest.DISABLEDjava index 42061de38e..2dea291c38 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/GEventEvaluatorTest.DISABLEDjava +++ b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/GEventEvaluatorTest.DISABLEDjava @@ -20,7 +20,7 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.boolex.EvaluationException; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.ContextUtil; import ch.qos.logback.core.util.StatusPrinter; import org.junit.Before; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/JaninoEventEvaluatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/JaninoEventEvaluatorTest.java deleted file mode 100644 index ba1f78111a..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/JaninoEventEvaluatorTest.java +++ /dev/null @@ -1,267 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.boolex; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; - -import org.junit.Test; -import org.slf4j.MDC; -import org.slf4j.MarkerFactory; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.pattern.ConverterTest; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.boolex.EvaluationException; -import ch.qos.logback.core.boolex.JaninoEventEvaluatorBase; -import ch.qos.logback.core.boolex.Matcher; -import ch.qos.logback.core.filter.EvaluatorFilter; -import ch.qos.logback.core.spi.FilterReply; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.util.StatusPrinter; - -public class JaninoEventEvaluatorTest { - - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger(ConverterTest.class); - - Matcher matcherX = new Matcher(); - - JaninoEventEvaluator jee = new JaninoEventEvaluator(); - - int diff = RandomUtil.getPositiveInt(); - - public JaninoEventEvaluatorTest() { - jee.setContext(loggerContext); - - matcherX.setName("x"); - matcherX.setRegex("^Some\\s.*"); - matcherX.start(); - - } - - LoggingEvent makeLoggingEvent(Exception ex) { - return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO, "Some message", ex, null); - } - - @Test - public void testBasic() throws Exception { - jee.setExpression("message.equals(\"Some message\")"); - jee.start(); - - StatusPrinter.print(loggerContext); - ILoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - } - - @Test - public void testLevel() throws Exception { - jee.setExpression("level > DEBUG"); - jee.start(); - - ILoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - } - - @Test - public void testtimeStamp() throws Exception { - jee.setExpression("timeStamp > 10"); - jee.start(); - - ILoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - } - - @Test - public void testWithMatcher() throws Exception { - jee.setExpression("x.matches(message)"); - jee.addMatcher(matcherX); - jee.start(); - - ILoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - } - - @Test - public void mdcAsString() throws Exception { - String k = "key" + diff; - - MDC.put("key" + diff, "value" + diff); - jee.setExpression("((String) mdc.get(\"" + k + "\")).contains(\"alue\")"); - jee.start(); - StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); - - LoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - MDC.remove(k); - } - - @Test - public void marker() throws Exception { - jee.setExpression("marker.contains(\"BLUE\")"); - jee.start(); - - LoggingEvent event = makeLoggingEvent(null); - event.setMarker(MarkerFactory.getMarker("BLUE")); - assertTrue(jee.evaluate(event)); - } - - @Test - public void withNullMarker_LBCORE_118() throws Exception { - jee.setExpression("marker.contains(\"BLUE\")"); - jee.start(); - - ILoggingEvent event = makeLoggingEvent(null); - try { - jee.evaluate(event); - fail("We should not reach this point"); - } catch (EvaluationException ee) { - // received an exception as expected - } - } - - @Test - public void evaluatorFilterWithNullMarker_LBCORE_118() throws Exception { - EvaluatorFilter ef = new EvaluatorFilter(); - ef.setContext(loggerContext); - - ef.setOnMatch(FilterReply.ACCEPT); - ef.setOnMismatch(FilterReply.DENY); - - jee.setExpression("marker.contains(\"BLUE\")"); - jee.start(); - - ef.setEvaluator(jee); - ef.start(); - ILoggingEvent event = makeLoggingEvent(null); - assertEquals(FilterReply.NEUTRAL, ef.decide(event)); - - } - - @Test - public void testComplex() throws Exception { - jee.setExpression("level >= INFO && x.matches(message) && marker.contains(\"BLUE\")"); - jee.addMatcher(matcherX); - jee.start(); - - LoggingEvent event = makeLoggingEvent(null); - event.setMarker(MarkerFactory.getMarker("BLUE")); - assertTrue(jee.evaluate(event)); - } - - /** - * check that evaluator with bogus exp does not start - * - * @throws Exception - */ - @Test - public void testBogusExp1() { - jee.setExpression("mzzzz.get(\"key\").equals(null)"); - jee.setName("bogus"); - jee.start(); - - assertFalse(jee.isStarted()); - } - - // check that eval stops after errors - @Test - public void testBogusExp2() { - jee.setExpression("mdc.get(\"keyXN89\").equals(null)"); - jee.setName("bogus"); - jee.start(); - - assertTrue(jee.isStarted()); - - ILoggingEvent event = makeLoggingEvent(null); - - for (int i = 0; i < JaninoEventEvaluatorBase.ERROR_THRESHOLD; i++) { - try { - jee.evaluate(event); - fail("should throw an exception"); - } catch (EvaluationException e) { - } - } - // after a few attempts the evaluator should processPriorToRemoval - assertFalse(jee.isStarted()); - - } - - static final long LEN = 10 * 1000; - - // with 6 parameters 400 nanos - // with 7 parameters 460 nanos (all levels + selected fields from - // LoggingEvent) - // with 10 parameters 510 nanos (all levels + fields) - void loop(JaninoEventEvaluator jee, String msg) throws Exception { - ILoggingEvent event = makeLoggingEvent(null); - // final long start = System.nanoTime(); - for (int i = 0; i < LEN; i++) { - jee.evaluate(event); - } - // final long end = System.nanoTime(); - // System.out.println(msg + (end - start) / LEN + " nanos"); - } - - @Test - public void testLoop1() throws Exception { - jee.setExpression("timeStamp > 10"); - jee.start(); - - loop(jee, "timestamp > 10]: "); - } - - @Test - public void testLoop2() throws Exception { - jee.setExpression("x.matches(message)"); - jee.addMatcher(matcherX); - jee.start(); - - loop(jee, "x.matches(message): "); - } - - @Test - public void throwable_LBCLASSIC_155_I() throws EvaluationException { - jee.setExpression("throwable instanceof java.io.IOException"); - jee.start(); - - LoggingEvent event = makeLoggingEvent(new IOException("")); - assertTrue(jee.evaluate(event)); - } - - @Test - public void throwable_LBCLASSIC_155_II() throws EvaluationException { - jee.setExpression("throwableProxy.getClassName().contains(\"IO\")"); - jee.start(); - - LoggingEvent event = makeLoggingEvent(new IOException("")); - assertTrue(jee.evaluate(event)); - } - - @Test - public void nullMDC() throws EvaluationException { - MDC.clear(); - jee.setExpression("mdc.isEmpty()"); - jee.start(); - - LoggingEvent event = makeLoggingEvent(null); - assertTrue(jee.evaluate(event)); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/OnMarkerEvaluatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/OnMarkerEvaluatorTest.java index 2441fa8894..ca2071cd2e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/OnMarkerEvaluatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/OnMarkerEvaluatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,8 @@ */ package ch.qos.logback.classic.boolex; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.MarkerFactory; import ch.qos.logback.classic.Level; @@ -25,13 +22,16 @@ import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.boolex.EvaluationException; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class OnMarkerEvaluatorTest { LoggerContext lc = new LoggerContext(); LoggingEvent event = makeEvent(); OnMarkerEvaluator evaluator = new OnMarkerEvaluator(); - @Before + @BeforeEach public void before() { evaluator.setContext(lc); } @@ -41,7 +41,7 @@ public void smoke() throws EvaluationException { evaluator.addMarker("M"); evaluator.start(); - event.setMarker(MarkerFactory.getMarker("M")); + event.addMarker(MarkerFactory.getMarker("M")); assertTrue(evaluator.evaluate(event)); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/boolex/PackageTest.java deleted file mode 100644 index f2a4a39079..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/boolex/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.boolex; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ JaninoEventEvaluatorTest.class, OnMarkerEvaluatorTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/CLCTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/CLCTest.java deleted file mode 100644 index 7243c99ffd..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/CLCTest.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.control; - -import junit.framework.TestCase; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.control.ControlLogger; -import ch.qos.logback.classic.control.ControlLoggerContext; - -/** - * This class is for testing ControlLoggerContext which is a control class for testing HLoggerContext. - */ -public class CLCTest extends TestCase { - ControlLoggerContext clc; - - protected void setUp() throws Exception { - clc = new ControlLoggerContext(); - } - - public void test1() { - ControlLogger x = clc.getLogger("x"); - assertEquals("x", x.getName()); - assertEquals(clc.getRootLogger(), x.parent); - - ControlLogger abc = clc.getLogger("a.b.c"); - assertEquals("a.b.c", abc.getName()); - assertEquals(Level.DEBUG, abc.getEffectiveLevel()); - } - - public void testCreation() { - ControlLogger xyz = clc.getLogger("x.y.z"); - assertEquals("x.y.z", xyz.getName()); - assertEquals("x.y", xyz.parent.getName()); - assertEquals("x", xyz.parent.parent.getName()); - assertEquals("root", xyz.parent.parent.parent.getName()); - - ControlLogger xyz_ = clc.exists("x.y.z"); - assertEquals("x.y.z", xyz_.getName()); - - } -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLogger.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLogger.java index 247ff76d69..731f641892 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLogger.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLogger.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,15 @@ */ package ch.qos.logback.classic.control; -import org.slf4j.helpers.MarkerIgnoringBase; +import org.slf4j.Marker; +import org.slf4j.helpers.LegacyAbstractLogger; import ch.qos.logback.classic.Level; /** * See javadoc for ControlLoggerContext. */ -public class ControlLogger extends MarkerIgnoringBase { +public class ControlLogger extends LegacyAbstractLogger { private static final long serialVersionUID = 1L; final ControlLogger parent; @@ -75,117 +76,47 @@ public final void trace(String o) { } } - public void trace(String msg, Throwable t) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void trace(String parameterizedMsg, Object param1) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void trace(String parameterizedMsg, Object param1, Object param2) { - // To change body of implemented methods use File | Settings | File Templates. - } - public final void debug(String o) { if (getEffectiveLevel().levelInt <= Level.DEBUG_INT) { throw new UnsupportedOperationException("not yet implemented"); } } - public void debug(String msg, Throwable t) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void debug(String parameterizedMsg, Object param1) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void debug(String parameterizedMsg, Object param1, Object param2) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void error(String msg) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void error(String msg, Throwable t) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void error(String parameterizedMsg, Object param1) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void error(String parameterizedMsg, Object param1, Object param2) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void info(String msg) { - // To change body of implemented methods use File | Settings | File Templates. + @Override + protected String getFullyQualifiedCallerName() { + return ControlLogger.class.getName(); } - public void info(String msg, Throwable t) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void info(String parameterizedMsg, Object param1) { - // To change body of implemented methods use File | Settings | File Templates. - } + @Override + protected void handleNormalizedLoggingCall(org.slf4j.event.Level level, Marker marker, String msg, + Object[] arguments, Throwable throwable) { + // TODO Auto-generated method stub - public void info(String parameterizedMsg, Object param1, Object param2) { - // To change body of implemented methods use File | Settings | File Templates. } + @Override public boolean isTraceEnabled() { return false; } + @Override public boolean isDebugEnabled() { - return false; // To change body of implemented methods use File | Settings | File Templates. - } - - public boolean isErrorEnabled() { - return false; // To change body of implemented methods use File | Settings | File Templates. + return false; } + @Override public boolean isInfoEnabled() { - return false; // To change body of implemented methods use File | Settings | File Templates. + return false; } + @Override public boolean isWarnEnabled() { - return false; // To change body of implemented methods use File | Settings | File Templates. - } - - public void warn(String msg) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void warn(String msg, Throwable t) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void warn(String parameterizedMsg, Object param1) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void warn(String parameterizedMsg, Object param1, Object param2) { - // To change body of implemented methods use File | Settings | File Templates. - } - - public void trace(String format, Object... argArray) { - } - - public void debug(String format, Object... argArray) { - } - - public void info(String format, Object... argArray) { - } - - public void warn(String format, Object... argArray) { + return false; } - public void error(String format, Object... argArray) { + @Override + public boolean isErrorEnabled() { + return false; } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContext.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContext.java index e2208f0b54..57ca02b2c1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContext.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,16 +22,19 @@ /** * This logger context quite optimized for logger retrieval. * - *

It uses a single loggerMap where the key is the logger name and the value - * is the logger. + *

+ * It uses a single loggerMap where the key is the logger name and the value is + * the logger. * - *

This approach acts a lower limit for what is achievable for low memory - * usage as well as low creation/retrieval times. However, this simplicity also + *

+ * This approach acts a lower limit for what is achievable for low memory usage + * as well as low creation/retrieval times. However, this simplicity also * results in slow effective level evaluation, the most frequently exercised * part of the API. * - *

This class is expected to contain correct results, and serve to verify - * the correctness of a more sophisticated implementation. + *

+ * This class is expected to contain correct results, and serve to verify the + * correctness of a more sophisticated implementation. * * @author ceki */ diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContextTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContextTest.java new file mode 100644 index 0000000000..5130930a4b --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ControlLoggerContextTest.java @@ -0,0 +1,57 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.control; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import ch.qos.logback.classic.Level; + +/** + * This class is for testing ControlLoggerContext which is a control class for + * testing HLoggerContext. + */ +public class ControlLoggerContextTest { + ControlLoggerContext clc; + + @BeforeEach + public void setUp() throws Exception { + clc = new ControlLoggerContext(); + } + + @Test + public void smoke() { + ControlLogger x = clc.getLogger("x"); + Assertions.assertEquals("x", x.getName()); + Assertions.assertEquals(clc.getRootLogger(), x.parent); + + ControlLogger abc = clc.getLogger("a.b.c"); + Assertions.assertEquals("a.b.c", abc.getName()); + Assertions.assertEquals(Level.DEBUG, abc.getEffectiveLevel()); + } + + @Test + public void testCreation() { + ControlLogger xyz = clc.getLogger("x.y.z"); + Assertions.assertEquals("x.y.z", xyz.getName()); + Assertions.assertEquals("x.y", xyz.parent.getName()); + Assertions.assertEquals("x", xyz.parent.parent.getName()); + Assertions.assertEquals("root", xyz.parent.parent.parent.getName()); + + ControlLogger xyz_ = clc.exists("x.y.z"); + Assertions.assertEquals("x.y.z", xyz_.getName()); + + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/CreateLogger.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/CreateLogger.java index f32c91fff5..e55821fd5e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/CreateLogger.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/CreateLogger.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/PackageTest.java deleted file mode 100644 index aa4f530761..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.control; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({}) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/Scenario.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/Scenario.java index 58c368d1a8..c0dd0aa543 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/Scenario.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/Scenario.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioAction.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioAction.java index 71526b1659..ba80cdf82f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioAction.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioMaker.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioMaker.java index c017c1cc3d..01457fbe6f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioMaker.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioMaker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,8 +31,8 @@ public class ScenarioMaker { /** * Makes a scenario with len logger creations. Logger names are generated * independently such that the overwhelming majority of logger names will be - * unrelated to each other. Each logger creation may be followed with a - * randomly generated set levelInt action on that logger. + * unrelated to each other. Each logger creation may be followed with a randomly + * generated set levelInt action on that logger. * * @param len * @return diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioRandomUtil.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioRandomUtil.java index 6815a4875f..cefcb6d224 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioRandomUtil.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/ScenarioRandomUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/control/SetLevel.java b/logback-classic/src/test/java/ch/qos/logback/classic/control/SetLevel.java index 1f19e9de04..d9b27e9067 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/control/SetLevel.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/control/SetLevel.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java index affdc18f38..d06998c019 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/Corpus.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,21 +22,27 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.PubLoggerContextVO; import ch.qos.logback.classic.spi.PubLoggingEventVO; import ch.qos.logback.classic.spi.ThrowableProxyUtil; import ch.qos.logback.core.CoreConstants; /** * - *

Usage: + *

+ * Usage: * - *

ILoggingEvent[] eventArray = Corpus.makeStandardCorpus(); + *

+ * ILoggingEvent[] eventArray = Corpus.makeStandardCorpus(); * - *

if you wish to dump the events into a file, say "/corpus.log" : + *

+ * if you wish to dump the events into a file, say "/corpus.log" : * - *

Corpus.dump(eventArray, "/corpus.log"); + *

+ * Corpus.dump(eventArray, "/corpus.log"); * - *

For the model behind the corpus, refer to {@link CorpusModel}. + *

+ * For the model behind the corpus, refer to {@link CorpusModel}. * * @author Ceki Gülcü * @@ -53,8 +59,8 @@ static public List getStandatdCorpusWordList() throws IOException { } /** - * Make a standard corpus. The standard corpus has - * {@link #STANDARD_CORPUS_SIZE} elements. + * Make a standard corpus. The standard corpus has {@link #STANDARD_CORPUS_SIZE} + * elements. * * @return event array representing the standard corpus * @throws IOException @@ -66,7 +72,7 @@ static public ILoggingEvent[] makeStandardCorpus() throws IOException { } static public ILoggingEvent[] make(CorpusModel corpusModel, int n, boolean withCallerData) { - LoggerContextVO lcVO = corpusModel.getRandomlyNamedLoggerContextVO(); + PubLoggerContextVO lcVO = corpusModel.getRandomlyNamedLoggerContextVO(); PubLoggingEventVO[] plevoArray = new PubLoggingEventVO[n]; for (int i = 0; i < n; i++) { PubLoggingEventVO e = new PubLoggingEventVO(); @@ -81,7 +87,8 @@ static public ILoggingEvent[] make(CorpusModel corpusModel, int n, boolean withC e.argumentArray = corpusModel.getRandomArgumentArray(logStatement.mat.numberOfArguments); if (withCallerData) { - e.callerDataArray = corpusModel.getRandomCallerData(ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH, e.loggerName); + e.callerDataArray = corpusModel.getRandomCallerData(ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH, + e.loggerName); } e.throwableProxy = logStatement.throwableProxy; e.threadName = corpusModel.getRandomThreadNameFromPool(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java index 94e22516b3..d07ae917ba 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/CorpusModel.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,6 +20,7 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ClassPackagingData; import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.PubLoggerContextVO; import ch.qos.logback.classic.spi.StackTraceElementProxy; import ch.qos.logback.classic.spi.ThrowableProxy; import ch.qos.logback.classic.spi.ThrowableProxyVO; @@ -27,7 +28,8 @@ /** * Models the corpus. * - *

This contains the probability distributions of levels, logger names, + *

+ * This contains the probability distributions of levels, logger names, * messages, message arguments. * * @author Ceki Gülcü @@ -161,14 +163,15 @@ private String getRandomLoggerNameFromPool(String[] loggerNamePool) { public long getRandomTimeStamp() { // subtract 1 so that 0 is allowed - lastTimeStamp += RandomUtil.gaussianAsPositiveInt(random, AVERAGE_MILLIS_INCREMENT, STD_DEV_FOR_MILLIS_INCREMENT) - 1; + lastTimeStamp += RandomUtil.gaussianAsPositiveInt(random, AVERAGE_MILLIS_INCREMENT, + STD_DEV_FOR_MILLIS_INCREMENT) - 1; return lastTimeStamp; } - LoggerContextVO getRandomlyNamedLoggerContextVO() { + PubLoggerContextVO getRandomlyNamedLoggerContextVO() { LoggerContext lc = new LoggerContext(); lc.setName(getRandomJavaIdentifier()); - return new LoggerContextVO(lc); + return new PubLoggerContextVO(lc); } String getRandomWord() { @@ -188,7 +191,8 @@ String extractLastPart(String loggerName) { public StackTraceElement[] getRandomCallerData(int depth, String loggerName) { StackTraceElement[] cda = new StackTraceElement[depth]; - StackTraceElement cd = new StackTraceElement(loggerName, getRandomJavaIdentifier(), extractLastPart(loggerName), 0); + StackTraceElement cd = new StackTraceElement(loggerName, getRandomJavaIdentifier(), extractLastPart(loggerName), + 0); cda[0] = cd; for (int i = 1; i < depth; i++) { String ln = getRandomLoggerNameFromPool(loggerNamePool); @@ -243,7 +247,8 @@ private LogStatement makeRandomLogStatement(String[] loggerNamePool) { private Throwable getRandomThrowable(Level level) { double rn = random.nextDouble(); - if ((level == Level.WARN && rn < THROWABLE_PROPABILITY_FOR_WARNING) || (level == Level.ERROR && rn < THROWABLE_PROPABILITY_FOR_ERRORS)) { + if ((level == Level.WARN && rn < THROWABLE_PROPABILITY_FOR_WARNING) + || (level == Level.ERROR && rn < THROWABLE_PROPABILITY_FOR_ERRORS)) { return ExceptionBuilder.build(random, NESTING_PROBABILITY); } else { return null; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/ExceptionBuilder.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/ExceptionBuilder.java index c288fe53ed..a163c0a3b2 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/ExceptionBuilder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/ExceptionBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/LogStatement.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/LogStatement.java index 68bfc2a44c..3cd94f43fc 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/LogStatement.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/LogStatement.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,9 +20,10 @@ * Captures the data contained within a log statement, that is the data that the * developer puts in the source code when he writes: * - *

logger.debug("hello world"); + *

+ * logger.debug("hello world"); * - * @author Ceki Gülcü + * @author Ceki Gülcü */ public class LogStatement { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/MessageArgumentTuple.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/MessageArgumentTuple.java index a05159c909..f970144c23 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/MessageArgumentTuple.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/MessageArgumentTuple.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/RandomUtil.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/RandomUtil.java index 77f4c5acf4..d5cea2c7e1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/RandomUtil.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/RandomUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,7 +18,7 @@ public class RandomUtil { /** - * Approximate a gaussian distrib with only positive integer values + * Approximate a gaussian distribution with only positive integer values * * @param average * @param stdDeviation diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/TextFileUtil.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/TextFileUtil.java index c3f742a501..63ce66567e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpus/TextFileUtil.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpus/TextFileUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/RandomUtilTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/RandomUtilTest.java index 2d85074b5c..daee17e3c9 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/RandomUtilTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/RandomUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,19 +13,18 @@ */ package ch.qos.logback.classic.corpusTest; -import static org.junit.Assert.assertEquals; - import java.util.Random; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; import ch.qos.logback.classic.corpus.RandomUtil; +import org.junit.jupiter.api.Test; public class RandomUtilTest { long now = System.currentTimeMillis(); - @Before + @BeforeEach public void setup() { System.out.println(RandomUtilTest.class.getName() + " now=" + now); } @@ -45,7 +44,7 @@ public void smoke() { } double avg = average(valArray); - assertEquals(EXPECTED_AVERAGE, avg, 0.3); + Assertions.assertEquals(EXPECTED_AVERAGE, avg, 0.3); } public double average(int[] va) { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/TextFileUtilTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/TextFileUtilTest.java index 72b7e26c62..328dcd8344 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/TextFileUtilTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/corpusTest/TextFileUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,24 +13,23 @@ */ package ch.qos.logback.classic.corpusTest; -import static org.junit.Assert.*; - import java.io.BufferedReader; import java.io.IOException; import java.io.StringReader; import java.util.List; -import org.junit.Test; - import ch.qos.logback.classic.corpus.TextFileUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class TextFileUtilTest { @Test public void smoke() throws IOException { String s = "When on board H.M.S. 'Beagle,' as naturalist, I was much struck with\r\n" - + "certain facts in the distribution of the inhabitants of South America,\r\n" - + "and in the geological relations of the present to the past inhabitants\r\n" + "of that continent."; + + "certain facts in the distribution of the inhabitants of South America,\r\n" + + "and in the geological relations of the present to the past inhabitants\r\n" + "of that continent."; StringReader sr = new StringReader(s); BufferedReader br = new BufferedReader(sr); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2Test.java deleted file mode 100644 index 88757aa693..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2Test.java +++ /dev/null @@ -1,218 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Date; -import java.util.Map; - -import org.apache.log4j.MDC; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.db.DriverManagerConnectionSource; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -public class DBAppenderH2Test { - - LoggerContext loggerContext = new LoggerContext();; - Logger logger; - DBAppender appender; - DriverManagerConnectionSource connectionSource; - DBAppenderH2TestFixture dbAppenderH2TestFixture; - int diff = RandomUtil.getPositiveInt(); - StatusChecker checker = new StatusChecker(loggerContext); - - @Before - public void setUp() throws SQLException { - dbAppenderH2TestFixture = new DBAppenderH2TestFixture(); - dbAppenderH2TestFixture.setUp(); - loggerContext.setName("default"); - logger = loggerContext.getLogger("root"); - appender = new DBAppender(); - appender.setName("DB"); - appender.setContext(loggerContext); - connectionSource = new DriverManagerConnectionSource(); - connectionSource.setContext(loggerContext); - connectionSource.setDriverClass(DBAppenderH2TestFixture.H2_DRIVER_CLASS); - connectionSource.setUrl(dbAppenderH2TestFixture.url); - System.out.println("cs.url=" + dbAppenderH2TestFixture.url); - connectionSource.setUser(dbAppenderH2TestFixture.user); - connectionSource.setPassword(dbAppenderH2TestFixture.password); - - connectionSource.start(); - appender.setConnectionSource(connectionSource); - appender.start(); - } - - @After - public void tearDown() throws SQLException { - logger = null; - loggerContext = null; - appender = null; - connectionSource = null; - dbAppenderH2TestFixture.tearDown(); - } - - @Test - public void testAppendLoggingEvent() throws SQLException { - ILoggingEvent event = createLoggingEvent(); - - appender.append(event); - - StatusPrinter.print(loggerContext); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM logging_event"); - if (rs.next()) { - assertEquals(event.getTimeStamp(), rs.getLong(DBAppender.TIMESTMP_INDEX)); - assertEquals(event.getFormattedMessage(), rs.getString(DBAppender.FORMATTED_MESSAGE_INDEX)); - assertEquals(event.getLoggerName(), rs.getString(DBAppender.LOGGER_NAME_INDEX)); - assertEquals(event.getLevel().toString(), rs.getString(DBAppender.LEVEL_STRING_INDEX)); - assertEquals(event.getThreadName(), rs.getString(DBAppender.THREAD_NAME_INDEX)); - assertEquals(DBHelper.computeReferenceMask(event), rs.getShort(DBAppender.REFERENCE_FLAG_INDEX)); - assertEquals(String.valueOf(diff), rs.getString(DBAppender.ARG0_INDEX)); - StackTraceElement callerData = event.getCallerData()[0]; - assertEquals(callerData.getFileName(), rs.getString(DBAppender.CALLER_FILENAME_INDEX)); - assertEquals(callerData.getClassName(), rs.getString(DBAppender.CALLER_CLASS_INDEX)); - assertEquals(callerData.getMethodName(), rs.getString(DBAppender.CALLER_METHOD_INDEX)); - } else { - fail("No row was inserted in the database"); - } - - rs.close(); - stmt.close(); - } - - @Test - public void testAppendThrowable() throws SQLException { - ILoggingEvent event = createLoggingEvent(); - appender.append(event); - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM LOGGING_EVENT_EXCEPTION WHERE EVENT_ID=1"); - rs.next(); - String expected = "java.lang.Exception: test Ex"; - String firstLine = rs.getString(3); - assertTrue("[" + firstLine + "] does not match [" + expected + "]", firstLine.contains(expected)); - - int i = 0; - while (rs.next()) { - expected = event.getThrowableProxy().getStackTraceElementProxyArray()[i].toString(); - String st = rs.getString(3); - assertTrue("[" + st + "] does not match [" + expected + "]", st.contains(expected)); - i++; - } - assertTrue(i != 0); - - rs.close(); - stmt.close(); - } - - @Test - public void withNullArgument() throws SQLException { - ILoggingEvent event = createLoggingEvent("Processing code {}; code type is {}; request date {}; record from date {}", new Object[] { 1, 2, new Date(), - null }); - appender.append(event); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM logging_event"); - if (rs.next()) { - assertEquals(event.getTimeStamp(), rs.getLong(DBAppender.TIMESTMP_INDEX)); - assertEquals(event.getFormattedMessage(), rs.getString(DBAppender.FORMATTED_MESSAGE_INDEX)); - } - } - - @Test - public void testContextInfo() throws SQLException { - loggerContext.putProperty("testKey1", "testValue1"); - MDC.put("k" + diff, "v" + diff); - ILoggingEvent event = createLoggingEvent(); - - appender.append(event); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM LOGGING_EVENT_PROPERTY WHERE EVENT_ID=1"); - Map map = appender.mergePropertyMaps(event); - int i = 0; - while (rs.next()) { - String key = rs.getString(2); - assertEquals(map.get(key), rs.getString(3)); - i++; - } - assertTrue(map.size() != 0); - assertEquals(map.size(), i); - rs.close(); - stmt.close(); - } - - @Test - public void testAppendMultipleEvents() throws SQLException { - for (int i = 0; i < 10; i++) { - ILoggingEvent event = createLoggingEvent(); - appender.append(event); - } - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM logging_event"); - int count = 0; - while (rs.next()) { - count++; - } - assertEquals(10, count); - - rs.close(); - stmt.close(); - } - - // http://jira.qos.ch/browse/LOGBACK-805 - @Test - public void emptyCallerDataShouldBeHandledGracefully() { - LoggingEvent event = createLoggingEvent(); - event.setCallerData(new StackTraceElement[0]); - appender.append(event); - - event.setCallerData(new StackTraceElement[] { null }); - appender.append(event); - - checker.assertIsErrorFree(); - } - - private LoggingEvent createLoggingEvent(String msg, Object[] args) { - return new LoggingEvent(this.getClass().getName(), logger, Level.DEBUG, msg, new Exception("test Ex"), args); - } - - private LoggingEvent createLoggingEvent() { - return createLoggingEvent("test message", new Integer[] { diff }); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2TestFixture.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2TestFixture.java deleted file mode 100644 index df41c0217b..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderH2TestFixture.java +++ /dev/null @@ -1,141 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static org.junit.Assert.assertNotNull; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Properties; - -import org.h2.Driver; - -import ch.qos.logback.core.testUtil.RandomUtil; - -public class DBAppenderH2TestFixture { - - public enum H2Mode { - MEM, FILE, NET; - } - - public static final String H2_DRIVER_CLASS = "org.h2.Driver"; - String url = null; - String user = "sa"; - String password = ""; - - // boolean isNetwork = true; - H2Mode mode = H2Mode.MEM; - - int diff = RandomUtil.getPositiveInt(); - - Connection connection; - - public void setUp() throws SQLException { - - switch (mode) { - case NET: - url = "jdbc:h2:tcp://localhost:4808/test"; - break; - case MEM: - url = "jdbc:h2:mem:test" + diff; - break; - case FILE: - url = "jdbc:hsqldb:file:test;sql.enforce_strict_size=true"; - break; - - } - connection = newConnection(); - createTables(); - } - - public void tearDown() throws SQLException { - dropTables(); - connection.close(); - } - - Connection newConnection() throws SQLException { - System.out.println("url=" + url); - org.h2.Driver driver = Driver.load(); - Properties props = new Properties(); - props.setProperty("user", user); - props.setProperty("password", password); - return driver.connect(url, props); - } - - private void createTables() throws SQLException { - assertNotNull(connection); - StringBuilder buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT ("); - buf.append("TIMESTMP BIGINT NOT NULL,"); - buf.append("FORMATTED_MESSAGE LONGVARCHAR NOT NULL,"); - buf.append("LOGGER_NAME VARCHAR(256) NOT NULL,"); - buf.append("LEVEL_STRING VARCHAR(256) NOT NULL,"); - buf.append("THREAD_NAME VARCHAR(256),"); - buf.append("REFERENCE_FLAG SMALLINT,"); - buf.append("ARG0 VARCHAR(256),"); - buf.append("ARG1 VARCHAR(256),"); - buf.append("ARG2 VARCHAR(256),"); - buf.append("ARG3 VARCHAR(256),"); - buf.append("CALLER_FILENAME VARCHAR(256), "); - buf.append("CALLER_CLASS VARCHAR(256), "); - buf.append("CALLER_METHOD VARCHAR(256), "); - buf.append("CALLER_LINE CHAR(4), "); - buf.append("EVENT_ID IDENTITY NOT NULL);"); - executeQuery(connection, buf.toString()); - - buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT_PROPERTY ("); - buf.append("EVENT_ID BIGINT NOT NULL,"); - buf.append("MAPPED_KEY VARCHAR(254) NOT NULL,"); - buf.append("MAPPED_VALUE LONGVARCHAR,"); - buf.append("PRIMARY KEY(EVENT_ID, MAPPED_KEY),"); - buf.append("FOREIGN KEY (EVENT_ID) REFERENCES LOGGING_EVENT(EVENT_ID));"); - executeQuery(connection, buf.toString()); - - buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT_EXCEPTION ("); - buf.append("EVENT_ID BIGINT NOT NULL,"); - buf.append("I SMALLINT NOT NULL,"); - buf.append("TRACE_LINE VARCHAR(256) NOT NULL,"); - buf.append("PRIMARY KEY(EVENT_ID, I),"); - buf.append("FOREIGN KEY (EVENT_ID) REFERENCES LOGGING_EVENT(EVENT_ID));"); - executeQuery(connection, buf.toString()); - } - - private void dropTables() throws SQLException { - StringBuilder buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT_EXCEPTION IF EXISTS;"); - executeQuery(connection, buf.toString()); - - buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT_PROPERTY IF EXISTS;"); - executeQuery(connection, buf.toString()); - - buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT IF EXISTS;"); - executeQuery(connection, buf.toString()); - } - - private void executeQuery(Connection conn, String expression) throws SQLException { - Statement st = null; - st = conn.createStatement(); - int i = st.executeUpdate(expression); - if (i == -1) { - throw new IllegalStateException("db error : " + expression); - } - st.close(); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTest.java deleted file mode 100644 index 3f007194bc..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTest.java +++ /dev/null @@ -1,199 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Map; - -import org.apache.log4j.MDC; -import org.junit.*; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.db.DriverManagerConnectionSource; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.util.StatusPrinter; - -public class DBAppenderHSQLTest { - - LoggerContext lc; - Logger logger; - DBAppender appender; - DriverManagerConnectionSource connectionSource; - - static DBAppenderHSQLTestFixture DB_APPENDER_HSQL_TEST_FIXTURE; - int diff = RandomUtil.getPositiveInt(); - int existingRowCount; - Statement stmt; - - @BeforeClass - public static void beforeClass() throws SQLException { - DB_APPENDER_HSQL_TEST_FIXTURE = new DBAppenderHSQLTestFixture(); - DB_APPENDER_HSQL_TEST_FIXTURE.setUp(); - } - - @AfterClass - public static void afterClass() throws SQLException { - DB_APPENDER_HSQL_TEST_FIXTURE.tearDown(); - } - - @Before - public void setUp() throws SQLException { - lc = new LoggerContext(); - lc.setName("default"); - logger = lc.getLogger("root"); - appender = new DBAppender(); - appender.setName("DB"); - appender.setContext(lc); - connectionSource = new DriverManagerConnectionSource(); - connectionSource.setContext(lc); - connectionSource.setDriverClass(DBAppenderHSQLTestFixture.HSQLDB_DRIVER_CLASS); - connectionSource.setUrl(DB_APPENDER_HSQL_TEST_FIXTURE.url); - connectionSource.setUser(DB_APPENDER_HSQL_TEST_FIXTURE.user); - connectionSource.setPassword(DB_APPENDER_HSQL_TEST_FIXTURE.password); - connectionSource.start(); - appender.setConnectionSource(connectionSource); - appender.start(); - - stmt = connectionSource.getConnection().createStatement(); - existingRowCount = existingRowCount(stmt); - - } - - @After - public void tearDown() throws SQLException { - logger = null; - lc = null; - appender = null; - connectionSource = null; - stmt.close(); - } - - int existingRowCount(Statement stmt) throws SQLException { - ResultSet rs = stmt.executeQuery("SELECT count(*) FROM logging_event"); - int result = -1; - if (rs.next()) { - result = rs.getInt(1); - } - rs.close(); - return result; - } - - @Test - public void testAppendLoggingEvent() throws SQLException { - - ILoggingEvent event = createLoggingEvent(); - appender.append(event); - StatusPrinter.printInCaseOfErrorsOrWarnings(lc); - - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM logging_event where EVENT_ID = " + existingRowCount); - if (rs.next()) { - assertEquals(event.getTimeStamp(), rs.getLong(DBAppender.TIMESTMP_INDEX)); - assertEquals(event.getFormattedMessage(), rs.getString(DBAppender.FORMATTED_MESSAGE_INDEX)); - assertEquals(event.getLoggerName(), rs.getString(DBAppender.LOGGER_NAME_INDEX)); - assertEquals(event.getLevel().toString(), rs.getString(DBAppender.LEVEL_STRING_INDEX)); - assertEquals(event.getThreadName(), rs.getString(DBAppender.THREAD_NAME_INDEX)); - assertEquals(DBHelper.computeReferenceMask(event), rs.getShort(DBAppender.REFERENCE_FLAG_INDEX)); - assertEquals(String.valueOf(diff), rs.getString(DBAppender.ARG0_INDEX)); - StackTraceElement callerData = event.getCallerData()[0]; - assertEquals(callerData.getFileName(), rs.getString(DBAppender.CALLER_FILENAME_INDEX)); - assertEquals(callerData.getClassName(), rs.getString(DBAppender.CALLER_CLASS_INDEX)); - assertEquals(callerData.getMethodName(), rs.getString(DBAppender.CALLER_METHOD_INDEX)); - } else { - fail("No row was inserted in the database"); - } - rs.close(); - } - - @Test - public void testAppendThrowable() throws SQLException { - ILoggingEvent event = createLoggingEvent(); - appender.append(event); - - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM LOGGING_EVENT_EXCEPTION where EVENT_ID = " + existingRowCount); - - rs.next(); - String expected = "java.lang.Exception: test Ex"; - String firstLine = rs.getString(3); - assertTrue("[" + firstLine + "] does not match [" + expected + "]", firstLine.contains(expected)); - - int i = 0; - while (rs.next()) { - expected = event.getThrowableProxy().getStackTraceElementProxyArray()[i].toString(); - String st = rs.getString(3); - assertTrue("[" + st + "] does not match [" + expected + "]", st.contains(expected)); - i++; - } - assertTrue(i != 0); - rs.close(); - } - - @Test - public void testContextInfo() throws SQLException { - lc.putProperty("testKey1", "testValue1"); - MDC.put("k" + diff, "v" + diff); - ILoggingEvent event = createLoggingEvent(); - - appender.append(event); - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM LOGGING_EVENT_PROPERTY WHERE EVENT_ID = " + existingRowCount); - Map map = appender.mergePropertyMaps(event); - System.out.println("ma.size=" + map.size()); - int i = 0; - while (rs.next()) { - String key = rs.getString(2); - assertEquals(map.get(key), rs.getString(3)); - i++; - } - assertTrue(map.size() != 0); - assertEquals(map.size(), i); - rs.close(); - } - - @Test - public void testAppendMultipleEvents() throws SQLException { - int numEvents = 3; - for (int i = 0; i < numEvents; i++) { - ILoggingEvent event = createLoggingEvent(); - appender.append(event); - } - - Statement stmt = connectionSource.getConnection().createStatement(); - ResultSet rs = null; - rs = stmt.executeQuery("SELECT * FROM logging_event WHERE EVENT_ID >=" + existingRowCount); - int count = 0; - while (rs.next()) { - count++; - } - assertEquals(numEvents, count); - rs.close(); - } - - private ILoggingEvent createLoggingEvent() { - return new LoggingEvent(this.getClass().getName(), logger, Level.DEBUG, "test message", new Exception("test Ex"), new Integer[] { diff }); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTestFixture.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTestFixture.java deleted file mode 100644 index f722b2129b..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderHSQLTestFixture.java +++ /dev/null @@ -1,175 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static org.junit.Assert.assertNotNull; - -import java.io.PrintWriter; -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.Properties; - -import org.hsqldb.Server; -import org.hsqldb.jdbcDriver; -import org.hsqldb.server.ServerConstants; - -public class DBAppenderHSQLTestFixture { - - public static final String HSQLDB_DRIVER_CLASS = "org.hsqldb.jdbcDriver"; - // String serverProps; - String url = null; - String user = "sa"; - String password = ""; - Server server; - - // boolean isNetwork = true; - HsqlMode mode = HsqlMode.MEM; - - public void setUp() throws SQLException { - - switch (mode) { - case NET: - url = "jdbc:hsqldb:hsql://localhost:4808/test"; - break; - case MEM: - url = "jdbc:hsqldb:mem:test;sql.enforce_strict_size=true"; - server = new Server(); - server.setDatabaseName(0, "test"); - server.setDatabasePath(0, url); - server.setLogWriter(new PrintWriter(System.out)); - server.setErrWriter(new PrintWriter(System.out)); - server.setTrace(false); - server.setSilent(false); - server.start(); - - break; - case FILE: - url = "jdbc:hsqldb:file:test;sql.enforce_strict_size=true"; - break; - - } - - // try { - // Class.forName(DRIVER_CLASS); - // } catch (Exception e) { - // e.printStackTrace(); - // System.out.println(this + ".setUp() error: " + e.getMessage()); - // } - // Thread.yield(); - System.out.println(server.getState()); - - int waitCount = 0; - while (server.getState() != ServerConstants.SERVER_STATE_ONLINE && waitCount < 5) { - try { - waitCount++; - Thread.sleep(1); - } catch (InterruptedException e) { - } - } - createTables(); - } - - public void tearDown() throws SQLException { - dropTables(); - - if (mode == HsqlMode.MEM) { - server.stop(); - server = null; - } - } - - Connection newConnection() throws SQLException { - jdbcDriver driver = new jdbcDriver(); - Properties props = new Properties(); - props.setProperty("user", user); - props.setProperty("password", password); - return driver.connect(url, props); - - // return DriverManager.getConnection(url, user, password); - } - - private void createTables() throws SQLException { - Connection conn = newConnection(); - assertNotNull(conn); - StringBuilder buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT ("); - buf.append("TIMESTMP BIGINT NOT NULL,"); - buf.append("FORMATTED_MESSAGE LONGVARCHAR NOT NULL,"); - buf.append("LOGGER_NAME VARCHAR(256) NOT NULL,"); - buf.append("LEVEL_STRING VARCHAR(256) NOT NULL,"); - buf.append("THREAD_NAME VARCHAR(256),"); - buf.append("REFERENCE_FLAG SMALLINT,"); - - buf.append("ARG0 VARCHAR(256),"); - buf.append("ARG1 VARCHAR(256),"); - buf.append("ARG2 VARCHAR(256),"); - buf.append("ARG3 VARCHAR(256),"); - - buf.append("CALLER_FILENAME VARCHAR(256), "); - buf.append("CALLER_CLASS VARCHAR(256), "); - buf.append("CALLER_METHOD VARCHAR(256), "); - buf.append("CALLER_LINE CHAR(4), "); - buf.append("EVENT_ID BIGINT NOT NULL IDENTITY);"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT_PROPERTY ("); - buf.append("EVENT_ID BIGINT NOT NULL,"); - buf.append("MAPPED_KEY VARCHAR(254) NOT NULL,"); - buf.append("MAPPED_VALUE LONGVARCHAR,"); - buf.append("PRIMARY KEY(EVENT_ID, MAPPED_KEY),"); - buf.append("FOREIGN KEY (EVENT_ID) REFERENCES LOGGING_EVENT(EVENT_ID));"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("CREATE TABLE LOGGING_EVENT_EXCEPTION ("); - buf.append("EVENT_ID BIGINT NOT NULL,"); - buf.append("I SMALLINT NOT NULL,"); - buf.append("TRACE_LINE VARCHAR(256) NOT NULL,"); - buf.append("PRIMARY KEY(EVENT_ID, I),"); - buf.append("FOREIGN KEY (EVENT_ID) REFERENCES LOGGING_EVENT(EVENT_ID));"); - query(conn, buf.toString()); - } - - private void dropTables() throws SQLException { - Connection conn = newConnection(); - StringBuilder buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT_EXCEPTION IF EXISTS;"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT_PROPERTY IF EXISTS;"); - query(conn, buf.toString()); - - buf = new StringBuilder(); - buf.append("DROP TABLE LOGGING_EVENT IF EXISTS;"); - query(conn, buf.toString()); - } - - private void query(Connection conn, String expression) throws SQLException { - - Statement st = null; - st = conn.createStatement(); - int i = st.executeUpdate(expression); - if (i == -1) { - System.out.println("db error : " + expression); - } - st.close(); - } - - public enum HsqlMode { - MEM, FILE, NET; - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java deleted file mode 100755 index b60bff9380..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/DBAppenderIntegrationTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.db.DriverManagerConnectionSource; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.EnvUtil; -import ch.qos.logback.core.util.StatusPrinter; -import org.junit.*; -import org.slf4j.Logger; -import org.slf4j.MDC; - -import java.net.InetAddress; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.*; - -public class DBAppenderIntegrationTest { - - static String LOCAL_HOST_NAME; - static String[] CONFORMING_HOST_LIST = new String[] { "Orion" }; - static String[] POSTGRES_CONFORMING_HOST_LIST = new String[] { "haro" }; - static String[] MYSQL_CONFORMING_HOST_LIST = new String[] { "xharo" }; - static String[] ORACLE_CONFORMING_HOST_LIST = new String[] { "xharo" }; - - int diff = RandomUtil.getPositiveInt(); - LoggerContext lc = new LoggerContext(); - - @BeforeClass - public static void setUpBeforeClass() throws Exception { - InetAddress localhostIA = InetAddress.getLocalHost(); - LOCAL_HOST_NAME = localhostIA.getHostName(); - } - - @AfterClass - public static void tearDownAfterClass() throws Exception { - } - - @Before - public void setUp() throws Exception { - lc.setName("lc" + diff); - } - - @After - public void tearDown() throws Exception { - // lc will never be used again - lc.stop(); - } - - DriverManagerConnectionSource getConnectionSource() { - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) lc.getLogger(Logger.ROOT_LOGGER_NAME); - - DBAppender dbAppender = (DBAppender) root.getAppender("DB"); - assertNotNull(dbAppender); - return (DriverManagerConnectionSource) dbAppender.getConnectionSource(); - - } - - public void doTest(String configFile) throws JoranException, SQLException { - JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(lc); - configurator.doConfigure(configFile); - - Logger logger = lc.getLogger(DBAppenderIntegrationTest.class); - - // the key userid is used in SiftingAppender test - // suffix with diff to avoid collision - MDC.put("userid" + diff, "user" + diff); - int runLength = 5; - for (int i = 1; i <= runLength; i++) { - logger.debug("This is a debug message. Message number: " + (diff + i)); - } - - Exception e = new Exception("Just testing", getCause()); - logger.error("At last an error.", e); - - StatusPrinter.printInCaseOfErrorsOrWarnings(lc); - - long lastEventId = getLastEventId(); - verify(lastEventId); - - // check that there were no errors - StatusChecker checker = new StatusChecker(lc); - checker.assertIsErrorFree(); - } - - long getLastEventId() throws SQLException { - DriverManagerConnectionSource cs = getConnectionSource(); - - Connection con = cs.getConnection(); - Statement statement = con.createStatement(); - statement.setMaxRows(1); - ResultSet rs = statement.executeQuery("select event_id from logging_event order by event_id desc"); - rs.next(); - long eventId = rs.getLong(1); - rs.close(); - statement.close(); - return eventId; - } - - void verify(long lastEventId) throws SQLException { - verifyDebugMsg(lastEventId); - verifyException(lastEventId); - verifyProperty(lastEventId); - - } - - void verifyDebugMsg(long lastEventId) throws SQLException { - DriverManagerConnectionSource cs = getConnectionSource(); - Connection con = cs.getConnection(); - Statement statement = con.createStatement(); - ResultSet rs = statement.executeQuery("select formatted_message from logging_event where event_id='" + (lastEventId - 1) + "'"); - rs.next(); - String msg = rs.getString(1); - assertEquals("This is a debug message. Message number: " + (diff + 5), msg); - } - - void verifyProperty(long lastEventId) throws SQLException { - DriverManagerConnectionSource cs = getConnectionSource(); - Connection con = cs.getConnection(); - Statement statement = con.createStatement(); - ResultSet rs = statement.executeQuery("select mapped_key, mapped_value from logging_event_property where event_id='" + (lastEventId - 1) + "'"); - - Map witness = lc.getCopyOfPropertyMap(); - witness.putAll(MDC.getCopyOfContextMap()); - - Map map = new HashMap(); - while (rs.next()) { - String key = rs.getString(1); - String val = rs.getString(2); - map.put(key, val); - } - - assertEquals(witness, map); - } - - void verifyException(long lastEventId) throws SQLException { - DriverManagerConnectionSource cs = getConnectionSource(); - Connection con = cs.getConnection(); - Statement statement = con.createStatement(); - ResultSet rs = statement.executeQuery("select trace_line from logging_event_exception where event_id='" + (lastEventId) + "' AND I='0'"); - rs.next(); - String traceLine = rs.getString(1); - assertEquals("java.lang.Exception: Just testing", traceLine); - } - - Throwable getCause() { - return new IllegalStateException("test cause"); - } - - static boolean isConformingHostAndJDK16OrHigher(String[] conformingHostList) { - if (!EnvUtil.isJDK6OrHigher()) { - return false; - } - for (String conformingHost : conformingHostList) { - if (conformingHost.equalsIgnoreCase(LOCAL_HOST_NAME)) { - return true; - } - } - return false; - } - - static boolean isConformingHostAndJDK16OrHigher() { - return isConformingHostAndJDK16OrHigher(CONFORMING_HOST_LIST); - } - - @Test - public void sqlserver() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/sqlserver-with-driver.xml"); - } - - @Test - public void oracle10g() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher(ORACLE_CONFORMING_HOST_LIST)) { - return; - } - doTest("src/test/input/integration/db/oracle10g-with-driver.xml"); - } - - @Test - @Ignore - public void oracle11g() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher()) { - return; - } - doTest("src/test/input/integration/db/oracle11g-with-driver.xml"); - } - - @Test - public void mysql() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher(MYSQL_CONFORMING_HOST_LIST)) { - return; - } - doTest("src/test/input/integration/db/mysql-with-driver.xml"); - } - - @Test - public void postgres() throws Exception { - // perform test only on conforming hosts - if (!isConformingHostAndJDK16OrHigher(POSTGRES_CONFORMING_HOST_LIST)) { - return; - } - System.out.println("running postgres() test"); - doTest("src/test/input/integration/db/postgresql-with-driver.xml"); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/PackageTest.java deleted file mode 100644 index abc0a3f1c3..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ DBAppenderHSQLTest.class, DBAppenderH2Test.class, DBAppenderIntegrationTest.class, SQLBuilderTest.class, - ch.qos.logback.classic.db.names.PackageTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/SQLBuilderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/SQLBuilderTest.java deleted file mode 100644 index e2b684476a..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/SQLBuilderTest.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.junit.Test; - -import ch.qos.logback.classic.db.names.DBNameResolver; -import ch.qos.logback.classic.db.names.DefaultDBNameResolver; -import ch.qos.logback.classic.db.names.SimpleDBNameResolver; - -/** - * @author Tomasz Nurkiewicz - * @since 2010-03-22 - */ -public class SQLBuilderTest { - - @Test - public void shouldReturnDefaultSqlInsertLoggingEventQuery() throws Exception { - // given - DBNameResolver nameResolver = new DefaultDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertSQL(nameResolver); - - // then - final String expected = "INSERT INTO logging_event (timestmp, formatted_message, logger_name, level_string, thread_name, reference_flag, arg0, arg1, arg2, arg3, caller_filename, caller_class, caller_method, caller_line) VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - - @Test - public void shouldReturnDefaultSqlInsertExceptionQuery() throws Exception { - // given - DBNameResolver nameResolver = new DefaultDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertExceptionSQL(nameResolver); - - // then - final String expected = "INSERT INTO logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - - @Test - public void shouldReturnDefaultSqlInsertLoggingPropertyQuery() throws Exception { - // given - DBNameResolver nameResolver = new DefaultDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertPropertiesSQL(nameResolver); - - // then - final String expected = "INSERT INTO logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - - private DBNameResolver createSimpleDBNameResolver() { - final SimpleDBNameResolver nameResolver = new SimpleDBNameResolver(); - nameResolver.setTableNamePrefix("tp_"); - nameResolver.setTableNameSuffix("_ts"); - nameResolver.setColumnNamePrefix("cp_"); - nameResolver.setColumnNameSuffix("_cs"); - return nameResolver; - } - - @Test - public void shouldReturnSimpleSqlInsertLoggingEventQuery() throws Exception { - // given - DBNameResolver nameResolver = createSimpleDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertSQL(nameResolver); - - // then - final String expected = "INSERT INTO tp_logging_event_ts (cp_timestmp_cs, cp_formatted_message_cs, cp_logger_name_cs, cp_level_string_cs, cp_thread_name_cs, cp_reference_flag_cs, cp_arg0_cs, cp_arg1_cs, cp_arg2_cs, cp_arg3_cs, cp_caller_filename_cs, cp_caller_class_cs, cp_caller_method_cs, cp_caller_line_cs) VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - - @Test - public void shouldReturnSimpleSqlInsertExceptionQuery() throws Exception { - // given - DBNameResolver nameResolver = createSimpleDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertExceptionSQL(nameResolver); - - // then - final String expected = "INSERT INTO tp_logging_event_exception_ts (cp_event_id_cs, cp_i_cs, cp_trace_line_cs) VALUES (?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - - @Test - public void shouldReturnSimpleSqlInsertLoggingPropertyQuery() throws Exception { - // given - DBNameResolver nameResolver = createSimpleDBNameResolver(); - - // when - String sql = SQLBuilder.buildInsertPropertiesSQL(nameResolver); - - // then - final String expected = "INSERT INTO tp_logging_event_property_ts (cp_event_id_cs, cp_mapped_key_cs, cp_mapped_value_cs) VALUES (?, ?, ?)"; - assertThat(sql).isEqualTo(expected); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/DefaultDBNameResolverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/names/DefaultDBNameResolverTest.java deleted file mode 100644 index 3781eae1b6..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/DefaultDBNameResolverTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -import org.junit.Before; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Tomasz Nurkiewicz - * @since 2010-03-18 - */ -public class DefaultDBNameResolverTest { - - private DefaultDBNameResolver resolver; - - @Before - public void setUp() throws Exception { - resolver = new DefaultDBNameResolver(); - } - - @Test - public void testGetLoggingEventColumnName() throws Exception { - // when - String columnName = resolver.getColumnName(ColumnName.LOGGER_NAME); - - // then - assertThat(columnName).isEqualTo("logger_name"); - } - - @Test - public void testGetLoggingEventPropertyColumnName() throws Exception { - // when - String columnName = resolver.getColumnName(ColumnName.MAPPED_KEY); - - // then - assertThat(columnName).isEqualTo("mapped_key"); - } - - @Test - public void testGetLoggingEventExceptionColumnName() throws Exception { - // when - String columnName = resolver.getColumnName(ColumnName.TRACE_LINE); - - // then - assertThat(columnName).isEqualTo("trace_line"); - } - - @Test - public void testGetTableName() throws Exception { - // when - String tableName = resolver.getTableName(TableName.LOGGING_EVENT_EXCEPTION); - - // then - assertThat(tableName).isEqualTo("logging_event_exception"); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/names/PackageTest.java deleted file mode 100644 index 4ef7120c5c..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ DefaultDBNameResolverTest.class, SimpleDBNameResolverTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/SimpleDBNameResolverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/db/names/SimpleDBNameResolverTest.java deleted file mode 100644 index ed91ea2633..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/db/names/SimpleDBNameResolverTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.db.names; - -import org.junit.Before; -import org.junit.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * @author Tomasz Nurkiewicz - * @since 2010-03-22 - */ -public class SimpleDBNameResolverTest { - - private SimpleDBNameResolver nameResolver; - - @Before - public void setUp() throws Exception { - nameResolver = new SimpleDBNameResolver(); - /* - * nameResolver.setTableNameSuffix("_ts"); nameResolver.setColumnNamePrefix("cp_"); - * nameResolver.setColumnNameSuffix("_cs"); - */ - } - - @Test - public void shouldReturnTableNameWithPrefix() throws Exception { - // given - - // when - nameResolver.setTableNamePrefix("tp_"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("tp_logging_event"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("thread_name"); - } - - @Test - public void shouldReturnTableNameWithSuffix() throws Exception { - // given - - // when - nameResolver.setTableNameSuffix("_ts"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("logging_event_ts"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("thread_name"); - } - - @Test - public void shouldReturnTableNameWithBothPrefixAndSuffix() throws Exception { - // given - - // when - nameResolver.setTableNamePrefix("tp_"); - nameResolver.setTableNameSuffix("_ts"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("tp_logging_event_ts"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("thread_name"); - } - - @Test - public void shouldReturnColumnNameWithPrefix() throws Exception { - // given - - // when - nameResolver.setColumnNamePrefix("cp_"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("logging_event"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("cp_thread_name"); - } - - @Test - public void shouldReturnColumnNameWithSuffix() throws Exception { - // given - - // when - nameResolver.setColumnNameSuffix("_cs"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("logging_event"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("thread_name_cs"); - } - - @Test - public void shouldReturnColumnNameWithBothPrefixAndSuffix() throws Exception { - // given - - // when - nameResolver.setColumnNamePrefix("cp_"); - nameResolver.setColumnNameSuffix("_cs"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("logging_event"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("cp_thread_name_cs"); - } - - @Test - public void shouldReturnTableAndColumnNamesWithBothPrefixAndSuffix() throws Exception { - // given - - // when - nameResolver.setTableNamePrefix("tp_"); - nameResolver.setTableNameSuffix("_ts"); - nameResolver.setColumnNamePrefix("cp_"); - nameResolver.setColumnNameSuffix("_cs"); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("tp_logging_event_ts"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("cp_thread_name_cs"); - } - - @Test - public void shouldHandleNullsAsEmptyStrings() throws Exception { - // given - - // when - nameResolver.setTableNamePrefix(null); - nameResolver.setTableNameSuffix(null); - nameResolver.setColumnNamePrefix(null); - nameResolver.setColumnNameSuffix(null); - - // then - assertThat(nameResolver.getTableName(TableName.LOGGING_EVENT)).isEqualTo("logging_event"); - assertThat(nameResolver.getColumnName(ColumnName.THREAD_NAME)).isEqualTo("thread_name"); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java new file mode 100644 index 0000000000..2dc204ccd4 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/JsonEncoderTest.java @@ -0,0 +1,389 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.encoder; + +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.jsonTest.JsonLoggingEvent; +import ch.qos.logback.classic.jsonTest.JsonStringToLoggingEventMapper; +import ch.qos.logback.classic.jsonTest.ThrowableProxyComparator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusPrinter; +import com.fasterxml.jackson.core.JsonProcessingException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; +import org.slf4j.helpers.BasicMarkerFactory; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +// When running from an IDE, add the following on the command line +// +// --add-opens ch.qos.logback.classic/ch.qos.logback.classic.jsonTest=ALL-UNNAMED +// +class JsonEncoderTest { + + int diff = RandomUtil.getPositiveInt(); + + LoggerContext loggerContext = new LoggerContext(); + StatusChecker statusChecker = new StatusChecker(loggerContext); + Logger logger = loggerContext.getLogger(JsonEncoderTest.class); + + JsonEncoder jsonEncoder = new JsonEncoder(); + + BasicMarkerFactory markerFactory = new BasicMarkerFactory(); + + Marker markerA = markerFactory.getMarker("A"); + + Marker markerB = markerFactory.getMarker("B"); + + ListAppender listAppender = new ListAppender(); + JsonStringToLoggingEventMapper stringToLoggingEventMapper = new JsonStringToLoggingEventMapper(markerFactory); + + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + + @BeforeEach + void setUp() { + loggerContext.setName("test_" + diff); + loggerContext.setMDCAdapter(logbackMDCAdapter); + + jsonEncoder.setContext(loggerContext); + jsonEncoder.start(); + + listAppender.setContext(loggerContext); + listAppender.start(); + } + + @AfterEach + void tearDown() { + } + + @Test + void smoke() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void contextWithProperties() throws JsonProcessingException { + loggerContext.putProperty("k", "v"); + loggerContext.putProperty("k" + diff, "v" + diff); + + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + // System.out.println(resultString); + + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + + } + + private static void compareEvents(LoggingEvent event, JsonLoggingEvent resultEvent) { + assertEquals(event.getSequenceNumber(), resultEvent.getSequenceNumber()); + assertEquals(event.getTimeStamp(), resultEvent.getTimeStamp()); + assertEquals(event.getLevel(), resultEvent.getLevel()); + assertEquals(event.getLoggerName(), resultEvent.getLoggerName()); + assertEquals(event.getThreadName(), resultEvent.getThreadName()); + assertEquals(event.getMarkerList(), resultEvent.getMarkerList()); + assertEquals(event.getMDCPropertyMap(), resultEvent.getMDCPropertyMap()); + assertTrue(compareKeyValuePairLists(event.getKeyValuePairs(), resultEvent.getKeyValuePairs())); + + assertEquals(event.getLoggerContextVO(), resultEvent.getLoggerContextVO()); + assertTrue(ThrowableProxyComparator.areEqual(event.getThrowableProxy(), resultEvent.getThrowableProxy())); + + assertEquals(event.getMessage(), resultEvent.getMessage()); + assertEquals(event.getFormattedMessage(), resultEvent.getFormattedMessage()); + + assertTrue(Arrays.equals(event.getArgumentArray(), resultEvent.getArgumentArray())); + + } + + private static boolean compareKeyValuePairLists(List leftList, List rightList) { + if (leftList == rightList) + return true; + + if (leftList == null || rightList == null) + return false; + + int length = leftList.size(); + if (rightList.size() != length) { + System.out.println("length discrepancy"); + return false; + } + + //System.out.println("checking KeyValuePair lists"); + + for (int i = 0; i < length; i++) { + KeyValuePair leftKVP = leftList.get(i); + KeyValuePair rightKVP = rightList.get(i); + + boolean result = Objects.equals(leftKVP.key, rightKVP.key) && Objects.equals(leftKVP.value, rightKVP.value); + + if (!result) { + System.out.println("mismatch oin kvp " + leftKVP + " and " + rightKVP); + return false; + } + } + return true; + + } + + @Test + void withMarkers() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, null); + event.addMarker(markerA); + event.addMarker(markerB); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withArguments() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello", null, new Object[] { "arg1", "arg2" }); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withKeyValuePairs() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null, + new Object[] { "arg1", "arg2" }); + event.addKeyValuePair(new KeyValuePair("k1", "v1")); + event.addKeyValuePair(new KeyValuePair("k2", "v2")); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withFormattedMessage() throws JsonProcessingException { + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello {} {}", null, + new Object[] { "arg1", "arg2" }); + jsonEncoder.setWithFormattedMessage(true); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withMDC() throws JsonProcessingException { + Map map = new HashMap<>(); + map.put("key", "value"); + map.put("a", "b"); + + LoggingEvent event = new LoggingEvent("x", logger, Level.WARN, "hello kvp", null, + new Object[] { "arg1", "arg2" }); + Map mdcMap = new HashMap<>(); + mdcMap.put("mdcK1", "v1"); + mdcMap.put("mdcK2", "v2"); + + event.setMDCPropertyMap(mdcMap); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withThrowable() throws JsonProcessingException { + Throwable t = new RuntimeException("test"); + LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withThrowableDisabled() throws JsonProcessingException { + Throwable t = new RuntimeException("withThrowableDisabled"); + LoggingEvent event = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", t, null); + jsonEncoder.setWithThrowable(false); + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + + LoggingEvent eventWithNoThrowable = new LoggingEvent("in withThrowable test", logger, Level.WARN, "hello kvp", null, null); + eventWithNoThrowable.setTimeStamp(event.getTimeStamp()); + + compareEvents(eventWithNoThrowable, resultEvent); + } + + + @Test + void withThrowableHavingCause() throws JsonProcessingException { + Throwable cause = new IllegalStateException("test cause"); + + Throwable t = new RuntimeException("test", cause); + + LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, + null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withThrowableHavingCyclicCause() throws JsonProcessingException { + Throwable cause = new IllegalStateException("test cause"); + + Throwable t = new RuntimeException("test", cause); + cause.initCause(t); + + LoggingEvent event = new LoggingEvent("in withThrowableHavingCyclicCause test", logger, Level.WARN, "hello kvp", + t, null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + @Test + void withThrowableHavingSuppressed() throws JsonProcessingException { + Throwable suppressed = new IllegalStateException("test suppressed"); + + Throwable t = new RuntimeException("test"); + t.addSuppressed(suppressed); + + LoggingEvent event = new LoggingEvent("in withThrowableHavingCause test", logger, Level.WARN, "hello kvp", t, + null); + + byte[] resultBytes = jsonEncoder.encode(event); + String resultString = new String(resultBytes, StandardCharsets.UTF_8); + //System.out.println(resultString); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(resultString); + compareEvents(event, resultEvent); + } + + void configure(String file) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + loggerContext.putProperty("diff", "" + diff); + jc.doConfigure(file); + } + + @Test + void withJoran() throws JoranException, IOException { + String configFilePathStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoder.xml"; + + configure(configFilePathStr); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + logger.addAppender(listAppender); + + logger.debug("hello"); + logbackMDCAdapter.put("a1", "v1" + diff); + logger.atInfo().addKeyValue("ik" + diff, "iv" + diff).addKeyValue("a", "b").log("bla bla \"x\" foobar"); + logbackMDCAdapter.put("a2", "v2" + diff); + logger.atWarn().addMarker(markerA).setMessage("some warning message").log(); + logbackMDCAdapter.remove("a2"); + logger.atError().addKeyValue("ek" + diff, "v" + diff).setCause(new RuntimeException("an error")) + .log("some error occurred"); + + //StatusPrinter.print(loggerContext); + + Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json"); + List lines = Files.readAllLines(outputFilePath); + int count = 4; + assertEquals(count, lines.size()); + + for (int i = 0; i < count; i++) { + //System.out.println("i = " + i); + LoggingEvent withnessEvent = (LoggingEvent) listAppender.list.get(i); + JsonLoggingEvent resultEvent = stringToLoggingEventMapper.mapStringToLoggingEvent(lines.get(i)); + compareEvents(withnessEvent, resultEvent); + } + } + + @Test + void withJoranAndEnabledFormattedMessage() throws JoranException, IOException { + String configFilePathStr = + ClassicTestConstants.JORAN_INPUT_PREFIX + "json/jsonEncoderAndEnabledFormattedMessage.xml"; + + configure(configFilePathStr); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + + //StatusPrinter.print(loggerContext); + statusChecker.isWarningOrErrorFree(0); + + logger.atError().addKeyValue("ek1", "v1").addArgument("arg1").log("this is {}"); + + Path outputFilePath = Path.of(ClassicTestConstants.OUTPUT_DIR_PREFIX + "json/test-" + diff + ".json"); + List lines = Files.readAllLines(outputFilePath); + + int count = 1; + assertEquals(count, lines.size()); + + String withness = "{\"sequenceNumber\":0,\"level\":\"ERROR\",\"threadName\":\"main\"," + + "\"loggerName\":\"ch.qos.logback.classic.encoder.JsonEncoderTest\",\"mdc\": {}," + + "\"kvpList\": [{\"ek1\":\"v1\"}],\"formattedMessage\":\"this is arg1\",\"throwable\":null}"; + + assertEquals(withness, lines.get(0)); + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/LayoutInsteadOfEncoderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/LayoutInsteadOfEncoderTest.java index e2d42d3760..419181bf3d 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/LayoutInsteadOfEncoderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/LayoutInsteadOfEncoderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,11 +14,11 @@ package ch.qos.logback.classic.encoder; import static ch.qos.logback.core.CoreConstants.CODES_URL; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import ch.qos.logback.classic.ClassicTestConstants; @@ -29,7 +29,7 @@ import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.StatusPrinter; public class LayoutInsteadOfEncoderTest { @@ -38,7 +38,7 @@ public class LayoutInsteadOfEncoderTest { JoranConfigurator jc = new JoranConfigurator(); LoggerContext loggerContext = new LoggerContext(); - @Before + @BeforeEach public void setUp() { jc.setContext(loggerContext); @@ -54,37 +54,41 @@ public void layoutInsteadOfEncoer() throws JoranException { checker.assertContainsMatch(Status.WARN, "This appender no longer admits a layout as a sub-component"); checker.assertContainsMatch(Status.WARN, "See also " + CODES_URL + "#layoutInsteadOfEncoder for details"); - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) loggerContext + .getLogger(Logger.ROOT_LOGGER_NAME); FileAppender fileAppender = (FileAppender) root.getAppender("LIOE"); assertTrue(fileAppender.isStarted()); assertTrue(fileAppender.getEncoder() instanceof LayoutWrappingEncoder); } - + @Test public void immediateFlushInEncoder_TRUE() throws JoranException { immediateFlushInEncoder(true); } - + @Test public void immediateFlushInEncoder_FALSE() throws JoranException { immediateFlushInEncoder(false); } - + public void immediateFlushInEncoder(Boolean immediateFlush) throws JoranException { loggerContext.putProperty("immediateFlush", immediateFlush.toString()); jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "compatibility/immediateFlushInEncoder.xml"); StatusPrinter.print(loggerContext); StatusChecker checker = new StatusChecker(loggerContext); - checker.assertContainsMatch(Status.WARN, "As of version 1.2.0 \"immediateFlush\" property should be set within the enclosing Appender."); - checker.assertContainsMatch(Status.WARN, "Please move \"immediateFlush\" property into the enclosing appender."); - checker.assertContainsMatch(Status.WARN, "Setting the \"immediateFlush\" property of the enclosing appender to "+immediateFlush); + checker.assertContainsMatch(Status.WARN, + "As of version 1.2.0 \"immediateFlush\" property should be set within the enclosing Appender."); + checker.assertContainsMatch(Status.WARN, + "Please move \"immediateFlush\" property into the enclosing appender."); + checker.assertContainsMatch(Status.WARN, + "Setting the \"immediateFlush\" property of the enclosing appender to " + immediateFlush); - ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) loggerContext + .getLogger(Logger.ROOT_LOGGER_NAME); FileAppender fileAppender = (FileAppender) root.getAppender("LIOE"); assertTrue(fileAppender.isStarted()); - assertEquals(immediateFlush, Boolean.valueOf(fileAppender.isImmediateFlush())); + assertEquals(immediateFlush, Boolean.valueOf(fileAppender.isImmediateFlush())); } - } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PackageTest.java deleted file mode 100644 index 9f1cdddf30..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.encoder; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ PatternLayoutEncoderTest.class, LayoutInsteadOfEncoderTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PatternLayoutEncoderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PatternLayoutEncoderTest.java index f1234cb190..61a43d93cc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PatternLayoutEncoderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/encoder/PatternLayoutEncoderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,13 @@ */ package ch.qos.logback.classic.encoder; -import static org.junit.Assert.*; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.Charset; import ch.qos.logback.classic.PatternLayout; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; @@ -29,6 +27,9 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class PatternLayoutEncoderTest { PatternLayoutEncoder ple = new PatternLayoutEncoder(); @@ -37,7 +38,7 @@ public class PatternLayoutEncoderTest { Logger logger = context.getLogger(PatternLayoutEncoderTest.class); Charset utf8Charset = Charset.forName("UTF-8"); - @Before + @BeforeEach public void setUp() { ple.setPattern("%m"); ple.setContext(context); @@ -77,4 +78,10 @@ public void charset() throws IOException { assertEquals(msg, new String(baos.toByteArray(), utf8Charset)); } + @Test + public void isStarted() throws IOException { + assertTrue(!ple.isStarted()); + ple.start(); + assertTrue(ple.isStarted()); + } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java index 85d3d52f77..f8f079bdec 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/html/HTMLLayoutTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,21 +13,16 @@ */ package ch.qos.logback.classic.html; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.io.ByteArrayInputStream; import java.util.List; import org.dom4j.Document; import org.dom4j.Element; import org.dom4j.io.SAXReader; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.xml.sax.EntityResolver; import ch.qos.logback.classic.ClassicTestConstants; @@ -35,7 +30,7 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.DummyThrowableProxy; +import ch.qos.logback.classic.spi.PubThrowableProxy; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.spi.StackTraceElementProxy; @@ -45,13 +40,18 @@ import ch.qos.logback.core.testUtil.StringListAppender; import ch.qos.logback.core.util.StatusPrinter; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class HTMLLayoutTest { LoggerContext lc; Logger root; HTMLLayout layout; - @Before + @BeforeEach public void setUp() throws Exception { lc = new LoggerContext(); lc.setName("default"); @@ -66,7 +66,7 @@ public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { lc = null; layout = null; @@ -80,6 +80,7 @@ public void testHeader() throws Exception { Document doc = parseOutput(header + ""); Element rootElement = doc.getRootElement(); assertNotNull(rootElement.element("body")); + } @Test @@ -103,7 +104,7 @@ public void testPresentationHeader() throws Exception { @Test public void testAppendThrowable() throws Exception { StringBuilder buf = new StringBuilder(); - DummyThrowableProxy tp = new DummyThrowableProxy(); + PubThrowableProxy tp = new PubThrowableProxy(); tp.setClassName("test1"); tp.setMessage("msg1"); @@ -191,15 +192,17 @@ public void layoutWithException() throws Exception { } @Test - @Ignore + @Disabled public void rawLimit() throws Exception { StringBuilder sb = new StringBuilder(); String header = layout.getFileHeader(); - assertTrue(header.startsWith("")); + assertTrue(header.startsWith( + "")); sb.append(header); sb.append(layout.getPresentationHeader()); for (int i = 0; i < CoreConstants.TABLE_ROW_LIMIT * 3; i++) { - sb.append(layout.doLayout(new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message" + i, null, null))); + sb.append(layout.doLayout( + new LoggingEvent(this.getClass().getName(), root, Level.DEBUG, "test message" + i, null, null))); } sb.append(layout.getPresentationFooter()); sb.append(layout.getFileFooter()); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/html/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/html/PackageTest.java deleted file mode 100644 index 7f7712abcb..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/html/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.html; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses(HTMLLayoutTest.class) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java b/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java index 006f163280..4a037dd035 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/html/XHTMLEntityResolver.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,7 +21,7 @@ import org.xml.sax.InputSource; public class XHTMLEntityResolver implements EntityResolver { - + // key: public id, value: relative path to DTD file static Map entityMap = new HashMap(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/Main.java deleted file mode 100644 index 48e56628f7..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/Main.java +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue.DarioCampagna; - -import ch.qos.cal10n.IMessageConveyor; -import ch.qos.cal10n.MessageConveyor; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.joran.spi.JoranException; -import org.slf4j.LoggerFactory; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; -import org.slf4j.cal10n.LocLogger; -import org.slf4j.cal10n.LocLoggerFactory; - -import java.util.Locale; - -public class Main { - public static void main(String[] args) throws JoranException { - - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - context.reset(); - JoranConfigurator joranConfigurator = new JoranConfigurator(); - joranConfigurator.setContext(context); - joranConfigurator.doConfigure("src/test/java/ch/qos/logback/classic/issue/DarioCampagna/logback-marker.xml"); - IMessageConveyor mc = new MessageConveyor(Locale.getDefault()); - LocLoggerFactory llFactory_default = new LocLoggerFactory(mc); - LocLogger locLogger = llFactory_default.getLocLogger("defaultLocLogger"); - Marker alwaysMarker = MarkerFactory.getMarker("ALWAYS"); - locLogger.info(alwaysMarker, "This will always appear."); - locLogger.info("Hello!"); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/logback-marker.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/logback-marker.xml deleted file mode 100644 index 2208a568da..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/DarioCampagna/logback-marker.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - %level - %date - %marker - %msg%n - - - - - ALWAYS - ACCEPT - NEUTRAL - - - - - - - \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/LBCORE63.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/LBCORE63.java index 7c5e32fddb..f2bd18a8fc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/LBCORE63.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/LBCORE63.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/LOGBACK_1393_Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/LOGBACK_1393_Test.java new file mode 100644 index 0000000000..0194ef6953 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/LOGBACK_1393_Test.java @@ -0,0 +1,121 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.filter.ThresholdFilter; +import ch.qos.logback.classic.tyler.TylerConfiguratorBase; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy; +import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +@Disabled +public class LOGBACK_1393_Test extends TylerConfiguratorBase { + + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter(); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + + + @BeforeEach + public void setup() { + loggerContext.setMDCAdapter(mdcAdapter); + } + public void configure(LoggerContext loggerCoontext) { + setContext(loggerCoontext); + addOnConsoleStatusListener(); + Appender appenderFILE = setupAppenderFILE(); + Logger logger_ROOT = setupLogger("ROOT", "DEBUG", null); + logger_ROOT.addAppender(appenderFILE); + } + + Appender setupAppenderFILE() { + RollingFileAppender appenderFILE = new RollingFileAppender(); + appenderFILE.setContext(loggerContext); + appenderFILE.setName("FILE"); + + appenderFILE.setImmediateFlush(true); + // Configure component of type TimeBasedRollingPolicy + TimeBasedRollingPolicy timeBasedRollingPolicy = new TimeBasedRollingPolicy(); + timeBasedRollingPolicy.setContext(loggerContext); + timeBasedRollingPolicy.setFileNamePattern(subst("/tmp/log/lb1393.%d{yyyy-MM-dd}.log")); + timeBasedRollingPolicy.setMaxHistory(6); + timeBasedRollingPolicy.setParent(appenderFILE); + timeBasedRollingPolicy.start(); + // Inject component of type TimeBasedRollingPolicy into parent + appenderFILE.setRollingPolicy(timeBasedRollingPolicy); + + // Configure component of type PatternLayoutEncoder + PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder(); + patternLayoutEncoder.setContext(context); + patternLayoutEncoder.setPattern("%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n"); + + patternLayoutEncoder.setParent(appenderFILE); + patternLayoutEncoder.start(); + // Inject component of type PatternLayoutEncoder into parent + appenderFILE.setEncoder(patternLayoutEncoder); + + // Configure component of type SizeBasedTriggeringPolicy + SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy(); + sizeBasedTriggeringPolicy.setContext(loggerContext); + sizeBasedTriggeringPolicy.setMaxFileSize(ch.qos.logback.core.util.FileSize.valueOf("1000")); + // ===========no parent setter + sizeBasedTriggeringPolicy.start(); + // Inject component of type SizeBasedTriggeringPolicy into parent + appenderFILE.setTriggeringPolicy(sizeBasedTriggeringPolicy); + + // Configure component of type ThresholdFilter + ThresholdFilter thresholdFilter = new ThresholdFilter(); + thresholdFilter.setContext(loggerContext); + thresholdFilter.setLevel("TRACE"); + // ===========no parent setter + thresholdFilter.start(); + // Inject component of type ThresholdFilter into parent + appenderFILE.addFilter(thresholdFilter); + + appenderFILE.start(); + return appenderFILE; + } + + @Test + void smoke() { + configure(loggerContext); + Logger logger = loggerContext.getLogger(this.getClass()); + for(int i = 0; i < 100; i++) { + logger.atInfo().addKeyValue("i", i).log("hello world xxasdaasfasf asdfasfdsfd"); + delay(100); + } + //statusPrinter2.print(loggerContext); + } + + private void delay(int i) { + try { + Thread.sleep(i); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + +} + + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/PackageTest.java deleted file mode 100644 index 649ba434ae..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -import ch.qos.logback.classic.issue.lbclassic135.lbclassic139.LB139_DeadlockTest; - -@RunWith(Suite.class) -@SuiteClasses({ LB139_DeadlockTest.class, ch.qos.logback.classic.issue.logback416.PackageTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github1010/SocketAppender1010Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github1010/SocketAppender1010Test.java new file mode 100644 index 0000000000..091565d332 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github1010/SocketAppender1010Test.java @@ -0,0 +1,183 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.github1010; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.net.SimpleSocketServer; +import ch.qos.logback.classic.net.SocketAppender; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.spi.LoggingEventVO; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusListenerConfigHelper; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; + +import java.util.List; +import java.util.Map; +import java.util.function.Predicate; + +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +public class SocketAppender1010Test { + + + private static final String HOST = "localhost"; + private final int port = RandomUtil.getRandomServerPort(); + private final String mdcKey = "moo" + RandomUtil.getPositiveInt(); + private final String mdcVal = "mdcVal" + RandomUtil.getPositiveInt(); + static final String LIST_APPENDER = "LIST_APPENDER"; + + + LogbackMDCAdapter mdcAdapterForClient = new LogbackMDCAdapter(); + + @Test + @Timeout(value = 2000, unit = MILLISECONDS) + public void smoke() { + System.out.println("Running on port " + port); + LoggerContext serverLoggerContext = buildAndConfigureContextForServer(); + SimpleSocketServer simpleSocketServer = new SimpleSocketServer(serverLoggerContext, port); + simpleSocketServer.start(); + + // wait until server is up + yieldLoop(Thread::isAlive, simpleSocketServer); + + LoggerContext clientLoggerContext = buildAndConfigureContextForClient(mdcAdapterForClient); + Logger clientLogger = clientLoggerContext.getLogger(SocketAppender1010Test.class); + Logger serverRoot = serverLoggerContext.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + mdcAdapterForClient.put(mdcKey, mdcVal); + clientLogger.info("hello"); + + StatusManager clientStatusManager = clientLoggerContext.getStatusManager(); + + // wait until connection is established + yieldLoop(csm -> csm.getCopyOfStatusList().stream().anyMatch(s -> s.getMessage().contains("connection established")), clientStatusManager); + + ListAppender listAppender = (ListAppender) serverRoot.getAppender(LIST_APPENDER); + assertNotNull(listAppender); + assertNotNull(listAppender.list); + yieldLoop(( list -> !list.isEmpty()), listAppender.list); + + + assertEquals(1, listAppender.list.size()); + ILoggingEvent loggingEvent = listAppender.list.get(0); + assertNotNull(loggingEvent); + assertTrue(loggingEvent instanceof LoggingEventVO); + LoggingEventVO loggingEventVO = (LoggingEventVO) loggingEvent; + Map mdcMap = loggingEventVO.getMdc(); + assertNotNull(mdcMap); + assertEquals(mdcVal, mdcMap.get(mdcKey)); + + } + + + void yieldLoop(Predicate predicate, T arg) { + while(true) { + if(predicate.test(arg)) { + break; + } + Thread.yield(); + } + } + + private static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + LoggerContext buildAndConfigureContextForServer() { + LoggerContext context = new LoggerContext(); + context.setName("serverContext"); + LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter(); + context.setMDCAdapter(mdcAdapter); + + StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); + + Logger root = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + root.setLevel(Level.DEBUG); + + + ListAppender listAppender = new ListAppender<>(); + listAppender.setName(LIST_APPENDER); + listAppender.setContext(context); + listAppender.start(); + root.addAppender(listAppender); + + ConsoleAppender consoleAppender = new ConsoleAppender<>(); + consoleAppender.setContext(context); + consoleAppender.setName("STDOUT"); + +// PatternLayoutEncoder patternLayoutEncoder = new PatternLayoutEncoder(); +// patternLayoutEncoder.setContext(context); +// patternLayoutEncoder.setPattern("%-4relative [%thread] %level %logger{35} =%mdc= -%kvp- %msg %n"); +// patternLayoutEncoder.setParent(consoleAppender); +// patternLayoutEncoder.start(); +// consoleAppender.setEncoder(patternLayoutEncoder); +// consoleAppender.start(); +// root.addAppender(consoleAppender); + + return context; + } + + + LoggerContext buildAndConfigureContextForClient(LogbackMDCAdapter mdcAdapter) { + LoggerContext context = new LoggerContext(); + context.setName("clientContext"); + + context.setMDCAdapter(mdcAdapter); + + StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); + + SocketAppender socketAppender = buildSocketAppender(context); + + Logger root = context.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME); + root.setLevel(Level.DEBUG); + root.addAppender(socketAppender); + + return context; + } + + + SocketAppender buildSocketAppender(LoggerContext context) { + SocketAppender socketAppender = new SocketAppender(); + socketAppender.setContext(context); + socketAppender.setName("socketAppender"); + socketAppender.setRemoteHost(HOST); + socketAppender.setPort(port); + socketAppender.setReconnectionDelay(ch.qos.logback.core.util.Duration.valueOf("100")); + socketAppender.setIncludeCallerData(true); + + socketAppender.start(); + return socketAppender; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/Issues450LoggerContextListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/Issues450LoggerContextListener.java new file mode 100644 index 0000000000..6f5428b492 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/Issues450LoggerContextListener.java @@ -0,0 +1,69 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.github450; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggerContextListener; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.LifeCycle; +import org.slf4j.MDC; + +public class Issues450LoggerContextListener extends ContextAwareBase implements LoggerContextListener, LifeCycle { + + boolean started = false; + + @Override + public void start() { + MDC.put("issues450", "12"); + started = true; + } + + @Override + public void stop() { + started = false; + } + + @Override + public boolean isStarted() { + return started; + } + + @Override + public boolean isResetResistant() { + return false; + } + + @Override + public void onStart(LoggerContext context) { + + } + + @Override + public void onReset(LoggerContext context) { + + } + + @Override + public void onStop(LoggerContext context) { + + } + + @Override + public void onLevelChange(Logger logger, Level level) { + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/SLF4JIssue450Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/SLF4JIssue450Test.java new file mode 100644 index 0000000000..d00885b625 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github450/SLF4JIssue450Test.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.github450; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.read.ListAppender; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class SLF4JIssue450Test { + + + @Test + public void smoke() { + System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, ClassicTestConstants.INPUT_PREFIX + "issue/gh_issues_450.xml"); + System.setProperty(CoreConstants.STATUS_LISTENER_CLASS_KEY, "stdout"); + Logger logger = LoggerFactory.getLogger(SLF4JIssue450Test.class); + logger.info("toto"); + ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + + LoggingEvent le0 = (LoggingEvent) listAppender.list.get(0); + + String val = le0.getMDCPropertyMap().get("issues450"); + assertNotNull(val); + assertEquals("12", val); + + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github876/Github876Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github876/Github876Test.java new file mode 100644 index 0000000000..2955f4b3dc --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github876/Github876Test.java @@ -0,0 +1,72 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.github876; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.read.ListAppender; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class Github876Test { + + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + ListAppender listAppender = new ListAppender(); + final Logger logger = loggerContext.getLogger(Github876Test.class); + + @BeforeEach + public void setUp() { + loggerContext.setMDCAdapter(logbackMDCAdapter); + + listAppender.setContext(loggerContext); + listAppender.setName("list"); + listAppender.start(); + + logger.addAppender(listAppender); + + } + + + @Test + public void traditionalTest() { + Exception ex = new Exception("Some message"); + logger.error("Exception Message: {}", ex, ex); + + assertEquals(1, listAppender.list.size()); + ILoggingEvent iLoggingEvent0 = listAppender.list.get(0); + + String formattedMessage0 = iLoggingEvent0.getFormattedMessage(); + assertEquals("Exception Message: java.lang.Exception: Some message", formattedMessage0); + } + + @Test + public void fluentTest() { + Exception ex = new Exception("Some message"); + logger.atError().addArgument(ex) + .setCause(ex).setMessage("Exception Message: {}") + .log(); + + assertEquals(1, listAppender.list.size()); + ILoggingEvent iLoggingEvent0 = listAppender.list.get(0); + + String formattedMessage0 = iLoggingEvent0.getFormattedMessage(); + assertEquals("Exception Message: java.lang.Exception: Some message", formattedMessage0); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/Main.java new file mode 100644 index 0000000000..38cb8448dd --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/Main.java @@ -0,0 +1,41 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.github879; + + +import ch.qos.logback.classic.ClassicConstants; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class Main { + + + static { + + System.setProperty("outputPath", "logback-classic/target/test-output/issue879"); + String configFilePath = "logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/"; + System.setProperty("logback.statusListenerClass", "stdout"); + System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, configFilePath+"logback-879.xml"); + } + + + public static void main(String[] args) { + final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + for (int i = 0; i < 20_000; i++) { + LOGGER.info("X".repeat(45)); + } + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/logback-879.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/logback-879.xml new file mode 100644 index 0000000000..8920190c0d --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/github879/logback-879.xml @@ -0,0 +1,40 @@ + + + + + + + + + + + e + + + + ${outputPath}/mylog-%d{yyyy-MM-dd}.%i.txt + 1MB + 90 + 20GB + + + %msg%n + + + + + + + \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Accessor.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Accessor.java index 62b6c53fb9..fa8a37e11a 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Accessor.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Accessor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,7 +16,7 @@ import org.slf4j.Logger; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; /** * diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/LB139_DeadlockTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/LB139_DeadlockTest.java index cccfe205d1..641c59e20f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/LB139_DeadlockTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/LB139_DeadlockTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,17 +13,17 @@ */ package ch.qos.logback.classic.issue.lbclassic135.lbclassic139; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; import ch.qos.logback.classic.BasicConfigurator; import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.Test; public class LB139_DeadlockTest { LoggerContext loggerContext = new LoggerContext(); - @Before + @BeforeEach public void setUp() { loggerContext.setName("LB139"); BasicConfigurator bc = new BasicConfigurator(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Worker.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Worker.java index b6dd4874e2..a060d9c701 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Worker.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic135/lbclassic139/Worker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,7 +16,7 @@ import org.slf4j.Logger; import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; /** * diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/HtmlEscapedMessageConverter.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/HtmlEscapedMessageConverter.java index fa9922244d..3485fafe5b 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/HtmlEscapedMessageConverter.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/HtmlEscapedMessageConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/Main.java index f8c2511c72..2f784d21cf 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/Main.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/logback.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/logback.xml index 5adca62c99..c4d1587aff 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/logback.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic180/logback.xml @@ -1,3 +1,17 @@ + + + smtp.gmail.com diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/Main.java index f519aeccba..9eac04eab0 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/Main.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/logback.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/logback.xml index 2bd364f334..82fc4febcd 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/logback.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic330/logback.xml @@ -1,3 +1,17 @@ + + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatOriginal_tzest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatOriginal_tzest.java index 7915f89259..a8793f92d8 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatOriginal_tzest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatOriginal_tzest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,7 @@ */ package ch.qos.logback.classic.issue.lbclassic36; -import junit.framework.TestCase; -import junit.framework.Test; -import junit.framework.TestSuite; +import org.junit.jupiter.api.Test; import java.text.SimpleDateFormat; import java.util.Date; @@ -24,38 +22,11 @@ //import org.joda.time.format.DateTimeFormat; //import org.joda.time.DateTime; -public class DateFormatOriginal_tzest extends TestCase { +public class DateFormatOriginal_tzest { public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; static final long NANOS_IN_ONE_SEC = 1000 * 1000 * 1000L; - /** - * Create the test case - * - * @param testName - * name of the test case - */ - public DateFormatOriginal_tzest(String testName) { - super(testName); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() { - return new TestSuite(DateFormatOriginal_tzest.class); - } - - public static void main(String[] args) { - junit.textui.TestRunner.run(suite()); - } - public void setUp() throws Exception { - super.setUp(); - } - - public void tearDown() throws Exception { - super.tearDown(); - } // public void testRaw() throws Exception { // SimpleDateFormat simpleFormat = new SimpleDateFormat(ISO8601_PATTERN); @@ -82,6 +53,7 @@ public void tearDown() throws Exception { // + " ns - Difference: " + diff + "%"); // } + @Test public void testSynchronized() throws Exception { SynchronizedDateFormatter formatter = new SynchronizedDateFormatter(); int threads = 10; @@ -104,7 +76,7 @@ public void testSynchronized() throws Exception { System.out.printf("Synchronized DateFormat: %,.4f seconds\n", actual); } - + @Test public void testUnSynchronized() throws Exception { UnsynchronizedDateFormatter formatter = new UnsynchronizedDateFormatter(); int threads = 10; @@ -127,7 +99,7 @@ public void testUnSynchronized() throws Exception { System.out.printf("Unsynchronized DateFormat: %,.4f seconds\n", actual); } - + @Test public void testThreadLocal() throws Exception { ThreadLocalDateFormatter formatter = new ThreadLocalDateFormatter(); int threads = 10; @@ -237,7 +209,8 @@ public void run() { // DateTime date; // long iterCount; // - // public DateTimeFormatThread(JodaFormatter f, DateTime date, long iterations) { + // public DateTimeFormatThread(JodaFormatter f, DateTime date, long iterations) + // { // this.formatter = f; // this.date = date; // this.iterCount = iterations; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatPerf_Tapp.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatPerf_Tapp.java index 6fbbac1e67..59b03c83f3 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatPerf_Tapp.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormatPerf_Tapp.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormattingThreadedThroughputCalculator.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormattingThreadedThroughputCalculator.java index 3a3fb11bf8..7e623b274b 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormattingThreadedThroughputCalculator.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/DateFormattingThreadedThroughputCalculator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ import ch.qos.logback.core.contention.ThreadedThroughputCalculator; /** - * Measure the threaded throughtput of date formatting operations + * Measure the threaded throughput of date formatting operations * * @author Joern Huxhorn * @author Ceki Gulcu @@ -37,11 +37,13 @@ public static void main(String args[]) throws InterruptedException { tp.execute(buildArray(FormattingModel.JODA)); } - tp.execute(buildArray(FormattingModel.JODA)); - tp.printThroughput("JODA: "); + SelectiveDateFormattingRunnable[] runnnableArrayJODA = buildArray(FormattingModel.JODA); + tp.execute(runnnableArrayJODA); + tp.printThroughput(runnnableArrayJODA, "JODA: "); - tp.execute(buildArray(FormattingModel.SDF)); - tp.printThroughput("SDF: "); + SelectiveDateFormattingRunnable[] runnnableArraySDF = buildArray(FormattingModel.JODA); + tp.execute(runnnableArraySDF); + tp.printThroughput(runnnableArraySDF, "SDF: "); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/SelectiveDateFormattingRunnable.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/SelectiveDateFormattingRunnable.java index 84f54b9014..9ac068f267 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/SelectiveDateFormattingRunnable.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbclassic36/SelectiveDateFormattingRunnable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ //import org.joda.time.format.DateTimeFormat; //import org.joda.time.format.DateTimeFormatter; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; /** * A runnable which behaves differently depending on the desired locking model. diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore211/Lbcore211.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore211/Lbcore211.java index e9e83331c1..3d7f20a4c2 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore211/Lbcore211.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore211/Lbcore211.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,12 +18,15 @@ import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.util.StatusPrinter; -import org.junit.Test; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; /** * @author Ceki Gülcü */ +@Disabled public class Lbcore211 { @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore224/Reduce.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore224/Reduce.java index 1f806aa351..2788e0a4d5 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore224/Reduce.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore224/Reduce.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,7 +20,8 @@ import java.util.regex.Pattern; /** - * Reduce a file consisting of lock and unlock operations by removing matching lock/unlocks. + * Reduce a file consisting of lock and unlock operations by removing matching + * lock/unlocks. */ public class Reduce { @@ -162,7 +163,8 @@ static class Structure { @Override public String toString() { - return "Structure{" + "time=" + time + ", thread='" + thread + '\'' + ", operationType=" + operationType + '}'; + return "Structure{" + "time=" + time + ", thread='" + thread + '\'' + ", operationType=" + operationType + + '}'; } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/Common.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/Common.java index 063b33e5d1..e34cd6711c 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/Common.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/Common.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLog4j.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLog4j.java index 05cfeceb05..087b858c9c 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLog4j.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLog4j.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLogback.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLogback.java index 7b40d77e26..55ea2a7038 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLogback.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/PerformanceComparatorLogback.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_with_immediateFlush.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_with_immediateFlush.xml index d885b582f9..64e44bf64d 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_with_immediateFlush.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_with_immediateFlush.xml @@ -1,4 +1,18 @@ + + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_without_immediateFlush.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_without_immediateFlush.xml index 51ed4f0972..1fe92a6bfe 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_without_immediateFlush.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/log4j_without_immediateFlush.xml @@ -1,4 +1,18 @@ + + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_with_immediateFlush.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_with_immediateFlush.xml index ec349151ad..9f349a2cdf 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_with_immediateFlush.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_with_immediateFlush.xml @@ -1,3 +1,17 @@ + + target/test-output/perf/logback_with_flush.log diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_without_immediateFlush.xml b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_without_immediateFlush.xml index b69f357576..7d4d5276a5 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_without_immediateFlush.xml +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore243/logback_without_immediateFlush.xml @@ -1,3 +1,17 @@ + + target/test-output/perf/logback_without_flush.log diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore26/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore26/Main.java index 13f8248405..86301b9aa0 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore26/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore26/Main.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/Main.java index e521651a75..3a28c9555a 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/Main.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/OThread.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/OThread.java index ba7caae104..e31aa0fe26 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/OThread.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/lbcore_155/OThread.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,7 +31,8 @@ public void run() { while (!isInterrupted()) { long start = System.nanoTime(); - for (long now = System.nanoTime(); now < start + 2 * WAIT_MILLIS * NANOS_IN_MILLI; now = System.nanoTime()) { + for (long now = System.nanoTime(); now < start + 2 * WAIT_MILLIS * NANOS_IN_MILLI; now = System + .nanoTime()) { logger.info("in time loop"); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListener.java deleted file mode 100755 index 1819fa016f..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListener.java +++ /dev/null @@ -1,37 +0,0 @@ -package ch.qos.logback.classic.issue.logback1159; - -import java.io.IOException; - -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.status.StatusListener; -import ch.qos.logback.core.status.ErrorStatus; - -public class LogbackListener extends ContextAwareBase implements StatusListener, LifeCycle { - private boolean started; - - @Override - public void start() { - this.started = true; - } - - @Override - public void stop() { - this.started = false; - } - - @Override - public boolean isStarted() { - return this.started; - } - - @Override - public void addStatusEvent(final Status status) { - if (status instanceof ErrorStatus && status.getThrowable() instanceof IOException) { - System.out.println("*************************LogbackListener.addStatusEvent"); - throw new LoggingError(status.getMessage(), status.getThrowable()); - } - } - -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListenerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListenerTest.java index 2436d63306..916cebf755 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListenerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LogbackListenerTest.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.issue.logback1159; import java.io.File; @@ -11,8 +25,11 @@ //import org.apache.commons.io.FileUtils; //import org.apache.commons.lang3.RandomStringUtils; -import org.junit.After; -import org.junit.Test; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactoryFriend; @@ -20,21 +37,28 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.core.joran.spi.JoranException; +import org.slf4j.spi.MDCAdapter; public class LogbackListenerTest { private File logFile = new File("target/test.log"); + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter(); + + @BeforeEach + void setUp() { + loggerContext.setMDCAdapter(mdcAdapter); + } + private void doConfigure() throws JoranException { - LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); JoranConfigurator configurator = new JoranConfigurator(); - configurator.setContext(context); + configurator.setContext(loggerContext); configurator.doConfigure(new File("src/test/input/issue/logback-1159.xml")); } - @After + @AfterEach public void after() { logFile.delete(); - LoggerFactoryFriend.reset(); } private void disableLogFileAccess() throws IOException { @@ -49,10 +73,12 @@ private void disableLogFileAccess() throws IOException { } } - @Test(expected = LoggingError.class) + @Test public void testThatErrorIsDetectedAtLogInit() throws Exception { - disableLogFileAccess(); - doConfigure(); + Assertions.assertThrows(LoggingError.class, () -> { + disableLogFileAccess(); + doConfigure(); + }); } @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LoggingError.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LoggingError.java index 811be6a1e0..b98e614238 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LoggingError.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback1159/LoggingError.java @@ -1,8 +1,23 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.issue.logback1159; /** - * Based error class to be thrown in a logging failsafe situation. I.e. any unexpected error - * situations during logging (e.g. database access, I/O failure) + * Based error class to be thrown in a logging failsafe situation. I.e. any + * unexpected error situations during logging (e.g. database access, I/O + * failure) * */ public class LoggingError extends Error { @@ -10,7 +25,7 @@ public class LoggingError extends Error { private static final long serialVersionUID = -4881940499551760472L; public LoggingError(String msg, Throwable cause) { - super(msg, cause); - } + super(msg, cause); + } } \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/ConcurrentSiftingTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/ConcurrentSiftingTest.java deleted file mode 100755 index e7f388d5ff..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/ConcurrentSiftingTest.java +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue.logback416; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.issue.lbclassic135.LoggingRunnable; -import ch.qos.logback.classic.issue.logback416.InstanceCountingAppender; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.contention.MultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; -import ch.qos.logback.core.joran.spi.JoranException; - -import org.junit.Test; - -import static org.junit.Assert.assertEquals; - -public class ConcurrentSiftingTest { - final static int THREAD_COUNT = 5; - static String FOLDER_PREFIX = ClassicTestConstants.JORAN_INPUT_PREFIX + "sift/"; - - LoggerContext loggerContext = new LoggerContext(); - protected Logger logger = loggerContext.getLogger(this.getClass().getName()); - protected Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - - int totalTestDuration = 50; - MultiThreadedHarness harness = new MultiThreadedHarness(totalTestDuration); - RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(); - - protected void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(file); - } - - RunnableWithCounterAndDone[] buildRunnableArray() { - RunnableWithCounterAndDone[] rArray = new RunnableWithCounterAndDone[THREAD_COUNT]; - for (int i = 0; i < THREAD_COUNT; i++) { - rArray[i] = new LoggingRunnable(logger); - } - return rArray; - } - - @Test - public void concurrentAccess() throws JoranException, InterruptedException { - configure(FOLDER_PREFIX + "logback_416.xml"); - harness.execute(runnableArray); - assertEquals(1, InstanceCountingAppender.INSTANCE_COUNT.get()); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/InstanceCountingAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/InstanceCountingAppender.java deleted file mode 100644 index ed674668f7..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/InstanceCountingAppender.java +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue.logback416; - -import java.util.concurrent.atomic.AtomicInteger; - -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; - -public class InstanceCountingAppender extends AppenderBase { - - static public AtomicInteger INSTANCE_COUNT = new AtomicInteger(0); - - public InstanceCountingAppender() { - INSTANCE_COUNT.getAndIncrement(); - } - - protected void append(ILoggingEvent e) { - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/PackageTest.java deleted file mode 100644 index 8c932c4b79..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback416/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue.logback416; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses(ConcurrentSiftingTest.class) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback474/LoggingAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback474/LoggingAppender.java deleted file mode 100755 index 04fc7d4d25..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback474/LoggingAppender.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.issue.logback474; - -import org.slf4j.Logger; - -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.AppenderBase; - -/** - * An appender which calls logback recursively - * - * @author Ralph Goers - */ - -public class LoggingAppender extends AppenderBase { - - Logger logger; - - public void start() { - super.start(); - logger = ((LoggerContext) getContext()).getLogger("Ignore"); - } - - protected void append(ILoggingEvent eventObject) { - logger.debug("Ignore this"); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/Main.java index d599e2b4d7..e5e309d2c5 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/Main.java @@ -1,4 +1,19 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.issue.logback_1162; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,13 +30,12 @@ public class Main { public static void main(String[] args) throws InterruptedException, JoranException { LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); lc.reset(); - lc.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX+"logback_issue_1162/"); - + lc.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX + "logback_issue_1162/"); + JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); - configurator.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX+ "issues/logback_1162.xml"); + configurator.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1162.xml"); - logger.info("Hello, world!"); TimeUnit.SECONDS.sleep(0); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/reproduce-bug.sh b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/reproduce-bug.sh index f30a49a83e..b6c9cd09ef 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/reproduce-bug.sh +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1162/reproduce-bug.sh @@ -1,5 +1,19 @@ #!/bin/bash +# +# Logback: the reliable, generic, fast and flexible logging framework. +# Copyright (C) 1999-2026, QOS.ch. All rights reserved. +# +# This program and the accompanying materials are dual-licensed under +# either the terms of the Eclipse Public License v2.0 as published by +# the Eclipse Foundation +# +# or (per the licensee's choosing) +# +# under the terms of the GNU Lesser General Public License version 2.1 +# as published by the Free Software Foundation. +# + set -e DD="dd" diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1277/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1277/Main.java index 20bf4881aa..d43425c08c 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1277/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1277/Main.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1361/Main.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1361/Main.java index 37f0d61b28..dabb92300e 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1361/Main.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1361/Main.java @@ -1,4 +1,19 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.issue.logback_1361; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -10,19 +25,19 @@ public class Main { private static Logger logger = LoggerFactory.getLogger(Main.class); private static String ONE_KB_STRING; - + public static void main(String[] args) throws Exception { LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); lc.reset(); - lc.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX+"logback_issue_1361/"); - + lc.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX + "logback_issue_1361/"); + JoranConfigurator configurator = new JoranConfigurator(); configurator.setContext(lc); - configurator.doConfigure(ClassicTestConstants.INPUT_PREFIX+ "issue/logback_1361.xml"); + configurator.doConfigure(ClassicTestConstants.INPUT_PREFIX + "issue/logback_1361.xml"); log1MegaByteInOneSecond(); } - + static { StringBuilder sb = new StringBuilder(); for (int j = 0; j < 100; j++) { @@ -32,7 +47,6 @@ public static void main(String[] args) throws Exception { ONE_KB_STRING = sb.toString(); } - private static void log1MegaByteInOneSecond() throws Exception { for (int i = 0; i < 1000; i++) { logger.warn(i + " - " + ONE_KB_STRING); @@ -40,5 +54,4 @@ private static void log1MegaByteInOneSecond() throws Exception { } } - } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1754/SafeModeTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1754/SafeModeTest.java new file mode 100644 index 0000000000..85ca46e387 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1754/SafeModeTest.java @@ -0,0 +1,93 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.logback_1754; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.core.testUtil.RandomUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class SafeModeTest { + + private static final int THREADS = 3; + + private void runTest() { + + CountDownLatch latch = new CountDownLatch(THREADS); + List threads = new ArrayList<>(THREADS); + for (int i = 0; i < THREADS; i++) { + LoggerThread thread = new LoggerThread(latch, "message from thread " + i); + thread.start(); + threads.add(thread); + } + int i = 0; + for (Thread thread : threads) { + try { + thread.join(); + System.out.println("joined thread "+thread.getName()); + } catch (InterruptedException e) { + e.printStackTrace(); + //Thread.currentThread().interrupt(); + //throw new RuntimeException(e); + } + } + } + + public static void main(String... args) { + int diff = RandomUtil.getPositiveInt(); + //System.setProperty("logback.statusListenerClass", "sysout"); + System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, ClassicTestConstants.INPUT_PREFIX+"issue/logback-1754.xml"); + System.setProperty("logback_1754_targetDirectory", ClassicTestConstants.OUTPUT_DIR_PREFIX+"safeWrite_"+diff); + + + new SafeModeTest().runTest(); + } + + private static final class LoggerThread extends Thread { + private static final Logger LOG = LoggerFactory.getLogger(LoggerThread.class); + private final CountDownLatch latch; + private final String message; + + LoggerThread(CountDownLatch latch, String message) { + setDaemon(false); + this.latch = latch; + this.message = message; + } + + @Override + public void run() { + latch.countDown(); + for(int i = 0; i < 100; i++) { + if(i % 10 == 0) { + delay(1); + } + LOG.info(message + " i=" + i); + } + } + + static void delay(long millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1759/Logback1759Test.java b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1759/Logback1759Test.java new file mode 100644 index 0000000000..ead56cade8 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/issue/logback_1759/Logback1759Test.java @@ -0,0 +1,77 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.issue.logback_1759; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class Logback1759Test { + + LoggerContext context = new LoggerContext(); + Logger logger = context.getLogger("toto.foo"); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + PatternLayoutEncoder patternLayoutEncoder = null; + ConsoleAppender consoleAppender = null; + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + + @BeforeEach + public void setup() { + context.setMDCAdapter(logbackMDCAdapter); + init(); + } + + void init() { + System.out.println("Init called"); + this.patternLayoutEncoder = new PatternLayoutEncoder(); + patternLayoutEncoder.setContext(context); + patternLayoutEncoder.setPattern("%highlight(%level) %message%n"); + patternLayoutEncoder.start(); + + this.consoleAppender = new ConsoleAppender(); + consoleAppender.setContext(context); + consoleAppender.setEncoder(patternLayoutEncoder); + } + + @Test + public void smoke() { + consoleAppender.setWithJansi(true); + consoleAppender.start(); + + //String fqcn, Logger logger, Level level, String message, Throwable throwable, + // Object[] argArray + + LoggingEvent le = new LoggingEvent("x", logger, Level.INFO, "hello", null, null); + + consoleAppender.doAppend(le); + + consoleAppender.stop(); + + init(); + consoleAppender.setWithJansi(true); + consoleAppender.start(); + + consoleAppender.doAppend(le); + + //statusPrinter2.print(context); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java deleted file mode 100644 index 0f82581d1f..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/jmx/JMXConfiguratorTest.java +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2016, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jmx; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.lang.management.ManagementFactory; -import java.util.List; - -import javax.management.MBeanServer; -import javax.management.ObjectName; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.LoggerContextListener; -import ch.qos.logback.core.testUtil.RandomUtil; - -import static org.slf4j.Logger.ROOT_LOGGER_NAME; - -public class JMXConfiguratorTest { - - MBeanServer mbs = ManagementFactory.getPlatformMBeanServer(); - LoggerContext lc = new LoggerContext(); - Logger testLogger = lc.getLogger(this.getClass()); - - List listenerList; - int diff = RandomUtil.getPositiveInt(); - - @Before - public void setUp() throws Exception { - lc.setName("context-" + diff); - assertNotNull(mbs); - } - - @After - public void tearDown() throws Exception { - lc.stop(); - } - - @Override - public String toString() { - return this.getClass().getName() + "(" + lc.getName() + ")"; - } - - @Test - public void contextReset() throws Exception { - String randomizedObjectNameAsStr = "ch.qos.logback." + diff + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - - ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr); - JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName); - mbs.registerMBean(jmxConfigurator, objectName); - - listenerList = lc.getCopyOfListenerList(); - assertEquals(1, listenerList.size()); - - lc.reset(); - - // check that after lc.reset, jmxConfigurator should still be - // registered as a listener in the loggerContext and also as an - // MBean in mbs - listenerList = lc.getCopyOfListenerList(); - assertEquals(1, listenerList.size()); - assertTrue(listenerList.contains(jmxConfigurator)); - - assertTrue(mbs.isRegistered(objectName)); - } - - @Test - public void contextStop() throws Exception { - String randomizedObjectNameAsStr = "ch.qos.logback." + diff + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - - ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, randomizedObjectNameAsStr); - JMXConfigurator jmxConfigurator = new JMXConfigurator(lc, mbs, objectName); - mbs.registerMBean(jmxConfigurator, objectName); - - listenerList = lc.getCopyOfListenerList(); - assertEquals(1, listenerList.size()); - - lc.stop(); - - // check that after lc.processPriorToRemoval, jmxConfigurator is no longer - // registered as a listener in the loggerContext nor as an - // MBean in mbs - listenerList = lc.getCopyOfListenerList(); - assertEquals(0, listenerList.size()); - - assertFalse(mbs.isRegistered(objectName)); - } - - @Test - public void testNonRemovalOfPreviousIntanceFromTheContextListenerList() { - String objectNameAsStr = "ch.qos.logback.toto" + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - ObjectName objectName = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr); - JMXConfigurator jmxConfigurator0 = new JMXConfigurator(lc, mbs, objectName); - - listenerList = lc.getCopyOfListenerList(); - assertTrue(listenerList.contains(jmxConfigurator0)); - - JMXConfigurator jmxConfigurator1 = new JMXConfigurator(lc, mbs, objectName); - listenerList = lc.getCopyOfListenerList(); - assertEquals(1, listenerList.size()); - assertTrue("old configurator should be present", listenerList.contains(jmxConfigurator0)); - assertFalse("new configurator should be absent", listenerList.contains(jmxConfigurator1)); - } - - @Test - public void getLoggerLevel_LBCLASSIC_78() { - String objectNameAsStr = "ch.qos" + diff + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - - ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr); - JMXConfigurator configurator = new JMXConfigurator(lc, mbs, on); - assertEquals("", configurator.getLoggerLevel(testLogger.getName())); - MBeanUtil.unregister(lc, mbs, on, this); - } - - @Test - public void setLoggerLevel_LBCLASSIC_79() { - String objectNameAsStr = "ch.qos" + diff + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - - ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr); - JMXConfigurator configurator = new JMXConfigurator(lc, mbs, on); - configurator.setLoggerLevel(testLogger.getName(), "DEBUG"); - assertEquals(Level.DEBUG, testLogger.getLevel()); - - configurator.setLoggerLevel(testLogger.getName(), "null"); - assertNull(testLogger.getLevel()); - - MBeanUtil.unregister(lc, mbs, on, this); - } - - @Test - public void testReloadDefaultConfiguration() throws Exception { - String objectNameAsStr = "ch.qos" + diff + ":Name=" + lc.getName() + ",Type=" + this.getClass().getName(); - - ObjectName on = MBeanUtil.string2ObjectName(lc, this, objectNameAsStr); - JMXConfigurator configurator = new JMXConfigurator(lc, mbs, on); - configurator.setLoggerLevel(testLogger.getName(), "DEBUG"); - assertEquals(Level.DEBUG, testLogger.getLevel()); - - configurator.reloadDefaultConfiguration(); - assertNull(testLogger.getLevel()); - assertEquals(Level.DEBUG, lc.getLogger(ROOT_LOGGER_NAME).getLevel()); - MBeanUtil.unregister(lc, mbs, on, this); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jmx/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/jmx/PackageTest.java deleted file mode 100644 index cc4654cd3f..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/jmx/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jmx; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses(JMXConfiguratorTest.class) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/EvaluatorJoranTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/EvaluatorJoranTest.java deleted file mode 100644 index 7c39ef2e58..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/EvaluatorJoranTest.java +++ /dev/null @@ -1,107 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.Map; - -import org.junit.Test; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.boolex.JaninoEventEvaluator; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.boolex.EvaluationException; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.joran.spi.JoranException; - -public class EvaluatorJoranTest { - - @Test - public void testSimpleEvaluator() throws NullPointerException, EvaluationException, JoranException { - JoranConfigurator jc = new JoranConfigurator(); - LoggerContext loggerContext = new LoggerContext(); - jc.setContext(loggerContext); - jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleEvaluator.xml"); - - @SuppressWarnings("unchecked") - Map> evalMap = (Map>) loggerContext.getObject(CoreConstants.EVALUATOR_MAP); - assertNotNull(evalMap); - JaninoEventEvaluator evaluator = (JaninoEventEvaluator) evalMap.get("msgEval"); - assertNotNull(evaluator); - - Logger logger = loggerContext.getLogger("xx"); - ILoggingEvent event0 = new LoggingEvent("foo", logger, Level.DEBUG, "Hello world", null, null); - assertTrue(evaluator.evaluate(event0)); - - ILoggingEvent event1 = new LoggingEvent("foo", logger, Level.DEBUG, "random blurb", null, null); - assertFalse(evaluator.evaluate(event1)); - } - - @Test - public void testIgnoreMarker() throws NullPointerException, EvaluationException, JoranException { - JoranConfigurator jc = new JoranConfigurator(); - LoggerContext loggerContext = new LoggerContext(); - jc.setContext(loggerContext); - jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ignore.xml"); - - @SuppressWarnings("unchecked") - Map> evalMap = (Map>) loggerContext.getObject(CoreConstants.EVALUATOR_MAP); - assertNotNull(evalMap); - - Logger logger = loggerContext.getLogger("xx"); - - JaninoEventEvaluator evaluator = (JaninoEventEvaluator) evalMap.get("IGNORE_EVAL"); - LoggingEvent event = new LoggingEvent("foo", logger, Level.DEBUG, "Hello world", null, null); - - Marker ignoreMarker = MarkerFactory.getMarker("IGNORE"); - event.setMarker(ignoreMarker); - assertTrue(evaluator.evaluate(event)); - - logger.debug("hello", new Exception("test")); - logger.debug(ignoreMarker, "hello ignore", new Exception("test")); - - // logger.debug("hello", new Exception("test")); - - // StatusPrinter.print(loggerContext.getStatusManager()); - } - - @Test - public void testMultipleConditionsInExpression() throws NullPointerException, EvaluationException { - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger("xx"); - JaninoEventEvaluator ee = new JaninoEventEvaluator(); - ee.setName("testEval"); - ee.setContext(loggerContext); - // && - // && - ee.setExpression("message.contains(\"stacktrace\") && message.contains(\"logging\")"); - ee.start(); - // StatusPrinter.print(loggerContext); - - String message = "stacktrace bla bla logging"; - ILoggingEvent event = new LoggingEvent(this.getClass().getName(), logger, Level.DEBUG, message, null, null); - - assertTrue(ee.evaluate(event)); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/FauxListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/FauxListener.java new file mode 100644 index 0000000000..4b1241da20 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/FauxListener.java @@ -0,0 +1,25 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +public class FauxListener { + + static public int COUNT = 0; + + public FauxListener() { + COUNT++; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/FileCollisionAnalyserTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/FileCollisionAnalyserTest.java new file mode 100644 index 0000000000..eaf3598cb9 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/FileCollisionAnalyserTest.java @@ -0,0 +1,125 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.spi.MDCAdapter; + +import static ch.qos.logback.core.model.processor.FileCollisionAnalyser.COLLISION_MESSAGE; +import static org.junit.jupiter.api.Assertions.*; + +public class FileCollisionAnalyserTest { + + LoggerContext loggerContext = new LoggerContext(); + MDCAdapter mdcAdapter = new LogbackMDCAdapter(); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + StatusChecker checker = new StatusChecker(loggerContext); + int diff = RandomUtil.getPositiveInt(); + + String aLoggerName = "ch.qos.logback"; + Logger aLogger = loggerContext.getLogger(aLoggerName); + + String outputTargetVal = ClassicTestConstants.OUTPUT_DIR_PREFIX + "collision/output-" + diff + ".log"; + String fileNamePatternVal = ClassicTestConstants.OUTPUT_DIR_PREFIX + "collision/output-%d{yyyy-MM-dd}-" + diff + ".log"; + + void configure(String file) throws JoranException { + loggerContext.setMDCAdapter(mdcAdapter); + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + loggerContext.putProperty("outputTargetKey", outputTargetVal); + loggerContext.putProperty("fileNamePatternKey", fileNamePatternVal); + + jc.doConfigure(file); + + } + + @Test + public void fileCollision() throws JoranException { + String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatFile.xml"; + runCollisionTest(configFile, 1, 0, "file", outputTargetVal); + } + + + @Test + public void testRollingFileAppenderCollisionByFile() throws JoranException { + String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatRollingFileAppenderByFile.xml"; + runCollisionTest(configFile, 0, 1, "file", outputTargetVal); + } + + @Test + public void testRollingFileAppenderCollisionByFilePattern() throws JoranException { + String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatRollingFileAppenderByFilePattern.xml"; + runCollisionTest(configFile, 0,1, "fileNamePattern", fileNamePatternVal); + } + + @Test + public void testMixedFileaAppenderRollingFileAppenderCollisionByFile() throws JoranException { + String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/repeatMixedFileAndRolling.xml"; + runCollisionTest(configFile, 1, 0, "file", outputTargetVal); + } + + @Test + public void testConditionalFileCollision() throws JoranException { + String configFile = ClassicTestConstants.JORAN_INPUT_PREFIX + "collision/conditionalRepeat.xml"; + configure(configFile); + //statusPrinter2.print(loggerContext); + + Appender fileAppender1 = root.getAppender("FILE1"); + assertNull(fileAppender1); + + Appender fileAppender2 = root.getAppender("FILE2"); + assertNotNull(fileAppender2); + checker.assertIsWarningOrErrorFree(); + } + + + public void runCollisionTest(String configFile, int fileAppenderCount, int rollingAppenderCount, String tagName, String value) throws JoranException { + configure(configFile); + //statusPrinter2.print(loggerContext); + + Appender fileAppender1 = root.getAppender("FILE1"); + assertNotNull(fileAppender1); + + Appender fileAppender2 = aLogger.getAppender("FILE2"); + assertNull(fileAppender2); + + //statusPrinter2.print(loggerContext); + + String expectationPattern = COLLISION_MESSAGE.replace("[", "\\[").replace("]", "\\]"); + + String sanitizeValue = value.replace("{", "\\{").replace("}", "\\}"); + String expected = String.format(expectationPattern, "FILE2", tagName, sanitizeValue, "FILE1"); + checker.assertContainsMatch(Status.ERROR, expected); + checker.assertMatchCount("About to instantiate appender of type \\[" + FileAppender.class.getName() + "\\]", fileAppenderCount); + checker.assertMatchCount("About to instantiate appender of type \\[" + RollingFileAppender.class.getName() + "\\]", rollingAppenderCount); + } + + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java old mode 100755 new mode 100644 index 0d4cb82691..5898abc58e --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/JoranConfiguratorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,64 +13,112 @@ */ package ch.qos.logback.classic.joran; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.Date; - -import org.junit.Ignore; -import org.junit.Test; -import org.slf4j.MDC; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.jul.JULHelper; +import ch.qos.logback.classic.*; +import ch.qos.logback.classic.joran.serializedModel.HardenedModelInputStream; +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.classic.model.LoggerModel; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.turbo.DebugUsersTurboFilter; import ch.qos.logback.classic.turbo.NOPTurboFilter; import ch.qos.logback.classic.turbo.TurboFilter; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; +import ch.qos.logback.core.helpers.NOPAppender; +import ch.qos.logback.core.joran.action.ParamAction; +import ch.qos.logback.core.joran.spi.ActionException; import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SerializeModelModel; import ch.qos.logback.core.pattern.parser.Parser; import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.spi.ErrorCodes; import ch.qos.logback.core.spi.ScanException; import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; import ch.qos.logback.core.testUtil.StringListAppender; import ch.qos.logback.core.util.CachingDateFormatter; +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +import org.slf4j.event.KeyValuePair; +import org.slf4j.spi.MDCAdapter; + +import java.io.Console; +import java.io.FileInputStream; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION; +import static ch.qos.logback.core.joran.sanity.AppenderWithinAppenderSanityChecker.NESTED_APPENDERS_WARNING; +import static ch.qos.logback.core.model.processor.ImplicitModelHandler.IGNORING_UNKNOWN_PROP; +import static ch.qos.logback.core.model.processor.ShutdownHookModelHandler.RENAME_WARNING; +import static ch.qos.logback.core.testUtil.CoreTestConstants.OUTPUT_DIR_PREFIX; +import static org.junit.jupiter.api.Assertions.*; public class JoranConfiguratorTest { LoggerContext loggerContext = new LoggerContext(); + MDCAdapter mdcAdapter = new LogbackMDCAdapter(); Logger logger = loggerContext.getLogger(this.getClass().getName()); Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); StatusChecker checker = new StatusChecker(loggerContext); int diff = RandomUtil.getPositiveInt(); void configure(String file) throws JoranException { + loggerContext.setMDCAdapter(mdcAdapter); JoranConfigurator jc = new JoranConfigurator(); jc.setContext(loggerContext); loggerContext.putProperty("diff", "" + diff); jc.doConfigure(file); + } @Test public void simpleList() throws JoranException { configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleList.xml"); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + assertEquals(0, listAppender.list.size()); + String msg = "hello world"; + logger.debug(msg); + assertEquals(1, listAppender.list.size()); + ILoggingEvent le = (ILoggingEvent) listAppender.list.get(0); + assertEquals(msg, le.getMessage()); + } + + + @Test + public void asyncWithMultipleAppendersInRoot() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "async/logback_1614.xml"); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + AsyncAppender asyncAppender = (AsyncAppender) root.getAppender("ASYNC"); + assertNotNull(asyncAppender); + ConsoleAppender console = (ConsoleAppender) root.getAppender("CONSOLE"); + assertNotNull(console); + assertTrue(console.isStarted()); + //assertEquals(0, listAppender.list.size()); + String msg = "hello world"; + logger.warn(msg); + } + @Test + public void simpleListWithImports() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleListWithImports.xml"); Logger logger = loggerContext.getLogger(this.getClass().getName()); Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); assertEquals(0, listAppender.list.size()); String msg = "hello world"; logger.debug(msg); @@ -116,7 +164,6 @@ public void loggerLevelSettingBySystemProperty() throws JoranException { String propertyName = "logback.level"; System.setProperty(propertyName, "DEBUG"); configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "loggerLevelByProperty.xml"); - // StatusPrinter.print(loggerContext); ListAppender listAppender = (ListAppender) root.getAppender("LIST"); assertEquals(0, listAppender.list.size()); String msg = "hello world"; @@ -135,13 +182,89 @@ public void appenderRefSettingBySystemProperty() throws JoranException { assertEquals(0, listAppender.list.size()); final String msg = "hello world"; logger.info(msg); + assertEquals(1, listAppender.list.size()); System.clearProperty(propertyName); } + @Test + public void appenderRefSettingBySystemPropertyDefault() throws JoranException { + + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "appenderRefByPropertyDefault.xml"); + StatusPrinter.print(loggerContext); + final Logger logger = loggerContext.getLogger("ch.qos.logback.classic.joran"); + final ListAppender listAppender = (ListAppender) root.getAppender("A"); + assertNotNull(listAppender); + assertEquals(0, listAppender.list.size()); + final String msg = "hello world"; + logger.info(msg); + + assertEquals(1, listAppender.list.size()); + } + + + @Test + public void refToUndefinedAppender() throws JoranException { + + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "refToUndefinedAppender.xml"); + //StatusPrinter.print(loggerContext); + final Logger logger = loggerContext.getLogger("ch.qos.logback.classic.joran"); + final ListAppender listAppender = (ListAppender) root.getAppender("A"); + assertNotNull(listAppender); + assertEquals(0, listAppender.list.size()); + final String msg = "hello world"; + logger.info(msg); + + assertEquals(1, listAppender.list.size()); + + checker.assertContainsMatch(Status.WARN, "Appender named \\[NON_EXISTENT_APPENDER\\] could not be found. Skipping attachment to Logger\\[ROOT\\]"); + } + + @Test + public void refViaDefaultSubstitution() throws JoranException { + + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "refViaDefaultSubstitution.xml"); + StatusPrinter.print(loggerContext); + final Logger logger = loggerContext.getLogger("ch.qos.logback.classic.joran"); + final ListAppender listAppender = (ListAppender) root.getAppender("A"); + final NOPAppender nopAppender = (NOPAppender) root.getAppender("NOP"); + + assertNotNull(listAppender); + assertNotNull(nopAppender); + + assertEquals(0, listAppender.list.size()); + final String msg = "hello world"; + logger.info(msg); + + assertEquals(1, listAppender.list.size()); + + checker.assertIsWarningOrErrorFree(); + } + @Test + public void fauxListener() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "fauxListener.xml"); + //StatusPrinter.print(loggerContext); + + assertEquals(0,FauxListener.COUNT); + checker.assertContainsMatch(Status.ERROR, "Could not create component \\[listener\\] of type \\[ch.qos.logback.classic.joran.FauxListener\\]"); + + } @Test public void statusListener() throws JoranException { configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "statusListener.xml"); + //StatusPrinter.print(loggerContext); + checker.assertIsErrorFree(); + checker.assertContainsMatch(Status.WARN, + "Please use \"level\" attribute within or elements instead."); + } + + @Test + public void statusListenerWithImports() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "statusListenerWithImports.xml"); + //StatusPrinter.print(loggerContext); + checker.assertIsErrorFree(); + checker.assertContainsMatch(Status.WARN, + "Please use \"level\" attribute within or elements instead."); } @Test @@ -152,21 +275,31 @@ public void contextRename() throws JoranException { } @Test - public void eval() throws JoranException { - configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "callerData.xml"); + public void missingConfigurationElement() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/noConfig.xml"); - String msg = "hello world"; - logger.debug("toto"); - logger.debug(msg); + String msg1 = "Exception in body\\(\\) method for action \\[" + ParamAction.class.getName() + "\\]"; + checker.assertContainsMatch(Status.ERROR, msg1); - StringListAppender slAppender = (StringListAppender) loggerContext.getLogger("root").getAppender("STR_LIST"); - assertNotNull(slAppender); - assertEquals(2, slAppender.strList.size()); - assertTrue(slAppender.strList.get(0).contains(" DEBUG - toto")); + String msg2 = "current model is null. Is element missing?"; + checker.assertContainsException(ActionException.class, msg2); + } + + @Test + public void ignoreUnknownProperty() throws JoranException { - String str1 = slAppender.strList.get(1); - assertTrue(str1.contains("Caller+0")); - assertTrue(str1.contains(" DEBUG - hello world")); + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/unknownProperty.xml"); + String msg = IGNORING_UNKNOWN_PROP + " \\[a\\] in \\[ch.qos.logback.classic.LoggerContext\\]"; + checker.assertContainsMatch(Status.WARN, msg); + } + + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=46995 + @Test + public void complexCollectionWihhNoKnownClass() throws JoranException { + + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/nestedComplexWithNoKnownClass.xml"); + String msg = "Could not find an appropriate class for property \\[listener\\]"; + checker.assertContainsMatch(Status.ERROR, msg); } @Test @@ -214,23 +347,6 @@ public void testLevelFilter() throws JoranException { assertEquals("hello", back.getMessage()); } - @Test - public void testEvaluatorFilter() throws JoranException { - configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "evaluatorFilter.xml"); - - // StatusPrinter.print(loggerContext); - - logger.warn("hello"); - logger.error("to be ignored"); - - ListAppender listAppender = (ListAppender) root.getAppender("LIST"); - - assertNotNull(listAppender); - assertEquals(1, listAppender.list.size()); - ILoggingEvent back = listAppender.list.get(0); - assertEquals(Level.WARN, back.getLevel()); - assertEquals("hello", back.getMessage()); - } @Test public void testTurboDynamicThreshold() throws JoranException { @@ -253,8 +369,12 @@ public void testTurboDynamicThreshold() throws JoranException { @Test public void testTurboDynamicThreshold2() throws JoranException { - configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turboDynamicThreshold2.xml"); + try { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "turboDynamicThreshold2.xml"); + } finally { + // StatusPrinter.print(loggerContext); + } ListAppender listAppender = (ListAppender) root.getAppender("LIST"); assertEquals(0, listAppender.list.size()); @@ -285,7 +405,7 @@ public void timestamp() throws JoranException, IOException, InterruptedException assertNotNull(r); CachingDateFormatter sdf = new CachingDateFormatter("yyyy-MM"); String expected = sdf.format(System.currentTimeMillis()); - assertEquals("expected \"" + expected + "\" but got " + r, expected, r); + assertEquals(expected, r, "expected \"" + expected + "\" but got " + r); } @Test @@ -318,60 +438,21 @@ public void encoderCharset() throws JoranException, IOException, InterruptedExce ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CONSOLE"); assertNotNull(consoleAppender); - LayoutWrappingEncoder encoder = (LayoutWrappingEncoder) consoleAppender.getEncoder(); + LayoutWrappingEncoder encoder = (LayoutWrappingEncoder) consoleAppender + .getEncoder(); assertEquals("UTF-8", encoder.getCharset().displayName()); - StatusChecker checker = new StatusChecker(loggerContext); checker.assertIsErrorFree(); } - void verifyJULLevel(String loggerName, Level expectedLevel) { - java.util.logging.Logger julLogger = JULHelper.asJULLogger(loggerName); - java.util.logging.Level julLevel = julLogger.getLevel(); - - if (expectedLevel == null) { - assertNull(julLevel); - } else { - assertEquals(JULHelper.asJULLevel(expectedLevel), julLevel); - } - - } - - @Test - public void levelChangePropagator0() throws JoranException, IOException, InterruptedException { - String loggerName = "changePropagator0" + diff; - java.util.logging.Logger.getLogger(loggerName).setLevel(java.util.logging.Level.INFO); - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator0.xml"; - configure(configFileAsStr); - StatusChecker checker = new StatusChecker(loggerContext); - checker.assertIsErrorFree(); - verifyJULLevel(loggerName, null); - verifyJULLevel("a.b.c." + diff, Level.WARN); - verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE); - } - @Test - public void levelChangePropagator1() throws JoranException, IOException, InterruptedException { - String loggerName = "changePropagator1" + diff; - java.util.logging.Logger logger1 = java.util.logging.Logger.getLogger(loggerName); - logger1.setLevel(java.util.logging.Level.INFO); - verifyJULLevel(loggerName, Level.INFO); - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/jul/levelChangePropagator1.xml"; - configure(configFileAsStr); - StatusChecker checker = new StatusChecker(loggerContext); - checker.assertIsErrorFree(); - verifyJULLevel(loggerName, Level.INFO); // - verifyJULLevel("a.b.c." + diff, Level.WARN); - verifyJULLevel(Logger.ROOT_LOGGER_NAME, Level.TRACE); - } + @Disabled // because slow @Test - @Ignore public void onConsoleRetro() throws JoranException, IOException, InterruptedException { String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "/onConsoleRetro.xml"; configure(configFileAsStr); - System.out.println("xxxxxxxxxxxxx"); Thread.sleep(400); loggerContext.reset(); @@ -379,10 +460,19 @@ public void onConsoleRetro() throws JoranException, IOException, InterruptedExce } @Test - public void lbcore193() throws JoranException { + public void unreferencedAppenderShouldNotTriggerUnknownPropertyMessages() throws JoranException { + String configFileAsStr = ClassicTestConstants.ISSUES_PREFIX + "/logback1572.xml"; + configure(configFileAsStr); + checker.assertContainsMatch(Status.WARN, + "Appender named \\[EMAIL\\] not referenced. Skipping further processing."); + checker.assertNoMatch(IGNORING_UNKNOWN_PROP + " \\[evaluator\\]"); + } + + @Test + public void LOGBACK_111() throws JoranException { String configFileAsStr = ClassicTestConstants.ISSUES_PREFIX + "lbcore193.xml"; configure(configFileAsStr); - checker.asssertContainsException(ScanException.class); + checker.assertContainsException(ScanException.class); checker.assertContainsMatch(Status.ERROR, "Expecting RIGHT_PARENTHESIS token but got null"); checker.assertContainsMatch(Status.ERROR, "See also " + Parser.MISSING_RIGHT_PARENTHESIS); } @@ -409,7 +499,7 @@ public void hostnameProperty() throws JoranException { assertEquals("A", loggerContext.getProperty(CoreConstants.HOSTNAME_KEY)); } - // see also http://jira.qos.ch/browse/LBCORE-254 + // see also http://jira.qos.ch/browse/LOGBACK-134 @Test public void sysProps() throws JoranException { System.setProperty("k.lbcore254", ClassicTestConstants.ISSUES_PREFIX + "lbcore254"); @@ -420,6 +510,15 @@ public void sysProps() throws JoranException { checker.assertIsErrorFree(); } + @Test + public void propsWithMissingRightCurlyBrace() throws JoranException { + System.setProperty("abc", "not important"); + JoranConfigurator configurator = new JoranConfigurator(); + configurator.setContext(loggerContext); + configurator.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "propsMissingRightCurlyBrace.xml"); + checker.assertContainsMatch(Status.ERROR, "Problem while parsing"); + } + @Test public void packageDataDisabledByConfigAttribute() throws JoranException { String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "packagingDataDisabled.xml"; @@ -430,7 +529,11 @@ public void packageDataDisabledByConfigAttribute() throws JoranException { @Test public void packageDataEnabledByConfigAttribute() throws JoranException { String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "packagingDataEnabled.xml"; - configure(configFileAsStr); + try { + configure(configFileAsStr); + } finally { + // StatusPrinter.print(loggerContext); + } assertTrue(loggerContext.isPackagingDataEnabled()); } @@ -440,13 +543,366 @@ public void valueOfConvention() throws JoranException { configure(configFileAsStr); checker.assertIsWarningOrErrorFree(); } - + @Test public void shutdownHookTest() throws JoranException { String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1162.xml"; - loggerContext.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX+"logback_issue_1162/"); + loggerContext.putProperty("output_dir", ClassicTestConstants.OUTPUT_DIR_PREFIX + "logback_issue_1162/"); + configure(configFileAsStr); + Thread thread = (Thread) loggerContext.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); + assertNotNull(thread); + } + + + @Test + public void nestedAppendersDisallowed() throws JoranException { + String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1674.xml"; + configure(configFileAsStr); + checker.assertContainsMatch(Status.WARN, NESTED_APPENDERS_WARNING); + checker.assertContainsMatch(Status.WARN, "Appender at line "); + } + + @Test + public void shutdownHookWithDelayParameter() throws JoranException { + String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1672.xml"; + configure(configFileAsStr); + + Thread thread = (Thread) loggerContext.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); + assertNotNull(thread); + checker.assertNoMatch(IGNORING_UNKNOWN_PROP); + } + + @Test + public void migrateShutdownHookClassName() throws JoranException { + String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "issues/logback_1678_shutdown.xml"; + configure(configFileAsStr); + + Thread thread = (Thread) loggerContext.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); + assertNotNull(thread); + checker.assertContainsMatch(RENAME_WARNING); + } + + @Test + public void appenderRefBeforeAppenderTest() throws JoranException { + String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "appenderRefBeforeAppender.xml"; configure(configFileAsStr); - assertNotNull(loggerContext.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD)); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + assertEquals(0, listAppender.list.size()); + String msg = "hello world"; + logger.debug(msg); + assertEquals(1, listAppender.list.size()); + ILoggingEvent le = (ILoggingEvent) listAppender.list.get(0); + assertEquals(msg, le.getMessage()); + checker.assertIsErrorFree(); + } + + @Test + public void unreferencedAppendersShouldBeSkipped() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "unreferencedAppender1.xml"); + + final ListAppender listAppenderA = (ListAppender) root.getAppender("A"); + assertNotNull(listAppenderA); + checker.assertContainsMatch(Status.WARN, "Appender named \\[B\\] not referenced. Skipping further processing."); + } + + @Test + public void asynAppenderListFirst() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "asyncAppender_list_first.xml"); + + final AsyncAppender asyncAppender = (AsyncAppender) root.getAppender("ASYNC"); + assertNotNull(asyncAppender); + assertTrue(asyncAppender.isStarted()); + } + + @Test + public void asynAppenderListAfter() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "asyncAppender_list_after.xml"); + StatusPrinter.print(loggerContext); + final AsyncAppender asyncAppender = (AsyncAppender) root.getAppender("ASYNC"); + assertNotNull(asyncAppender); + assertTrue(asyncAppender.isStarted()); + } + + // https://jira.qos.ch/browse/LOGBACK-1570 + @Test + public void missingPropertyErrorHandling() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "missingProperty.xml"); + + final ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + assertTrue(listAppender.isStarted()); + checker.assertContainsMatch(Status.WARN, + IGNORING_UNKNOWN_PROP + " \\[inexistent\\] in \\[ch.qos.logback.core.read.ListAppender\\]"); + } + + @Test + public void sequenceNumberGenerator() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "sequenceNumberGenerator.xml"); + final ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + + logger.atDebug().setMessage("hello").log(); + logger.atDebug().setMessage("world").log(); + + ILoggingEvent le0 = listAppender.list.get(0); + ILoggingEvent le1 = listAppender.list.get(1); + + long se0 = le0.getSequenceNumber(); + long se1 = le1.getSequenceNumber(); + assertEquals(1, se1 - se0); + } + + @Test + public void sequenceNumberGenerator_missingClass() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "sequenceNumberGenerator-missingClass.xml"); + //StatusPrinter.print(loggerContext); + final ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + checker.assertContainsMatch(Status.ERROR, "Missing attribute \\[class\\]. See element \\[sequenceNumberGenerator\\]"); + } + + @Test + public void kvp() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "pattern/kvp.xml"); + + String msg = "hello kvp"; + + KeyValuePair kvp1 = new KeyValuePair("k" + diff, "v" + diff); + KeyValuePair kvp2 = new KeyValuePair("k" + (diff + 1), "v" + (diff + 1)); + KeyValuePair kvpNullKey = new KeyValuePair(null, "v" + (diff + 2)); + KeyValuePair kvpNullValue = new KeyValuePair("k" + (diff + 3), null); + + logger.atDebug().addKeyValue(kvp1.key, kvp1.value).log(msg); + logger.atDebug().addKeyValue(kvp2.key, kvp2.value).log(msg); + logger.atDebug().addKeyValue(kvpNullKey.key, kvpNullKey.value).log(msg); + logger.atDebug().addKeyValue(kvpNullValue.key, kvpNullValue.value).log(msg); + + StringListAppender slAppender = (StringListAppender) loggerContext + .getLogger("root").getAppender("LIST"); + assertNotNull(slAppender); + assertEquals(4, slAppender.strList.size()); + assertTrue(slAppender.strList.get(0).contains(kvp1.key + "=\"" + kvp1.value + "\" " + msg)); + assertTrue(slAppender.strList.get(1).contains(kvp2.key + "=\"" + kvp2.value + "\" " + msg)); + assertTrue(slAppender.strList.get(2).contains("null=\"" + kvpNullKey.value + "\" " + msg)); + assertTrue(slAppender.strList.get(3).contains(kvpNullValue.key + "=\"null\" " + msg)); + } + + + // See LOGBACK-1746 + @Test + public void inclusionWithVariables() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "include/topLevel0.xml"); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + //statusPrinter2.print(loggerContext); + assertEquals(Level.ERROR, root.getLevel()); + } + + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=46697 + @Test + public void ossFuzz_46697() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-46697.xml"); + + checker.assertContainsMatch(Status.ERROR, ErrorCodes.EMPTY_MODEL_STACK); + } + + // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=47093 + // In previous versions of the code, we honored String literal + // escape sequences for the 'value' attribute named 'value'. After + // analysis this was deemed superfluous. + @Test + public void ossFuzz_47093() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47093.xml"); + assertEquals("a\\t", loggerContext.getProperty("fuzz-47093-a")); + assertEquals("a\\\\", loggerContext.getProperty("fuzz-47093-b")); + } + + @Test + public void ossFuzz_41117() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47117.xml"); + checker.assertContainsMatch(Status.ERROR, ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + checker.assertErrorCount(2); + //StatusPrinter.print(loggerContext); + } + + @Test + public void ossFuzz_41117_bis() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47117-bis.xml"); + checker.assertContainsMatch(Status.ERROR, ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + } + + @Test + public void ossFuzz_41117_bis2() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47117-bis2.xml"); + checker.assertContainsMatch(Status.ERROR, ErrorCodes.ROOT_LEVEL_CANNOT_BE_SET_TO_NULL); + } + + @Test + public void ossFuzz_47293() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47293.xml"); + checker.assertContainsMatch(Status.ERROR, ErrorCodes.MISSING_IF_EMPTY_MODEL_STACK); + } + + + @Test + public void dateConverterWithLocale() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "dateWithLocale.xml"); + checker.assertContainsMatch(Status.INFO, "Setting zoneId to \"Australia/Perth\""); + checker.assertContainsMatch(Status.INFO, "Setting locale to \"en_AU\""); + //StatusPrinter.print(loggerContext); + } + + @Test + public void propertyConfiguratorSmoke() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "propertiesConfigurator/smoke.xml"); + Logger com_foo_Logger = loggerContext.getLogger("com.toto"); + //StatusPrinter.print(loggerContext); + assertEquals(Level.WARN, com_foo_Logger.getLevel()); + + } + @Test + public void consoleCharsetTest() throws JoranException { + if (EnvUtil.isJDK21OrHigher()) { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "consoleCharset.xml"); + checker.assertContainsMatch(Status.INFO, "About to instantiate property definer of type \\[ch.qos.logback.core.property.ConsoleCharsetPropertyDefiner\\]"); + + Console console = System.console(); + if(console == null) { + checker.assertContainsMatch(Status.WARN, "System.console\\(\\) returned null. Cannot compute console's charset, returning"); + } else { + + boolean nullCharset = checker.containsMatch("System.console() returned null charset. Returning \"NULL\" string as defined value."); + boolean foundCharset = checker.containsMatch("Found value '.*' as returned by System.console()."); + + } + //StatusPrinter.print(loggerContext); + } + } + + @Test + public void levelFromAPropertyTest() throws JoranException { + + + String loggerASysLevelKey = "LOGGER_A_SYS_LEVEL"; + String loggerNestedSysLevelKey = "LOGGER_NESTED_SYS_LEVEL"; + System.setProperty(loggerASysLevelKey, "WARN"); + System.setProperty(loggerNestedSysLevelKey, "ERROR"); + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "levelFromAProperty.xml"); + + + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + + + Logger loggerA = loggerContext.getLogger("A"); + Logger loggerA_SYS = loggerContext.getLogger("A_SYS"); + + Logger loggerNESTED = loggerContext.getLogger("NESTED"); + + Logger loggerNESTED_SYS = loggerContext.getLogger("NESTED_SYS"); + + + assertEquals(Level.TRACE, root.getLevel()); + assertEquals(Level.WARN, loggerA.getLevel()); + assertEquals(Level.WARN, loggerA_SYS.getLevel()); + + assertEquals(Level.ERROR, loggerNESTED.getLevel()); + assertEquals(Level.ERROR, loggerNESTED_SYS.getLevel()); + + checker.assertContainsMatch(Status.INFO, "value \\\"WARN\\\" substituted for \\\"\\$\\{LOGGER_A_LEVEL\\}\\\""); + + System.clearProperty(loggerASysLevelKey); + System.clearProperty(loggerNestedSysLevelKey); + + } + + @Test + public void exceptionEventFilter() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "boolex/exceptionEvaluator.xml"); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + root.info("deny"); + root.info("allow", new RuntimeException("test")); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + assertNotNull(listAppender); + assertEquals(1, listAppender.list.size()); + + ILoggingEvent le = listAppender.list.get(0); + + assertNotNull(le.getThrowableProxy()); + assertEquals(RuntimeException.class.getName(), le.getThrowableProxy().getClassName()); + + } + + @Test + public void modelSerialization() throws JoranException, IOException, ClassNotFoundException { + String outputPath = OUTPUT_DIR_PREFIX + "minimal_" + diff + MODEL_CONFIG_FILE_EXTENSION; + + loggerContext.putProperty("targetModelFile", outputPath); + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "model/minimal.xml"); + StatusPrinter.print(loggerContext); + + FileInputStream fis = new FileInputStream(outputPath); + HardenedModelInputStream hmis = new HardenedModelInputStream(fis); + + Model model = (Model) hmis.readObject(); + + assertNotNull(model); + assertTrue(model instanceof ConfigurationModel); + + ConfigurationModel configurationModel = (ConfigurationModel) model; + + assertEquals("false", configurationModel.getDebugStr()); + + assertEquals(2, configurationModel.getSubModels().size()); + + SerializeModelModel smm = (SerializeModelModel) configurationModel.getSubModels().get(0); + assertEquals("${targetModelFile}", smm.getFile()); + + + LoggerModel loggerModel = (LoggerModel) configurationModel.getSubModels().get(1); + assertEquals("ModelSerializationTest", loggerModel.getName()); + + // + // + } + + + // reproduction requires placing a binary properties file. Probably not worth the effort. +// @Test +// public void ossFuzz_47249() throws JoranException { +// configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "ossfuzz/fuzz-47249.xml"); +// StatusPrinter.print(loggerContext); +// } + +// @Test +// public void doTest() throws JoranException { +// int LIMIT = 0; +// boolean oss = true; +// for (int i = 0; i < LIMIT; i++) { +// innerDoT(oss); +// } +// long start = System.currentTimeMillis(); +// innerDoT(oss); +// long diff = System.currentTimeMillis() - start; +// double average = (1.0d * diff); +// System.out.println("Average time " + average + " ms. By serialization " + oss); +// +// } + +// private void innerDoT(boolean oss) throws JoranException { +// JoranConfigurator jc = new JoranConfigurator(); +// jc.setContext(loggerContext); +// if (oss) { +// System.out.println("jc.doT"); +// jc.doT(); +// } else { +// System.out.println("jc.doConfigure"); +// jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "twoAppenders.xml"); +// } +// } + } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/PackageTest.java deleted file mode 100644 index 46ff15d903..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ JoranConfiguratorTest.class, EvaluatorJoranTest.class, ch.qos.logback.classic.joran.conditional.PackageTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertiesConfiguratorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertiesConfiguratorTest.java new file mode 100644 index 0000000000..057389764a --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/PropertiesConfiguratorTest.java @@ -0,0 +1,101 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.classic.ClassicConstants; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class PropertiesConfiguratorTest { + + LoggerContext lc = new LoggerContext(); + Properties props = new Properties(); + PropertiesConfigurator pc = new PropertiesConfigurator(); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + @BeforeEach + public void setup() throws Exception { + pc.setContext(lc); + } + + @Test + public void smoke() { + String TOTO_STR = "toto"; + props.setProperty(PropertiesConfigurator.LOGBACK_ROOT_LOGGER_PREFIX, Level.INFO.levelStr); + props.setProperty(PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + TOTO_STR, Level.ERROR.levelStr); + pc.doConfigure(props); + + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger totoLogger = lc.getLogger(TOTO_STR); + + assertEquals(Level.INFO, rootLogger.getLevel()); + + assertEquals(Level.ERROR, totoLogger.getLevel()); + + } + + @Test + public void withVariables() { + String TOTO_STR = "toto"; + String ROOT_LEVEL_STR = "rootLevel"; + String TOTO_LEVEL_STR = "totoLevel"; + + props.setProperty(ROOT_LEVEL_STR, Level.INFO.levelStr); + System.setProperty("totoLevel", Level.ERROR.levelStr); + props.setProperty(PropertiesConfigurator.LOGBACK_ROOT_LOGGER_PREFIX, asVar(ROOT_LEVEL_STR)); + props.setProperty(PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + TOTO_STR, asVar(TOTO_LEVEL_STR)); + pc.doConfigure(props); + + Logger rootLogger = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger totoLogger = lc.getLogger(TOTO_STR); + statusPrinter2.print(lc); + assertEquals(Level.INFO, rootLogger.getLevel()); + assertEquals(Level.ERROR, totoLogger.getLevel()); + + } + + @Test + void inheritedLevelString() { + String loggerName0 = "com.abc.some0"; + String loggerName1 = "com.abc.some1"; + + Logger aLogger0 = lc.getLogger(loggerName0); + aLogger0.setLevel(Level.ERROR); + + Logger aLogger1 = lc.getLogger(loggerName1); + aLogger1.setLevel(Level.WARN); + + + props.setProperty(PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + loggerName0, JoranConstants.INHERITED); + props.setProperty(PropertiesConfigurator.LOGBACK_LOGGER_PREFIX + loggerName1, JoranConstants.NULL); + pc.doConfigure(props); + + //statusPrinter2.print(lc); + assertEquals(null, aLogger0.getLevel()); + assertEquals(null, aLogger1.getLevel()); + } + + String asVar(String v) { + return "${"+v+"}"; + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/ROCConfigurationEventListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/ROCConfigurationEventListener.java new file mode 100644 index 0000000000..c3fc660949 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/ROCConfigurationEventListener.java @@ -0,0 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran; + +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; + +public class ROCConfigurationEventListener implements ConfigurationEventListener { + ReconfigureOnChangeTask reconfigureOnChangeTask; + + @Override + public void listen(ConfigurationEvent configurationEvent) { + switch(configurationEvent.getEventType()) { + case CHANGE_DETECTED: + Object data = configurationEvent.getData(); + if(data instanceof ReconfigureOnChangeTask) { + reconfigureOnChangeTask = (ReconfigureOnChangeTask) data; + } else { + // ignore + } + break; + default: // nop; + } + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskTest.java deleted file mode 100755 index 8301cdb968..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/ReconfigureOnChangeTaskTest.java +++ /dev/null @@ -1,486 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran; - -import static ch.qos.logback.classic.ClassicTestConstants.JORAN_INPUT_PREFIX; -import static ch.qos.logback.classic.joran.ReconfigureOnChangeTask.DETECTED_CHANGE_IN_CONFIGURATION_FILES; -import static ch.qos.logback.classic.joran.ReconfigureOnChangeTask.FALLING_BACK_TO_SAFE_CONFIGURATION; -import static ch.qos.logback.classic.joran.ReconfigureOnChangeTask.RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION; -import static ch.qos.logback.core.CoreConstants.RECONFIGURE_ON_CHANGE_TASK; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.ByteArrayInputStream; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStream; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledFuture; - -import org.junit.BeforeClass; -import org.junit.Test; - -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.issue.lbclassic135.LoggingRunnable; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.contention.AbstractMultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; -import ch.qos.logback.core.joran.spi.ConfigurationWatchList; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.status.InfoStatus; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.FileTestUtil; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -public class ReconfigureOnChangeTaskTest { - final static int THREAD_COUNT = 5; - - int diff = RandomUtil.getPositiveInt(); - - // the space in the file name mandated by - // http://jira.qos.ch/browse/LBCORE-119 - final static String SCAN1_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan 1.xml"; - - final static String G_SCAN1_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan 1.groovy"; - - final static String SCAN_LOGBACK_474_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan_logback_474.xml"; - - final static String INCLUSION_SCAN_TOPLEVEL0_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/topLevel0.xml"; - - final static String INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/topByResource.xml"; - - final static String INCLUSION_SCAN_INNER0_AS_STR = JORAN_INPUT_PREFIX + "roct/inclusion/inner0.xml"; - - final static String INCLUSION_SCAN_INNER1_AS_STR = "target/test-classes/asResource/inner1.xml"; - - private static final String SCAN_PERIOD_DEFAULT_FILE_AS_STR = JORAN_INPUT_PREFIX + "roct/scan_period_default.xml"; - - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger(this.getClass()); - StatusChecker statusChecker = new StatusChecker(loggerContext); - - @BeforeClass - static public void classSetup() { - FileTestUtil.makeTestOutputDir(); - } - - void configure(File file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(file); - } - - void configure(InputStream is) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(is); - } - -// void gConfigure(File file) throws JoranException { -// GafferConfigurator gc = new GafferConfigurator(loggerContext); -// gc.run(file); -// } - - @Test(timeout = 4000L) - public void checkBasicLifecyle() throws JoranException, IOException, InterruptedException { - File file = new File(SCAN1_FILE_AS_STR); - configure(file); - List fileList = getConfigurationWatchList(loggerContext); - assertThatListContainsFile(fileList, file); - checkThatTaskHasRan(); - checkThatTaskCanBeStopped(); - } - -// @Test(timeout = 4000L) -// public void checkBasicLifecyleWithGaffer() throws JoranException, IOException, InterruptedException { -// File file = new File(G_SCAN1_FILE_AS_STR); -// gConfigure(file); -// List fileList = getConfigurationWatchList(loggerContext); -// assertThatListContainsFile(fileList, file); -// checkThatTaskHasRan(); -// checkThatTaskCanBeStopped(); -// } - - private void checkThatTaskCanBeStopped() { - ScheduledFuture future = loggerContext.getScheduledFutures().get(0); - loggerContext.stop(); - assertTrue(future.isCancelled()); - } - - private void checkThatTaskHasRan() throws InterruptedException { - waitForReconfigureOnChangeTaskToRun(); - } - - List getConfigurationWatchList(LoggerContext context) { - ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); - return configurationWatchList.getCopyOfFileWatchList(); - } - - @Test(timeout = 4000L) - public void scanWithFileInclusion() throws JoranException, IOException, InterruptedException { - File topLevelFile = new File(INCLUSION_SCAN_TOPLEVEL0_AS_STR); - File innerFile = new File(INCLUSION_SCAN_INNER0_AS_STR); - configure(topLevelFile); - List fileList = getConfigurationWatchList(loggerContext); - assertThatListContainsFile(fileList, topLevelFile); - assertThatListContainsFile(fileList, innerFile); - checkThatTaskHasRan(); - checkThatTaskCanBeStopped(); - } - - @Test(timeout = 4000L) - public void scanWithResourceInclusion() throws JoranException, IOException, InterruptedException { - File topLevelFile = new File(INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR); - File innerFile = new File(INCLUSION_SCAN_INNER1_AS_STR); - configure(topLevelFile); - List fileList = getConfigurationWatchList(loggerContext); - assertThatListContainsFile(fileList, topLevelFile); - assertThatListContainsFile(fileList, innerFile); - } - - // See also http://jira.qos.ch/browse/LOGBACK-338 - @Test(timeout = 4000L) - public void reconfigurationIsNotPossibleInTheAbsenceOfATopFile() throws IOException, JoranException, InterruptedException { - String configurationStr = ""; - configure(new ByteArrayInputStream(configurationStr.getBytes("UTF-8"))); - - ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); - assertNull(configurationWatchList); - // assertNull(configurationWatchList.getMainURL()); - - statusChecker.containsMatch(Status.WARN, "Due to missing top level"); - StatusPrinter.print(loggerContext); - ReconfigureOnChangeTask roct = getRegisteredReconfigureTask(); - assertNull(roct); - assertEquals(0, loggerContext.getScheduledFutures().size()); - } - - @Test(timeout = 3000L) - public void fallbackToSafe_FollowedByRecovery() throws IOException, JoranException, InterruptedException { - String path = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_fallbackToSafe-" + diff + ".xml"; - File topLevelFile = new File(path); - writeToFile(topLevelFile, " "); - configure(topLevelFile); - CountDownLatch changeDetectedLatch = waitForReconfigurationToBeDone(null); - ReconfigureOnChangeTask oldRoct = getRegisteredReconfigureTask(); - assertNotNull(oldRoct); - writeToFile(topLevelFile, "\n" + " "); - changeDetectedLatch.await(); - statusChecker.assertContainsMatch(Status.WARN, FALLING_BACK_TO_SAFE_CONFIGURATION); - statusChecker.assertContainsMatch(Status.INFO, RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION); - - loggerContext.getStatusManager().clear(); - - CountDownLatch secondDoneLatch = waitForReconfigurationToBeDone(oldRoct); - writeToFile(topLevelFile, " "); - secondDoneLatch.await(); - StatusPrinter.print(loggerContext); - statusChecker.assertIsErrorFree(); - statusChecker.containsMatch(DETECTED_CHANGE_IN_CONFIGURATION_FILES); - } - - @Test(timeout = 4000L) - public void fallbackToSafeWithIncludedFile_FollowedByRecovery() throws IOException, JoranException, InterruptedException, ExecutionException { - String topLevelFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_top-" + diff + ".xml"; - String innerFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_inner-" + diff + ".xml"; - File topLevelFile = new File(topLevelFileAsStr); - writeToFile(topLevelFile, " "); - - File innerFile = new File(innerFileAsStr); - writeToFile(innerFile, " "); - configure(topLevelFile); - - CountDownLatch doneLatch = waitForReconfigurationToBeDone(null); - ReconfigureOnChangeTask oldRoct = getRegisteredReconfigureTask(); - assertNotNull(oldRoct); - - writeToFile(innerFile, "\n\n"); - doneLatch.await(); - - statusChecker.assertContainsMatch(Status.WARN, FALLING_BACK_TO_SAFE_CONFIGURATION); - statusChecker.assertContainsMatch(Status.INFO, RE_REGISTERING_PREVIOUS_SAFE_CONFIGURATION); - - loggerContext.getStatusManager().clear(); - - CountDownLatch secondDoneLatch = waitForReconfigurationToBeDone(oldRoct); - writeToFile(innerFile, " "); - secondDoneLatch.await(); - - StatusPrinter.print(loggerContext); - statusChecker.assertIsErrorFree(); - statusChecker.containsMatch(DETECTED_CHANGE_IN_CONFIGURATION_FILES); - } - - private ReconfigureOnChangeTask getRegisteredReconfigureTask() { - return (ReconfigureOnChangeTask) loggerContext.getObject(RECONFIGURE_ON_CHANGE_TASK); - } - - class RunMethodInvokedListener extends ReconfigureOnChangeTaskListener { - CountDownLatch countDownLatch; - - RunMethodInvokedListener(CountDownLatch countDownLatch) { - this.countDownLatch = countDownLatch; - } - - @Override - public void enteredRunMethod() { - countDownLatch.countDown(); - } - }; - - class ChangeDetectedListener extends ReconfigureOnChangeTaskListener { - CountDownLatch countDownLatch; - - ChangeDetectedListener(CountDownLatch countDownLatch) { - this.countDownLatch = countDownLatch; - } - - @Override - public void changeDetected() { - countDownLatch.countDown(); - } - }; - - class ReconfigurationDoneListener extends ReconfigureOnChangeTaskListener { - CountDownLatch countDownLatch; - - ReconfigurationDoneListener(CountDownLatch countDownLatch) { - this.countDownLatch = countDownLatch; - } - - @Override - public void doneReconfiguring() { - countDownLatch.countDown(); - } - }; - - private ReconfigureOnChangeTask waitForReconfigureOnChangeTaskToRun() throws InterruptedException { - ReconfigureOnChangeTask roct = null; - while (roct == null) { - roct = getRegisteredReconfigureTask(); - Thread.yield(); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - roct.addListener(new RunMethodInvokedListener(countDownLatch)); - countDownLatch.await(); - return roct; - } - - private CountDownLatch waitForReconfigurationToBeDone(ReconfigureOnChangeTask oldTask) throws InterruptedException { - ReconfigureOnChangeTask roct = oldTask; - while (roct == oldTask) { - roct = getRegisteredReconfigureTask(); - Thread.yield(); - } - - CountDownLatch countDownLatch = new CountDownLatch(1); - roct.addListener(new ReconfigurationDoneListener(countDownLatch)); - return countDownLatch; - } - - private RunnableWithCounterAndDone[] buildRunnableArray(File configFile, UpdateType updateType) { - RunnableWithCounterAndDone[] rArray = new RunnableWithCounterAndDone[THREAD_COUNT]; - rArray[0] = new Updater(configFile, updateType); - for (int i = 1; i < THREAD_COUNT; i++) { - rArray[i] = new LoggingRunnable(logger); - } - return rArray; - } - - @Test - public void checkReconfigureTaskScheduledWhenDefaultScanPeriodUsed() throws JoranException { - File file = new File(SCAN_PERIOD_DEFAULT_FILE_AS_STR); - configure(file); - - final List> scheduledFutures = loggerContext.getScheduledFutures(); - StatusPrinter.print(loggerContext); - assertFalse(scheduledFutures.isEmpty()); - statusChecker.containsMatch("No 'scanPeriod' specified. Defaulting to"); - - } - - // check for deadlocks - @Test(timeout = 4000L) - public void scan_LOGBACK_474() throws JoranException, IOException, InterruptedException { - loggerContext.setName("scan_LOGBACK_474"); - File file = new File(SCAN_LOGBACK_474_FILE_AS_STR); - // StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener()); - configure(file); - - // ReconfigureOnChangeTask roct = waitForReconfigureOnChangeTaskToRun(); - - int expectedResets = 2; - Harness harness = new Harness(expectedResets); - - RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(file, UpdateType.TOUCH); - harness.execute(runnableArray); - - loggerContext.getStatusManager().add(new InfoStatus("end of execution ", this)); - StatusPrinter.print(loggerContext); - checkResetCount(expectedResets); - } - - private void assertThatListContainsFile(List fileList, File file) { - // conversion to absolute file seems to work nicely - assertTrue(fileList.contains(file.getAbsoluteFile())); - } - - private void checkResetCount(int expected) { - StatusChecker checker = new StatusChecker(loggerContext); - checker.assertIsErrorFree(); - - int effectiveResets = checker.matchCount(CoreConstants.RESET_MSG_PREFIX); - assertEquals(expected, effectiveResets); - - // String failMsg = "effective=" + effectiveResets + ", expected=" + expected; - // - // there might be more effective resets than the expected amount - // since the harness may be sleeping while a reset occurs - // assertTrue(failMsg, expected <= effectiveResets && (expected + 2) >= effectiveResets); - - } - - void addInfo(String msg, Object o) { - loggerContext.getStatusManager().add(new InfoStatus(msg, o)); - } - - enum UpdateType { - TOUCH, MALFORMED, MALFORMED_INNER - } - - void writeToFile(File file, String contents) throws IOException { - FileWriter fw = new FileWriter(file); - fw.write(contents); - fw.close(); - // on linux changes to last modified are not propagated if the - // time stamp is near the previous time stamp hence the random delta - file.setLastModified(System.currentTimeMillis()+RandomUtil.getPositiveInt()); - } - - class Harness extends AbstractMultiThreadedHarness { - int changeCountLimit; - - Harness(int changeCount) { - this.changeCountLimit = changeCount; - } - - public void waitUntilEndCondition() throws InterruptedException { - ReconfigureOnChangeTaskTest.this.addInfo("Entering " + this.getClass() + ".waitUntilEndCondition()", this); - - int changeCount = 0; - ReconfigureOnChangeTask lastRoct = null; - CountDownLatch countDownLatch = null; - - while (changeCount < changeCountLimit) { - ReconfigureOnChangeTask roct = (ReconfigureOnChangeTask) loggerContext.getObject(RECONFIGURE_ON_CHANGE_TASK); - if (lastRoct != roct && roct != null) { - lastRoct = roct; - countDownLatch = new CountDownLatch(1); - roct.addListener(new ChangeDetectedListener(countDownLatch)); - } else if (countDownLatch != null) { - countDownLatch.await(); - countDownLatch = null; - changeCount++; - } - Thread.yield(); - } - ReconfigureOnChangeTaskTest.this.addInfo("*****Exiting " + this.getClass() + ".waitUntilEndCondition()", this); - } - - } - - class Updater extends RunnableWithCounterAndDone { - File configFile; - UpdateType updateType; - - // it actually takes time for Windows to propagate file modification changes - // values below 100 milliseconds can be problematic the same propagation - // latency occurs in Linux but is even larger (>600 ms) - // final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 60; - - int sleepBetweenUpdates = 100; - - Updater(File configFile, UpdateType updateType) { - this.configFile = configFile; - this.updateType = updateType; - } - - Updater(File configFile) { - this(configFile, UpdateType.TOUCH); - } - - public void run() { - while (!isDone()) { - try { - Thread.sleep(sleepBetweenUpdates); - } catch (InterruptedException e) { - } - if (isDone()) { - ReconfigureOnChangeTaskTest.this.addInfo("Exiting Updater.run()", this); - return; - } - counter++; - ReconfigureOnChangeTaskTest.this.addInfo("Touching [" + configFile + "]", this); - switch (updateType) { - case TOUCH: - touchFile(); - break; - case MALFORMED: - try { - malformedUpdate(); - } catch (IOException e) { - e.printStackTrace(); - fail("malformedUpdate failed"); - } - break; - case MALFORMED_INNER: - try { - malformedInnerUpdate(); - } catch (IOException e) { - e.printStackTrace(); - fail("malformedInnerUpdate failed"); - } - } - } - ReconfigureOnChangeTaskTest.this.addInfo("Exiting Updater.run()", this); - } - - private void malformedUpdate() throws IOException { - writeToFile(configFile, "\n" + " \n" + ""); - } - - private void malformedInnerUpdate() throws IOException { - writeToFile(configFile, "\n" + " \n" + ""); - } - - void touchFile() { - configFile.setLastModified(System.currentTimeMillis()); - } - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/ConditionalTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/ConditionalTest.java deleted file mode 100755 index bff58ed387..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/ConditionalTest.java +++ /dev/null @@ -1,145 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran.conditional; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.UnknownHostException; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.ConsoleAppender; -import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.read.ListAppender; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -public class ConditionalTest { - - LoggerContext context = new LoggerContext(); - Logger root = context.getLogger(Logger.ROOT_LOGGER_NAME); - - int diff = RandomUtil.getPositiveInt(); - String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; - - @Before - public void setUp() throws UnknownHostException { - context.setName("c" + diff); - context.putProperty("randomOutputDir", randomOutputDir); - } - - @After - public void tearDown() { - StatusPrinter.printIfErrorsOccured(context); - } - - void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(context); - jc.doConfigure(file); - } - - @SuppressWarnings("rawtypes") - @Test - public void conditionalConsoleApp_IF_THEN_True() throws JoranException, IOException, InterruptedException { - InetAddress localhost = InetAddress.getLocalHost(); - System.out.println("In conditionalConsoleApp_IF_THEN_True, canonicalHostName=\"" + localhost.getCanonicalHostName() + "] and hostNmae=\"" - + localhost.getHostName() + "\""); - context.putProperty("aHost", localhost.getHostName()); - - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp.xml"; - configure(configFileAsStr); - FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); - assertNotNull(fileAppender); - - ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); - assertNotNull(consoleAppender); - StatusChecker checker = new StatusChecker(context); - checker.assertIsErrorFree(); - } - - @SuppressWarnings("rawtypes") - @Test - public void conditionalConsoleApp_IF_THEN_False() throws JoranException, IOException, InterruptedException { - - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp.xml"; - configure(configFileAsStr); - FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); - assertNotNull(fileAppender); - - ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); - assertNull(consoleAppender); - StatusChecker checker = new StatusChecker(context); - checker.assertIsErrorFree(); - } - - @SuppressWarnings("rawtypes") - @Test - public void conditionalConsoleApp_IF_THEN_ELSE() throws JoranException, IOException, InterruptedException { - - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalConsoleApp_ELSE.xml"; - configure(configFileAsStr); - - FileAppender fileAppender = (FileAppender) root.getAppender("FILE"); - assertNotNull(fileAppender); - - ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); - assertNull(consoleAppender); - - ListAppender listAppender = (ListAppender) root.getAppender("LIST"); - assertNotNull(listAppender); - - // StatusPrinter.printIfErrorsOccured(context); - StatusChecker checker = new StatusChecker(context); - checker.assertIsErrorFree(); - } - - @Test - public void conditionalInclusionWithExistingFile() throws JoranException, IOException, InterruptedException { - - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalIncludeExistingFile.xml"; - configure(configFileAsStr); - - ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); - assertNotNull(consoleAppender); - StatusChecker checker = new StatusChecker(context); - checker.assertIsErrorFree(); - } - - @Test - public void conditionalInclusionWithInexistentFile() throws JoranException, IOException, InterruptedException { - - String configFileAsStr = ClassicTestConstants.JORAN_INPUT_PREFIX + "conditional/conditionalIncludeInexistentFile.xml"; - configure(configFileAsStr); - - ConsoleAppender consoleAppender = (ConsoleAppender) root.getAppender("CON"); - assertNull(consoleAppender); - StatusChecker checker = new StatusChecker(context); - checker.assertIsErrorFree(); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/PackageTest.java deleted file mode 100644 index c5b46918d1..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/joran/conditional/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.joran.conditional; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ConditionalTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/ClassicTopModel.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/ClassicTopModel.java new file mode 100644 index 0000000000..15d5b0be8c --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/ClassicTopModel.java @@ -0,0 +1,28 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.sanity; + +import ch.qos.logback.core.model.Model; + +public class ClassicTopModel extends Model { + + private static final long serialVersionUID = 6378962040610737208L; + + @Override + protected ClassicTopModel makeNewInstance() { + return new ClassicTopModel(); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/EvaluatorStubTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/EvaluatorStubTest.java new file mode 100644 index 0000000000..f95ae78ec9 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/EvaluatorStubTest.java @@ -0,0 +1,81 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.sanity; + +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.boolex.StubEventEvaluator; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.read.ListAppender; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import org.slf4j.ILoggerFactory; +import org.slf4j.spi.MDCAdapter; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EvaluatorStubTest { + LoggerContext loggerContext = new LoggerContext(); + JoranConfigurator jc = new JoranConfigurator(); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + StatusChecker statusChecker = new StatusChecker(loggerContext); + MDCAdapter mdcAdapter = new LogbackMDCAdapter(); + + @BeforeEach + void setUp() { + loggerContext.setMDCAdapter(mdcAdapter); + } + + @Test + public void standaloneEventEvaluatorTest() throws JoranException { + jc.setContext(loggerContext); + jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "simpleEvaluator.xml"); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_0); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_1); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_2); + //statusPrinter2.print(loggerContext); + } + + @Test + public void eventEvaluatorEmbeddedInFilterTest() throws JoranException { + jc.setContext(loggerContext); + jc.doConfigure(ClassicTestConstants.JORAN_INPUT_PREFIX + "basicEventEvaluator.xml"); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_0); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_1); + statusChecker.assertContainsMatch(StubEventEvaluator.MSG_2); + + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + + ListAppender listAppender = (ListAppender) root.getAppender("LIST"); + List eventList = listAppender.list; + + String message = "hello"; + Logger logger = loggerContext.getLogger(this.getClass()); + logger.warn(message); + assertEquals(1, eventList.size()); + assertEquals(message, eventList.get(0).getMessage()); + statusPrinter2.print(loggerContext); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSCTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSCTest.java new file mode 100644 index 0000000000..465ff17b79 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/sanity/IfNestedWithinSecondPhaseElementSCTest.java @@ -0,0 +1,103 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.sanity; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.model.LoggerModel; +import ch.qos.logback.classic.model.RootLoggerModel; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +public class IfNestedWithinSecondPhaseElementSCTest { + + LoggerContext context = new LoggerContext(); + IfNestedWithinSecondPhaseElementSC inwspeChecker = new IfNestedWithinSecondPhaseElementSC(); + StatusChecker statusChecker = new StatusChecker(context); + + @BeforeEach + public void setUp() throws Exception { + inwspeChecker.setContext(context); + } + + @Test + public void smoke() { + + ClassicTopModel topModel = new ClassicTopModel(); + inwspeChecker.check(topModel); + statusChecker.assertIsWarningOrErrorFree(); + } + + @Test + public void singleAppender() { + ClassicTopModel topModel = new ClassicTopModel(); + AppenderModel appenderModel0 = new AppenderModel(); + appenderModel0.setLineNumber(1); + topModel.addSubModel(appenderModel0); + inwspeChecker.check(topModel); + statusChecker.assertIsWarningOrErrorFree(); + } + + @Test + public void singleLoggerWithNestedIf() { + ClassicTopModel topModel = new ClassicTopModel(); + Model rootLoggerModel = setupModel(new RootLoggerModel(), "root", 1); + topModel.addSubModel(rootLoggerModel); + + Model ifModel0 = setupModel(new IfModel(), "if", 2); + rootLoggerModel.addSubModel(ifModel0); + + Model loggerModel = setupModel(new LoggerModel(), "logger", 3); + topModel.addSubModel(loggerModel); + + Model ifModel1 = setupModel(new IfModel(), "if", 4); + loggerModel.addSubModel(ifModel1); + + Model appenderModel = setupModel(new LoggerModel(), "appender", 5); + topModel.addSubModel(appenderModel); + + Model ifModel2 = setupModel(new IfModel(), "if", 6); + appenderModel.addSubModel(ifModel2); + + + inwspeChecker.check(topModel); + StatusPrinter.print(context); + // Element at line 1 contains a nested element at line 2 + String regex0 = "Element at line 1 contains a nested element at line 2"; + statusChecker.assertContainsMatch(Status.WARN, regex0); + + String regex1 = "Element at line 3 contains a nested element at line 4"; + statusChecker.assertContainsMatch(Status.WARN, regex1); + + String regex2 = "Element at line 5 contains a nested element at line 6"; + statusChecker.assertContainsMatch(Status.WARN, regex2); + + } + + + + private Model setupModel(Model m, String tag, int line) { + m.setLineNumber(line); + m.setTag(tag); + return m; + } + + +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/joran/serializedModel/ModelSerializationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/joran/serializedModel/ModelSerializationTest.java new file mode 100644 index 0000000000..3d6be367f8 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/joran/serializedModel/ModelSerializationTest.java @@ -0,0 +1,80 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.joran.serializedModel; + +import ch.qos.logback.classic.model.ConfigurationModel; +import ch.qos.logback.classic.model.LoggerModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.net.HardenedObjectInputStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ModelSerializationTest { + + + ByteArrayOutputStream bos; + ObjectOutputStream oos; + HardenedObjectInputStream inputStream; + //String[] whitelist = new String[] { }; + + + @BeforeEach + public void setUp() throws Exception { + bos = new ByteArrayOutputStream(); + oos = new ObjectOutputStream(bos); + } + + @AfterEach + public void tearDown() throws Exception { + } + + @Test + public void smoke() throws ClassNotFoundException, IOException { + ConfigurationModel configurationModel = new ConfigurationModel(); + configurationModel.setTag("configuration"); + configurationModel.setDebugStr("true"); + + LoggerModel loggerModel = new LoggerModel(); + loggerModel.setTag("logger"); + loggerModel.setLevel("DEBUG"); + configurationModel.addSubModel(loggerModel); + + Model back = writeAndRead(configurationModel); + assertEquals(configurationModel, back); + } + + private Model writeAndRead(Model model) throws IOException, ClassNotFoundException { + writeObject(oos, model); + ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); + inputStream = new HardenedModelInputStream(bis); + Model fooBack = (Model) inputStream.readObject(); + inputStream.close(); + return fooBack; + } + + private void writeObject(ObjectOutputStream oos, Object o) throws IOException { + oos.writeObject(o); + oos.flush(); + oos.close(); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java new file mode 100644 index 0000000000..187677c646 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonLoggingEvent.java @@ -0,0 +1,198 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.PubLoggerContextVO; +import ch.qos.logback.classic.spi.PubThrowableProxy; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; +import org.slf4j.helpers.MessageFormatter; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@JsonIgnoreProperties({ }) +public class JsonLoggingEvent implements ILoggingEvent { + public String threadName; + public String loggerName; + + + @JsonAlias({"context"}) + public LoggerContextVO loggerContextVO; + + public Level level; + public String message; + + private String formattedMessage; + + @JsonAlias({"arguments"}) + public Object[] argumentArray; + + @JsonAlias({"throwable"}) + public PubThrowableProxy throwableProxy; + + @JsonIgnore + public StackTraceElement[] callerDataArray; + + @JsonAlias({"markers"}) + public List markerList; + + @JsonAlias({"kvp"}) + public List kvpList; + + @JsonAlias({"mdc"}) + public Map mdcPropertyMap; + + public long timestamp; + public int nanoseconds; + public long sequenceNumber; + + public String getThreadName() { + return threadName; + } + + + public LoggerContextVO getLoggerContextVO() { + return loggerContextVO; + } + + public String getLoggerName() { + return loggerName; + } + + + //@JsonIgnore + public Level getLevel() { + return level; + } + + //@JsonIgnore + public void setLevel(Level aLavel) { + level = aLavel; + } + + public String getMessage() { + return message; + } + + public String getFormattedMessage() { + if (formattedMessage != null) { + return formattedMessage; + } + + if (argumentArray != null) { + formattedMessage = MessageFormatter.arrayFormat(message, argumentArray).getMessage(); + } else { + formattedMessage = message; + } + + return formattedMessage; + } + + public Object[] getArgumentArray() { + return argumentArray; + } + + public IThrowableProxy getThrowableProxy() { + return throwableProxy; + } + + public StackTraceElement[] getCallerData() { + return callerDataArray; + } + + public boolean hasCallerData() { + return callerDataArray != null; + } + + public List getMarkerList() { + return markerList; + } + + public long getTimeStamp() { + return timestamp; + } + + @Override + public int getNanoseconds() { + return nanoseconds; + } + + public long getSequenceNumber() { + return sequenceNumber; + } + + public void setSequenceNumber(long sequenceNumber) { + this.sequenceNumber = sequenceNumber; + } + + public long getContextBirthTime() { + return loggerContextVO.getBirthTime(); + } + + public LoggerContextVO getContextLoggerRemoteView() { + return loggerContextVO; + } + + public Map getMDCPropertyMap() { + return mdcPropertyMap; + } + + public Map getMdc() { + return mdcPropertyMap; + } + + public void setMdc( Map map) { + mdcPropertyMap = map; + } + + public void prepareForDeferredProcessing() { + } + + @Override + public List getKeyValuePairs() { + return kvpList; + } + + public void setKeyValuePairs( List aList) { + kvpList = aList; + } + + + + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(timestamp); + sb.append(" "); + sb.append(level); + sb.append(" ["); + sb.append(threadName); + sb.append("] "); + sb.append(loggerName); + sb.append(" - "); + sb.append(getFormattedMessage()); + return sb.toString(); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java new file mode 100644 index 0000000000..a946d999ee --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/JsonStringToLoggingEventMapper.java @@ -0,0 +1,106 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.LoggerContextVO; +import ch.qos.logback.classic.spi.PubThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +public class JsonStringToLoggingEventMapper { + IMarkerFactory markerFactory; + + + public JsonStringToLoggingEventMapper(IMarkerFactory markerFactory) { + this.markerFactory = markerFactory; + } + + public JsonLoggingEvent mapStringToLoggingEvent(String resultString) throws JsonProcessingException { + ObjectMapper objectMapper = new ObjectMapper(); + SimpleModule module = new SimpleModule(); + module.addDeserializer(StackTraceElementProxy.class, new STEPDeserializer()); + module.addDeserializer(Level.class, new LevelDeserializer()); + module.addDeserializer(Marker.class, new MarkerDeserializer(markerFactory)); + module.addDeserializer(KeyValuePair.class, new KeyValuePairDeserializer()); + module.addDeserializer(LoggerContextVO.class, new LoggerContextVODeserializer()); + module.addDeserializer(PubThrowableProxy.class, new PubThrowableProxyDeserializer()); + + objectMapper.registerModule(module); + + JsonNode jsonNode = objectMapper.readTree(resultString); + JsonLoggingEvent resultEvent = objectMapper.treeToValue(jsonNode, JsonLoggingEvent.class); + //buildLevel(jsonNode, resultEvent); + + //xbuildMarkersList(jsonNode, resultEvent); + //xbuildKVPList(jsonNode, resultEvent); + //buildThrowableProxy(jsonNode, resultEvent); + return resultEvent; + } + + private static void UNUSED_buildLevel(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + String levelStr = jsonNode.at("/"+ JsonEncoder.LEVEL_ATTR_NAME).asText(); + Level level = Level.toLevel(levelStr); + resultEvent.level = level; + } + + private void UNUSED_buildMarkersList(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + JsonNode markersNode = jsonNode.at("/"+JsonEncoder.MARKERS_ATTR_NAME); + if(markersNode!=null && markersNode.isArray()) { + List markerList = new ArrayList<>(); + Iterator itr = markersNode.iterator(); + while (itr.hasNext()) { + JsonNode item=itr.next(); + String markerStr = item.asText(); + Marker marker = markerFactory.getMarker(markerStr); + markerList.add(marker); + } + resultEvent.markerList = markerList; + } + } + + + private void UNUSED_buildKVPList(JsonNode jsonNode, JsonLoggingEvent resultEvent) { + JsonNode kvpNode = jsonNode.at("/"+JsonEncoder.KEY_VALUE_PAIRS_ATTR_NAME); + if(kvpNode!=null && kvpNode.isArray()) { + System.out.println("in buildKVPList"); + List kvpList = new ArrayList<>(); + Iterator itr = kvpNode.iterator(); + while (itr.hasNext()) { + JsonNode item=itr.next(); + + Map.Entry entry = item.fields().next(); + String key = entry.getKey(); + String val = entry.getValue().asText(); + kvpList.add(new KeyValuePair(key, val)); + + } + resultEvent.kvpList =kvpList; + } + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java new file mode 100644 index 0000000000..606bfb0f4b --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/KeyValuePairDeserializer.java @@ -0,0 +1,58 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.event.KeyValuePair; + +import java.io.IOException; +import java.util.Iterator; +import java.util.Map; + +public class KeyValuePairDeserializer extends StdDeserializer { + public KeyValuePairDeserializer() { + this(null); + } + + + public KeyValuePairDeserializer(Class vc) { + super(vc); + } + + + @Override + public KeyValuePair deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + if(node.isObject()) { + Iterator> it = node.fields(); + if(it.hasNext()) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue().asText(); + KeyValuePair kvp = new KeyValuePair(key, value); + return kvp; + } + } + + return null; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java new file mode 100644 index 0000000000..0aaf6aca8e --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LevelDeserializer.java @@ -0,0 +1,44 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; + +public class LevelDeserializer extends StdDeserializer { + + public LevelDeserializer() { + this(null); + } + + public LevelDeserializer(Class vc) { + super(vc); + } + + @Override + public Level deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + String levelStr = node.asText(); + Level level = Level.toLevel(levelStr); + return level; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java new file mode 100644 index 0000000000..40730aa2bd --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/LoggerContextVODeserializer.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.LoggerContextVO; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.event.KeyValuePair; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +public class LoggerContextVODeserializer extends StdDeserializer { + + public LoggerContextVODeserializer() { + this(null); + } + + public LoggerContextVODeserializer(Class vc) { + super(vc); + } + + @Override + public LoggerContextVO deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + if(node.isObject()) { + JsonNode nameNode = node.get(JsonEncoder.NAME_ATTR_NAME); + String name = nameNode.asText(); + JsonNode bdayNode = node.get(JsonEncoder.BIRTHDATE_ATTR_NAME); + long birthday = bdayNode.asLong(); + + JsonNode propertiesNode = node.get(JsonEncoder.CONTEXT_PROPERTIES_ATTR_NAME); + Map propertiesMap = new HashMap<>(); + Iterator> it = propertiesNode.fields(); + while(it.hasNext()) { + Map.Entry entry = it.next(); + String key = entry.getKey(); + String value = entry.getValue().asText(); + propertiesMap.put(key, value); + } + return new LoggerContextVO(name, propertiesMap, birthday); + } + return null; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java new file mode 100644 index 0000000000..828761dfe4 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/MarkerDeserializer.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.Level; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; + +import java.io.IOException; + +public class MarkerDeserializer extends StdDeserializer { + + IMarkerFactory markerFactory; + + public MarkerDeserializer(IMarkerFactory markerFactory) { + this(null, markerFactory); + } + + public MarkerDeserializer(Class vc, IMarkerFactory markerFactory) { + super(vc); + this.markerFactory = markerFactory; + } + + @Override + public Marker deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + String markerStr = node.asText(); + Marker marker = markerFactory.getMarker(markerStr); + return marker; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/PubThrowableProxyDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/PubThrowableProxyDeserializer.java new file mode 100644 index 0000000000..bfa7e8541b --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/PubThrowableProxyDeserializer.java @@ -0,0 +1,110 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.encoder.JsonEncoder; +import ch.qos.logback.classic.spi.PubThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.core.JacksonException; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class PubThrowableProxyDeserializer extends StdDeserializer { + + protected PubThrowableProxyDeserializer() { + this(null); + } + protected PubThrowableProxyDeserializer(Class vc) { + super(vc); + } + + @Override + public PubThrowableProxy deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) + throws IOException, JacksonException { + JsonNode node = jsonParser.getCodec().readTree(jsonParser); + + return jsonNodeThrowableProxy(node); + } + + static StackTraceElementProxy[] EMPTY_STEP_ARRAY = new StackTraceElementProxy[0]; + static PubThrowableProxy[] EMPTY_PTP_ARRAY = new PubThrowableProxy[0]; + + private static PubThrowableProxy jsonNodeThrowableProxy(JsonNode node) { + JsonNode classNameJN = node.get(JsonEncoder.CLASS_NAME_ATTR_NAME); + JsonNode messageJN = node.get(JsonEncoder.MESSAGE_ATTR_NAME); + JsonNode stepArrayJN = node.get(JsonEncoder.STEP_ARRAY_NAME_ATTRIBUTE); + JsonNode causeJN = node.get(JsonEncoder.CAUSE_ATTR_NAME); + JsonNode commonFramesCountJN = node.get(JsonEncoder.COMMON_FRAMES_COUNT_ATTR_NAME); + + JsonNode suppressedJN = node.get(JsonEncoder.SUPPRESSED_ATTR_NAME); + + PubThrowableProxy ptp = new PubThrowableProxy(); + ptp.setClassName(classNameJN.textValue()); + ptp.setMessage(messageJN.textValue()); + + List stepList = stepNodeToList(stepArrayJN); + ptp.setStackTraceElementProxyArray(stepList.toArray(EMPTY_STEP_ARRAY)); + + if(commonFramesCountJN != null) { + int commonFramesCount = commonFramesCountJN.asInt(); + ptp.setCommonFramesCount(commonFramesCount); + } + + if(causeJN != null) { + PubThrowableProxy cause = jsonNodeThrowableProxy(causeJN); + ptp.setCause(cause); + } + + if(suppressedJN != null) { + //System.out.println("suppressedJN "+suppressedJN); + List ptpList = suppressedNodeToList(suppressedJN); + System.out.println("iiiiiiiiiiii"); + System.out.println("ptpList="+ptpList); + + ptp.setSuppressed(ptpList.toArray(EMPTY_PTP_ARRAY)); + } + + System.out.println("xxxxxxxxxxxxx"); + System.out.println(ptp.getSuppressed()); + + return ptp; + } + + private static List stepNodeToList(JsonNode stepArrayJN) { + List stepList = new ArrayList<>(); + for(JsonNode jsonNode: stepArrayJN) { + StackTraceElementProxy step = STEPDeserializer.jsonNodeToSTEP(jsonNode); + stepList.add(step); + } + return stepList; + } + + private static List suppressedNodeToList(JsonNode ptpArrayJN) { + List ptpList = new ArrayList<>(); + for(JsonNode jsonNode: ptpArrayJN) { + //System.out.println("---in suppressedNodeToList seeing "+jsonNode); + PubThrowableProxy ptp = jsonNodeThrowableProxy(jsonNode); + //System.out.println("--in suppressedNodeToList ptp="+ptp); + ptpList.add(ptp); + } + return ptpList; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java new file mode 100644 index 0000000000..8ef23918a4 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/STEPDeserializer.java @@ -0,0 +1,54 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import com.fasterxml.jackson.databind.node.IntNode; + +import java.io.IOException; + +public class STEPDeserializer extends StdDeserializer { + + public STEPDeserializer() { + this(null); + } + + public STEPDeserializer(Class vc) { + super(vc); + } + + @Override + public StackTraceElementProxy deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + JsonNode node = jp.getCodec().readTree(jp); + return jsonNodeToSTEP(node); + } + + public static StackTraceElementProxy jsonNodeToSTEP(JsonNode node) { + String className = node.get("className").asText(); + String methodName = node.get("methodName").asText(); + String fileName = node.get("fileName").asText(); + + int lineNumber = (Integer) ((IntNode) node.get("lineNumber")).numberValue(); + + StackTraceElement ste = new StackTraceElement(className, methodName, fileName, lineNumber); + return new StackTraceElementProxy(ste); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java new file mode 100644 index 0000000000..2e02a072bc --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jsonTest/ThrowableProxyComparator.java @@ -0,0 +1,152 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.jsonTest; + +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; + +import java.util.Arrays; +import java.util.Objects; + +public class ThrowableProxyComparator { + + + static public boolean areEqual(IThrowableProxy left, IThrowableProxy right) { + + if(left == right) + return true; + + if(left == null) + return false; + + if(!left.getClassName().equals(right.getClassName())) + return false; + + + + if(!left.getMessage().equals(right.getMessage())) + return false; + + System.out.println("before equalsSTEPArray left.message="+left.getMessage()+", right.message="+right.getMessage()); + + + StackTraceElementProxy[] leftStepArray = left.getStackTraceElementProxyArray(); + StackTraceElementProxy[] rightStepArray = right.getStackTraceElementProxyArray(); + + if(left.getCommonFrames() != right.getCommonFrames()) { + return false; + } + + if(!equalsSTEPArray(leftStepArray, rightStepArray, left.getCommonFrames())) + return false; + + boolean causeComparaison = areEqual(left.getCause(), right.getCause()); + if(!causeComparaison) + return causeComparaison; + + if (!compareSuppressedThrowables(left, right)) + return false; + + return true; + } + + private static boolean compareSuppressedThrowables(IThrowableProxy left, IThrowableProxy right) { + IThrowableProxy[] leftSuppressedThrowableArray = left.getSuppressed(); + IThrowableProxy[] rightSuppressedThrowableArray = right.getSuppressed(); + + + //System.out.println("leftSuppressedThrowableArray="+leftSuppressedThrowableArray); + //System.out.println("rightSuppressedThrowableArray="+rightSuppressedThrowableArray); + + if(leftSuppressedThrowableArray == null && rightSuppressedThrowableArray == null) { + return true; + } + if(leftSuppressedThrowableArray.length == 0 && rightSuppressedThrowableArray == null) { + return true; + } + + if(leftSuppressedThrowableArray.length != rightSuppressedThrowableArray.length) { + System.out.println("suppressed array length discrepancy"); + return false; + } + + for(int i = 0; i < leftSuppressedThrowableArray.length; i++) { + IThrowableProxy leftSuppressed = leftSuppressedThrowableArray[i]; + IThrowableProxy rightSuppressed = rightSuppressedThrowableArray[i]; + + boolean suppressedComparison = areEqual(leftSuppressed, rightSuppressed); + if(!suppressedComparison) { + System.out.println("suppressed ITP comparison failed at position "+i); + return false; + } + } + return true; + } + + static public boolean equalsSTEPArray( StackTraceElementProxy[] leftStepArray, StackTraceElementProxy[] rightStepArray, int commonFrames) { + if (leftStepArray==rightStepArray) + return true; + if (leftStepArray==null || rightStepArray==null) + return false; + + int length = leftStepArray.length - commonFrames; + if (rightStepArray.length != length) { + System.out.println("length discrepancy"); + return false; + } + + System.out.println("checking ste array elements "); + + for (int i=0; i< (length -commonFrames); i++) { + StackTraceElementProxy leftStep = leftStepArray[i]; + StackTraceElementProxy rightStep = rightStepArray[i]; + + if (!equalsSTEP(leftStep, rightStep)) { + System.out.println("left "+leftStep); + System.out.println("right "+rightStep); + return false; + } + } + return true; + } + + static public boolean equalsSTEP(StackTraceElementProxy left, StackTraceElementProxy right) { + if (left==right) + return true; + + if (right == null) + return false; + + StackTraceElement l = left.getStackTraceElement(); + StackTraceElement r = right.getStackTraceElement(); + + if(!Objects.equals(l.getClassName(), (r.getClassName()))) + return false; + + + if(!Objects.equals(l.getMethodName(), (r.getMethodName()))) + return false; + + + if(!Objects.equals(l.getFileName(), (r.getFileName()))) + return false; + + if(l.getLineNumber() != r.getLineNumber()) + return false; + + + return true; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jul/LevelChangePropagatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/jul/LevelChangePropagatorTest.java index 9e0177e8b8..06b18636fc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/jul/LevelChangePropagatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/jul/LevelChangePropagatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,22 +18,19 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.core.testUtil.RandomUtil; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.ExpectedException; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; public class LevelChangePropagatorTest { int rand = RandomUtil.getPositiveInt(); LoggerContext loggerContext = new LoggerContext(); LevelChangePropagator levelChangePropagator = new LevelChangePropagator(); - @Rule - public ExpectedException exception = ExpectedException.none(); - - @Before + @BeforeEach public void setUp() { levelChangePropagator.setContext(loggerContext); loggerContext.addListener(levelChangePropagator); @@ -48,6 +45,8 @@ void checkLevelChange(String loggerName, Level level) { assertEquals(julLevel, julLogger.getLevel()); } + + @Test public void smoke() { checkLevelChange("a", Level.INFO); @@ -72,11 +71,25 @@ public void gc() { assertEquals(julLevel, julLogger.getLevel()); } + // https://jira.qos.ch/browse/LOGBACK-1612 + @Test + public void jonathan() { + Level level = Level.INFO; + Logger logger = loggerContext.getLogger("aaa"); + logger.setLevel(level); + + java.util.logging.Logger julLogger = JULHelper.asJULLogger(logger); + java.util.logging.Level julLevel = JULHelper.asJULLevel(level); + + assertFalse(julLogger.isLoggable(java.util.logging.Level.CONFIG)); + } + @Test public void julHelperAsJulLevelRejectsNull() { - exception.expect(IllegalArgumentException.class); - exception.expectMessage("Unexpected level [null]"); - JULHelper.asJULLevel(null); + Exception e = assertThrows(IllegalArgumentException.class, () -> { + JULHelper.asJULLevel(null); + }); + assertEquals("Unexpected level [null]", e.getMessage()); } @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/jul/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/jul/PackageTest.java deleted file mode 100644 index 80e669bbd9..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/jul/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.jul; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ LevelChangePropagatorTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/layout/TTLLLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/layout/TTLLLayoutTest.java index 6f44bb9301..cffa1f1e42 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/layout/TTLLLayoutTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/layout/TTLLLayoutTest.java @@ -1,14 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.layout; -import static org.junit.Assert.assertTrue; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; public class TTLLLayoutTest { @@ -16,7 +31,7 @@ public class TTLLLayoutTest { Logger logger = context.getLogger(TTLLLayoutTest.class); TTLLLayout layout = new TTLLLayout(); - @Before + @BeforeEach public void setUp() { layout.setContext(context); layout.start(); @@ -27,9 +42,10 @@ public void nullMessage() { LoggingEvent event = new LoggingEvent("", logger, Level.INFO, null, null, null); event.setTimeStamp(0); String result = layout.doLayout(event); - + String resultSuffix = result.substring(13).trim(); - - assertTrue("[" + resultSuffix + "] did not match regexs", resultSuffix.matches("\\[.*\\] INFO ch.qos.logback.classic.layout.TTLLLayoutTest - null")); + + assertTrue(resultSuffix.matches("\\[.*\\] INFO ch.qos.logback.classic.layout.TTLLLayoutTest -- null"), + "[" + resultSuffix + "] did not match regex"); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/log4j/XMLLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/log4j/XMLLayoutTest.java deleted file mode 100644 index 1efa6cc912..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/log4j/XMLLayoutTest.java +++ /dev/null @@ -1,209 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 2014, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.log4j; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.Iterator; - -import javax.xml.XMLConstants; -import javax.xml.namespace.NamespaceContext; -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import javax.xml.xpath.XPath; -import javax.xml.xpath.XPathConstants; -import javax.xml.xpath.XPathFactory; - -import org.apache.log4j.MDC; -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; -import org.xml.sax.EntityResolver; -import org.xml.sax.InputSource; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; - -/** - * A test for correct (well-formed, valid) log4j XML layout. - * - * @author Gabriel Corona - */ -public class XMLLayoutTest { - - private static final String DOCTYPE = ""; - private static final String NAMESPACE = "http://jakarta.apache.org/log4j/"; - private static final String DTD_URI = "http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/doc-files/log4j.dtd"; - - private static final String MDC_KEY = "key <&>'\"]]>"; - private static final String MDC_VALUE = "value <&>'\"]]>"; - - private static final String MESSAGE = "test message, <&>'\""; - - private LoggerContext lc; - private Logger root; - private XMLLayout layout; - - @Before - public void setUp() throws Exception { - lc = new LoggerContext(); - lc.setName("default"); - - layout = new XMLLayout(); - layout.setLocationInfo(true); - layout.setContext(lc); - layout.setProperties(true); - layout.setLocationInfo(true); - layout.start(); - - root = lc.getLogger(Logger.ROOT_LOGGER_NAME); - - } - - @After - public void tearDown() throws Exception { - lc = null; - layout = null; - MDC.clear(); - } - - @Test - public void testDoLayout() throws Exception { - ILoggingEvent le = createLoggingEvent(); - - String result = DOCTYPE + ""; - if (layout.getFileHeader() != null) { - result += layout.getFileHeader(); - } - if (layout.getPresentationHeader() != null) { - result += layout.getPresentationHeader(); - } - result += layout.doLayout(le); - if (layout.getPresentationFooter() != null) { - result += layout.getPresentationFooter(); - } - if (layout.getFileFooter() != null) { - result += layout.getFileFooter(); - } - result += ""; - - Document document = parse(result); - - XPath xpath = this.newXPath(); - - // Test log4j:event: - NodeList eventNodes = (NodeList) xpath.compile("//log4j:event").evaluate(document, XPathConstants.NODESET); - Assert.assertEquals(1, eventNodes.getLength()); - - // Test log4g:message: - Assert.assertEquals(MESSAGE, xpath.compile("//log4j:message").evaluate(document, XPathConstants.STRING)); - - // Test log4j:data: - NodeList dataNodes = (NodeList) xpath.compile("//log4j:data").evaluate(document, XPathConstants.NODESET); - boolean foundMdc = false; - for (int i = 0; i != dataNodes.getLength(); ++i) { - Node dataNode = dataNodes.item(i); - if (dataNode.getAttributes().getNamedItem("name").getNodeValue().equals(MDC_KEY)) { - foundMdc = true; - Assert.assertEquals(MDC_VALUE, dataNode.getAttributes().getNamedItem("value").getNodeValue()); - break; - } - } - Assert.assertTrue(foundMdc); - } - - /** - * Create a XPath instance with xmlns:log4j="http://jakarta.apache.org/log4j/" - * - * @return XPath instance with log4 namespace - */ - private XPath newXPath() { - XPathFactory xPathfactory = XPathFactory.newInstance(); - XPath xpath = xPathfactory.newXPath(); - - xpath.setNamespaceContext(new NamespaceContext() { - @SuppressWarnings("rawtypes") - public Iterator getPrefixes(String namespaceURI) { - throw new UnsupportedOperationException(); - } - - public String getPrefix(String namespaceURI) { - throw new UnsupportedOperationException(); - } - - public String getNamespaceURI(String prefix) { - if ("log4j".equals(prefix)) { - return NAMESPACE; - } else { - return XMLConstants.NULL_NS_URI; - } - } - }); - - return xpath; - } - - private LoggingEvent createLoggingEvent() { - MDC.put(MDC_KEY, MDC_VALUE); - LoggingEvent event = new LoggingEvent("com.example.XMLLayoutTest-<&>'\"]]>", root, Level.DEBUG, MESSAGE, new RuntimeException( - "Dummy exception: <&>'\"]]>"), null); - event.setThreadName("Dummy thread <&>'\""); - - StackTraceElement ste1 = new StackTraceElement("c1", "m1", "f1", 1); - StackTraceElement ste2 = new StackTraceElement("c2", "m2", "f2", 2); - event.setCallerData(new StackTraceElement[] { ste1, ste2 }); - - return event; - } - - /** - * Parse and validate Log4j XML - * - * @param output Log4j XML - * @return Document - * @throws Exception - */ - private Document parse(String output) throws Exception { - - // Lookup the DTD in log4j.jar: - EntityResolver resolver = new EntityResolver() { - public InputSource resolveEntity(String publicId, String systemId) { - if (publicId == null && systemId != null && systemId.equals(DTD_URI)) { - final String path = "/org/apache/log4j/xml/log4j.dtd"; - InputStream in = this.getClass().getResourceAsStream(path); - return new InputSource(in); - } else { - throw new RuntimeException("Not found"); - } - } - }; - - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - factory.setValidating(true); - - DocumentBuilder builder = factory.newDocumentBuilder(); - builder.setEntityResolver(resolver); - - return builder.parse(new ByteArrayInputStream(output.getBytes("UTF-8"))); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/model/ModelDuplicationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/model/ModelDuplicationTest.java new file mode 100644 index 0000000000..effa5e684b --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/model/ModelDuplicationTest.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.model; + +import ch.qos.logback.core.model.ImportModel; +import ch.qos.logback.core.model.Model; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class ModelDuplicationTest { + + @Test + public void smoke() { + ConfigurationModel cm = new ConfigurationModel(); + cm.setDebugStr("x"); + Model copy = Model.duplicate(cm); + assertEquals(cm, copy); + } + + @Test + public void test() { + ConfigurationModel cm = new ConfigurationModel(); + cm.setDebugStr("x"); + + ImportModel importModel = new ImportModel(); + importModel.setClassName("a"); + + cm.addSubModel(importModel); + + Model copy = Model.duplicate(cm); + assertEquals(cm, copy); + } + + + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java index 746e1048c6..0264323c14 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/Checker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -26,8 +26,9 @@ public class Checker { static void usage(String msg) { System.err.println(msg); System.err.println("Usage: java " + Checker.class.getName() + " runLength filename stamp0 stamp1 ..stampN\n" - + " runLength (integer) the number of logs to generate perthread\n" + " filename (string) the filename where to write\n" - + " stamp0 JVM instance stamp0\n" + " stamp1 JVM instance stamp1\n"); + + " runLength (integer) the number of logs to generate perthread\n" + + " filename (string) the filename where to write\n" + " stamp0 JVM instance stamp0\n" + + " stamp1 JVM instance stamp1\n"); System.exit(1); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/FileAppenderPerf.java b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/FileAppenderPerf.java index 2565a77397..57003a90fc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/FileAppenderPerf.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/FileAppenderPerf.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -86,7 +86,8 @@ static void perfCase(boolean safetyMode) throws Exception { lc.stop(); - System.out.println("Average duration of " + (durationPerLog) + " microseconds per log. Prudent mode=" + safetyMode); + System.out.println( + "Average duration of " + (durationPerLog) + " microseconds per log. Prudent mode=" + safetyMode); System.out.println("------------------------------------------------"); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java index 6afd767117..4f3e868016 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/LoggingThread.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java index 94f14bc624..108d7951e4 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeFileAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -45,8 +45,9 @@ static public void main(String[] argv) throws Exception { static void usage(String msg) { System.err.println(msg); - System.err.println("Usage: java " + SafeModeFileAppender.class.getName() + " stamp runLength filename\n" + " stamp JVM instance stamp\n" - + " runLength (integer) the number of logs to generate perthread" + " filename (string) the filename where to write\n"); + System.err.println("Usage: java " + SafeModeFileAppender.class.getName() + " stamp runLength filename\n" + + " stamp JVM instance stamp\n" + " runLength (integer) the number of logs to generate perthread" + + " filename (string) the filename where to write\n"); System.exit(1); } @@ -84,7 +85,8 @@ static void writeContinously(String stamp, String filename, boolean safetyMode) lc.stop(); double durationPerLog = (System.nanoTime() - before) / (LEN * 1000.0); - System.out.println("Average duration of " + (durationPerLog) + " microseconds per log. Safety mode " + safetyMode); + System.out.println( + "Average duration of " + (durationPerLog) + " microseconds per log. Safety mode " + safetyMode); System.out.println("------------------------------------------------"); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeRollingFileAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeRollingFileAppender.java index a444bb72a9..00d9e06969 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeRollingFileAppender.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/multiJVM/SafeModeRollingFileAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -49,8 +49,9 @@ static public void main(String[] argv) throws Exception { static void usage(String msg) { System.err.println(msg); - System.err.println("Usage: java " + SafeModeRollingFileAppender.class.getName() + " stamp runLength filename\n" + " stamp JVM instance stamp\n" - + " runLength (integer) the number of logs to generate perthread" + " filename (string) the filename where to write\n"); + System.err.println("Usage: java " + SafeModeRollingFileAppender.class.getName() + " stamp runLength filename\n" + + " stamp JVM instance stamp\n" + " runLength (integer) the number of logs to generate perthread" + + " filename (string) the filename where to write\n"); System.exit(1); } @@ -98,7 +99,8 @@ static void writeContinously(String stamp, String filename, boolean safetyMode) StatusPrinter.print(lc); double durationPerLog = (System.nanoTime() - before) / (LEN * 1000.0); - System.out.println("Average duration of " + (durationPerLog) + " microseconds per log. Safety mode " + safetyMode); + System.out.println( + "Average duration of " + (durationPerLog) + " microseconds per log. Safety mode " + safetyMode); System.out.println("------------------------------------------------"); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java index 62a40183f3..06ac9a9249 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/DilutedSMTPAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,11 @@ */ package ch.qos.logback.classic.net; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.LoggerContext; @@ -29,15 +28,21 @@ import ch.qos.logback.core.helpers.CyclicBuffer; import ch.qos.logback.core.spi.CyclicBufferTracker; -public class DilutedSMTPAppenderTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +public class DilutedSMTPAppenderTest { + LoggerContext lc = new LoggerContext(); + Logger logger = lc.getLogger(this.getClass()); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); SMTPAppender appender; CyclicBufferTracker cbTracker; CyclicBuffer cb; - @Before + @BeforeEach public void setUp() throws Exception { - LoggerContext lc = new LoggerContext(); + + lc.setMDCAdapter(logbackMDCAdapter); appender = new SMTPAppender(); appender.setContext(lc); appender.setName("smtp"); @@ -62,7 +67,7 @@ private static Layout buildLayout(LoggerContext lc) { return layout; } - @After + @AfterEach public void tearDown() throws Exception { appender = null; } @@ -81,6 +86,7 @@ public void testAppendNonTriggeringEvent() { LoggingEvent event = new LoggingEvent(); event.setThreadName("thead name"); event.setLevel(Level.DEBUG); + event.setLoggerContext(lc); appender.subAppend(cb, event); assertEquals(1, cb.length()); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/ExternalMockSocketServer.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/ExternalMockSocketServer.java index 463ba1c91e..fce10adfb9 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/ExternalMockSocketServer.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/ExternalMockSocketServer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/NOPOutputStream.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/NOPOutputStream.java index d8388da705..89b77783a1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/NOPOutputStream.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/NOPOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/PackageTest.java deleted file mode 100644 index f03e8fc7db..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ SyslogAppenderTest.class, DilutedSMTPAppenderTest.class, SMTPAppender_GreenTest.class, - SMTPAppender_SubethaSMTPTest.class, SocketReceiverTest.class, SSLSocketReceiverTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java deleted file mode 100755 index a415774c66..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_GreenTest.java +++ /dev/null @@ -1,369 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.PatternLayout; -import ch.qos.logback.classic.html.HTMLLayout; -import ch.qos.logback.classic.html.XHTMLEntityResolver; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.Layout; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.status.OnConsoleStatusListener; -import ch.qos.logback.core.testUtil.EnvUtilForTests; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.util.StatusListenerConfigHelper; - -import com.icegreen.greenmail.util.GreenMail; -import com.icegreen.greenmail.util.GreenMailUtil; -import com.icegreen.greenmail.util.ServerSetup; - -import org.dom4j.DocumentException; -import org.dom4j.io.SAXReader; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; - -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.*; - -public class SMTPAppender_GreenTest { - - static final String HEADER = "HEADER\n"; - static final String FOOTER = "FOOTER\n"; - static final String DEFAULT_PATTERN = "%-4relative %mdc [%thread] %-5level %class - %msg%n"; - - static final boolean SYNCHRONOUS = false; - static final boolean ASYNCHRONOUS = true; - - int port = RandomUtil.getRandomServerPort(); - // GreenMail cannot be static. As a shared server induces race conditions - GreenMail greenMailServer; - - SMTPAppender smtpAppender; - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger(this.getClass()); - - @Before - public void setUp() throws Exception { - - StatusListenerConfigHelper.addOnConsoleListenerInstance(loggerContext, new OnConsoleStatusListener()); - MDC.clear(); - ServerSetup serverSetup = new ServerSetup(port, "localhost", ServerSetup.PROTOCOL_SMTP); - greenMailServer = new GreenMail(serverSetup); - greenMailServer.start(); - // give the server a head start - if (EnvUtilForTests.isRunningOnSlowJenkins()) { - Thread.sleep(2000); - } else { - Thread.sleep(50); - } - } - - @After - public void tearDown() throws Exception { - greenMailServer.stop(); - } - - void buildSMTPAppender(String subject, boolean synchronicity) throws Exception { - smtpAppender = new SMTPAppender(); - smtpAppender.setContext(loggerContext); - smtpAppender.setName("smtp"); - smtpAppender.setFrom("user@host.dom"); - smtpAppender.setSMTPHost("localhost"); - smtpAppender.setSMTPPort(port); - smtpAppender.setSubject(subject); - smtpAppender.addTo("nospam@qos.ch"); - smtpAppender.setAsynchronousSending(synchronicity); - } - - private Layout buildPatternLayout(String pattern) { - PatternLayout layout = new PatternLayout(); - layout.setContext(loggerContext); - layout.setFileHeader(HEADER); - layout.setOutputPatternAsHeader(false); - layout.setPattern(pattern); - layout.setFileFooter(FOOTER); - layout.start(); - return layout; - } - - private Layout buildHTMLLayout() { - HTMLLayout layout = new HTMLLayout(); - layout.setContext(loggerContext); - layout.setPattern("%level%class%msg"); - layout.start(); - return layout; - } - - private void waitForServerToReceiveEmails(int emailCount) throws InterruptedException { - greenMailServer.waitForIncomingEmail(5000, emailCount); - } - - private MimeMultipart verifyAndExtractMimeMultipart(String subject) throws MessagingException, IOException, InterruptedException { - int oldCount = 0; - int expectedEmailCount = 1; - // wait for the server to receive the messages - waitForServerToReceiveEmails(expectedEmailCount); - MimeMessage[] mma = greenMailServer.getReceivedMessages(); - assertNotNull(mma); - assertEquals(expectedEmailCount, mma.length); - MimeMessage mm = mma[oldCount]; - // http://jira.qos.ch/browse/LBCLASSIC-67 - assertEquals(subject, mm.getSubject()); - return (MimeMultipart) mm.getContent(); - } - - void waitUntilEmailIsSent() throws InterruptedException { - loggerContext.getExecutorService().shutdown(); - loggerContext.getExecutorService().awaitTermination(1000, TimeUnit.MILLISECONDS); - } - - @Test - public void synchronousSmoke() throws Exception { - String subject = "synchronousSmoke"; - buildSMTPAppender(subject, SYNCHRONOUS); - - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - - MimeMultipart mp = verifyAndExtractMimeMultipart(subject); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertTrue(body.startsWith(HEADER.trim())); - assertTrue(body.endsWith(FOOTER.trim())); - } - - @Test - public void asynchronousSmoke() throws Exception { - String subject = "asynchronousSmoke"; - buildSMTPAppender(subject, ASYNCHRONOUS); - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - - waitUntilEmailIsSent(); - MimeMultipart mp = verifyAndExtractMimeMultipart(subject); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertTrue(body.startsWith(HEADER.trim())); - assertTrue(body.endsWith(FOOTER.trim())); - } - - // See also http://jira.qos.ch/browse/LOGBACK-734 - @Test - public void callerDataShouldBeCorrectlySetWithAsynchronousSending() throws Exception { - String subject = "LOGBACK-734"; - buildSMTPAppender("LOGBACK-734", ASYNCHRONOUS); - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.setIncludeCallerData(true); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("LOGBACK-734"); - logger.error("callerData", new Exception("ShouldBeCorrectlySetWithAsynchronousSending")); - - waitUntilEmailIsSent(); - MimeMultipart mp = verifyAndExtractMimeMultipart(subject); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertTrue("actual [" + body + "]", body.contains("DEBUG " + this.getClass().getName() + " - LOGBACK-734")); - } - - // lost MDC - @Test - public void LBCLASSIC_104() throws Exception { - String subject = "LBCLASSIC_104"; - buildSMTPAppender(subject, SYNCHRONOUS); - smtpAppender.setAsynchronousSending(false); - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.start(); - logger.addAppender(smtpAppender); - MDC.put("key", "val"); - logger.debug("LBCLASSIC_104"); - MDC.clear(); - logger.error("en error", new Exception("test")); - - MimeMultipart mp = verifyAndExtractMimeMultipart(subject); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertTrue(body.startsWith(HEADER.trim())); - System.out.println(body); - assertTrue(body.contains("key=val")); - assertTrue(body.endsWith(FOOTER.trim())); - } - - @Test - public void html() throws Exception { - String subject = "html"; - buildSMTPAppender(subject, SYNCHRONOUS); - smtpAppender.setAsynchronousSending(false); - smtpAppender.setLayout(buildHTMLLayout()); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("html"); - logger.error("en error", new Exception("an exception")); - - MimeMultipart mp = verifyAndExtractMimeMultipart(subject); - - // verifyAndExtractMimeMultipart strict adherence to xhtml1-strict.dtd - SAXReader reader = new SAXReader(); - reader.setValidation(true); - reader.setEntityResolver(new XHTMLEntityResolver()); - byte[] messageBytes = getAsByteArray(mp.getBodyPart(0).getInputStream()); - ByteArrayInputStream bais = new ByteArrayInputStream(messageBytes); - try { - reader.read(bais); - } catch (DocumentException de) { - System.out.println("incoming message:"); - System.out.println(new String(messageBytes)); - throw de; - } - } - - private byte[] getAsByteArray(InputStream inputStream) throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - byte[] buffer = new byte[1024]; - int n = -1; - while ((n = inputStream.read(buffer)) != -1) { - baos.write(buffer, 0, n); - } - return baos.toByteArray(); - } - - private void configure(String file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - loggerContext.putProperty("port", "" + port); - jc.doConfigure(file); - } - - @Test - public void testCustomEvaluator() throws Exception { - configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customEvaluator.xml"); - - logger.debug("test"); - String msg2 = "CustomEvaluator"; - logger.debug(msg2); - logger.debug("invisible"); - waitUntilEmailIsSent(); - MimeMultipart mp = verifyAndExtractMimeMultipart("testCustomEvaluator " + this.getClass().getName() + " - " + msg2); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertEquals("testCustomEvaluator", body); - } - - @Test - public void testCustomBufferSize() throws Exception { - configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "smtp/customBufferSize.xml"); - - logger.debug("invisible1"); - logger.debug("invisible2"); - String msg = "hello"; - logger.error(msg); - waitUntilEmailIsSent(); - MimeMultipart mp = verifyAndExtractMimeMultipart("testCustomBufferSize " + this.getClass().getName() + " - " + msg); - String body = GreenMailUtil.getBody(mp.getBodyPart(0)); - assertEquals(msg, body); - } - - // this test fails intermittently on Jenkins. - @Test - public void testMultipleTo() throws Exception { - buildSMTPAppender("testMultipleTo", SYNCHRONOUS); - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - // buildSMTPAppender() already added one destination address - smtpAppender.addTo("Test , other-test@example.com"); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("testMultipleTo hello"); - logger.error("testMultipleTo en error", new Exception("an exception")); - Thread.yield(); - int expectedEmailCount = 3; - waitForServerToReceiveEmails(expectedEmailCount); - MimeMessage[] mma = greenMailServer.getReceivedMessages(); - assertNotNull(mma); - assertEquals(expectedEmailCount, mma.length); - } - - // http://jira.qos.ch/browse/LBCLASSIC-221 - @Test - public void bufferShouldBeResetBetweenMessages() throws Exception { - buildSMTPAppender("bufferShouldBeResetBetweenMessages", SYNCHRONOUS); - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.start(); - logger.addAppender(smtpAppender); - String msg0 = "hello zero"; - logger.debug(msg0); - logger.error("error zero"); - - String msg1 = "hello one"; - logger.debug(msg1); - logger.error("error one"); - - Thread.yield(); - int oldCount = 0; - int expectedEmailCount = oldCount + 2; - waitForServerToReceiveEmails(expectedEmailCount); - - MimeMessage[] mma = greenMailServer.getReceivedMessages(); - assertNotNull(mma); - assertEquals(expectedEmailCount, mma.length); - - MimeMessage mm0 = mma[oldCount]; - MimeMultipart content0 = (MimeMultipart) mm0.getContent(); - @SuppressWarnings("unused") - String body0 = GreenMailUtil.getBody(content0.getBodyPart(0)); - - MimeMessage mm1 = mma[oldCount + 1]; - MimeMultipart content1 = (MimeMultipart) mm1.getContent(); - String body1 = GreenMailUtil.getBody(content1.getBodyPart(0)); - // second body should not contain content from first message - assertFalse(body1.contains(msg0)); - } - - @Test - public void multiLineSubjectTruncatedAtFirstNewLine() throws Exception { - String line1 = "line 1 of subject"; - String subject = line1 + "\nline 2 of subject\n"; - buildSMTPAppender(subject, ASYNCHRONOUS); - - smtpAppender.setLayout(buildPatternLayout(DEFAULT_PATTERN)); - smtpAppender.start(); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - - Thread.yield(); - waitUntilEmailIsSent(); - waitForServerToReceiveEmails(1); - - MimeMessage[] mma = greenMailServer.getReceivedMessages(); - assertEquals(1, mma.length); - assertEquals(line1, mma[0].getSubject()); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_SubethaSMTPTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_SubethaSMTPTest.java deleted file mode 100644 index d032ef39d5..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SMTPAppender_SubethaSMTPTest.java +++ /dev/null @@ -1,395 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.ByteArrayOutputStream; -import java.util.List; -import java.util.Random; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import javax.mail.Part; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; - -import org.dom4j.io.SAXReader; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.subethamail.smtp.auth.EasyAuthenticationHandlerFactory; -import org.subethamail.smtp.auth.LoginFailedException; -import org.subethamail.smtp.auth.UsernamePasswordValidator; -import org.subethamail.wiser.Wiser; -import org.subethamail.wiser.WiserMessage; - -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.PatternLayout; -import ch.qos.logback.classic.html.HTMLLayout; -import ch.qos.logback.classic.html.XHTMLEntityResolver; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.Layout; -import ch.qos.logback.core.util.StatusPrinter; - -public class SMTPAppender_SubethaSMTPTest { - static final String TEST_SUBJECT = "test subject"; - static final String HEADER = "HEADER\n"; - static final String FOOTER = "FOOTER\n"; - - static int DIFF = 1024 + new Random().nextInt(30000); - static Wiser WISER; - - SMTPAppender smtpAppender; - LoggerContext loggerContext = new LoggerContext(); - - int numberOfOldMessages; - -// @BeforeClass -// static public void beforeClass() { -// WISER = new Wiser(); -// WISER.setPort(DIFF); -// WISER.start(); -// } - -// @AfterClass -// static public void afterClass() throws Exception { -// WISER.stop(); -// } - - @Before - public void setUp() throws Exception { - WISER = new Wiser(); - WISER.setPort(DIFF); - WISER.start(); - numberOfOldMessages = WISER.getMessages().size(); - buildSMTPAppender(); - } - - @After - public void tearDown() { - // clear any authentication handler factory - //WISER.getServer().setAuthenticationHandlerFactory(null); - WISER.stop(); - } - - void buildSMTPAppender() throws Exception { - smtpAppender = new SMTPAppender(); - smtpAppender.setContext(loggerContext); - smtpAppender.setName("smtp"); - smtpAppender.setFrom("user@host.dom"); - smtpAppender.setSMTPHost("localhost"); - smtpAppender.setSMTPPort(DIFF); - smtpAppender.setSubject(TEST_SUBJECT); - smtpAppender.addTo("noreply@qos.ch"); - } - - private Layout buildPatternLayout(LoggerContext lc) { - PatternLayout layout = new PatternLayout(); - layout.setContext(lc); - layout.setFileHeader(HEADER); - layout.setPattern("%-4relative [%thread] %-5level %logger %class - %msg%n"); - layout.setFileFooter(FOOTER); - layout.start(); - return layout; - } - - private Layout buildHTMLLayout(LoggerContext lc) { - HTMLLayout layout = new HTMLLayout(); - layout.setContext(lc); - // layout.setFileHeader(HEADER); - layout.setPattern("%level%class%msg"); - // layout.setFileFooter(FOOTER); - layout.start(); - return layout; - } - - private static String getWholeMessage(Part msg) { - try { - ByteArrayOutputStream bodyOut = new ByteArrayOutputStream(); - msg.writeTo(bodyOut); - return bodyOut.toString("US-ASCII").trim(); - } catch (Exception e) { - throw new RuntimeException(e); - } - } - - void waitUntilEmailIsSent() throws Exception { - System.out.println("About to wait for sending thread to finish"); - loggerContext.getExecutorService().shutdown(); - loggerContext.getExecutorService().awaitTermination(3000, TimeUnit.MILLISECONDS); - } - - private static String getBody(Part msg) { - String all = getWholeMessage(msg); - int i = all.indexOf("\r\n\r\n"); - return all.substring(i + 4, all.length()); - } - - @Test - public void smoke() throws Exception { - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - - waitUntilEmailIsSent(); - System.out.println("*** " + ((ThreadPoolExecutor) loggerContext.getExecutorService()).getCompletedTaskCount()); - List wiserMsgList = WISER.getMessages(); - - assertNotNull(wiserMsgList); - assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); - WiserMessage wm = wiserMsgList.get(numberOfOldMessages); - // http://jira.qos.ch/browse/LBCLASSIC-67 - MimeMessage mm = wm.getMimeMessage(); - assertEquals(TEST_SUBJECT, mm.getSubject()); - - MimeMultipart mp = (MimeMultipart) mm.getContent(); - String body = getBody(mp.getBodyPart(0)); - System.out.println("[" + body); - assertTrue(body.startsWith(HEADER.trim())); - assertTrue(body.endsWith(FOOTER.trim())); - } - - @Test - public void html() throws Exception { - - smtpAppender.setLayout(buildHTMLLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - waitUntilEmailIsSent(); - - List wiserMsgList = WISER.getMessages(); - - assertNotNull(wiserMsgList); - assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); - WiserMessage wm = wiserMsgList.get(numberOfOldMessages); - MimeMessage mm = wm.getMimeMessage(); - assertEquals(TEST_SUBJECT, mm.getSubject()); - - MimeMultipart mp = (MimeMultipart) mm.getContent(); - - // verify strict adherence to xhtml1-strict.dtd - SAXReader reader = new SAXReader(); - reader.setValidation(true); - reader.setEntityResolver(new XHTMLEntityResolver()); - reader.read(mp.getBodyPart(0).getInputStream()); - // System.out.println(GreenMailUtil.getBody(mp.getBodyPart(0))); - } - - @Test - /** - * Checks that even when many events are processed, the output is still - * conforms to xhtml-strict.dtd. - * - * Note that SMTPAppender only keeps only 500 or so (=buffer size) events. So - * the generated output will be rather short. - */ - public void htmlLong() throws Exception { - smtpAppender.setLayout(buildHTMLLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - for (int i = 0; i < CoreConstants.TABLE_ROW_LIMIT * 3; i++) { - logger.debug("hello " + i); - } - logger.error("en error", new Exception("an exception")); - waitUntilEmailIsSent(); - List wiserMsgList = WISER.getMessages(); - - assertNotNull(wiserMsgList); - assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); - WiserMessage wm = wiserMsgList.get(numberOfOldMessages); - MimeMessage mm = wm.getMimeMessage(); - assertEquals(TEST_SUBJECT, mm.getSubject()); - - MimeMultipart mp = (MimeMultipart) mm.getContent(); - - // verify strict adherence to xhtml1-strict.dtd - SAXReader reader = new SAXReader(); - reader.setValidation(true); - reader.setEntityResolver(new XHTMLEntityResolver()); - reader.read(mp.getBodyPart(0).getInputStream()); - } - - static String REQUIRED_USERNAME = "user"; - static String REQUIRED_PASSWORD = "password"; - - class RequiredUsernamePasswordValidator implements UsernamePasswordValidator { - public void login(String username, String password) throws LoginFailedException { - if (!username.equals(REQUIRED_USERNAME) || !password.equals(REQUIRED_PASSWORD)) { - throw new LoginFailedException(); - } - } - } - - - - - @Test - public void authenticated() throws Exception { - setAuthenticanHandlerFactory(); - // MessageListenerAdapter mla = (MessageListenerAdapter) WISER.getServer().getMessageHandlerFactory(); - // mla.setAuthenticationHandlerFactory(new TrivialAuthHandlerFactory()); - - smtpAppender.setUsername(REQUIRED_USERNAME); - smtpAppender.setPassword(REQUIRED_PASSWORD); - - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - waitUntilEmailIsSent(); - List wiserMsgList = WISER.getMessages(); - - assertNotNull(wiserMsgList); - assertEquals(numberOfOldMessages + 1, wiserMsgList.size()); - WiserMessage wm = wiserMsgList.get(numberOfOldMessages); - // http://jira.qos.ch/browse/LBCLASSIC-67 - MimeMessage mm = wm.getMimeMessage(); - assertEquals(TEST_SUBJECT, mm.getSubject()); - - MimeMultipart mp = (MimeMultipart) mm.getContent(); - String body = getBody(mp.getBodyPart(0)); - assertTrue(body.startsWith(HEADER.trim())); - assertTrue(body.endsWith(FOOTER.trim())); - } - - private void setAuthenticanHandlerFactory() { - UsernamePasswordValidator validator = new RequiredUsernamePasswordValidator(); - EasyAuthenticationHandlerFactory authenticationHandlerFactory = new EasyAuthenticationHandlerFactory(validator); - WISER.getServer().setAuthenticationHandlerFactory(authenticationHandlerFactory); - } - - - // Unfortunately, there seems to be a problem with SubethaSMTP's implementation - // of startTLS. The same SMTPAppender code works fine when tested with gmail. - @Test - public void authenticatedSSL() throws Exception { - - setAuthenticanHandlerFactory(); - - smtpAppender.setSTARTTLS(true); - smtpAppender.setUsername(REQUIRED_USERNAME); - smtpAppender.setPassword(REQUIRED_PASSWORD); - - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - - waitUntilEmailIsSent(); - List wiserMsgList = WISER.getMessages(); - - assertNotNull(wiserMsgList); - assertEquals(1, wiserMsgList.size()); - } - - - static String GMAIL_USER_NAME = "xx@gmail.com"; - static String GMAIL_PASSWORD = "xxx"; - - @Ignore - @Test - public void authenticatedGmailStartTLS() throws Exception { - smtpAppender.setSMTPHost("smtp.gmail.com"); - smtpAppender.setSMTPPort(587); - smtpAppender.setAsynchronousSending(false); - smtpAppender.addTo(GMAIL_USER_NAME); - - smtpAppender.setSTARTTLS(true); - smtpAppender.setUsername(GMAIL_USER_NAME); - smtpAppender.setPassword(GMAIL_PASSWORD); - - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.setSubject("authenticatedGmailStartTLS - %level %logger{20} - %m"); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("authenticatedGmailSTARTTLS"); - logger.addAppender(smtpAppender); - logger.debug("authenticatedGmailStartTLS =- hello"); - logger.error("en error", new Exception("an exception")); - - StatusPrinter.print(loggerContext); - } - - @Ignore - @Test - public void authenticatedGmail_SSL() throws Exception { - smtpAppender.setSMTPHost("smtp.gmail.com"); - smtpAppender.setSMTPPort(465); - smtpAppender.setSubject("authenticatedGmail_SSL - %level %logger{20} - %m"); - smtpAppender.addTo(GMAIL_USER_NAME); - smtpAppender.setSSL(true); - smtpAppender.setUsername(GMAIL_USER_NAME); - smtpAppender.setPassword(GMAIL_PASSWORD); - smtpAppender.setAsynchronousSending(false); - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("authenticatedGmail_SSL"); - logger.addAppender(smtpAppender); - logger.debug("hello"+new java.util.Date()); - logger.error("en error", new Exception("an exception")); - - StatusPrinter.print(loggerContext); - - - } - - @Test - public void testMultipleTo() throws Exception { - smtpAppender.setLayout(buildPatternLayout(loggerContext)); - smtpAppender.addTo("Test , other-test@example.com"); - smtpAppender.start(); - Logger logger = loggerContext.getLogger("test"); - logger.addAppender(smtpAppender); - logger.debug("hello"); - logger.error("en error", new Exception("an exception")); - waitUntilEmailIsSent(); - - List wiserMsgList = WISER.getMessages(); - assertNotNull(wiserMsgList); - assertEquals(numberOfOldMessages + 3, wiserMsgList.size()); - } - -// public class TrivialAuthHandlerFactory implements AuthenticationHandlerFactory { -// public AuthenticationHandler create() { -// PluginAuthenticationHandler ret = new PluginAuthenticationHandler(); -// UsernamePasswordValidator validator = new UsernamePasswordValidator() { -// public void login(String username, String password) throws LoginFailedException { -// if (!username.equals(password)) { -// throw new LoginFailedException("username=" + username + ", password=" + password); -// } -// } -// }; -// ret.addPlugin(new PlainAuthenticationHandler(validator)); -// ret.addPlugin(new LoginAuthenticationHandler(validator)); -// return ret; -// } -// } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SSLSocketReceiverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SSLSocketReceiverTest.java deleted file mode 100644 index d35cffda3b..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SSLSocketReceiverTest.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import static org.junit.Assert.assertNotNull; - -import java.net.InetAddress; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.LoggerFactory; - -import ch.qos.logback.classic.LoggerContext; - -/** - * Unit tests for {@link SSLSocketReceiver}. - * - * @author Carl Harris - */ -public class SSLSocketReceiverTest { - - private SSLSocketReceiver remote = new SSLSocketReceiver(); - - @Before - public void setUp() throws Exception { - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - remote.setContext(lc); - } - - @Test - public void testUsingDefaultConfig() throws Exception { - // should be able to start successfully with no SSL configuration at all - remote.setRemoteHost(InetAddress.getLocalHost().getHostAddress()); - remote.setPort(6000); - remote.start(); - assertNotNull(remote.getSocketFactory()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SerializationPerfTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SerializationPerfTest.java index 2229b6e4e7..b75d34a5a3 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SerializationPerfTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/SerializationPerfTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,14 +17,20 @@ import java.io.ObjectOutputStream; import java.net.Socket; -import junit.framework.TestCase; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; + import ch.qos.logback.classic.net.testObjectBuilders.Builder; import ch.qos.logback.classic.net.testObjectBuilders.MinimalSer; import ch.qos.logback.classic.net.testObjectBuilders.MinimalSerBuilder; import ch.qos.logback.classic.net.testObjectBuilders.TrivialLoggingEventVOBuilder; import ch.qos.logback.classic.spi.LoggingEventVO; +import org.junit.jupiter.api.Test; -public class SerializationPerfTest extends TestCase { +@Disabled +public class SerializationPerfTest { ObjectOutputStream oos; @@ -44,8 +50,8 @@ public class SerializationPerfTest extends TestCase { * receiving. * *

- * For example, with 4 test methods, you can launch the - * ExternalMockSocketServer this way: + * For example, with 4 test methods, you can launch the ExternalMockSocketServer + * this way: *

*

* java ch.qos.logback.classic.net.ExternalMockSocketServer 4 @@ -54,57 +60,40 @@ public class SerializationPerfTest extends TestCase { boolean runWithExternalMockServer = true; /** - * Last results: - * Data sent mesured in kilobytes. - * Avg time mesured in microsecs. + * Last results: Data sent measured in kilobytes. Avg time measured in microsecs. * - * NOPOutputStream: - * | | Runs | Avg time | Data sent | - * | MinimalObj Ext | 10000 | | | - * | MinimalObj Ser | 10000 | | | - * | LoggEvent Ext | 10000 | | | - * | LoggEvent Ser | 10000 | | | + * NOPOutputStream: | | Runs | Avg time | Data sent | | MinimalObj Ext | 10000 | + * | | | MinimalObj Ser | 10000 | | | | LoggEvent Ext | 10000 | | | | LoggEvent + * Ser | 10000 | | | * - * External MockServer with 45 letters-long message: on localhost - * (always the same message) - * | | Runs | Avg time | Data sent | - * | MinimalObj Ext | 10000 | - | - | - * | MinimalObj Ser | 10000 | 74 | 248 | - * | LoggEvent Ext | 10000 | - | - | - * | LoggEvent Ser | 10000 | 156 | 835 | - * pauseFrequency = 10 and pauseLengthInMillis = 20 + * External MockServer with 45 letters-long message: on localhost (always the + * same message) | | Runs | Avg time | Data sent | | MinimalObj Ext | 10000 | - + * | - | | MinimalObj Ser | 10000 | 74 | 248 | | LoggEvent Ext | 10000 | - | - | + * | LoggEvent Ser | 10000 | 156 | 835 | pauseFrequency = 10 and + * pauseLengthInMillis = 20 * - * External MockServer with 45 letters-long message: on localhost - * (different message each time) - * | | Runs | Avg time | Data sent | - * | MinimalObj Ext | 10000 | | | - * | MinimalObj Ser | 10000 | 73 | 1139 | - * | LoggEvent Ext | 10000 | | | - * | LoggEvent Ser | 10000 | 162 | 1752 | - * pauseFrequency = 10 and pauseLengthInMillis = 20 + * External MockServer with 45 letters-long message: on localhost (different + * message each time) | | Runs | Avg time | Data sent | | MinimalObj Ext | 10000 + * | | | | MinimalObj Ser | 10000 | 73 | 1139 | | LoggEvent Ext | 10000 | | | | + * LoggEvent Ser | 10000 | 162 | 1752 | pauseFrequency = 10 and + * pauseLengthInMillis = 20 * - * External MockServer with 45 letters-long message: on PIXIE - * (always the same message) - * | | Runs | Avg time | Data sent | - * | MinimalObj Ext | 10000 | - | - | - * | MinimalObj Ser | 10000 | 29 | 248 | - * | LoggEvent Ext | 10000 | - | - | - * | LoggEvent Ser | 10000 | 42 | 835 | - * pauseFrequency = 10 and pauseLengthInMillis = 20 + * External MockServer with 45 letters-long message: on PIXIE (always the same + * message) | | Runs | Avg time | Data sent | | MinimalObj Ext | 10000 | - | - | + * | MinimalObj Ser | 10000 | 29 | 248 | | LoggEvent Ext | 10000 | - | - | | + * LoggEvent Ser | 10000 | 42 | 835 | pauseFrequency = 10 and + * pauseLengthInMillis = 20 * - * External MockServer with 45 letters-long message: on PIXIE - * (different message each time) - * | | Runs | Avg time | Data sent | - * | MinimalObj Ext | 10000 | | | - * | MinimalObj Ser | 10000 | 27 | 1139 | - * | LoggEvent Ext | 10000 | | | - * | LoggEvent Ser | 10000 | 44 | 1752 | - * pauseFrequency = 10 and pauseLengthInMillis = 20 + * External MockServer with 45 letters-long message: on PIXIE (different message + * each time) | | Runs | Avg time | Data sent | | MinimalObj Ext | 10000 | | | | + * MinimalObj Ser | 10000 | 27 | 1139 | | LoggEvent Ext | 10000 | | | | + * LoggEvent Ser | 10000 | 44 | 1752 | pauseFrequency = 10 and + * pauseLengthInMillis = 20 * */ + @BeforeEach public void setUp() throws Exception { - super.setUp(); if (runWithExternalMockServer) { oos = new ObjectOutputStream(new Socket("localhost", ExternalMockSocketServer.PORT).getOutputStream()); } else { @@ -112,8 +101,8 @@ public void setUp() throws Exception { } } + @AfterEach public void tearDown() throws Exception { - super.tearDown(); oos.close(); oos = null; } @@ -140,7 +129,7 @@ public void runPerfTest(Builder builder, String label) throws Exception { } } catch (IOException ex) { - fail(ex.getMessage()); + Assertions.fail(ex.getMessage()); } } @@ -150,7 +139,7 @@ public void runPerfTest(Builder builder, String label) throws Exception { Long total = 0L; resetCounter = 0; pauseCounter = 0; - // System.out.println("Beginning mesured run"); + // System.out.println("Beginning measured run"); for (int i = 0; i < loopNumber; i++) { try { t1 = System.nanoTime(); @@ -167,11 +156,12 @@ public void runPerfTest(Builder builder, String label) throws Exception { pauseCounter = 0; } } catch (IOException ex) { - fail(ex.getMessage()); + Assertions.fail(ex.getMessage()); } } total /= 1000; - System.out.println(label + " : average time = " + total / loopNumber + " microsecs after " + loopNumber + " writes."); + System.out.println( + label + " : average time = " + total / loopNumber + " microsecs after " + loopNumber + " writes."); // long time2 = System.nanoTime(); // System.out.println("********* -> Time needed to run the test method: " + @@ -183,6 +173,7 @@ public void runPerfTest(Builder builder, String label) throws Exception { // runPerfTest(builder, "Minimal object externalization"); // } + @Test public void testWithMinimalSerialization() throws Exception { Builder builder = new MinimalSerBuilder(); runPerfTest(builder, "Minimal object serialization"); @@ -193,6 +184,7 @@ public void testWithMinimalSerialization() throws Exception { // runPerfTest(builder, "LoggingEvent object externalization"); // } + @Test public void testWithSerialization() throws Exception { Builder builder = new TrivialLoggingEventVOBuilder(); runPerfTest(builder, "LoggingEventVO object serialization"); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderMessageLossTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderMessageLossTest.java index f68ea72c0f..02bcde17cf 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderMessageLossTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketAppenderMessageLossTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,13 @@ */ package ch.qos.logback.classic.net; -import static org.junit.Assert.assertTrue; - import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; -import org.junit.Ignore; -import org.junit.Test; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; @@ -28,15 +27,17 @@ import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.util.Duration; +import org.junit.jupiter.api.Timeout; + +import static org.junit.jupiter.api.Assertions.assertTrue; -@Ignore public class SocketAppenderMessageLossTest { int runLen = 100; Duration reconnectionDelay = new Duration(1000); static final int TIMEOUT = 3000; - @Test // (timeout = TIMEOUT) + @Test // (timeout = TIMEOUT) public void synchronousSocketAppender() throws Exception { SocketAppender socketAppender = new SocketAppender(); @@ -46,7 +47,8 @@ public void synchronousSocketAppender() throws Exception { runTest(socketAppender); } - @Test(timeout = TIMEOUT) + @Test + @Timeout(value = TIMEOUT, unit= TimeUnit.MILLISECONDS) public void smallQueueSocketAppender() throws Exception { SocketAppender socketAppender = new SocketAppender(); @@ -56,7 +58,8 @@ public void smallQueueSocketAppender() throws Exception { runTest(socketAppender); } - @Test(timeout = TIMEOUT) + @Test + @Timeout(value = TIMEOUT, unit= TimeUnit.MILLISECONDS) public void largeQueueSocketAppender() throws Exception { SocketAppender socketAppender = new SocketAppender(); socketAppender.setReconnectionDelay(reconnectionDelay); @@ -65,7 +68,8 @@ public void largeQueueSocketAppender() throws Exception { runTest(socketAppender); } - // appender used to signal when the N'th event (as set in the latch) is received by the server + // appender used to signal when the N'th event (as set in the latch) is received + // by the server // this allows us to have test which are both more robust and quicker. static public class ListAppenderWithLatch extends AppenderBase { public List list = new ArrayList(); @@ -85,6 +89,8 @@ public void runTest(SocketAppender socketAppender) throws Exception { final int port = RandomUtil.getRandomServerPort(); LoggerContext serverLoggerContext = new LoggerContext(); + LogbackMDCAdapter serverLogbackMDCAdapter = new LogbackMDCAdapter(); + serverLoggerContext.setMDCAdapter(serverLogbackMDCAdapter); serverLoggerContext.setName("serverLoggerContext"); CountDownLatch allMessagesReceivedLatch = new CountDownLatch(runLen); @@ -97,6 +103,9 @@ public void runTest(SocketAppender socketAppender) throws Exception { serverRootLogger.addAppender(listAppender); LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + loggerContext.setMDCAdapter(logbackMDCAdapter); + loggerContext.setName("clientLoggerContext"); socketAppender.setContext(loggerContext); @@ -125,6 +134,5 @@ public void runTest(SocketAppender socketAppender) throws Exception { loggerContext.stop(); simpleSocketServer.close(); - } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketMin.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketMin.java index edfcca6ac4..debe495157 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketMin.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketMin.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketReceiverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketReceiverTest.java deleted file mode 100644 index ddca8d94b5..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SocketReceiverTest.java +++ /dev/null @@ -1,281 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.io.ObjectOutputStream; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.UnknownHostException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - -import javax.net.SocketFactory; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.mock.MockAppender; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.spi.LoggingEventVO; -import ch.qos.logback.core.net.SocketConnector; -import ch.qos.logback.core.net.server.test.ServerSocketUtil; -import ch.qos.logback.core.status.Status; - -/** - * Unit tests for {@link SocketReceiver}. - * - * @author Carl Harris - */ -@Ignore -public class SocketReceiverTest { - - private static final int DELAY = 1000; - private static final String TEST_HOST_NAME = "NOT.A.VALID.HOST.NAME"; - - private ServerSocket serverSocket; - private Socket socket; - private MockSocketFactory socketFactory = new MockSocketFactory(); - private MockSocketConnector connector; - private MockAppender appender; - private LoggerContext lc; - private Logger logger; - - private InstrumentedSocketReceiver receiver = new InstrumentedSocketReceiver(); - - @Before - public void setUp() throws Exception { - serverSocket = ServerSocketUtil.createServerSocket(); - socket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort()); - connector = new MockSocketConnector(socket); - - lc = new LoggerContext(); - lc.reset(); - receiver.setContext(lc); - appender = new MockAppender(); - appender.start(); - logger = lc.getLogger(getClass()); - logger.addAppender(appender); - } - - @After - public void tearDown() throws Exception { - receiver.stop(); - ExecutorService executor = lc.getExecutorService(); - executor.shutdownNow(); - assertTrue(executor.awaitTermination(DELAY, TimeUnit.MILLISECONDS)); - socket.close(); - serverSocket.close(); - lc.stop(); - } - - @Test - public void testStartNoRemoteAddress() throws Exception { - receiver.start(); - assertFalse(receiver.isStarted()); - int count = lc.getStatusManager().getCount(); - Status status = lc.getStatusManager().getCopyOfStatusList().get(count - 1); - assertTrue(status.getMessage().contains("host")); - } - - @Test - public void testStartNoPort() throws Exception { - receiver.setRemoteHost(TEST_HOST_NAME); - receiver.start(); - assertFalse(receiver.isStarted()); - int count = lc.getStatusManager().getCount(); - Status status = lc.getStatusManager().getCopyOfStatusList().get(count - 1); - assertTrue(status.getMessage().contains("port")); - } - - @Test - public void testStartUnknownHost() throws Exception { - receiver.setPort(6000); - receiver.setRemoteHost(TEST_HOST_NAME); - receiver.start(); - assertFalse(receiver.isStarted()); - int count = lc.getStatusManager().getCount(); - Status status = lc.getStatusManager().getCopyOfStatusList().get(count - 1); - assertTrue(status.getMessage().contains("unknown host")); - } - - @Test - public void testStartStop() throws Exception { - receiver.setRemoteHost(InetAddress.getLocalHost().getHostName()); - receiver.setPort(6000); - receiver.setAcceptConnectionTimeout(DELAY / 2); - receiver.start(); - assertTrue(receiver.isStarted()); - receiver.awaitConnectorCreated(DELAY); - receiver.stop(); - assertFalse(receiver.isStarted()); - } - - @Test - public void testServerSlowToAcceptConnection() throws Exception { - receiver.setRemoteHost(InetAddress.getLocalHost().getHostName()); - receiver.setPort(6000); - receiver.setAcceptConnectionTimeout(DELAY / 4); - receiver.start(); - assertTrue(receiver.awaitConnectorCreated(DELAY / 2)); - // note that we don't call serverSocket.accept() here - // but processPriorToRemoval (in tearDown) should still clean up everything - } - - @Test - public void testServerDropsConnection() throws Exception { - receiver.setRemoteHost(InetAddress.getLocalHost().getHostName()); - receiver.setPort(6000); - receiver.start(); - assertTrue(receiver.awaitConnectorCreated(DELAY)); - Socket socket = serverSocket.accept(); - socket.close(); - } - - @Test - public void testDispatchEventForEnabledLevel() throws Exception { - receiver.setRemoteHost(InetAddress.getLocalHost().getHostName()); - receiver.setPort(6000); - receiver.start(); - assertTrue(receiver.awaitConnectorCreated(DELAY)); - Socket socket = serverSocket.accept(); - - ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); - - logger.setLevel(Level.DEBUG); - ILoggingEvent event = new LoggingEvent(logger.getName(), logger, Level.DEBUG, "test message", null, new Object[0]); - - LoggingEventVO eventVO = LoggingEventVO.build(event); - oos.writeObject(eventVO); - oos.flush(); - - ILoggingEvent rcvdEvent = appender.awaitAppend(DELAY); - assertNotNull(rcvdEvent); - assertEquals(event.getLoggerName(), rcvdEvent.getLoggerName()); - assertEquals(event.getLevel(), rcvdEvent.getLevel()); - assertEquals(event.getMessage(), rcvdEvent.getMessage()); - } - - @Test - public void testNoDispatchEventForDisabledLevel() throws Exception { - receiver.setRemoteHost(InetAddress.getLocalHost().getHostName()); - receiver.setPort(6000); - receiver.start(); - assertTrue(receiver.awaitConnectorCreated(DELAY)); - Socket socket = serverSocket.accept(); - - ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); - - logger.setLevel(Level.INFO); - ILoggingEvent event = new LoggingEvent(logger.getName(), logger, Level.DEBUG, "test message", null, new Object[0]); - - LoggingEventVO eventVO = LoggingEventVO.build(event); - oos.writeObject(eventVO); - oos.flush(); - - assertNull(appender.awaitAppend(DELAY)); - } - - /** - * A {@link SocketReceiver} with instrumentation for unit testing. - */ - private class InstrumentedSocketReceiver extends SocketReceiver { - - private boolean connectorCreated; - - @Override - protected synchronized SocketConnector newConnector(InetAddress address, int port, int initialDelay, int retryDelay) { - connectorCreated = true; - notifyAll(); - return connector; - } - - @Override - protected SocketFactory getSocketFactory() { - return socketFactory; - } - - public synchronized boolean awaitConnectorCreated(long delay) throws InterruptedException { - while (!connectorCreated) { - wait(delay); - } - return connectorCreated; - } - - } - - /** - * A {@link SocketConnector} with instrumentation for unit testing. - */ - private static class MockSocketConnector implements SocketConnector { - - private final Socket socket; - - public MockSocketConnector(Socket socket) { - this.socket = socket; - } - - public Socket call() throws InterruptedException { - return socket; - } - - public void setExceptionHandler(ExceptionHandler exceptionHandler) { - } - - public void setSocketFactory(SocketFactory socketFactory) { - } - - } - - /** - * A no-op {@link SocketFactory} to support unit testing. - */ - private static class MockSocketFactory extends SocketFactory { - - @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(InetAddress host, int port) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { - throw new UnsupportedOperationException(); - } - - @Override - public Socket createSocket(String host, int port) throws IOException, UnknownHostException { - throw new UnsupportedOperationException(); - } - - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/SyslogAppenderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/SyslogAppenderTest.java index 08594eb6ce..5b9a9f5cef 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/SyslogAppenderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/SyslogAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,11 @@ */ package ch.qos.logback.classic.net; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.slf4j.LoggerFactory; import ch.qos.logback.classic.ClassicTestConstants; @@ -35,24 +33,30 @@ import ch.qos.logback.core.util.StatusPrinter; import java.nio.charset.Charset; -@Ignore + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class SyslogAppenderTest { private static final String SYSLOG_PREFIX_REGEX = "<\\d{2}>\\w{3} [\\d ]\\d \\d{2}(:\\d{2}){2} [\\w.-]* "; LoggerContext lc = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + SyslogAppender sa = new SyslogAppender(); MockSyslogServer mockServer; String loggerName = this.getClass().getName(); Logger logger = lc.getLogger(loggerName); - @Before + @BeforeEach public void setUp() throws Exception { lc.setName("test"); + lc.setMDCAdapter(logbackMDCAdapter); sa.setContext(lc); } - @After + @AfterEach public void tearDown() throws Exception { } @@ -126,7 +130,8 @@ public void suffixPatternWithTag() throws InterruptedException { String expected = "<" + (SyslogConstants.LOG_MAIL + SyslogConstants.DEBUG_SEVERITY) + ">"; assertTrue(msg.startsWith(expected)); - checkRegexMatch(msg, SYSLOG_PREFIX_REGEX + "test/something \\[" + threadName + "\\] " + loggerName + " " + logMsg); + checkRegexMatch(msg, + SYSLOG_PREFIX_REGEX + "test/something \\[" + threadName + "\\] " + loggerName + " " + logMsg); } @@ -171,7 +176,7 @@ public void tException() throws InterruptedException { } private void checkRegexMatch(String s, String regex) { - assertTrue("The string [" + s + "] did not match regex [" + regex + "]", s.matches(regex)); + assertTrue(s.matches(regex), "The string [" + s + "] did not match regex [" + regex + "]"); } @Test @@ -201,7 +206,8 @@ public void large() throws Exception { final int maxMessageSize = sa.getMaxMessageSize(); String largeMsg = new String(mockServer.getMessageList().get(0)); assertTrue(largeMsg.startsWith(expected)); - String largeRegex = SYSLOG_PREFIX_REGEX + "\\[" + threadName + "\\] " + loggerName + " " + "a{" + (maxMessageSize - 2000) + "," + maxMessageSize + "}"; + String largeRegex = SYSLOG_PREFIX_REGEX + "\\[" + threadName + "\\] " + loggerName + " " + "a{" + + (maxMessageSize - 2000) + "," + maxMessageSize + "}"; checkRegexMatch(largeMsg, largeRegex); String msg = new String(mockServer.getMessageList().get(1)); @@ -238,7 +244,8 @@ public void nonAsciiMessageEncoding() throws Exception { // See LOGBACK-732 setMockServerAndConfigure(1); - // Use a string that can be encoded in a somewhat odd encoding (ISO-8859-4) to minimize + // Use a string that can be encoded in a somewhat odd encoding (ISO-8859-4) to + // minimize // the probability of the encoding test to work by accident String logMsg = "R\u0129ga"; // Riga spelled with the i having a tilda on top diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockAppender.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockAppender.java index 842278fe66..aada100902 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockAppender.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockSyslogServer.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockSyslogServer.java index 46ee016f70..67446e7f67 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockSyslogServer.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/mock/MockSyslogServer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/InstrumentedServerSocketReceiver.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/InstrumentedServerSocketReceiver.java deleted file mode 100644 index 45522281a9..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/InstrumentedServerSocketReceiver.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.util.concurrent.Executor; - -import javax.net.ServerSocketFactory; - -import ch.qos.logback.classic.net.server.RemoteAppenderClient; -import ch.qos.logback.classic.net.server.RemoteAppenderServerListener; -import ch.qos.logback.classic.net.server.ServerSocketReceiver; -import ch.qos.logback.core.net.server.ServerListener; -import ch.qos.logback.core.net.server.ServerRunner; - -/** - * A {@link ServerSocketReceiver} with instrumentation for unit testing. - * - * @author Carl Harris - */ -public class InstrumentedServerSocketReceiver extends ServerSocketReceiver { - - private final ServerSocket serverSocket; - private final ServerListener listener; - private final ServerRunner runner; - - @SuppressWarnings("rawtypes") - private ServerListener lastListener; - - public InstrumentedServerSocketReceiver(ServerSocket serverSocket) { - this(serverSocket, new RemoteAppenderServerListener(serverSocket), null); - } - - public InstrumentedServerSocketReceiver(ServerSocket serverSocket, ServerListener listener, ServerRunner runner) { - this.serverSocket = serverSocket; - this.listener = listener; - this.runner = runner; - } - - @Override - protected ServerSocketFactory getServerSocketFactory() throws Exception { - return new ServerSocketFactory() { - - @Override - public ServerSocket createServerSocket(int port) throws IOException { - return serverSocket; - } - - @Override - public ServerSocket createServerSocket(int port, int backlog) throws IOException { - return serverSocket; - } - - @Override - public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddress) throws IOException { - return serverSocket; - } - }; - } - - @SuppressWarnings("rawtypes") - @Override - protected ServerRunner createServerRunner(ServerListener listener, Executor executor) { - lastListener = listener; - return runner != null ? runner : super.createServerRunner(listener, executor); - } - - @Override - protected ServerListener createServerListener(ServerSocket socket) { - return listener; - } - - @SuppressWarnings("rawtypes") - public ServerListener getLastListener() { - return lastListener; - } - -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLConfiguration.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLConfiguration.java index 1c21d035a3..bdf5bca0f2 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLConfiguration.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLConfiguration.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -35,8 +35,8 @@ class MockSSLConfiguration extends SSLConfiguration { private boolean contextCreated; @Override - public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException, - UnrecoverableKeyException, KeyStoreException, CertificateException { + public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, + KeyManagementException, UnrecoverableKeyException, KeyStoreException, CertificateException { contextCreated = true; return super.createContext(context); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLParametersConfiguration.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLParametersConfiguration.java index 4d36e022cb..98f563fb1e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLParametersConfiguration.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/MockSSLParametersConfiguration.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,8 @@ import ch.qos.logback.core.net.ssl.SSLParametersConfiguration; /** - * A mock {@link SSLParametersConfiguration} with instrumentation for - * unit testing. + * A mock {@link SSLParametersConfiguration} with instrumentation for unit + * testing. * * @author Carl Harris */ diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClientTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClientTest.java deleted file mode 100644 index 55cd864df7..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/RemoteAppenderStreamClientTest.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.ObjectOutputStream; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.LoggerFactory; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.mock.MockAppender; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.spi.LoggingEventVO; - -/** - * Unit tests for {@link RemoteAppenderStreamClient}. - * - * @author Carl Harris - */ -public class RemoteAppenderStreamClientTest { - - private MockAppender appender; - private Logger logger; - private LoggingEvent event; - private RemoteAppenderStreamClient client; - - @Before - public void setUp() throws Exception { - LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); - - appender = new MockAppender(); - appender.start(); - - logger = lc.getLogger(getClass()); - logger.addAppender(appender); - - event = new LoggingEvent(logger.getName(), logger, Level.DEBUG, "test message", null, new Object[0]); - - LoggingEventVO eventVO = LoggingEventVO.build(event); - - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - ObjectOutputStream oos = new ObjectOutputStream(bos); - oos.writeObject(eventVO); - oos.close(); - - ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); - client = new RemoteAppenderStreamClient("some client ID", bis); - client.setLoggerContext(lc); - } - - @Test - public void testWithEnabledLevel() throws Exception { - logger.setLevel(Level.DEBUG); - client.run(); - client.close(); - - ILoggingEvent rcvdEvent = appender.getLastEvent(); - assertEquals(event.getLoggerName(), rcvdEvent.getLoggerName()); - assertEquals(event.getLevel(), rcvdEvent.getLevel()); - assertEquals(event.getMessage(), rcvdEvent.getMessage()); - } - - @Test - public void testWithDisabledLevel() throws Exception { - logger.setLevel(Level.INFO); - client.run(); - client.close(); - assertNull(appender.getLastEvent()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiverTest.java deleted file mode 100644 index 2fd60a0c8a..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/SSLServerSocketReceiverTest.java +++ /dev/null @@ -1,56 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import javax.net.ServerSocketFactory; - -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.core.net.mock.MockContext; - -/** - * Unit tests for {@link SSLServerSocketReceiver}. - * - * @author Carl Harris - */ -public class SSLServerSocketReceiverTest { - - private MockContext context = new MockContext(); - - private MockSSLConfiguration ssl = new MockSSLConfiguration(); - - private MockSSLParametersConfiguration parameters = new MockSSLParametersConfiguration(); - - private SSLServerSocketReceiver receiver = new SSLServerSocketReceiver(); - - @Before - public void setUp() throws Exception { - receiver.setContext(context); - receiver.setSsl(ssl); - ssl.setParameters(parameters); - } - - @Test - public void testGetServerSocketFactory() throws Exception { - ServerSocketFactory socketFactory = receiver.getServerSocketFactory(); - assertNotNull(socketFactory); - assertTrue(ssl.isContextCreated()); - assertTrue(parameters.isContextInjected()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverFunctionalTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverFunctionalTest.java deleted file mode 100644 index 3d50674cd0..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverFunctionalTest.java +++ /dev/null @@ -1,113 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.ObjectOutputStream; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.mock.MockAppender; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.spi.LoggingEventVO; -import ch.qos.logback.core.net.server.test.ServerSocketUtil; - -/** - * A functional test for {@link ServerSocketReceiver}. - *

- * In this test we create a SocketServer, connect to it over the local - * network interface, and validate that it receives messages and delivers - * them to its appender. - */ -@Ignore -public class ServerSocketReceiverFunctionalTest { - - private static final int EVENT_COUNT = 10; - private static final int SHUTDOWN_DELAY = 10000; - - private MockAppender appender; - private Logger logger; - private ServerSocket serverSocket; - private InstrumentedServerSocketReceiver receiver; - private LoggerContext lc; - - @Before - public void setUp() throws Exception { - lc = new LoggerContext(); - - appender = new MockAppender(); - appender.start(); - - logger = lc.getLogger(getClass()); - logger.addAppender(appender); - - serverSocket = ServerSocketUtil.createServerSocket(); - - receiver = new InstrumentedServerSocketReceiver(serverSocket); - - receiver.setContext(lc); - receiver.start(); - } - - @After - public void tearDown() throws Exception { - receiver.stop(); - ExecutorService executor = lc.getExecutorService(); - executor.shutdownNow(); - executor.awaitTermination(SHUTDOWN_DELAY, TimeUnit.MILLISECONDS); - assertTrue(executor.isTerminated()); - } - - @Test - public void testLogEventFromClient() throws Exception { - ILoggingEvent event = new LoggingEvent(logger.getName(), logger, Level.DEBUG, "test message", null, new Object[0]); - Socket socket = new Socket(InetAddress.getLocalHost(), serverSocket.getLocalPort()); - - try { - LoggingEventVO eventVO = LoggingEventVO.build(event); - - ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream()); - for (int i = 0; i < EVENT_COUNT; i++) { - oos.writeObject(eventVO); - } - - oos.writeObject(eventVO); - oos.flush(); - } finally { - socket.close(); - } - - ILoggingEvent rcvdEvent = appender.awaitAppend(SHUTDOWN_DELAY); - assertNotNull(rcvdEvent); - assertEquals(event.getLoggerName(), rcvdEvent.getLoggerName()); - assertEquals(event.getLevel(), rcvdEvent.getLevel()); - assertEquals(event.getMessage(), rcvdEvent.getMessage()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverTest.java deleted file mode 100644 index 9c88dd7cc7..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/server/ServerSocketReceiverTest.java +++ /dev/null @@ -1,103 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.net.server; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -import java.io.IOException; -import java.net.ServerSocket; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.core.net.mock.MockContext; -import ch.qos.logback.core.net.server.test.MockServerListener; -import ch.qos.logback.core.net.server.test.MockServerRunner; -import ch.qos.logback.core.net.server.test.ServerSocketUtil; -import ch.qos.logback.core.status.ErrorStatus; -import ch.qos.logback.core.status.Status; - -/** - * Unit tests for {@link ServerSocketReceiver}. - * - * @author Carl Harris - */ -public class ServerSocketReceiverTest { - - private MockContext context = new MockContext(); - - private MockServerRunner runner = new MockServerRunner(); - - private MockServerListener listener = new MockServerListener(); - - private ServerSocket serverSocket; - private InstrumentedServerSocketReceiver receiver; - - @Before - public void setUp() throws Exception { - serverSocket = ServerSocketUtil.createServerSocket(); - receiver = new InstrumentedServerSocketReceiver(serverSocket, listener, runner); - receiver.setContext(context); - } - - @After - public void tearDown() throws Exception { - serverSocket.close(); - } - - @Test - public void testStartStop() throws Exception { - receiver.start(); - assertTrue(runner.isContextInjected()); - assertTrue(runner.isRunning()); - assertSame(listener, receiver.getLastListener()); - - receiver.stop(); - assertFalse(runner.isRunning()); - } - - @Test - public void testStartWhenAlreadyStarted() throws Exception { - receiver.start(); - receiver.start(); - assertEquals(1, runner.getStartCount()); - } - - @Test - public void testStopThrowsException() throws Exception { - receiver.start(); - assertTrue(receiver.isStarted()); - IOException ex = new IOException("test exception"); - runner.setStopException(ex); - receiver.stop(); - - Status status = context.getLastStatus(); - assertNotNull(status); - assertTrue(status instanceof ErrorStatus); - assertTrue(status.getMessage().contains(ex.getMessage())); - assertSame(ex, status.getThrowable()); - } - - @Test - public void testStopWhenNotStarted() throws Exception { - receiver.stop(); - assertEquals(0, runner.getStartCount()); - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/Builder.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/Builder.java index d2b02b2b79..4805e3062b 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/Builder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/Builder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventBuilderInContext.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventBuilderInContext.java index 59faf055df..681a17d007 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventBuilderInContext.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventBuilderInContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventWithParametersBuilder.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventWithParametersBuilder.java index 8e27133486..396f6d9334 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventWithParametersBuilder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/LoggingEventWithParametersBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -37,7 +37,7 @@ public LoggingEvent build(int i) { le.setMessage(msg); // compute formatted message - // this forces le.formmatedMessage to be set (this is the whole point of the + // this forces le.formattedMessage to be set (this is the whole point of the // exercise) le.getFormattedMessage(); le.setLevel(Level.DEBUG); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSer.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSer.java index f5114b12db..0a199b6a0b 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSer.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSer.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.net.testObjectBuilders; import java.io.Serializable; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSerBuilder.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSerBuilder.java index 7f78ffa3cd..d17430b277 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSerBuilder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/MinimalSerBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,4 +20,3 @@ public MinimalSer build(int i) { } } - diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventBuilder.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventBuilder.java index 545b6632ff..9ae4f9ee19 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventBuilder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventVOBuilder.java b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventVOBuilder.java index a3207d850e..18226ab3cb 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventVOBuilder.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/net/testObjectBuilders/TrivialLoggingEventVOBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/CompositeConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/CompositeConverterTest.java new file mode 100644 index 0000000000..fcc8d8d80d --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/CompositeConverterTest.java @@ -0,0 +1,61 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; +//import ch.qos.logback.core.util.StatusPrinter; + +public class CompositeConverterTest { + + private PatternLayout pl = new PatternLayout(); + private LoggerContext lc = new LoggerContext(); + Logger logger = lc.getLogger(this.getClass()); + StatusChecker sc = new StatusChecker(lc); + + + @BeforeEach + public void setUp() { + pl.setContext(lc); + } + + ILoggingEvent makeLoggingEvent(String msg, Exception ex) { + return new LoggingEvent(CompositeConverterTest.class.getName(), logger, Level.INFO, msg, ex, null); + } + + + + @Test + public void testLogback1582() { + // EVAL_REF is searched within the context, if context is not set (== null), then + // a NullPointerException will be thrown + pl.setPattern("%m %replace(%rootException{5, EVAL_REF}){'\\n', 'XYZ'}\""); + pl.start(); + ILoggingEvent le = makeLoggingEvent("assert", new Exception("test")); + + String result = pl.doLayout(le); + sc.assertIsErrorFree(); + assertTrue(result.contains("XYZ")); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java index 5466a6e885..dfc1de0376 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,52 +13,50 @@ */ package ch.qos.logback.classic.pattern; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Pattern; - -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; -import org.slf4j.MarkerFactory; - -import ch.qos.logback.classic.ClassicConstants; -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.*; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.net.SyslogConstants; import ch.qos.logback.core.pattern.DynamicConverter; import ch.qos.logback.core.pattern.FormatInfo; +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.MarkerFactory; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +import static org.junit.jupiter.api.Assertions.*; public class ConverterTest { - LoggerContext lc = new LoggerContext(); - Logger logger = lc.getLogger(ConverterTest.class); + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger logger = loggerContext.getLogger(ConverterTest.class); LoggingEvent le; - List optionList = new ArrayList(); + //List optionList = new ArrayList(); // The LoggingEvent is massaged with an FCQN of FormattingConverter. This - // forces the returned caller information to match the caller stack for this + // forces the returned caller information to match the caller stack for // this particular test. LoggingEvent makeLoggingEvent(Exception ex) { - return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO, "Some message", ex, null); + return new LoggingEvent(ch.qos.logback.core.pattern.FormattingConverter.class.getName(), logger, Level.INFO, + "Some message", ex, null); } Exception getException(String msg, Exception cause) { return new Exception(msg, cause); } - @Before + @BeforeEach public void setUp() throws Exception { + loggerContext.setMDCAdapter(logbackMDCAdapter); Exception rootEx = getException("Innermost", null); Exception nestedEx = getException("Nested", rootEx); @@ -74,7 +72,7 @@ public void testLineOfCaller() { StringBuilder buf = new StringBuilder(); converter.write(buf, le); // the number below should be the line number of the previous line - assertEquals("75", buf.toString()); + assertEquals("73", buf.toString()); } } @@ -131,8 +129,7 @@ public void testException() { { DynamicConverter converter = new ThrowableProxyConverter(); - this.optionList.add("3"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("3")); StringBuilder buf = new StringBuilder(); converter.write(buf, le); } @@ -149,8 +146,7 @@ public void testLogger() { { ClassicConverter converter = new LoggerConverter(); - this.optionList.add("20"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("20")); converter.start(); StringBuilder buf = new StringBuilder(); converter.write(buf, le); @@ -159,9 +155,7 @@ public void testLogger() { { DynamicConverter converter = new LoggerConverter(); - this.optionList.clear(); - this.optionList.add("0"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("0")); converter.start(); StringBuilder buf = new StringBuilder(); converter.write(buf, le); @@ -172,8 +166,7 @@ public void testLogger() { @Test public void testVeryLongLoggerName() { ClassicConverter converter = new LoggerConverter(); - this.optionList.add("5"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("5")); converter.start(); StringBuilder buf = new StringBuilder(); @@ -185,11 +178,7 @@ public void testVeryLongLoggerName() { for (int i = 0; i < totalParts; i++) { loggerNameBuf.append(c).append(c).append(c); - if (i < ClassicConstants.MAX_DOTS) { - witness.append(c); - } else { - witness.append(c).append(c).append(c); - } + witness.append(c); loggerNameBuf.append('.'); witness.append('.'); } @@ -240,14 +229,12 @@ public void testCallerData() { { DynamicConverter converter = new CallerDataConverter(); - this.optionList.add("2"); - this.optionList.add("XXX"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("2", "XXX")); converter.start(); StringBuilder buf = new StringBuilder(); LoggingEvent event = makeLoggingEvent(null); - event.setMarker(MarkerFactory.getMarker("XXX")); + event.addMarker(MarkerFactory.getMarker("XXX")); converter.write(buf, event); if (buf.length() < 10) { fail("buf is too short"); @@ -256,16 +243,12 @@ public void testCallerData() { { DynamicConverter converter = new CallerDataConverter(); - this.optionList.clear(); - this.optionList.add("2"); - this.optionList.add("XXX"); - this.optionList.add("*"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("2", "XXX", "*")); converter.start(); StringBuilder buf = new StringBuilder(); LoggingEvent event = makeLoggingEvent(null); - event.setMarker(MarkerFactory.getMarker("YYY")); + event.addMarker(MarkerFactory.getMarker("YYY")); converter.write(buf, event); if (buf.length() < 10) { fail("buf is too short"); @@ -273,16 +256,12 @@ public void testCallerData() { } { DynamicConverter converter = new CallerDataConverter(); - this.optionList.clear(); - this.optionList.add("2"); - this.optionList.add("XXX"); - this.optionList.add("+"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("2", "XXX", "*")); converter.start(); StringBuilder buf = new StringBuilder(); LoggingEvent event = makeLoggingEvent(null); - event.setMarker(MarkerFactory.getMarker("YYY")); + event.addMarker(MarkerFactory.getMarker("YYY")); converter.write(buf, event); if (buf.length() < 10) { fail("buf is too short"); @@ -291,11 +270,7 @@ public void testCallerData() { { DynamicConverter converter = new CallerDataConverter(); - this.optionList.clear(); - this.optionList.add("2"); - this.optionList.add("XXX"); - this.optionList.add("*"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("2", "XXX", "*")); converter.start(); StringBuilder buf = new StringBuilder(); @@ -308,19 +283,24 @@ public void testCallerData() { { DynamicConverter converter = new CallerDataConverter(); - this.optionList.clear(); - this.optionList.add("4..5"); - converter.setOptionList(this.optionList); + + boolean jdk18 = EnvUtil.isJDK18OrHigher(); + // jdk 18EA creates a different stack trace + converter.setOptionList(jdk18 ? List.of("2..3") : List.of("4..5")); converter.start(); StringBuilder buf = new StringBuilder(); converter.write(buf, le); - assertTrue("buf is too short", buf.length() >= 10); + assertTrue( buf.length() >= 10, "buf is too short"); - String expectedRegex = "Caller\\+4\t at (java.base\\/)?java.lang.reflect.Method.invoke.*$"; + String expectedRegex = "Caller\\+4"; + if(jdk18) { + expectedRegex = "Caller\\+2"; + } + expectedRegex+="\t at (java.base\\/)?java.lang.reflect.Method.invoke.*$"; String actual = buf.toString(); - assertTrue("actual: "+actual, Pattern.compile(expectedRegex).matcher(actual).find()); - + assertTrue( Pattern.compile(expectedRegex).matcher(actual).find(), "actual: " + actual); + } } @@ -342,9 +322,7 @@ public void testRelativeTime() throws Exception { @Test public void testSyslogStart() throws Exception { DynamicConverter converter = new SyslogStartConverter(); - this.optionList.clear(); - this.optionList.add("MAIL"); - converter.setOptionList(this.optionList); + converter.setOptionList(List.of("MAIL")); converter.start(); ILoggingEvent event = makeLoggingEvent(null); @@ -358,12 +336,10 @@ public void testSyslogStart() throws Exception { @Test public void testMDCConverter() throws Exception { - MDC.clear(); - MDC.put("someKey", "someValue"); + logbackMDCAdapter.clear(); + logbackMDCAdapter.put("someKey", "someValue"); MDCConverter converter = new MDCConverter(); - this.optionList.clear(); - this.optionList.add("someKey"); - converter.setOptionList(optionList); + converter.setOptionList(List.of("someKey")); converter.start(); ILoggingEvent event = makeLoggingEvent(null); @@ -380,7 +356,7 @@ public void contextNameConverter() { lcOther.setName("another"); converter.setContext(lcOther); - lc.setName("aValue"); + loggerContext.setName("aValue"); ILoggingEvent event = makeLoggingEvent(null); String result = converter.convert(event); @@ -390,16 +366,55 @@ public void contextNameConverter() { @Test public void contextProperty() { PropertyConverter converter = new PropertyConverter(); - converter.setContext(lc); - List ol = new ArrayList(); - ol.add("k"); - converter.setOptionList(ol); + converter.setContext(loggerContext); + converter.setOptionList(List.of("k")); converter.start(); - lc.setName("aValue"); - lc.putProperty("k", "v"); + loggerContext.setName("aValue"); + loggerContext.putProperty("k", "v"); ILoggingEvent event = makeLoggingEvent(null); String result = converter.convert(event); assertEquals("v", result); } + + @Test + public void testSequenceNumber() { + //lc.setSequenceNumberGenerator(new BasicSequenceNumberGenerator()); + SequenceNumberConverter converter = new SequenceNumberConverter(); + converter.setContext(loggerContext); + converter.start(); + + assertTrue(converter.isStarted()); + LoggingEvent event = makeLoggingEvent(null); + + event.setSequenceNumber(123); + assertEquals("123", converter.convert(event)); + StatusPrinter.print(loggerContext); + } + + @Test + void dateConverterTest() { + // 2024-08-14T1Z:29:25,956 GMT + long millis = 1_723_649_365_956L; + dateConverterChecker(millis, List.of("STRICT", "GMT"), "2024-08-14T15:29:25,956"); + dateConverterChecker(millis, List.of("ISO8601", "GMT"), "2024-08-14 15:29:25,956"); + dateConverterChecker(millis, List.of("ISO8601", "UTC"), "2024-08-14 15:29:25,956"); + dateConverterChecker(millis, List.of("yyyy-MM-EE", "UTC", "fr-CH"), "2024-08-mer."); + + } + + void dateConverterChecker(long millis, List options, String expected) { + DateConverter dateConverter = new DateConverter(); + dateConverter.setOptionList(options) ; + dateConverter.setContext(loggerContext); + dateConverter.start(); + + assertTrue(dateConverter.isStarted()); + LoggingEvent event = makeLoggingEvent(null); + + Instant now = Instant.ofEpochMilli(millis); + event.setInstant(now); + String result = dateConverter.convert(event); + assertEquals(expected, result); + } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EnsureExceptionHandlingTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EnsureExceptionHandlingTest.java new file mode 100644 index 0000000000..9dbd79e2c8 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EnsureExceptionHandlingTest.java @@ -0,0 +1,62 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import org.junit.jupiter.api.BeforeEach; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.Test; + +public class EnsureExceptionHandlingTest { + + private PatternLayout pl = new PatternLayout(); + private LoggerContext lc = new LoggerContext(); + Logger logger = lc.getLogger(this.getClass()); + + static final String XTH = "xth"; + static final String XCC = "xcc"; + + @BeforeEach + public void setUp() { + pl.setContext(lc); + pl.getInstanceConverterMap().put(XTH, XThrowableHandlingConverter::new); + pl.getInstanceConverterMap().put(XCC, XCompositeConverter::new); + } + + ILoggingEvent makeLoggingEvent(String msg, Exception ex) { + return new LoggingEvent(EnsureExceptionHandlingTest.class.getName(), logger, Level.INFO, msg, ex, null); + } + + @Test + public void smoke() { + pl.setPattern("%m %" + XTH + ")"); + pl.start(); + ILoggingEvent le = makeLoggingEvent("assert", null); + pl.doLayout(le); + } + + @Test + public void withinComposite() { + pl.setPattern("%m %" + XCC + "(%" + XTH + ")"); + pl.start(); + ILoggingEvent le = makeLoggingEvent("assert", null); + pl.doLayout(le); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EpochConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EpochConverterTest.java new file mode 100644 index 0000000000..d929e126ac --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/EpochConverterTest.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Collections; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class EpochConverterTest { + EpochConverter ec = new EpochConverter(); + + @Test + public void withDefaultConfiguration() { + ec.setOptionList(null); + ec.start(); + LoggingEvent le = new LoggingEvent(); + Instant instant = Instant.parse("2026-01-15T10:15:30Z"); + instant = instant.plusMillis(321); + le.setInstant(instant); + + String result = ec.convert(le); + assertEquals("1768472130321", result); // includes millis + } + + @Test + public void withSecondsConfiguration() { + ec.setOptionList(Collections.singletonList("seconds")); + ec.start(); + LoggingEvent le = new LoggingEvent(); + Instant instant = Instant.parse("2026-01-15T10:15:30Z"); + instant = instant.plusMillis(321); // millis should be ignored + le.setInstant(instant); + + String result = ec.convert(le); + assertEquals("1768472130", result); + } + + @Test + public void withUnknownNonsenseConfiguration() { + ec.setOptionList(Collections.singletonList("nonsense")); + ec.start(); + LoggingEvent le = new LoggingEvent(); + Instant instant = Instant.parse("2026-01-15T10:15:30Z"); + instant = instant.plusMillis(321); + le.setInstant(instant); + + String result = ec.convert(le); + assertEquals("1768472130321", result); // includes millis, default behaviour + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExceptionalConverter2.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExceptionalConverter2.java new file mode 100644 index 0000000000..7d7976bb81 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExceptionalConverter2.java @@ -0,0 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.DynamicConverter; + +public class ExceptionalConverter2 extends DynamicConverter { + + public String convert(ILoggingEvent iLoggingEvent) { + if (!isStarted()) { + throw new IllegalStateException("this converter must be started before use"); + } + return ""; + } +} + diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java index e3a49440eb..0128701968 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ExtendedThrowableProxyConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,24 +13,24 @@ */ package ch.qos.logback.classic.pattern; -import static org.junit.Assert.assertEquals; -import static org.assertj.core.api.Assertions.assertThat; - -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.PatternLayout; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.util.EnvUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ExtendedThrowableProxyConverterTest { @@ -39,19 +39,20 @@ public class ExtendedThrowableProxyConverterTest { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - @Before + @BeforeEach public void setUp() throws Exception { lc.setPackagingDataEnabled(true); etpc.setContext(lc); etpc.start(); } - @After + @AfterEach public void tearDown() throws Exception { } private ILoggingEvent createLoggingEvent(Throwable t) { - return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", t, null); + return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, + "test message", t, null); } @Test @@ -85,13 +86,42 @@ public void nested() { verify(t); } + @Test + public void cyclicCause() { + // the identical formatting check, see verify(e) call below, fails + // under JDK 11. this does not mean that the presently tested code is wrong + // but that JDK 11 formats things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.initCause(e2); + verify(e); + } + + @Test + public void cyclicSuppressed() { + // the identical formatting check, see verify(e) call below, fails + // under JDK 11. this does not mean that the presently tested code is wrong + // but that JDK 11 formats things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.addSuppressed(e2); + verify(e); + } + void verify(Throwable t) { t.printStackTrace(pw); ILoggingEvent le = createLoggingEvent(t); String result = etpc.convert(le); result = result.replace("common frames omitted", "more"); - result = result.replaceAll(" ~?\\[.*\\]", ""); + // replace ~[something:other] with "" but not if it contains "CIRCULAR" + result = result.replaceAll(" ~?\\[(?!CIRCULAR).*\\]", ""); assertEquals(sw.toString(), result); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/KeyValuePairConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/KeyValuePairConverterTest.java new file mode 100644 index 0000000000..e7aed45237 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/KeyValuePairConverterTest.java @@ -0,0 +1,74 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.event.KeyValuePair; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class KeyValuePairConverterTest { + LoggerContext lc; + KeyValuePairConverter converter; + LoggingEvent event; + + @BeforeEach + public void setUp() throws Exception { + lc = new LoggerContext(); + converter = new KeyValuePairConverter(); + converter.start(); + event = createLoggingEvent(); + } + + @AfterEach + public void tearDown() throws Exception { + lc = null; + converter.stop(); + converter = null; + } + + @Test + public void smoke() { + event.addKeyValuePair(new KeyValuePair("a", "b")); + event.addKeyValuePair(new KeyValuePair("k", "v")); + String result = converter.convert(event); + assertEquals("a=\"b\" k=\"v\"", result); + } + + @Test + public void testWithNullKVPList() { + // event.getKeyValuePairs().add(new KeyValuePair("k", "v")); + String result = converter.convert(event); + assertEquals("", result); + } + + @Test + public void testWithOnelKVP() { + event.addKeyValuePair(new KeyValuePair("k", "v")); + String result = converter.convert(event); + assertEquals("k=\"v\"", result); + } + + private LoggingEvent createLoggingEvent() { + LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), + Level.DEBUG, "test message", null, null); + return le; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LegacyPatternLayoutTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LegacyPatternLayoutTest.java new file mode 100644 index 0000000000..33ef7a4c51 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LegacyPatternLayoutTest.java @@ -0,0 +1,74 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.CoreConstants; +import org.junit.jupiter.api.Test; + +import java.util.HashMap; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class LegacyPatternLayoutTest { + + LoggerContext context = new LoggerContext(); + + /** + * Test backward compatibility for classes derived from + * PatternLayout that add custom conversion words. + */ + @Test public void subPattern() { + SubPatternLayout layout = new SubPatternLayout(); + layout.setPattern("%"+SubPatternLayout.DOOO); + layout.setContext(context); + layout.start(); + LoggingEvent event = new LoggingEvent(); + event.setTimeStamp(0); + event.setLevel(Level.INFO); + + String result = layout.doLayout(event); + assertEquals("INFO", result); + } + + @Test + public void fromContext() { + Map registry = (Map) this.context + .getObject(CoreConstants.PATTERN_RULE_REGISTRY); + // + assertNull(registry); + if(registry == null) { + registry = new HashMap(); + this.context.putObject(CoreConstants.PATTERN_RULE_REGISTRY, registry); + } + + registry.put("legacy", LevelConverter.class.getName()); + + PatternLayout patternLayout = new PatternLayout(); + patternLayout.setPattern("%legacy"); + patternLayout.setContext(context); + patternLayout.start(); + LoggingEvent event = new LoggingEvent(); + event.setLevel(Level.WARN); + String result = patternLayout.doLayout(event); + assertEquals("WARN", result); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameConverterPerfTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameConverterPerfTest.java new file mode 100644 index 0000000000..180879859c --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameConverterPerfTest.java @@ -0,0 +1,129 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.testUtil.Gaussian; +import ch.qos.logback.core.status.OnConsoleStatusListener; + +@Disabled +public class LoggerNameConverterPerfTest { + + static final String NAMES_FILE = ClassicTestConstants.INPUT_PREFIX + "fqcn.txt"; + + static List NAMES_LIST; + + static int SIZE; + static double MEAN; + static double DEVIATION; + static Gaussian G; + + LoggerContext loggerContext = new LoggerContext(); + LoggerConverter loggerConverter = new LoggerConverter(); + + LoggerNameOnlyLoggingEvent event = new LoggerNameOnlyLoggingEvent(); + + Logger logger = LoggerFactory.getLogger(this.getClass()); + + @BeforeAll + static public void loadClassNames() throws IOException { + + NAMES_LIST = Files.lines(Paths.get(NAMES_FILE)).collect(Collectors.toList()); + + SIZE = NAMES_LIST.size(); + MEAN = SIZE / 2; + DEVIATION = MEAN / 8; + G = new Gaussian(MEAN, DEVIATION); + System.out.println("names list size=" + SIZE); + } + + @BeforeEach + public void setUp() { + OnConsoleStatusListener ocsl = new OnConsoleStatusListener(); + ocsl.setContext(loggerContext); + ocsl.start(); + loggerContext.getStatusManager().add(ocsl); + loggerConverter.setOptionList(Arrays.asList("30")); + loggerConverter.setContext(loggerContext); + loggerConverter.start(); + } + + @AfterEach + public void tearDown() { + + } + + @Test + public void measureAbbreviationPerf() { + for (int i = 0; i < 10 * 1000; i++) { + performAbbreviation(); + } + for (int i = 0; i < 10 * 1000; i++) { + performAbbreviation(); + } + final int runLength = 1000 * 1000; + System.out.println("Start measurements"); + long start = System.nanoTime(); + for (int i = 0; i < runLength; i++) { + performAbbreviation(); + } + long end = System.nanoTime(); + long diff = end - start; + double average = diff * 1.0D / runLength; + logger.atInfo().addArgument(average).log("Average = {} nanos"); + int cacheMisses = loggerConverter.getCacheMisses(); + + logger.atInfo().addArgument(cacheMisses).log("cacheMisses = {} "); + logger.atInfo().addArgument(runLength).log("total calls= = {} "); + + double cacheMissRate = loggerConverter.getCacheMissRate() * 100; + logger.atInfo().addArgument(cacheMissRate).log("cacheMiss rate %= {} "); + + } + + public void performAbbreviation() { + String fqn = getFQN(); + event.setLoggerName(fqn); + loggerConverter.convert(event); + } + + private String getFQN() { + while (true) { + int index = (int) G.getGaussian(); + if (index >= 0 && index < SIZE) { + return NAMES_LIST.get(index); + } else { + continue; + } + } + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameOnlyLoggingEvent.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameOnlyLoggingEvent.java new file mode 100644 index 0000000000..9571d686db --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/LoggerNameOnlyLoggingEvent.java @@ -0,0 +1,135 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import java.util.List; +import java.util.Map; + +import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.LoggerContextVO; + +public class LoggerNameOnlyLoggingEvent implements ILoggingEvent { + + String loggerName = ""; + + @Override + public String getThreadName() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Level getLevel() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getMessage() { + // TODO Auto-generated method stub + return null; + } + + @Override + public String getLoggerName() { + return loggerName; + } + + public void setLoggerName(String loggerName) { + this.loggerName = loggerName; + } + + @Override + public LoggerContextVO getLoggerContextVO() { + // TODO Auto-generated method stub + return null; + } + + @Override + public IThrowableProxy getThrowableProxy() { + // TODO Auto-generated method stub + return null; + } + + @Override + public StackTraceElement[] getCallerData() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean hasCallerData() { + // TODO Auto-generated method stub + return false; + } + + @Override + public List getMarkerList() { + return null; + } + + @Override + public Map getMDCPropertyMap() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map getMdc() { + return null; + } + + @Override + public long getTimeStamp() { + return 0; + } + + @Override + public int getNanoseconds() { + return 0; + } + + @Override + public long getSequenceNumber() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void prepareForDeferredProcessing() { + // TODO Auto-generated method stub + } + + @Override + public Object[] getArgumentArray() { + return null; + } + + @Override + public String getFormattedMessage() { + return null; + } + + @Override + public List getKeyValuePairs() { + return null; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MDCConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MDCConverterTest.java index 8c28c19b87..859dd1c596 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MDCConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MDCConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,41 +13,41 @@ */ package ch.qos.logback.classic.pattern; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MDCConverterTest { - LoggerContext lc; + LoggerContext loggerContext; + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); MDCConverter converter; int diff = RandomUtil.getPositiveInt(); - @Before + @BeforeEach public void setUp() throws Exception { - lc = new LoggerContext(); + loggerContext = new LoggerContext(); + loggerContext.setMDCAdapter(logbackMDCAdapter); converter = new MDCConverter(); converter.start(); - MDC.clear(); } - @After + @AfterEach public void tearDown() throws Exception { - lc = null; + loggerContext = null; converter.stop(); converter = null; - MDC.clear(); } @Test @@ -55,7 +55,7 @@ public void testConvertWithOneEntry() { String k = "MDCConverterTest_k" + diff; String v = "MDCConverterTest_v" + diff; - MDC.put(k, v); + logbackMDCAdapter.put(k, v); ILoggingEvent le = createLoggingEvent(); String result = converter.convert(le); assertEquals(k + "=" + v, result); @@ -63,15 +63,16 @@ public void testConvertWithOneEntry() { @Test public void testConvertWithMultipleEntries() { - MDC.put("testKey", "testValue"); - MDC.put("testKey2", "testValue2"); + logbackMDCAdapter.put("testKey", "testValue"); + logbackMDCAdapter.put("testKey2", "testValue2"); ILoggingEvent le = createLoggingEvent(); String result = converter.convert(le); boolean isConform = result.matches("testKey2?=testValue2?, testKey2?=testValue2?"); - assertTrue(result + " is not conform", isConform); + assertTrue( isConform, result + " is not conform"); } private ILoggingEvent createLoggingEvent() { - return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", null, null); + return new LoggingEvent(this.getClass().getName(), loggerContext.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, + "test message", null, null); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MarkerConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MarkerConverterTest.java index 3b8ff80b30..1fb33757f8 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MarkerConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MarkerConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,36 +13,35 @@ */ package ch.qos.logback.classic.pattern; -import static org.junit.Assert.assertEquals; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.IMarkerFactory; -import org.slf4j.Marker; -import org.slf4j.helpers.BasicMarkerFactory; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.IMarkerFactory; +import org.slf4j.Marker; +import org.slf4j.helpers.BasicMarkerFactory; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class MarkerConverterTest { LoggerContext lc; MarkerConverter converter; - // use a different facotry for each test so that they are independent + // use a different factory for each test so that they are independent IMarkerFactory markerFactory = new BasicMarkerFactory(); - @Before + @BeforeEach public void setUp() throws Exception { lc = new LoggerContext(); converter = new MarkerConverter(); converter.start(); } - @After + @AfterEach public void tearDown() throws Exception { lc = null; converter.stop(); @@ -86,8 +85,9 @@ public void testWithSeveralChildMarker() { } private ILoggingEvent createLoggingEvent(Marker marker) { - LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", null, null); - le.setMarker(marker); + LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), + Level.DEBUG, "test message", null, null); + le.addMarker(marker); return le; } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverterTest.java new file mode 100644 index 0000000000..d10b5c332d --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MaskedKeyValuePairConverterTest.java @@ -0,0 +1,114 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.event.KeyValuePair; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MaskedKeyValuePairConverterTest { + + LoggerContext lc = new LoggerContext(); + MaskedKeyValuePairConverter converter; + LoggingEvent event; + + StatusChecker statusChecker = new StatusChecker(lc); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + + @BeforeEach + public void setUp() throws Exception { + converter = new MaskedKeyValuePairConverter(); + converter.setContext(lc); + } + + @AfterEach + public void tearDown() throws Exception { + lc = null; + converter.stop(); + converter = null; + } + + @Test + public void smoke() { + event = createLoggingEvent(); + converter.setOptionList(List.of("k1")); + converter.start(); + + event.addKeyValuePair(new KeyValuePair("k1", "v1")); + event.addKeyValuePair(new KeyValuePair("k2", "v2")); + + String result = converter.convert(event); + assertEquals("k1=\""+MaskedKeyValuePairConverter.MASK+"\" k2=\"v2\"", result); + } + + @Test + public void smokeSingle() { + event = createLoggingEvent(); + converter.setOptionList(List.of("SINGLE", "k1")); + converter.start(); + + event.addKeyValuePair(new KeyValuePair("k1", "v1")); + event.addKeyValuePair(new KeyValuePair("k2", "v2")); + + String result = converter.convert(event); + assertEquals("k1='"+MaskedKeyValuePairConverter.MASK+"' k2='v2'", result); + } + + @Test + public void wrongOrder() { + event = createLoggingEvent(); + converter.setOptionList(List.of("k1", "SINGLE")); + converter.start(); + + event.addKeyValuePair(new KeyValuePair("k1", "v1")); + event.addKeyValuePair(new KeyValuePair("k2", "v2")); + + statusPrinter2.print(lc); + statusChecker.assertContainsMatch(Status.WARN, "extra quote spec SINGLE found in the wrong order"); + String result = converter.convert(event); + assertEquals("k1=\""+MaskedKeyValuePairConverter.MASK+"\" k2=\"v2\"", result); + } + + @Test + public void testWithOnelKVP() { + event = createLoggingEvent(); + converter.setOptionList(List.of("k")); + converter.start(); + event.addKeyValuePair(new KeyValuePair("k", "v")); + String result = converter.convert(event); + assertEquals("k=\""+MaskedKeyValuePairConverter.MASK+"\"", result); + } + + + + private LoggingEvent createLoggingEvent() { + LoggingEvent le = new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), + Level.DEBUG, "test message", null, null); + return le; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MicrosecondConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MicrosecondConverterTest.java new file mode 100644 index 0000000000..b38e92c0e2 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/MicrosecondConverterTest.java @@ -0,0 +1,40 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.LoggingEvent; +import org.junit.jupiter.api.Test; + +import java.time.Instant; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class MicrosecondConverterTest { + + MicrosecondConverter mc = new MicrosecondConverter(); + public long timeStamp; + public int nanoseconds; + + @Test + public void smoke() { + LoggingEvent le = new LoggingEvent(); + Instant instant = Instant.parse("2011-12-03T10:15:30Z"); + instant = instant.plusNanos(123_456_789); + le.setInstant(instant); + + String result = mc.convert(le); + assertEquals("456", result); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java deleted file mode 100644 index 90547f2b91..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.pattern; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ConverterTest.class, TargetLengthBasedClassNameAbbreviatorTest.class, MDCConverterTest.class, MarkerConverterTest.class, - ExtendedThrowableProxyConverterTest.class, ThrowableProxyConverterTest.class, RootCauseFirstThrowableProxyConverterTest.class }) -public class PackageTest { - -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java index 05497b66cb..1806e135b1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/RootCauseFirstThrowableProxyConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.PrintWriter; import java.io.StringWriter; @@ -43,14 +43,15 @@ public class RootCauseFirstThrowableProxyConverterTest { private StringWriter stringWriter = new StringWriter(); private PrintWriter printWriter = new PrintWriter(stringWriter); - @Before + @BeforeEach public void setUp() throws Exception { converter.setContext(context); converter.start(); } private ILoggingEvent createLoggingEvent(Throwable t) { - return new LoggingEvent(this.getClass().getName(), context.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", t, null); + return new LoggingEvent(this.getClass().getName(), context.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, + "test message", t, null); } @Test @@ -109,4 +110,30 @@ public void nested() { assertThat(positionOf("nesting level =1").in(result)).isLessThan(positionOf("nesting level =2").in(result)); } + @Test + public void cyclicCause() { + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.initCause(e2); + ILoggingEvent le = createLoggingEvent(e); + String result = converter.convert(le); + + assertThat(result).startsWith("[CIRCULAR REFERENCE: java.lang.Exception: foo]"); + } + + @Test + public void cyclicSuppressed() { + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.addSuppressed(e2); + ILoggingEvent le = createLoggingEvent(e); + String result = converter.convert(le); + + assertThat(result).startsWith("java.lang.Exception: foo"); + String circular = "Suppressed: [CIRCULAR REFERENCE: java.lang.Exception: foo]"; + String wrapped = "Wrapped by: java.lang.Exception: java.lang.Exception: foo"; + + assertThat(positionOf(circular).in(result)).isLessThan(positionOf(wrapped).in(result)); + } + } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/SampleConverter.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SampleConverter.java similarity index 80% rename from logback-classic/src/test/java/ch/qos/logback/classic/testUtil/SampleConverter.java rename to logback-classic/src/test/java/ch/qos/logback/classic/pattern/SampleConverter.java index 3f4baa555a..eed6344499 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/SampleConverter.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SampleConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.testUtil; +package ch.qos.logback.classic.pattern; import ch.qos.logback.classic.pattern.ClassicConverter; import ch.qos.logback.classic.spi.ILoggingEvent; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SubPatternLayout.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SubPatternLayout.java new file mode 100644 index 0000000000..9c872b0481 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SubPatternLayout.java @@ -0,0 +1,35 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.PatternLayout; + +import java.util.Map; + +/** + * Test backward compatibility support by virtue of correct compilation. + * + * See also SubPatternLayoutTest + */ +public class SubPatternLayout extends PatternLayout { + + static String DOOO = "dooo"; + + SubPatternLayout() { + Map defaultConverterMap = getDefaultConverterMap(); + defaultConverterMap.put(DOOO, LevelConverter.class.getName()); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SyslogStartConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SyslogStartConverterTest.java index 7ca926d5b4..b443ad62d0 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SyslogStartConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/SyslogStartConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,10 +17,10 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.LoggingEvent; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.net.InetAddress; import java.net.UnknownHostException; @@ -28,7 +28,7 @@ import java.util.Calendar; import java.util.Locale; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SyslogStartConverterTest { @@ -37,7 +37,7 @@ public class SyslogStartConverterTest { private final String HOSTNAME = findHostname(); private final Calendar calendar = Calendar.getInstance(Locale.US); - @Before + @BeforeEach public void setUp() throws Exception { lc = new LoggerContext(); converter = new SyslogStartConverter(); @@ -45,7 +45,7 @@ public void setUp() throws Exception { converter.start(); } - @After + @AfterEach public void tearDown() throws Exception { lc = null; converter.stop(); @@ -57,7 +57,7 @@ public void datesLessThanTen() { // RFC 3164, section 4.1.2: // If the day of the month is less than 10, then it MUST be represented as // a space and then the number. For example, the 7th day of August would be - // represented as "Aug 7", with two spaces between the "g" and the "7". + // represented as "Aug 7", with two spaces between the "g" and the "7". LoggingEvent le = createLoggingEvent(); calendar.set(2012, Calendar.AUGUST, 7, 13, 15, 0); le.setTimeStamp(calendar.getTimeInMillis()); @@ -104,7 +104,7 @@ public void ignoreDefaultLocale() { } @Test - @Ignore + @Disabled public void hostnameShouldNotIncludeDomain() throws Exception { // RFC 3164, section 4.1.2: // The Domain Name MUST NOT be included in the HOSTNAME field. @@ -120,7 +120,8 @@ public void hostnameShouldNotIncludeDomain() throws Exception { } private LoggingEvent createLoggingEvent() { - return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", null, null); + return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, + "test message", null, null); } private static String findHostname() { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java index c1a30334f5..ae59553dbc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/TargetLengthBasedClassNameAbbreviatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,9 @@ */ package ch.qos.logback.classic.pattern; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; -import org.junit.Test; - -import ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator; +import static org.junit.jupiter.api.Assertions.assertEquals; public class TargetLengthBasedClassNameAbbreviatorTest { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java index a00f3ec727..c0e140f62e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/ThrowableProxyConverterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,18 @@ */ package ch.qos.logback.classic.pattern; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.TestHelper; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.EnvUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + import java.io.BufferedReader; import java.io.PrintWriter; import java.io.StringReader; @@ -21,22 +33,11 @@ import java.util.Arrays; import java.util.List; -import ch.qos.logback.core.CoreConstants; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.spi.ILoggingEvent; -import ch.qos.logback.classic.spi.LoggingEvent; -import ch.qos.logback.classic.util.TestHelper; - -import static ch.qos.logback.classic.util.TestHelper.addSuppressed; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.*; -import static org.junit.Assume.assumeTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ThrowableProxyConverterTest { @@ -45,32 +46,31 @@ public class ThrowableProxyConverterTest { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - @Before + @BeforeEach public void setUp() throws Exception { tpc.setContext(lc); tpc.start(); } - @After + @AfterEach public void tearDown() throws Exception { } private ILoggingEvent createLoggingEvent(Throwable t) { - return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, "test message", t, null); + return new LoggingEvent(this.getClass().getName(), lc.getLogger(Logger.ROOT_LOGGER_NAME), Level.DEBUG, + "test message", t, null); } @Test public void suppressed() throws InvocationTargetException, IllegalAccessException { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. Exception ex = null; try { someMethod(); } catch (Exception e) { Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(e, fooException); - addSuppressed(e, barException); + e.addSuppressed(fooException); + e.addSuppressed(barException); ex = e; } verify(ex); @@ -78,8 +78,6 @@ public void suppressed() throws InvocationTargetException, IllegalAccessExceptio @Test public void suppressedWithCause() throws InvocationTargetException, IllegalAccessException { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. Exception ex = null; try { someMethod(); @@ -87,16 +85,14 @@ public void suppressedWithCause() throws InvocationTargetException, IllegalAcces ex = new Exception("Wrapper", e); Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(ex, fooException); - addSuppressed(e, barException); + e.addSuppressed(fooException); + e.addSuppressed(barException); } verify(ex); } @Test public void suppressedWithSuppressed() throws Exception { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. Exception ex = null; try { someMethod(); @@ -104,8 +100,8 @@ public void suppressedWithSuppressed() throws Exception { ex = new Exception("Wrapper", e); Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(barException, fooException); - addSuppressed(e, barException); + barException.addSuppressed(fooException); + e.addSuppressed(barException); } verify(ex); } @@ -122,6 +118,29 @@ public void nested() { verify(t); } + @Test + public void cyclicCause() { + // Earlier JDKs may format things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.initCause(e2); + verify(e); + } + + @Test + public void cyclicSuppressed() { + // Earlier JDKs may format things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.addSuppressed(e2); + verify(e); + } + @Test public void withArgumentOfOne() throws Exception { final Throwable t = TestHelper.makeNestedException(0); @@ -134,10 +153,11 @@ public void withArgumentOfOne() throws Exception { final String result = tpc.convert(le); + System.out.println(result); final BufferedReader reader = new BufferedReader(new StringReader(result)); assertTrue(reader.readLine().contains(t.getMessage())); assertNotNull(reader.readLine()); - assertNull("Unexpected line in stack trace", reader.readLine()); + assertNull(reader.readLine(), "Unexpected line in stack trace"); } @Test @@ -155,7 +175,7 @@ public void withShortArgument() throws Exception { final BufferedReader reader = new BufferedReader(new StringReader(result)); assertTrue(reader.readLine().contains(t.getMessage())); assertNotNull(reader.readLine()); - assertNull("Unexpected line in stack trace", reader.readLine()); + assertNull(reader.readLine(), "Unexpected line in stack trace"); } @Test @@ -173,7 +193,7 @@ public void skipSelectedLine() throws Exception { // then assertThat(result).doesNotContain(nameOfContainingMethod); - + } @Test @@ -219,7 +239,7 @@ void verify(Throwable t) { ILoggingEvent le = createLoggingEvent(t); String result = tpc.convert(le); - System.out.println(result); + // System.out.println(result); result = result.replace("common frames omitted", "more"); assertEquals(sw.toString(), result); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XCompositeConverter.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XCompositeConverter.java new file mode 100644 index 0000000000..8767c123cc --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XCompositeConverter.java @@ -0,0 +1,35 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.pattern.CompositeConverter; + +import static org.junit.jupiter.api.Assertions.assertNull; + +public class XCompositeConverter extends CompositeConverter { + + void assertNoNext() { + assertNull( this.getNext(), "converter instance has next element"); + } + + @Override + protected String transform(ILoggingEvent event, String in) { + if (event.getMessage().contains("assert")) + assertNoNext(); + return ""; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XThrowableHandlingConverter.java b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XThrowableHandlingConverter.java new file mode 100644 index 0000000000..239b69f5c5 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/pattern/XThrowableHandlingConverter.java @@ -0,0 +1,34 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.pattern; + +import ch.qos.logback.classic.spi.ILoggingEvent; + +import static org.junit.jupiter.api.Assertions.assertNull; + +public class XThrowableHandlingConverter extends ThrowableHandlingConverter { + + void assertNoNext() { + assertNull(this.getNext(), "has next"); + } + + @Override + public String convert(ILoggingEvent event) { + if (event.getMessage().contains("assert")) + assertNoNext(); + return ""; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/rolling/PackageTest.java deleted file mode 100644 index 041106b117..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.rolling; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ UniqueFileTest.class, TimeBasedRollingWithConfigFileTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/TimeBasedRollingWithConfigFileTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/rolling/TimeBasedRollingWithConfigFileTest.java index 865d641f70..457e9193c6 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/TimeBasedRollingWithConfigFileTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/rolling/TimeBasedRollingWithConfigFileTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,54 +13,57 @@ */ package ch.qos.logback.classic.rolling; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import java.util.Date; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - import ch.qos.logback.classic.ClassicTestConstants; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.rolling.RollingFileAppender; import ch.qos.logback.core.rolling.TimeBasedFileNamingAndTriggeringPolicy; import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; +import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests; import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Date; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class TimeBasedRollingWithConfigFileTest extends ScaffoldingForRollingTests { - LoggerContext lc = new LoggerContext(); - StatusChecker statusChecker = new StatusChecker(lc); - Logger logger = lc.getLogger(this.getClass()); + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + StatusChecker statusChecker = new StatusChecker(loggerContext); + Logger logger = loggerContext.getLogger(this.getClass()); int fileSize = 0; int fileIndexCounter = -1; int sizeThreshold; - @Before + @BeforeEach @Override public void setUp() { - lc.setName("test"); + loggerContext.setName("test"); + loggerContext.setMDCAdapter(logbackMDCAdapter); super.setUp(); - lc.putProperty("randomOutputDir", randomOutputDir); + loggerContext.putProperty("randomOutputDir", randomOutputDir); } - @After + @AfterEach public void tearDown() throws Exception { } void loadConfig(String confifFile) throws JoranException { JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(lc); + jc.setContext(loggerContext); jc.doConfigure(confifFile); currentTime = System.currentTimeMillis(); recomputeRolloverThreshold(currentTime); @@ -69,11 +72,11 @@ void loadConfig(String confifFile) throws JoranException { @Test public void basic() throws Exception { String testId = "basic"; - lc.putProperty("testId", testId); + loggerContext.putProperty("testId", testId); loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); statusChecker.assertIsErrorFree(); - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); expectedFilenameList.add(randomOutputDir + "z" + testId); @@ -98,30 +101,31 @@ public void basic() throws Exception { @Test public void depratedSizeAndTimeBasedFNATPWarning() throws Exception { String testId = "depratedSizeAndTimeBasedFNATPWarning"; - lc.putProperty("testId", testId); + loggerContext.putProperty("testId", testId); loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); - StatusPrinter.print(lc); + StatusPrinter.print(loggerContext); statusChecker.assertContainsMatch(Status.WARN, CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); } - + @Test public void timeAndSize() throws Exception { String testId = "timeAndSize"; - lc.putProperty("testId", testId); + loggerContext.putProperty("testId", testId); String prefix = "Hello-----"; // the number of times the log file will be written to before time based // roll-over occurs int approxWritesPerPeriod = 64; sizeThreshold = prefix.length() * approxWritesPerPeriod; - lc.putProperty("sizeThreshold", "" + sizeThreshold); + loggerContext.putProperty("sizeThreshold", "" + sizeThreshold); + System.out.println("timeAndSize.sizeThreshold="+sizeThreshold); loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); - - StatusPrinter.print(lc); + + StatusPrinter.print(loggerContext); // Test http://jira.qos.ch/browse/LOGBACK-1236 statusChecker.assertNoMatch(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); - - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); + + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); expectedFilenameList.add(randomOutputDir + "z" + testId); @@ -133,7 +137,8 @@ public void timeAndSize() throws Exception { TimeBasedFileNamingAndTriggeringPolicy tbnatp = tprp.getTimeBasedFileNamingAndTriggeringPolicy(); int timeIncrement = 1000 / approxWritesPerPeriod; - int runLength = approxWritesPerPeriod * 3; + int targetPeriodCount = 3; + int runLength = approxWritesPerPeriod * targetPeriodCount; for (int i = 0; i < runLength; i++) { String msg = prefix + i; logger.debug(msg); @@ -147,51 +152,51 @@ public void timeAndSize() throws Exception { // for various reasons, it is extremely difficult to have the files // match exactly the expected archive files. Thus, we aim for // an approximate match - assertTrue("exitenceCount=" + eCount + ", expectedFilenameList.size=" + expectedFilenameList.size(), - eCount >= 4 && eCount > expectedFilenameList.size() / 2); + assertTrue(eCount >= targetPeriodCount || eCount >= expectedFilenameList.size() / 2, + "existenceCount=" + eCount + ", expectedFilenameList.size=" + expectedFilenameList.size()); } @Test public void timeAndSizeWithoutIntegerToken() throws Exception { String testId = "timeAndSizeWithoutIntegerToken"; loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); expectedFilenameList.add(randomOutputDir + "z" + testId); RollingFileAppender rfa = (RollingFileAppender) root.getAppender("ROLLING"); - StatusPrinter.print(lc); + StatusPrinter.print(loggerContext); statusChecker.assertContainsMatch("Missing integer token"); assertFalse(rfa.isStarted()); } - // see also LOGBACK-1176 @Test public void timeAndSizeWithoutMaxFileSize() throws Exception { String testId = "timeAndSizeWithoutMaxFileSize"; loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); - //expectedFilenameList.add(randomOutputDir + "z" + testId); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + // expectedFilenameList.add(randomOutputDir + "z" + testId); RollingFileAppender rfa = (RollingFileAppender) root.getAppender("ROLLING"); - - //statusChecker.assertContainsMatch("Missing integer token"); + // statusChecker.assertContainsMatch("Missing integer token"); assertFalse(rfa.isStarted()); - StatusPrinter.print(lc); + StatusPrinter.print(loggerContext); } @Test public void totalSizeCapSmallerThanMaxFileSize() throws Exception { String testId = "totalSizeCapSmallerThanMaxFileSize"; - lc.putProperty("testId", testId); + loggerContext.putProperty("testId", testId); loadConfig(ClassicTestConstants.JORAN_INPUT_PREFIX + "rolling/" + testId + ".xml"); - Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); - //expectedFilenameList.add(randomOutputDir + "z" + testId); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + // expectedFilenameList.add(randomOutputDir + "z" + testId); RollingFileAppender rfa = (RollingFileAppender) root.getAppender("ROLLING"); - - statusChecker.assertContainsMatch("totalSizeCap of \\[\\d* \\w*\\] is smaller than maxFileSize \\[\\d* \\w*\\] which is non-sensical"); - assertFalse(rfa.isStarted()); - + + // totalSizeCap of [10 Bytes] is much smaller than maxFileSize [250 Bytes] which is non-sensical, even taking compression into account. + statusChecker.assertContainsMatch(Status.WARN, + "totalSizeCap of \\[\\d* \\w*\\] is much smaller than maxFileSize \\[\\d* \\w*\\] which is non-sensical, even taking compression into account."); + assertTrue(rfa.isStarted()); + } void addExpectedFileNamedIfItsTime(String testId, String msg, boolean gzExtension) { @@ -217,10 +222,10 @@ void addExpectedFileNamedIfItsTime(String testId, String msg, boolean gzExtensio void addExpectedFileName(String testId, Date date, int fileIndexCounter, boolean gzExtension) { String fn = randomOutputDir + testId + "-" + SDF.format(date) + "." + fileIndexCounter; - System.out.println("Adding " + fn); if (gzExtension) { fn += ".gz"; } + System.out.println("Adding " + fn); expectedFilenameList.add(fn); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/UniqueFileTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/rolling/UniqueFileTest.java index bdbd062014..62f8121957 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/rolling/UniqueFileTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/rolling/UniqueFileTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,9 @@ */ package ch.qos.logback.classic.rolling; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.ClassicTestConstants; import ch.qos.logback.classic.Logger; @@ -25,12 +25,12 @@ import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.CachingDateFormatter; /** - * Test that we can create time-stamped log files with the help of - * the <timestamp> element in configuration files. + * Test that we can create time-stamped log files with the help of the + * <timestamp> element in configuration files. * * @author Ceki Gülcü * @@ -44,12 +44,12 @@ public class UniqueFileTest { int diff = RandomUtil.getPositiveInt() % 1000; String diffAsStr = Integer.toString(diff); - @Before + @BeforeEach public void setUp() { System.setProperty(UNIK_DIFF, diffAsStr); } - @After + @AfterEach public void tearDown() { System.clearProperty(UNIK_DIFF); } @@ -71,6 +71,7 @@ public void basic() throws Exception { Logger root = lc.getLogger(Logger.ROOT_LOGGER_NAME); root.info("hello"); - ScaffoldingForRollingTests.existenceCheck(CoreTestConstants.OUTPUT_DIR_PREFIX + "UNIK_" + timestamp + diffAsStr + "log.txt"); + ScaffoldingForRollingTests + .existenceCheck(CoreTestConstants.OUTPUT_DIR_PREFIX + "UNIK_" + timestamp + diffAsStr + "log.txt"); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextDetachingSCLTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextDetachingSCLTest.java index 09c19bd6c7..b70399ea64 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextDetachingSCLTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextDetachingSCLTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,27 +13,28 @@ */ package ch.qos.logback.classic.selector; -import static org.junit.Assert.assertEquals; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.LoggerFactory; -import org.slf4j.LoggerFactoryFriend; - import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.selector.servlet.ContextDetachingSCL; import ch.qos.logback.classic.util.ContextSelectorStaticBinder; -import ch.qos.logback.classic.util.MockInitialContext; -import ch.qos.logback.classic.util.MockInitialContextFactory; +import ch.qos.logback.core.testUtil.MockInitialContext; +import ch.qos.logback.core.testUtil.MockInitialContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.slf4j.LoggerFactoryFriend; + +import static org.junit.jupiter.api.Assertions.assertEquals; +@Disabled public class ContextDetachingSCLTest { static String INITIAL_CONTEXT_KEY = "java.naming.factory.initial"; ContextDetachingSCL contextDetachingSCL; - @Before + @BeforeEach public void setUp() throws Exception { System.setProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR, "JNDI"); @@ -44,26 +45,29 @@ public void setUp() throws Exception { MockInitialContext mic = MockInitialContextFactory.getContext(); mic.map.put(ClassicConstants.JNDI_CONTEXT_NAME, "toto"); - // The property must be set after we setup the Mock + // The property must be set after we set up the Mock System.setProperty(INITIAL_CONTEXT_KEY, MockInitialContextFactory.class.getName()); - // reinitialize the LoggerFactory, These reset methods are reserved for internal use + // reinitialize the LoggerFactory, These reset methods are reserved for internal + // use LoggerFactoryFriend.reset(); // this call will create the context "toto" LoggerFactory.getLogger(ContextDetachingSCLTest.class); } - @After + @AfterEach public void tearDown() throws Exception { System.clearProperty(INITIAL_CONTEXT_KEY); - // reinitialize the LoggerFactory, These resets method are reserved for internal use + // reinitialize the LoggerFactory, These resets method are reserved for internal + // use LoggerFactoryFriend.reset(); } @Test public void testDetach() { - ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton().getContextSelector(); + ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton() + .getContextSelector(); contextDetachingSCL.contextDestroyed(null); assertEquals(0, selector.getCount()); } @@ -72,7 +76,8 @@ public void testDetach() { public void testDetachWithMissingContext() { MockInitialContext mic = MockInitialContextFactory.getContext(); mic.map.put(ClassicConstants.JNDI_CONTEXT_NAME, "tata"); - ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton().getContextSelector(); + ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton() + .getContextSelector(); assertEquals("tata", selector.getLoggerContext().getName()); mic.map.put(ClassicConstants.JNDI_CONTEXT_NAME, "titi"); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextJNDISelectorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextJNDISelectorTest.java index 0c7ce19254..65d2a56d0f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextJNDISelectorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/selector/ContextJNDISelectorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,25 +13,26 @@ */ package ch.qos.logback.classic.selector; -import static org.junit.Assert.assertEquals; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.LoggerFactory; -import org.slf4j.LoggerFactoryFriend; - import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.util.ContextSelectorStaticBinder; -import ch.qos.logback.classic.util.MockInitialContext; -import ch.qos.logback.classic.util.MockInitialContextFactory; import ch.qos.logback.core.Context; +import ch.qos.logback.core.testUtil.MockInitialContext; +import ch.qos.logback.core.testUtil.MockInitialContextFactory; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; +import org.slf4j.LoggerFactoryFriend; + +import static org.junit.jupiter.api.Assertions.assertEquals; +@Disabled public class ContextJNDISelectorTest { static String INITIAL_CONTEXT_KEY = "java.naming.factory.initial"; - @Before + @BeforeEach public void setUp() throws Exception { System.setProperty(ClassicConstants.LOGBACK_CONTEXT_SELECTOR, "JNDI"); @@ -41,14 +42,14 @@ public void setUp() throws Exception { MockInitialContext mic = MockInitialContextFactory.getContext(); mic.map.put(ClassicConstants.JNDI_CONTEXT_NAME, "toto"); - // The property must be set after we setup the Mock + // The property must be set after we set up the Mock System.setProperty(INITIAL_CONTEXT_KEY, MockInitialContextFactory.class.getName()); // this call will create the context "toto" LoggerFactory.getLogger(ContextDetachingSCLTest.class); } - @After + @AfterEach public void tearDown() throws Exception { System.clearProperty(INITIAL_CONTEXT_KEY); } @@ -67,7 +68,8 @@ public void testCreateContext() { LoggerFactory.getLogger(ContextDetachingSCLTest.class); - ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton().getContextSelector(); + ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton() + .getContextSelector(); Context context = selector.getLoggerContext(); assertEquals("tata", context.getName()); System.out.println(selector.getContextNames()); @@ -79,7 +81,8 @@ public void defaultContext() { MockInitialContext mic = MockInitialContextFactory.getContext(); mic.map.put(ClassicConstants.JNDI_CONTEXT_NAME, null); - ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton().getContextSelector(); + ContextJNDISelector selector = (ContextJNDISelector) ContextSelectorStaticBinder.getSingleton() + .getContextSelector(); Context context = selector.getLoggerContext(); assertEquals("default", context.getName()); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/selector/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/selector/PackageTest.java deleted file mode 100644 index 30571484ee..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/selector/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.selector; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ContextJNDISelectorTest.class, ContextDetachingSCLTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializerTest.java index 245f9578b3..f04e12083d 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/servlet/LogbackServletContainerInitializerTest.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.classic.servlet; import static org.mockito.ArgumentMatchers.any; @@ -6,24 +20,24 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; import ch.qos.logback.core.CoreConstants; +import org.junit.jupiter.api.Test; public class LogbackServletContainerInitializerTest { LogbackServletContainerInitializer lsci = new LogbackServletContainerInitializer(); - - @Before + + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } @@ -37,7 +51,8 @@ public void testOnStartup() throws ServletException { @Test public void noListenerShouldBeAddedWhenDisabled() throws ServletException { ServletContext mockedServletContext = mock(ServletContext.class); - when(mockedServletContext.getInitParameter(CoreConstants.DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY)).thenReturn("true"); + when(mockedServletContext.getInitParameter(CoreConstants.DISABLE_SERVLET_CONTAINER_INITIALIZER_KEY)) + .thenReturn("true"); lsci.onStartup(null, mockedServletContext); verify(mockedServletContext, times(0)).addListener(any(LogbackServletContextListener.class)); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/sift/MDCBasedDiscriminatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/sift/MDCBasedDiscriminatorTest.java index 8c8ad82611..6887005b71 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/sift/MDCBasedDiscriminatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/sift/MDCBasedDiscriminatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,19 +16,18 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; - import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.testUtil.RandomUtil; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import org.slf4j.MDC; import java.util.HashMap; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Ceki Gülcü @@ -38,32 +37,33 @@ public class MDCBasedDiscriminatorTest { static String DEFAULT_VAL = "DEFAULT_VAL"; MDCBasedDiscriminator discriminator = new MDCBasedDiscriminator(); - LoggerContext context = new LoggerContext(); - Logger logger = context.getLogger(this.getClass()); + LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); + Logger logger = loggerContext.getLogger(this.getClass()); int diff = RandomUtil.getPositiveInt(); String key = "MDCBasedDiscriminatorTest_key" + diff; String value = "MDCBasedDiscriminatorTest_val" + diff; LoggingEvent event; - @Before + @BeforeEach public void setUp() { - MDC.clear(); - discriminator.setContext(context); + loggerContext.setMDCAdapter(logbackMDCAdapter); + discriminator.setContext(loggerContext); discriminator.setKey(key); discriminator.setDefaultValue(DEFAULT_VAL); discriminator.start(); assertTrue(discriminator.isStarted()); } - @After + @AfterEach public void teaDown() { MDC.clear(); } @Test public void smoke() { - MDC.put(key, value); + logbackMDCAdapter.put(key, value); event = new LoggingEvent("a", logger, Level.DEBUG, "", null, null); String discriminatorValue = discriminator.getDiscriminatingValue(event); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/sift/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/sift/PackageTest.java deleted file mode 100644 index b8cd386757..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/sift/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.sift; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ MDCBasedDiscriminatorTest.class, SiftingAppenderTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/sift/SiftingAppenderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/sift/SiftingAppenderTest.java old mode 100755 new mode 100644 index 908eb3cbfc..2cb8458a10 --- a/logback-classic/src/test/java/ch/qos/logback/classic/sift/SiftingAppenderTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/sift/SiftingAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,22 +13,6 @@ */ package ch.qos.logback.classic.sift; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static ch.qos.logback.core.testUtil.CoreTestConstants.OUTPUT_DIR_PREFIX; -import static org.assertj.core.api.Assertions.*; - -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.slf4j.MDC; - import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.ClassicTestConstants; import ch.qos.logback.classic.Level; @@ -38,37 +22,52 @@ import ch.qos.logback.classic.joran.JoranConfigurator; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.classic.util.LogbackMDCAdapter; import ch.qos.logback.core.Appender; import ch.qos.logback.core.Context; import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.helpers.NOPAppender; import ch.qos.logback.core.joran.spi.JoranException; import ch.qos.logback.core.read.ListAppender; import ch.qos.logback.core.rolling.RollingFileAppender; -import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP; +import ch.qos.logback.core.rolling.SizeAndTimeBasedFileNamingAndTriggeringPolicy; import ch.qos.logback.core.rolling.TimeBasedRollingPolicy; import ch.qos.logback.core.sift.AppenderFactory; import ch.qos.logback.core.sift.AppenderTracker; import ch.qos.logback.core.spi.AbstractComponentTracker; import ch.qos.logback.core.spi.ComponentTracker; import ch.qos.logback.core.status.ErrorStatus; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; + import ch.qos.logback.core.testUtil.StringListAppender; import ch.qos.logback.core.util.FileSize; -import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +//import ch.qos.logback.core.util.StatusPrinter; + public class SiftingAppenderTest { static String SIFT_FOLDER_PREFIX = ClassicTestConstants.JORAN_INPUT_PREFIX + "sift/"; LoggerContext loggerContext = new LoggerContext(); + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); Logger logger = loggerContext.getLogger(this.getClass().getName()); Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); StatusChecker statusChecker = new StatusChecker(loggerContext); int diff = RandomUtil.getPositiveInt(); - String randomOutputDir = OUTPUT_DIR_PREFIX + diff + "/"; + String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; int now = 0; protected void configure(String file) throws JoranException { @@ -77,14 +76,14 @@ protected void configure(String file) throws JoranException { jc.doConfigure(file); } - @Before + @BeforeEach public void setUp() { - MDC.clear(); + loggerContext.setMDCAdapter(logbackMDCAdapter); } - @After + @AfterEach public void tearDown() { - MDC.clear(); + } @Test @@ -105,6 +104,7 @@ public void smoke() throws JoranException { List eventList = listAppender.list; assertEquals(1, listAppender.list.size()); assertEquals("smoke", eventList.get(0).getMessage()); + statusChecker.assertIsWarningOrErrorFree(); } private AppenderTracker getAppenderTracker() { @@ -121,10 +121,8 @@ public void zeroNesting() throws JoranException { logger.debug("hello"); logger.debug("hello"); - Appender nopa = getAppenderTracker().find("zeroDefault"); - assertNotNull(nopa); - assertThat(nopa).isInstanceOf(NOPAppender.class); - StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); + assertNull(getAppenderTracker()); + //StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); statusChecker.assertContainsMatch(ErrorStatus.ERROR, "No nested appenders found"); } @@ -136,10 +134,9 @@ public void multipleNesting() throws JoranException { logger.debug("hello"); logger.debug("hello"); - Appender listAppender = getAppenderTracker().find("multipleDefault"); - StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); + assertNull(getAppenderTracker()); + //StatusPrinter.printInCaseOfErrorsOrWarnings(loggerContext); - assertNotNull(listAppender); statusChecker.assertContainsMatch(ErrorStatus.ERROR, "Only and only one appender can be nested"); } @@ -165,17 +162,17 @@ public void fileAppenderCollision() throws JoranException, InterruptedException long timestamp = System.currentTimeMillis(); - MDC.put(key, "A-"+diff); + logbackMDCAdapter.put(key, "A-"+diff); logNewEventViaSiftingAppender(sa, timestamp); FileAppender fileAppenderA = (FileAppender) sa.getAppenderTracker().find("A-"+diff); assertNotNull(fileAppenderA); assertTrue(fileAppenderA.isStarted()); timestamp += ComponentTracker.DEFAULT_TIMEOUT + 1; - MDC.put(key, "B-"+diff); + logbackMDCAdapter.put(key, "B-"+diff); logNewEventViaSiftingAppender(sa, timestamp); assertFalse(fileAppenderA.isStarted()); - - MDC.put(key, "A-"+diff); + + logbackMDCAdapter.put(key, "A-"+diff); timestamp += 1; logNewEventViaSiftingAppender(sa, timestamp); FileAppender fileAppenderA_2 = (FileAppender) sa.getAppenderTracker().find("A-"+diff); @@ -193,7 +190,7 @@ private void logNewEventViaSiftingAppender(SiftingAppender sa, long timestamp) { public void testWholeCycle() throws JoranException { String mdcKey = "cycle"; configure(SIFT_FOLDER_PREFIX + "completeCycle.xml"); - MDC.put(mdcKey, "a"); + logbackMDCAdapter.put(mdcKey, "a"); logger.debug("smoke"); long timestamp = System.currentTimeMillis(); SiftingAppender sa = (SiftingAppender) root.getAppender("SIFT"); @@ -203,7 +200,7 @@ public void testWholeCycle() throws JoranException { assertEquals(1, listAppender.list.size()); assertEquals("smoke", eventList.get(0).getMessage()); - MDC.remove(mdcKey); + logbackMDCAdapter.remove(mdcKey); logNewEventViaSiftingAppender(sa, timestamp); assertFalse(listAppender.isStarted()); assertEquals(1, sa.getAppenderTracker().allKeys().size()); @@ -215,7 +212,7 @@ public void sessionFinalizationShouldCauseLingering() throws JoranException { String mdcKey = "linger"; String mdcVal = "session" + diff; configure(SIFT_FOLDER_PREFIX + "lingering.xml"); - MDC.put(mdcKey, mdcVal); + logbackMDCAdapter.put(mdcKey, mdcVal); logger.debug("linger 1"); logger.debug(ClassicConstants.FINALIZE_SESSION_MARKER, "linger 2"); long now = System.currentTimeMillis(); @@ -240,11 +237,13 @@ public void localPropertiesShouldBeVisible() throws JoranException { String msg = "localPropertiesShouldBeVisible"; String prefix = "Y"; configure(SIFT_FOLDER_PREFIX + "propertyPropagation.xml"); - MDC.put(mdcKey, mdcVal); + + logbackMDCAdapter.put(mdcKey, mdcVal); logger.debug(msg); SiftingAppender sa = (SiftingAppender) root.getAppender("SIFT"); StringListAppender listAppender = (StringListAppender) sa.getAppenderTracker().find(mdcVal); assertNotNull(listAppender); + List strList = listAppender.strList; assertEquals(1, listAppender.strList.size()); assertEquals(prefix + msg, strList.get(0)); @@ -257,7 +256,7 @@ public void propertyDefinedWithinSiftElementShouldBeVisible() throws JoranExcept String msg = "propertyDefinedWithinSiftElementShouldBeVisible"; String prefix = "Y"; configure(SIFT_FOLDER_PREFIX + "propertyDefinedInSiftElement.xml"); - MDC.put(mdcKey, mdcVal); + logbackMDCAdapter.put(mdcKey, mdcVal); logger.debug(msg); SiftingAppender sa = (SiftingAppender) root.getAppender("SIFT"); StringListAppender listAppender = (StringListAppender) sa.getAppenderTracker().find(mdcVal); @@ -274,7 +273,7 @@ public void compositePropertyShouldCombineWithinAndWithoutSiftElement() throws J String msg = "compositePropertyShouldCombineWithinAndWithoutSiftElement"; String prefix = "composite"; configure(SIFT_FOLDER_PREFIX + "compositeProperty.xml"); - MDC.put(mdcKey, mdcVal); + logbackMDCAdapter.put(mdcKey, mdcVal); logger.debug(msg); SiftingAppender sa = (SiftingAppender) root.getAppender("SIFT"); StringListAppender listAppender = (StringListAppender) sa.getAppenderTracker().find(mdcVal); @@ -291,7 +290,7 @@ public void maxAppendersCountPropertyShouldBeHonored() throws JoranException { SiftingAppender sa = (SiftingAppender) root.getAppender("SIFT"); String mdcKey = "max"; for (int i = 0; i <= max; i++) { - MDC.put(mdcKey, "" + (diff + i)); + logbackMDCAdapter.put(mdcKey, "" + (diff + i)); LoggingEvent event = new LoggingEvent("", logger, Level.DEBUG, "max" + i, null, null); event.setTimeStamp(now); sa.doAppend(event); @@ -326,7 +325,7 @@ public void timeoutPropertyShouldBeHonored() throws JoranException, InterruptedE } // LOGBACK-1127 - @Ignore + @Disabled @Test public void programmicSiftingAppender() { @@ -357,7 +356,7 @@ public Appender buildAppender(Context context, String discriminat policy.setParent(appender); policy.setCleanHistoryOnStart(true); - SizeAndTimeBasedFNATP innerpolicy = new SizeAndTimeBasedFNATP(); + SizeAndTimeBasedFileNamingAndTriggeringPolicy innerpolicy = new SizeAndTimeBasedFileNamingAndTriggeringPolicy(); innerpolicy.setContext(context); innerpolicy.setMaxFileSize(FileSize.valueOf("5KB")); innerpolicy.setTimeBasedRollingPolicy(policy); @@ -384,15 +383,15 @@ public Appender buildAppender(Context context, String discriminat logger.setLevel(Level.DEBUG); logger.setAdditive(false); - MDC.put("SKEY", "K1"); + logbackMDCAdapter.put("SKEY", "K1"); logger.info("bla1"); - MDC.clear(); + logbackMDCAdapter.clear(); - MDC.put("SKEY", "K2"); + logbackMDCAdapter.put("SKEY", "K2"); logger.info("bla2"); - MDC.clear(); + logbackMDCAdapter.clear(); - StatusPrinter.print(loggerContext); + //StatusPrinter.print(loggerContext); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java index 4146a47a65..415bc70b50 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/BasicContextListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -42,13 +42,11 @@ public void onReset(LoggerContext context) { public void onStart(LoggerContext context) { updateType = UpdateType.START; - ; this.context = context; } public void onStop(LoggerContext context) { updateType = UpdateType.STOP; - ; this.context = context; } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/BogusClassLoader.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/BogusClassLoader.java index 8131c94b67..f71e2d739f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/BogusClassLoader.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/BogusClassLoader.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/CPDCSpecial.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/CPDCSpecial.java index 2ec9397ab7..f4c3384449 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/CPDCSpecial.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/CPDCSpecial.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/CallerDataTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/CallerDataTest.java index b6f26c373f..e833405433 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/CallerDataTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/CallerDataTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,13 @@ */ package ch.qos.logback.classic.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import java.util.Arrays; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class CallerDataTest { @@ -26,15 +28,16 @@ public void testBasic() { Throwable t = new Throwable(); StackTraceElement[] steArray = t.getStackTrace(); - StackTraceElement[] cda = CallerData.extract(t, CallerDataTest.class.getName(), 50, null); + StackTraceElement[] cda = CallerData.extract(t, CallerDataTest.class.getName(), 100, null); + Arrays.stream(cda).forEach( ste -> System.out.println(ste)); assertNotNull(cda); assertTrue(cda.length > 0); assertEquals(steArray.length - 1, cda.length); } /** - * This test verifies that in case caller data cannot be - * extracted, CallerData.extract does not throw an exception + * This test verifies that in case caller data cannot be extracted, + * CallerData.extract does not throw an exception * */ @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java index ed40c4ad70..70fb50d1e6 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ContextListenerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,59 +13,61 @@ */ package ch.qos.logback.classic.spi; -import static org.junit.Assert.assertEquals; import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.BasicContextListener.UpdateType; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ContextListenerTest { - LoggerContext context; + LoggerContext loggerContext; BasicContextListener listener; - @Before + @BeforeEach public void setUp() throws Exception { - context = new LoggerContext(); + loggerContext = new LoggerContext(); + loggerContext.start(); listener = new BasicContextListener(); - context.addListener(listener); + loggerContext.addListener(listener); } @Test public void testNotifyOnReset() { - context.reset(); + loggerContext.reset(); assertEquals(UpdateType.RESET, listener.updateType); - assertEquals(listener.context, context); + assertEquals(listener.context, loggerContext); } @Test - public void testNotifyOnStopResistant() { + public void testResistantListener_NotifyOnStop() { listener.setResetResistant(true); - context.stop(); + loggerContext.stop(); assertEquals(UpdateType.STOP, listener.updateType); - assertEquals(listener.context, context); + assertEquals(listener.context, loggerContext); } @Test - public void testNotifyOnStopNotResistant() { - context.stop(); + public void testNotResistantListener_NotifyOnStop() { + loggerContext.stop(); assertEquals(UpdateType.RESET, listener.updateType); - assertEquals(listener.context, context); + assertEquals(listener.context, loggerContext); } @Test public void testNotifyOnStart() { - context.start(); + loggerContext.start(); assertEquals(UpdateType.START, listener.updateType); - assertEquals(listener.context, context); + assertEquals(listener.context, loggerContext); } void checkLevelChange(String loggerName, Level level) { - Logger logger = context.getLogger(loggerName); + Logger logger = loggerContext.getLogger(loggerName); logger.setLevel(level); assertEquals(UpdateType.LEVEL_CHANGE, listener.updateType); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java deleted file mode 100644 index 8a416f6524..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/DummyThrowableProxy.java +++ /dev/null @@ -1,72 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.spi; - -public class DummyThrowableProxy implements IThrowableProxy { - - private String className; - private String message; - private int commonFramesCount; - private StackTraceElementProxy[] stackTraceElementProxyArray; - private IThrowableProxy cause; - private IThrowableProxy[] suppressed; - - public String getClassName() { - return className; - } - - public void setClassName(String className) { - this.className = className; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - public int getCommonFrames() { - return commonFramesCount; - } - - public void setCommonFramesCount(int commonFramesCount) { - this.commonFramesCount = commonFramesCount; - } - - public StackTraceElementProxy[] getStackTraceElementProxyArray() { - return stackTraceElementProxyArray; - } - - public void setStackTraceElementProxyArray(StackTraceElementProxy[] stackTraceElementProxyArray) { - this.stackTraceElementProxyArray = stackTraceElementProxyArray; - } - - public IThrowableProxy getCause() { - return cause; - } - - public void setCause(IThrowableProxy cause) { - this.cause = cause; - } - - public IThrowableProxy[] getSuppressed() { - return suppressed; - } - - public void setSuppressed(IThrowableProxy[] suppressed) { - this.suppressed = suppressed; - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/InvocationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/InvocationTest.java new file mode 100644 index 0000000000..fed7ea712e --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/InvocationTest.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import java.io.PrintStream; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.LoggerFactoryFriend; + +import ch.qos.logback.classic.testUtil.StringPrintStream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.slf4j.helpers.Reporter.SLF4J_INTERNAL_VERBOSITY_KEY; + +public class InvocationTest { + + private final PrintStream oldErr = System.err; + final String loggerName = this.getClass().getName(); + StringPrintStream sps = new StringPrintStream(oldErr, true); + + String CONNECTED_WITH_MESSAGE = "SLF4J(D): Connected with provider of type [ch.qos.logback.classic.spi.LogbackServiceProvider]"; + + @BeforeEach + public void setUp() throws Exception { + System.setProperty(SLF4J_INTERNAL_VERBOSITY_KEY, "debug"); + System.setErr(sps); + } + + @AfterEach + public void tearDown() throws Exception { + LoggerFactoryFriend.reset(); + System.setErr(oldErr); + System.clearProperty(SLF4J_INTERNAL_VERBOSITY_KEY); + } + + // https://jira.qos.ch/browse/LOGBACK-1568 would have been prevented + // had this silly test existed. + @Test + public void smoke() { + Logger logger = LoggerFactory.getLogger(this.getClass()); + logger.debug("Hello world."); + + assertEquals(1, sps.stringList.size()); + assertEquals(CONNECTED_WITH_MESSAGE, sps.stringList.get(0)); + + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/ListContextListener.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ListContextListener.java new file mode 100644 index 0000000000..d3d8eed405 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ListContextListener.java @@ -0,0 +1,55 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.BasicContextListener.UpdateType; + +public class ListContextListener implements LoggerContextListener { + + List updateList = new ArrayList<>(); + + @Override + public boolean isResetResistant() { + return false; + } + + @Override + public void onStart(LoggerContext context) { + updateList.add(UpdateType.START); + } + + @Override + public void onReset(LoggerContext context) { + updateList.add(UpdateType.RESET); + } + + @Override + public void onStop(LoggerContext context) { + updateList.add(UpdateType.STOP); + + } + + @Override + public void onLevelChange(Logger logger, Level level) { + updateList.add(UpdateType.LEVEL_CHANGE); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LocalFirstClassLoader.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LocalFirstClassLoader.java index 8f2dcaade1..2739c8f9da 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LocalFirstClassLoader.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LocalFirstClassLoader.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LogbackServiceProviderTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LogbackServiceProviderTest.java new file mode 100644 index 0000000000..c648691609 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LogbackServiceProviderTest.java @@ -0,0 +1,34 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class LogbackServiceProviderTest { + + LogbackServiceProvider provider = new LogbackServiceProvider(); + + @Test + public void testContrxtStart() { + provider.initialize(); + LoggerContext loggerFactory = (LoggerContext) provider.getLoggerFactory(); + + assertTrue(loggerFactory.isStarted()); + + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerComparatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerComparatorTest.java index 7472ed5b79..51b867d79f 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerComparatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerComparatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,12 @@ */ package ch.qos.logback.classic.spi; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.*; - import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class LoggerComparatorTest { @@ -30,7 +30,7 @@ public class LoggerComparatorTest { Logger a = lc.getLogger("a"); Logger b = lc.getLogger("b"); - @Before + @BeforeEach public void setUp() throws Exception { } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerContextLifeCycleTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerContextLifeCycleTest.java new file mode 100644 index 0000000000..94622b5781 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggerContextLifeCycleTest.java @@ -0,0 +1,70 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import ch.qos.logback.classic.ClassicTestConstants; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.joran.JoranConfigurator; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class LoggerContextLifeCycleTest { + + LoggerContext loggerContext = new LoggerContext(); + Logger logger = loggerContext.getLogger(this.getClass().getName()); + Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); + StatusChecker checker = new StatusChecker(loggerContext); + int diff = RandomUtil.getPositiveInt(); + + void configure(String file) throws JoranException { + JoranConfigurator jc = new JoranConfigurator(); + jc.setContext(loggerContext); + loggerContext.putProperty("diff", "" + diff); + jc.doConfigure(file); + loggerContext.start(); + } + + @Test + public void smoke() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "spi/contextListener.xml"); + + List listenerList = loggerContext.getCopyOfListenerList(); + assertEquals(1, listenerList.size()); + + ListContextListener lcl = (ListContextListener) listenerList.get(0); + // lcl.updateList.stream().forEach(System.out::println); + assertEquals(BasicContextListener.UpdateType.START, lcl.updateList.get(1)); + } + + @Test + public void smokeWithImports() throws JoranException { + configure(ClassicTestConstants.JORAN_INPUT_PREFIX + "spi/contextListenerWithImports.xml"); + + List listenerList = loggerContext.getCopyOfListenerList(); + assertEquals(1, listenerList.size()); + + ListContextListener lcl = (ListContextListener) listenerList.get(0); + // lcl.updateList.stream().forEach(System.out::println); + assertEquals(BasicContextListener.UpdateType.START, lcl.updateList.get(1)); + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationPerfTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationPerfTest.java index a3390fe0e2..15b9cc49d2 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationPerfTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationPerfTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,23 +13,23 @@ */ package ch.qos.logback.classic.spi; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.io.IOException; -import java.io.ObjectOutputStream; - -import ch.qos.logback.core.testUtil.EnvUtilForTests; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; - import ch.qos.logback.classic.net.NOPOutputStream; import ch.qos.logback.classic.net.testObjectBuilders.Builder; import ch.qos.logback.classic.net.testObjectBuilders.LoggingEventWithParametersBuilder; import ch.qos.logback.classic.net.testObjectBuilders.TrivialLoggingEventBuilder; import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.testUtil.EnvUtilForTests; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; + +import java.io.IOException; +import java.io.ObjectOutputStream; + +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; // As of logback 0.9.15, // average time per logging event: 3979 nanoseconds @@ -44,6 +44,7 @@ // average time per logging event: 4034 // average size 57, with params, average size=148 +@Disabled public class LoggingEventSerializationPerfTest { static int LOOP_LEN = 10 * 1000; @@ -51,13 +52,13 @@ public class LoggingEventSerializationPerfTest { NOPOutputStream noos = new NOPOutputStream(); ObjectOutputStream oos; - @Before + @BeforeEach public void setUp() throws Exception { MDC.clear(); oos = new ObjectOutputStream(noos); } - @After + @AfterEach public void tearDown() throws Exception { } @@ -101,12 +102,12 @@ public void testPerformance() { System.out.println("noos size " + noos.size() + " average size=" + averageSize); double averageSizeLimit = 62.1; - assertTrue("average size " + averageSize + " should be less than " + averageSizeLimit, averageSizeLimit > averageSize); + assertTrue(averageSizeLimit > averageSize, "average size " + averageSize + " should be less than " + averageSizeLimit); // the reference was computed on Orion (Ceki's computer) @SuppressWarnings("unused") long referencePerf = 5000; - //BogoPerf.assertDuration(rt, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(rt, referencePerf, CoreConstants.REFERENCE_BIPS); } @Test @@ -128,11 +129,12 @@ public void testPerformanceWithParameters() { System.out.println("noos size " + noos.size() + " average size=" + averageSize); double averageSizeLimit = 160; - assertTrue("averageSize " + averageSize + " should be less than " + averageSizeLimit, averageSizeLimit > averageSize); + assertTrue(averageSizeLimit > averageSize, + "averageSize " + averageSize + " should be less than " + averageSizeLimit); // the reference was computed on Orion (Ceki's computer) @SuppressWarnings("unused") long referencePerf = 7000; - //BogoPerf.assertDuration(rt, referencePerf, CoreConstants.REFERENCE_BIPS); + // BogoPerf.assertDuration(rt, referencePerf, CoreConstants.REFERENCE_BIPS); } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationTest.java index c2d9ec0934..4b9374c168 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventSerializationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,19 @@ */ package ch.qos.logback.classic.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer; +import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream; +import ch.qos.logback.classic.util.LogbackMDCAdapter; +import ch.qos.logback.core.spi.PreSerializationTransformer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; +import org.slf4j.Marker; +import org.slf4j.MarkerFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -23,25 +33,17 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.Arrays; import java.util.Map; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; -import org.slf4j.Marker; -import org.slf4j.MarkerFactory; - -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.net.LoggingEventPreSerializationTransformer; -import ch.qos.logback.classic.net.server.HardenedLoggingEventInputStream; -import ch.qos.logback.core.spi.PreSerializationTransformer; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; public class LoggingEventSerializationTest { LoggerContext loggerContext; + LogbackMDCAdapter logbackMDCAdapter = new LogbackMDCAdapter(); Logger logger; ByteArrayOutputStream bos; @@ -49,17 +51,18 @@ public class LoggingEventSerializationTest { ObjectInputStream inputStream; PreSerializationTransformer pst = new LoggingEventPreSerializationTransformer(); - @Before + @BeforeEach public void setUp() throws Exception { loggerContext = new LoggerContext(); loggerContext.setName("testContext"); + loggerContext.setMDCAdapter(logbackMDCAdapter); logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); // create the byte output stream bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); } - @After + @AfterEach public void tearDown() throws Exception { loggerContext = null; logger = null; @@ -93,7 +96,7 @@ public void context() throws Exception { @Test public void MDC() throws Exception { - MDC.put("key", "testValue"); + logbackMDCAdapter.put("key", "testValue"); ILoggingEvent event = createLoggingEvent(); ILoggingEvent remoteEvent = writeAndRead(event); checkForEquality(event, remoteEvent); @@ -103,17 +106,17 @@ public void MDC() throws Exception { @Test public void updatedMDC() throws Exception { - MDC.put("key", "testValue"); + logbackMDCAdapter.put("key", "testValue"); ILoggingEvent event1 = createLoggingEvent(); Serializable s1 = pst.transform(event1); oos.writeObject(s1); - MDC.put("key", "updatedTestValue"); + logbackMDCAdapter.put("key", "updatedTestValue"); ILoggingEvent event2 = createLoggingEvent(); Serializable s2 = pst.transform(event2); oos.writeObject(s2); - // create the input stream based on the ouput stream + // create the input stream based on the output stream ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); inputStream = new ObjectInputStream(bis); @@ -150,40 +153,38 @@ public void testWithThrowable() throws Exception { checkForEquality(event, remoteEvent); } - @Test public void testWithMarker() throws Exception { Marker marker = MarkerFactory.getMarker("A_MARKER"); LoggingEvent event = createLoggingEvent(); - - - event.setMarker(marker); - assertNotNull(event.getMarker()); - + + event.addMarker(marker); + assertNotNull(event.getMarkerList()); + ILoggingEvent remoteEvent = writeAndRead(event); checkForEquality(event, remoteEvent); - assertNotNull(remoteEvent.getMarker()); - assertEquals(marker, remoteEvent.getMarker()); + assertNotNull(remoteEvent.getMarkerList()); + assertEquals(Arrays.asList(marker), remoteEvent.getMarkerList()); } - + @Test public void testWithTwoMarkers() throws Exception { Marker marker = MarkerFactory.getMarker("A_MARKER"); Marker marker2 = MarkerFactory.getMarker("B_MARKER"); marker.add(marker2); LoggingEvent event = createLoggingEvent(); - - event.setMarker(marker); - assertNotNull(event.getMarker()); - + + event.addMarker(marker); + assertNotNull(event.getMarkerList()); + ILoggingEvent remoteEvent = writeAndRead(event); checkForEquality(event, remoteEvent); - assertNotNull(remoteEvent.getMarker()); - assertEquals(marker, remoteEvent.getMarker()); + assertNotNull(remoteEvent.getMarkerList()); + assertEquals(Arrays.asList(marker), remoteEvent.getMarkerList()); } - + @Test public void testWithCallerData() throws Exception { LoggingEvent event = createLoggingEvent(); @@ -250,7 +251,7 @@ private ILoggingEvent writeAndRead(ILoggingEvent event) throws IOException, Clas oos.writeObject(ser); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); inputStream = new HardenedLoggingEventInputStream(bis); - + return (ILoggingEvent) inputStream.readObject(); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventTest.java index 948b66352a..e33d8f76ce 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LoggingEventTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,18 +16,18 @@ import ch.qos.logback.classic.Level; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; public class LoggingEventTest { LoggerContext loggerContext = new LoggerContext(); Logger logger = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - @Before + @BeforeEach public void setUp() { } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LuckyCharms.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LuckyCharms.java index 8e70339ac8..32e38482b1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/LuckyCharms.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/LuckyCharms.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java deleted file mode 100644 index e19c6de3ad..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.spi; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ContextListenerTest.class, CallerDataTest.class, LoggerComparatorTest.class, LoggingEventTest.class, LoggingEventSerializationTest.class, - LoggingEventSerializationPerfTest.class, ThrowableProxyTest.class, PackagingDataCalculatorTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackagingDataCalculatorTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackagingDataCalculatorTest.java index c46d7fbcd6..207880ab3a 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackagingDataCalculatorTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PackagingDataCalculatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,17 @@ */ package ch.qos.logback.classic.spi; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import ch.qos.logback.classic.util.TestHelper; +import ch.qos.logback.core.util.SystemInfo; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.classic.util.TestHelper; -import ch.qos.logback.core.util.SystemInfo; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class PackagingDataCalculatorTest { @@ -75,7 +74,7 @@ public void doCalculateClassPackagingData(boolean withClassPackagingCalculation) return (1.0 * System.nanoTime() - start) / len / 1000; } - @Ignore + @Disabled @Test public void perfTest() { int len = 1000; @@ -93,14 +92,15 @@ public void perfTest() { // be more lenient with other JDKs slackFactor = 15; } - assertTrue("computing class packaging data (" + d1 + ") should have been less than " + slackFactor - + " times the time it takes to process an exception " + (d0 * slackFactor), d0 * slackFactor > d1); + assertTrue(d0 * slackFactor > d1, + "computing class packaging data (" + d1 + ") should have been less than " + slackFactor + + " times the time it takes to process an exception " + (d0 * slackFactor)); } private ClassLoader makeBogusClassLoader() throws MalformedURLException { ClassLoader currentClassLoader = this.getClass().getClassLoader(); - return new BogusClassLoader(new URL[] {}, currentClassLoader); + return new BogusClassLoader(new URL[]{}, currentClassLoader); } @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggerContextVO.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggerContextVO.java new file mode 100644 index 0000000000..d2dbd2705a --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggerContextVO.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.spi; + +import ch.qos.logback.classic.LoggerContext; + +import java.util.Map; +import java.util.Objects; + +/** + * PubLoggerContextVO is a very open (public) version of LoggerContextVO + * + * @since 1.4.8 + */ +public class PubLoggerContextVO extends LoggerContextVO { + + + public PubLoggerContextVO(LoggerContext lc) { + super(lc); + } + + public PubLoggerContextVO(String name, Map propertyMap, long birthTime) { + super(name, propertyMap, birthTime); + } + + public void setName(String name) { + this.name = name; + } + + public void setPropertyMap(Map propertyMap) { + this.propertyMap = propertyMap; + } + + + public void setBirthTime(long birthTime) { + this.birthTime = birthTime; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + PubLoggerContextVO that = (PubLoggerContextVO) o; + return birthTime == that.birthTime && Objects.equals(name, that.name) && Objects.equals(propertyMap, + that.propertyMap); + } + + @Override + public int hashCode() { + return Objects.hash(name, propertyMap, birthTime); + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java index 11f74cfadc..58dc984141 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubLoggingEventVO.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,9 +17,15 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; +import java.util.List; import java.util.Map; +import com.fasterxml.jackson.annotation.JacksonInject; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.databind.annotation.JsonValueInstantiator; import org.slf4j.Marker; +import org.slf4j.event.KeyValuePair; import org.slf4j.helpers.MessageFormatter; import ch.qos.logback.classic.Level; @@ -38,20 +44,23 @@ public class PubLoggingEventVO implements ILoggingEvent, Serializable { public String threadName; public String loggerName; - public LoggerContextVO loggerContextVO; + public PubLoggerContextVO loggerContextVO; public transient Level level; public String message; private transient String formattedMessage; + @JsonAlias public Object[] argumentArray; public IThrowableProxy throwableProxy; public StackTraceElement[] callerDataArray; - public Marker marker; + public List markerList; + public List kvpList; public Map mdcPropertyMap; public long timeStamp; + public int nanoseconds; public long sequenceNumber; public String getThreadName() { @@ -66,6 +75,8 @@ public String getLoggerName() { return loggerName; } + + @JsonIgnore public Level getLevel() { return level; } @@ -104,14 +115,19 @@ public boolean hasCallerData() { return callerDataArray != null; } - public Marker getMarker() { - return marker; + public List getMarkerList() { + return markerList; } public long getTimeStamp() { return timeStamp; } + @Override + public int getNanoseconds() { + return nanoseconds; + } + public long getSequenceNumber() { return sequenceNumber; } @@ -119,7 +135,7 @@ public long getSequenceNumber() { public void setSequenceNumber(long sequenceNumber) { this.sequenceNumber = sequenceNumber; } - + public long getContextBirthTime() { return loggerContextVO.getBirthTime(); } @@ -139,6 +155,11 @@ public Map getMdc() { public void prepareForDeferredProcessing() { } + @Override + public List getKeyValuePairs() { + return kvpList; + } + private void writeObject(ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeInt(level.levelInt); @@ -214,10 +235,10 @@ public boolean equals(Object obj) { if (timeStamp != other.timeStamp) return false; - if (marker == null) { - if (other.marker != null) + if (markerList == null) { + if (other.markerList != null) return false; - } else if (!marker.equals(other.marker)) + } else if (!markerList.equals(other.markerList)) return false; if (mdcPropertyMap == null) { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java new file mode 100644 index 0000000000..513a4d5f18 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/PubThrowableProxy.java @@ -0,0 +1,100 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.spi; + +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.StackTraceElementProxy; +import com.fasterxml.jackson.annotation.JsonAlias; + +import java.util.Arrays; + +/** + * Used in tests to represent a ThrowableProxy in a JSON-friendly way. + */ +public class PubThrowableProxy implements IThrowableProxy { + + private String className; + private String message; + private int commonFramesCount; + + @JsonAlias("stepArray") + private StackTraceElementProxy[] stackTraceElementProxyArray; + private PubThrowableProxy cause; + private PubThrowableProxy[] suppressed; + private boolean cyclic; + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + public int getCommonFrames() { + return commonFramesCount; + } + + public void setCommonFramesCount(int commonFramesCount) { + this.commonFramesCount = commonFramesCount; + } + + public StackTraceElementProxy[] getStackTraceElementProxyArray() { + return stackTraceElementProxyArray; + } + + public void setStackTraceElementProxyArray(StackTraceElementProxy[] stackTraceElementProxyArray) { + this.stackTraceElementProxyArray = stackTraceElementProxyArray; + } + + public PubThrowableProxy getCause() { + return cause; + } + + public void setCause(PubThrowableProxy cause) { + this.cause = cause; + } + + public PubThrowableProxy[] getSuppressed() { + return suppressed; + } + + public void setSuppressed(PubThrowableProxy[] suppressed) { + this.suppressed = suppressed; + } + + public boolean isCyclic() { + return cyclic; + } + + public void setCyclic(boolean cyclic) { + this.cyclic = cyclic; + } + + @Override + public String toString() { + return "PubThrowableProxy{" + "className='" + className + '\'' + ", message='" + message + '\'' + + ", commonFramesCount=" + commonFramesCount + ", stackTraceElementProxyArray=" + Arrays.toString( + stackTraceElementProxyArray) + ", cause=" + cause + ", suppressed=" + Arrays.toString(suppressed) + + ", cyclic=" + cyclic + '}'; + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableProxyTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableProxyTest.java index 7a5b4a05af..66ae19d6b3 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableProxyTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/ThrowableProxyTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,33 +13,31 @@ */ package ch.qos.logback.classic.spi; -import static ch.qos.logback.classic.util.TestHelper.addSuppressed; -import static org.junit.Assert.assertEquals; -import static org.junit.Assume.assumeTrue; +import ch.qos.logback.core.util.EnvUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.InvocationTargetException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.classic.util.TestHelper; +import static org.junit.jupiter.api.Assertions.assertEquals; public class ThrowableProxyTest { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } + // compares Throwable.printStackTrace with output by ThrowableProxy public void verify(Throwable t) { t.printStackTrace(pw); @@ -47,14 +45,13 @@ public void verify(Throwable t) { String result = ThrowableProxyUtil.asString(tp); result = result.replace("common frames omitted", "more"); - String expected = sw.toString(); - System.out.println("========expected"); - System.out.println(expected); - - System.out.println("========result"); - System.out.println(result); +// System.out.println("========expected"); +// System.out.println(expected); +// +// System.out.println("========result"); +// System.out.println(result); assertEquals(expected, result); } @@ -78,16 +75,15 @@ public void nested() { @Test public void suppressed() throws InvocationTargetException, IllegalAccessException { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. Exception ex = null; try { someMethod(); } catch (Exception e) { Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(e, fooException); - addSuppressed(e, barException); + e.addSuppressed(fooException); + e.addSuppressed(barException); + ex = e; } verify(ex); @@ -95,8 +91,7 @@ public void suppressed() throws InvocationTargetException, IllegalAccessExceptio @Test public void suppressedWithCause() throws InvocationTargetException, IllegalAccessException { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. + // sense. Exception ex = null; try { someMethod(); @@ -104,16 +99,16 @@ public void suppressedWithCause() throws InvocationTargetException, IllegalAcces ex = new Exception("Wrapper", e); Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(ex, fooException); - addSuppressed(e, barException); + + ex.addSuppressed(fooException); + e.addSuppressed(barException); + } verify(ex); } @Test public void suppressedWithSuppressed() throws Exception { - assumeTrue(TestHelper.suppressedSupported()); // only execute on Java 7, would work anyway but doesn't make - // sense. Exception ex = null; try { someMethod(); @@ -121,13 +116,14 @@ public void suppressedWithSuppressed() throws Exception { ex = new Exception("Wrapper", e); Exception fooException = new Exception("Foo"); Exception barException = new Exception("Bar"); - addSuppressed(barException, fooException); - addSuppressed(e, barException); + barException.addSuppressed(fooException); + e.addSuppressed(barException); + } verify(ex); } - // see also http://jira.qos.ch/browse/LBCLASSIC-216 + // see also https://jira.qos.ch/browse/LOGBACK-453 @Test public void nullSTE() { Throwable t = new Exception("someMethodWithNullException") { @@ -160,6 +156,40 @@ public void multiNested() { verify(w); } + // see also https://jira.qos.ch/browse/LOGBACK-1454 + @Test + public void cyclicCause() { + // Earlier JDKs may format things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.initCause(e2); + verify(e); + } + + // see also https://jira.qos.ch/browse/LOGBACK-1454 + @Test + public void cyclicSuppressed() { + // Earlier JDKs may format things differently + if (!EnvUtil.isJDK16OrHigher()) + return; + Exception e = new Exception("foo"); + Exception e2 = new Exception(e); + e.addSuppressed(e2); + verify(e); + } + + @Test + public void overriddenToString() { + Exception e = new Exception() { + public String toString() { + return getClass().getName() + " [extra]"; + } + }; + verify(e); + } + void someMethod() throws Exception { throw new Exception("someMethod"); } @@ -182,4 +212,6 @@ void someOtherMethod() throws Exception { throw new Exception("someOtherMethod", e); } } + + } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/spi/special/CPDCSpecialImpl.java b/logback-classic/src/test/java/ch/qos/logback/classic/spi/special/CPDCSpecialImpl.java index a716653037..bfb15182a1 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/spi/special/CPDCSpecialImpl.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/spi/special/CPDCSpecialImpl.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/Gaussian.java b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/Gaussian.java new file mode 100644 index 0000000000..b6e6a6cae4 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/Gaussian.java @@ -0,0 +1,42 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.testUtil; + +import java.util.Random; + +public class Gaussian { + + Random random; + + double mean; + double variance; + + public Gaussian(double mean, double variance) { + this.random = new Random(); + this.mean = mean; + this.variance = variance; + } + + public Gaussian(long seed, double mean, double variance) { + this.random = new Random(seed); + this.mean = mean; + this.variance = variance; + } + + public double getGaussian() { + return mean + random.nextGaussian() * variance; + } + +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/GaussianDump.java b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/GaussianDump.java new file mode 100644 index 0000000000..6c74f89aaf --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/GaussianDump.java @@ -0,0 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.testUtil; + +import org.junit.jupiter.api.Test; + +public class GaussianDump { + + @Test + public void dump() { + Gaussian g = new Gaussian(1000, 100); + for (int i = 0; i < 5000; i++) { + int r = (int) g.getGaussian(); + System.out.println(r); + } + } +} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/StringPrintStream.java b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/StringPrintStream.java new file mode 100644 index 0000000000..aab38c63e6 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/testUtil/StringPrintStream.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.classic.testUtil; + +import java.io.PrintStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This class is duplicated in slf4j-api-testsjar since 2.0.0-alpha4. + * + * Use the copy in since 2.0.0-alpha4-tests.jar once it is released. + * + * @author Ceki + * + */ +public class StringPrintStream extends PrintStream { + + public static final String LINE_SEP = System.getProperty("line.separator"); + PrintStream other; + boolean duplicate = false; + + public List stringList = Collections.synchronizedList(new ArrayList()); + + public StringPrintStream(PrintStream ps, boolean duplicate) { + super(ps); + other = ps; + this.duplicate = duplicate; + } + + public StringPrintStream(PrintStream ps) { + this(ps, false); + } + + public void print(String s) { + if (duplicate) + other.print(s); + stringList.add(s); + } + + public void println(String s) { + if (duplicate) + other.println(s); + stringList.add(s); + } + + public void println(Object o) { + if (duplicate) + other.println(o); + stringList.add(o.toString()); + } +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DebugUsersTurboFilter.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DebugUsersTurboFilter.java index ce2856ad17..110726b505 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DebugUsersTurboFilter.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DebugUsersTurboFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -29,8 +29,8 @@ * * If the level passed as a parameter is of level DEBUG, then the "user" value * taken from the MDC is checked against the configured user list. When the user - * belongs to the list, the request is accepted. Otherwise a NEUTRAL response - * is sent, thus not influencing the filter chain. + * belongs to the list, the request is accepted. Otherwise a NEUTRAL response is + * sent, thus not influencing the filter chain. * * @author Ceki Gülcü * @author Sébastien Pennec diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java index e69ee8f251..5a195be213 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/DuplicateMessageFilterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,10 @@ */ package ch.qos.logback.classic.turbo; -import static org.junit.Assert.*; - -import org.junit.Test; - import ch.qos.logback.core.spi.FilterReply; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class DuplicateMessageFilterTest { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/LRUMessageCacheTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/LRUMessageCacheTest.java index 45590dce63..ccc834f6cc 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/LRUMessageCacheTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/LRUMessageCacheTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,8 @@ */ package ch.qos.logback.classic.turbo; - - -import static org.junit.Assert.assertEquals; - -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.Test; public class LRUMessageCacheTest { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MDCFilterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MDCFilterTest.java index 4657aad809..16d1dde8fd 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MDCFilterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MDCFilterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,16 @@ */ package ch.qos.logback.classic.turbo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.slf4j.MDC; import ch.qos.logback.core.spi.FilterReply; import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.MDC; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; public class MDCFilterTest { @@ -32,7 +32,7 @@ public class MDCFilterTest { private MDCFilter filter; - @Before + @BeforeEach public void setUp() { filter = new MDCFilter(); filter.setOnMatch("ACCEPT"); @@ -43,7 +43,7 @@ public void setUp() { MDC.clear(); } - @After + @AfterEach public void tearDown() { MDC.clear(); } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java index c5fcb49047..059fed3d77 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/MarkerFilterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,14 @@ */ package ch.qos.logback.classic.turbo; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; +import ch.qos.logback.core.spi.FilterReply; +import org.junit.jupiter.api.Test; import org.slf4j.Marker; import org.slf4j.MarkerFactory; -import ch.qos.logback.core.spi.FilterReply; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class MarkerFilterTest { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/NOPTurboFilter.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/NOPTurboFilter.java index a4be245a4a..93e8257d42 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/NOPTurboFilter.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/NOPTurboFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,7 +22,8 @@ public class NOPTurboFilter extends TurboFilter { @Override - public FilterReply decide(final Marker marker, final Logger logger, final Level level, final String format, final Object[] params, final Throwable t) { + public FilterReply decide(final Marker marker, final Logger logger, final Level level, final String format, + final Object[] params, final Throwable t) { return FilterReply.NEUTRAL; } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java deleted file mode 100644 index 763f4b7fff..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.turbo; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ReconfigureOnChangeTest.class, MarkerFilterTest.class, DuplicateMessageFilterTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java deleted file mode 100755 index 4524a2c3ec..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigureOnChangeTest.java +++ /dev/null @@ -1,429 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.turbo; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Level; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.issue.lbclassic135.LoggingRunnable; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.classic.spi.TurboFilterList; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.contention.AbstractMultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; -import ch.qos.logback.core.contention.WaitOnExecutionMultiThreadedHarness; -import ch.qos.logback.core.joran.spi.ConfigurationWatchList; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.status.InfoStatus; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.EnvUtilForTests; -import ch.qos.logback.core.testUtil.FileTestUtil; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; -import org.junit.*; - -import java.io.*; -import java.net.MalformedURLException; -import java.util.List; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; - -import static org.junit.Assert.*; - -@Ignore -public class ReconfigureOnChangeTest { - final static int THREAD_COUNT = 5; - final static int LOOP_LEN = 1000 * 1000; - - int diff = RandomUtil.getPositiveInt(); - - // the space in the file name mandated by - // http://jira.qos.ch/browse/LBCORE-119 - final static String SCAN1_FILE_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/scan 1.xml"; - - final static String G_SCAN1_FILE_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/scan 1.groovy"; - - final static String SCAN_LOGBACK_474_FILE_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/scan_logback_474.xml"; - - final static String INCLUSION_SCAN_TOPLEVEL0_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/inclusion/topLevel0.xml"; - - final static String INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/inclusion/topByResource.xml"; - - final static String INCLUSION_SCAN_INNER0_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/inclusion/inner0.xml"; - - final static String INCLUSION_SCAN_INNER1_AS_STR = "target/test-classes/asResource/inner1.xml"; - - // it actually takes time for Windows to propagate file modification changes - // values below 100 milliseconds can be problematic the same propagation - // latency occurs in Linux but is even larger (>600 ms) - // final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 60; - - int sleepBetweenUpdates = 100; - - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger(this.getClass()); - ExecutorService executorService = loggerContext.getExecutorService(); - - StatusChecker checker = new StatusChecker(loggerContext); - AbstractMultiThreadedHarness harness; - - ThreadPoolExecutor executor = (ThreadPoolExecutor) loggerContext.getExecutorService(); - - int expectedResets = 2; - - @BeforeClass - static public void classSetup() { - FileTestUtil.makeTestOutputDir(); - } - - @Before - public void setUp() { - harness = new WaitOnExecutionMultiThreadedHarness(executor, expectedResets); - } - - @After - public void tearDown() { - } - - void configure(File file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(file); - } - - void configure(InputStream is) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(is); - } - -// void gConfigure(File file) throws JoranException { -// GafferConfigurator gc = new GafferConfigurator(loggerContext); -// gc.run(file); -// } - - RunnableWithCounterAndDone[] buildRunnableArray(File configFile, UpdateType updateType) { - RunnableWithCounterAndDone[] rArray = new RunnableWithCounterAndDone[THREAD_COUNT]; - rArray[0] = new Updater(configFile, updateType); - for (int i = 1; i < THREAD_COUNT; i++) { - rArray[i] = new LoggingRunnable(logger); - } - return rArray; - } - - // Tests whether ConfigurationAction is installing ReconfigureOnChangeFilter - @Test - public void installFilter() throws JoranException, IOException, InterruptedException { - File file = new File(SCAN1_FILE_AS_STR); - configure(file); - List fileList = getConfigurationFileList(loggerContext); - assertThatListContainsFile(fileList, file); - assertThatFirstFilterIsROCF(); - StatusPrinter.print(loggerContext); - } - -// @Test -// public void gafferInstallFilter() throws JoranException, IOException, InterruptedException { -// File file = new File(G_SCAN1_FILE_AS_STR); -// gConfigure(file); -// List fileList = getConfigurationFileList(loggerContext); -// assertThatListContainsFile(fileList, file); -// assertThatFirstFilterIsROCF(); -// -// rocfDetachReconfigurationToNewThreadAndAwaitTermination(); -// -// fileList = getConfigurationFileList(loggerContext); -// assertThatListContainsFile(fileList, file); -// assertThatFirstFilterIsROCF(); -// -// // check that rcof filter installed on two occasions -// assertEquals(2, checker.matchCount("Will scan for changes in")); -// } - - private void rocfDetachReconfigurationToNewThreadAndAwaitTermination() throws InterruptedException { - ReconfigureOnChangeFilter reconfigureOnChangeFilter = (ReconfigureOnChangeFilter) getFirstTurboFilter(); - reconfigureOnChangeFilter.detachReconfigurationToNewThread(); - executorService.shutdown(); - executorService.awaitTermination(1000, TimeUnit.MILLISECONDS); - } - - List getConfigurationFileList(LoggerContext context) { - ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); - return configurationWatchList.getCopyOfFileWatchList(); - } - - @Test(timeout = 4000L) - public void scanWithFileInclusion() throws JoranException, IOException, InterruptedException { - File topLevelFile = new File(INCLUSION_SCAN_TOPLEVEL0_AS_STR); - File innerFile = new File(INCLUSION_SCAN_INNER0_AS_STR); - configure(topLevelFile); - List fileList = getConfigurationFileList(loggerContext); - assertThatListContainsFile(fileList, topLevelFile); - assertThatListContainsFile(fileList, innerFile); - } - - @Test(timeout = 4000L) - public void scanWithResourceInclusion() throws JoranException, IOException, InterruptedException { - File topLevelFile = new File(INCLUSION_SCAN_TOP_BY_RESOURCE_AS_STR); - File innerFile = new File(INCLUSION_SCAN_INNER1_AS_STR); - configure(topLevelFile); - - List fileList = getConfigurationFileList(loggerContext); - assertThatListContainsFile(fileList, topLevelFile); - assertThatListContainsFile(fileList, innerFile); - } - - // See also http://jira.qos.ch/browse/LOGBACK-338 - @Test - public void includeScanViaInputStreamSuppliedConfigFile() throws IOException, JoranException, InterruptedException { - String configurationStr = ""; - configure(new ByteArrayInputStream(configurationStr.getBytes("UTF-8"))); - - ConfigurationWatchList configurationWatchList = ConfigurationWatchListUtil.getConfigurationWatchList(loggerContext); - assertNull(configurationWatchList.getMainURL()); - - ReconfigureOnChangeFilter reconfigureOnChangeFilter = (ReconfigureOnChangeFilter) getFirstTurboFilter(); - // without a top level file, reconfigureOnChangeFilter should not start - assertFalse(reconfigureOnChangeFilter.isStarted()); - } - - @Test(timeout = 4000L) - public void fallbackToSafe() throws IOException, JoranException, InterruptedException { - String path = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_fallbackToSafe-" + diff + ".xml"; - File topLevelFile = new File(path); - writeToFile(topLevelFile, " "); - configure(topLevelFile); - - writeToFile(topLevelFile, "\n" + " "); - - rocfDetachReconfigurationToNewThreadAndAwaitTermination(); - - checker.assertContainsMatch(Status.WARN, "Falling back to previously registered safe configuration."); - checker.assertContainsMatch(Status.INFO, "Re-registering previous fallback configuration once more"); - - assertThatFirstFilterIsROCF(); - } - - @Test(timeout = 4000L) - public void fallbackToSafeWithIncludedFile() throws IOException, JoranException, InterruptedException { - String topLevelFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_top-" + diff + ".xml"; - String innerFileAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "reconfigureOnChangeConfig_inner-" + diff + ".xml"; - File topLevelFile = new File(topLevelFileAsStr); - writeToFile(topLevelFile, " "); - - File innerFile = new File(innerFileAsStr); - writeToFile(innerFile, " "); - configure(topLevelFile); - writeToFile(innerFile, "\n\n"); - rocfDetachReconfigurationToNewThreadAndAwaitTermination(); - - checker.assertContainsMatch(Status.WARN, "Falling back to previously registered safe configuration."); - checker.assertContainsMatch(Status.INFO, "Re-registering previous fallback configuration once more"); - - assertThatFirstFilterIsROCF(); - } - - // check for deadlocks - @Test(timeout = 4000L) - public void scan_LOGBACK_474() throws JoranException, IOException, InterruptedException { - File file = new File(SCAN_LOGBACK_474_FILE_AS_STR); - configure(file); - - RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(file, UpdateType.TOUCH); - harness.execute(runnableArray); - - loggerContext.getStatusManager().add(new InfoStatus("end of execution ", this)); - - verify(expectedResets); - } - - private void assertThatListContainsFile(List fileList, File file) { - // conversion to absolute file seems to work nicely - assertTrue(fileList.contains(file.getAbsoluteFile())); - } - - private TurboFilter getFirstTurboFilter() { - TurboFilterList turboFilterList = loggerContext.getTurboFilterList(); - return turboFilterList.get(0); - } - - private void assertThatFirstFilterIsROCF() { - assertTrue(getFirstTurboFilter() instanceof ReconfigureOnChangeFilter); - } - - private void verify(int expected) { - StatusChecker checker = new StatusChecker(loggerContext); - // StatusPrinter.print(loggerContext); - checker.assertIsErrorFree(); - - int effectiveResets = checker.matchCount(CoreConstants.RESET_MSG_PREFIX); - - String failMsg = "effective=" + effectiveResets + ", expected=" + expected; - - // there might be more effective resets than the expected amount - // since the harness may be sleeping while a reset occurs - assertTrue(failMsg, expected <= effectiveResets && (expected + 2) >= effectiveResets); - - } - - ReconfigureOnChangeFilter initROCF() throws MalformedURLException { - ReconfigureOnChangeFilter rocf = new ReconfigureOnChangeFilter(); - rocf.setContext(loggerContext); - File file = new File(SCAN1_FILE_AS_STR); - ConfigurationWatchListUtil.setMainWatchURL(loggerContext, file.toURI().toURL()); - rocf.start(); - return rocf; - } - - @Test - @Ignore - public void directPerfTest() throws MalformedURLException { - if (EnvUtilForTests.isLinux()) { - // for some reason this test does not pass on Linux (AMD 64 bit, - // Dual Core Opteron 170) - return; - } - - ReconfigureOnChangeFilter rocf = initROCF(); - assertTrue(rocf.isStarted()); - - for (int i = 0; i < 30; i++) { - directLoop(rocf); - } - double avg = directLoop(rocf); - System.out.println("directPerfTest: " + avg); - } - - public double directLoop(ReconfigureOnChangeFilter rocf) { - long start = System.nanoTime(); - for (int i = 0; i < LOOP_LEN; i++) { - rocf.decide(null, logger, Level.DEBUG, " ", null, null); - } - long end = System.nanoTime(); - return (end - start) / (1.0d * LOOP_LEN); - } - - @Ignore - @Test - public void indirectPerfTest() throws MalformedURLException { - if (EnvUtilForTests.isLinux()) { - // for some reason this test does not pass on Linux (AMD 64 bit, - // Dual Core - // Opteron 170) - return; - } - - ReconfigureOnChangeFilter rocf = initROCF(); - assertTrue(rocf.isStarted()); - loggerContext.addTurboFilter(rocf); - logger.setLevel(Level.ERROR); - - indirectLoop(); - double avg = indirectLoop(); - System.out.println(avg); - // the reference was computed on Orion (Ceki's computer) - @SuppressWarnings("unused") - long referencePerf = 68; - //BogoPerf.assertDuration(avg, referencePerf, CoreConstants.REFERENCE_BIPS); - } - - void addInfo(String msg, Object o) { - loggerContext.getStatusManager().add(new InfoStatus(msg, o)); - } - - public double indirectLoop() { - long start = System.nanoTime(); - for (int i = 0; i < LOOP_LEN; i++) { - logger.debug("hello"); - } - long end = System.nanoTime(); - return (end - start) / (1.0d * LOOP_LEN); - } - - enum UpdateType { - TOUCH, MALFORMED, MALFORMED_INNER - } - - void writeToFile(File file, String contents) throws IOException { - FileWriter fw = new FileWriter(file); - fw.write(contents); - fw.close(); - } - - class Updater extends RunnableWithCounterAndDone { - File configFile; - UpdateType updateType; - - Updater(File configFile, UpdateType updateType) { - this.configFile = configFile; - this.updateType = updateType; - } - - Updater(File configFile) { - this(configFile, UpdateType.TOUCH); - } - - public void run() { - while (!isDone()) { - try { - Thread.sleep(sleepBetweenUpdates); - } catch (InterruptedException e) { - } - if (isDone()) { - return; - } - counter++; - ReconfigureOnChangeTest.this.addInfo("***settting last modified", this); - switch (updateType) { - case TOUCH: - touchFile(); - break; - case MALFORMED: - try { - malformedUpdate(); - } catch (IOException e) { - e.printStackTrace(); - fail("malformedUpdate failed"); - } - break; - case MALFORMED_INNER: - try { - malformedInnerUpdate(); - } catch (IOException e) { - e.printStackTrace(); - fail("malformedInnerUpdate failed"); - } - } - } - } - - private void malformedUpdate() throws IOException { - writeToFile(configFile, "\n" + " \n" + ""); - } - - private void malformedInnerUpdate() throws IOException { - writeToFile(configFile, "\n" + " \n" + ""); - } - - void touchFile() { - configFile.setLastModified(System.currentTimeMillis()); - } - } - -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java deleted file mode 100644 index 0eb7c26d01..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/ReconfigurePerf.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.turbo; - -import java.io.File; -import java.io.IOException; - -import ch.qos.logback.core.testUtil.EnvUtilForTests; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.classic.ClassicTestConstants; -import ch.qos.logback.classic.Logger; -import ch.qos.logback.classic.LoggerContext; -import ch.qos.logback.classic.issue.lbclassic135.LoggingRunnable; -import ch.qos.logback.classic.joran.JoranConfigurator; -import ch.qos.logback.core.contention.MultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; -import ch.qos.logback.core.joran.spi.JoranException; - -@Ignore -public class ReconfigurePerf { - final static int THREAD_COUNT = 500; - // final static int LOOP_LEN = 1000 * 1000; - - // the space in the file name mandated by - // http://jira.qos.ch/browse/LBCORE-119 - final static String CONF_FILE_AS_STR = ClassicTestConstants.INPUT_PREFIX + "turbo/scan_perf.xml"; - - // it actually takes time for Windows to propagate file modification changes - // values below 100 milliseconds can be problematic the same propagation - // latency occurs in Linux but is even larger (>600 ms) - final static int DEFAULT_SLEEP_BETWEEN_UPDATES = 110; - - int sleepBetweenUpdates = DEFAULT_SLEEP_BETWEEN_UPDATES; - - static int numberOfCycles = 100; - static int totalTestDuration; - - LoggerContext loggerContext = new LoggerContext(); - Logger logger = loggerContext.getLogger(this.getClass()); - MultiThreadedHarness harness; - - @Before - public void setUp() { - // take into account propagation latency occurs on Linux - if (EnvUtilForTests.isLinux()) { - sleepBetweenUpdates = 850; - totalTestDuration = sleepBetweenUpdates * numberOfCycles; - } else { - totalTestDuration = sleepBetweenUpdates * numberOfCycles * 2; - } - harness = new MultiThreadedHarness(totalTestDuration); - } - - void configure(File file) throws JoranException { - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(loggerContext); - jc.doConfigure(file); - } - - RunnableWithCounterAndDone[] buildRunnableArray() { - RunnableWithCounterAndDone[] rArray = new RunnableWithCounterAndDone[THREAD_COUNT]; - for (int i = 0; i < THREAD_COUNT; i++) { - rArray[i] = new LoggingRunnable(logger); - } - return rArray; - } - - // Tests whether ConfigurationAction is installing ReconfigureOnChangeFilter - @Test - public void scan1() throws JoranException, IOException, InterruptedException { - File file = new File(CONF_FILE_AS_STR); - configure(file); - System.out.println("Running scan1()"); - doRun(); - } - - void doRun() throws InterruptedException { - RunnableWithCounterAndDone[] runnableArray = buildRunnableArray(); - harness.execute(runnableArray); - } -} diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Event.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Event.java index 01378954e0..3732a2976e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Event.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Event.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCache.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCache.java index 71615e6ad8..d03fdc401e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCache.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCache.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,7 +19,7 @@ import java.util.Map; /** - * An lru cache based on Java's LinkedHashMap. + * An LRU cache based on Java's LinkedHashMap. * * @author Ceki Gulcu * diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCacheTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCacheTest.java index fd11fa21d5..ece416eec7 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCacheTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/LRUCacheTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,15 @@ */ package ch.qos.logback.classic.turbo.lru; -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.util.LinkedList; import java.util.List; -import org.junit.Ignore; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; -@Ignore +@Disabled public class LRUCacheTest { @Test @@ -71,11 +71,12 @@ void doScenario(int simulationLen, int cacheSize, int worldSize) { simulator.simulate(scenario, lruCache, tlruCache); // assertEquals(tlruCache.keyList(), lruCache.keyList()); long end = System.nanoTime(); - System.out.println("cacheSize=" + cacheSize + ", worldSize=" + worldSize + ", elapsed time=" + ((end - start) / (1000 * 1000)) + " in millis"); + System.out.println("cacheSize=" + cacheSize + ", worldSize=" + worldSize + ", elapsed time=" + + ((end - start) / (1000 * 1000)) + " in millis"); } @Test - @Ignore + @Disabled // slow test that is known to pass public void multiThreadedScenario() throws InterruptedException { int cacheSize = 100; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Simulator.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Simulator.java index 7e20d4f7d9..f2e674d004 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Simulator.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/Simulator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,12 @@ */ package ch.qos.logback.classic.turbo.lru; -import static org.junit.Assert.assertEquals; - import java.util.ArrayList; import java.util.List; import java.util.Random; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class Simulator { Random random; @@ -53,7 +53,8 @@ public List> generateScenario(int len) { return scenario; } - public void simulate(List> scenario, LRUCache lruCache, T_LRUCache tlruCache) { + public void simulate(List> scenario, LRUCache lruCache, + T_LRUCache tlruCache) { for (Event e : scenario) { if (e.put) { lruCache.put(e.k, e.k); @@ -62,7 +63,7 @@ public void simulate(List> scenario, LRUCache lruC String r0 = lruCache.get(e.k); String r1 = tlruCache.get(e.k); if (!multiThreaded) { - // if the simulation is used in a multi-threaded + // if the simulation is used in a multithreaded // context, then the state of lruCache may be different than // that of tlruCache. In single threaded mode, they should // return the same values all the time diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_Entry.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_Entry.java index 348f14c66a..693ab9adfe 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_Entry.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_Entry.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_LRUCache.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_LRUCache.java index 0be4b4e733..d735d29db0 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_LRUCache.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/T_LRUCache.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/X_LRUCache.java b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/X_LRUCache.java index e792c48911..77f4c46a34 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/X_LRUCache.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/turbo/lru/X_LRUCache.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,7 +19,7 @@ import java.util.Map; /** - * An lru cache based on Java's LinkedHashMap. + * An LRU cache based on Java's LinkedHashMap. * * @author Ceki Gulcu * diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerAutoConfigTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerAutoConfigTest.java index e307fc1ea1..5a54ffa3a3 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerAutoConfigTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerAutoConfigTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,6 @@ */ package ch.qos.logback.classic.util; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.slf4j.LoggerFactory; - import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.LoggerContext; @@ -29,28 +20,37 @@ import ch.qos.logback.core.Appender; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.CoreConstants; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class ContextInitializerAutoConfigTest { org.slf4j.Logger logger = LoggerFactory.getLogger(this.getClass()); Logger root = (Logger) LoggerFactory.getLogger("root"); - @Before + @BeforeEach public void setUp() throws Exception { logger.debug("Hello-didily-odily"); } - @After + @AfterEach public void tearDown() throws Exception { System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY); System.clearProperty(CoreConstants.STATUS_LISTENER_CLASS_KEY); } @Test - @Ignore - // this test works only if logback-test.xml or logback.xml files are on the classpath. + @Disabled + // this test works only if logback-test.xml or logback.xml files are on the + // classpath. // However, this is something we try to avoid in order to simplify the life - // of users trying to follows the manual and logback-examples from an IDE + // of users trying to follow the manual and logback-examples from an IDE public void autoconfig() { LoggerContext iLoggerFactory = (LoggerContext) LoggerFactory.getILoggerFactory(); iLoggerFactory.reset(); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerTest.java index 48f578968a..28687eb0b7 100755 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/ContextInitializerTest.java @@ -1,39 +1,16 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.classic.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; -import static org.junit.Assume.assumeTrue; - -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URL; -import java.util.Enumeration; -import java.util.List; -import java.util.Vector; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - import ch.qos.logback.classic.ClassicConstants; import ch.qos.logback.classic.ClassicTestConstants; import ch.qos.logback.classic.Logger; @@ -47,26 +24,51 @@ import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.testUtil.TrivialStatusListener; import ch.qos.logback.core.util.Loader; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Enumeration; +import java.util.List; +import java.util.Vector; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static org.junit.jupiter.api.Assumptions.assumeTrue; public class ContextInitializerTest { + static final String PATH_TO_META_INF_CONF_SERVICE = "META-INF/services/ch.qos.logback.classic.spi.Configurator"; + static final String FAKE_META_INF_SERVICES = "FAKE_META_INF_SERVICES_ch_qos_logback_classic_spi_Configurator"; LoggerContext loggerContext = new LoggerContext(); Logger root = loggerContext.getLogger(Logger.ROOT_LOGGER_NAME); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY); System.clearProperty(CoreConstants.STATUS_LISTENER_CLASS_KEY); + //ClassicEnvUtil.testServiceLoaderClassLoader = null; MockConfigurator.context = null; } @Test - @Ignore - // this test works only if logback-test.xml or logback.xml files are on the classpath. + @Disabled + // this test works only if logback-test.xml or logback.xml files are on the + // classpath. // However, this is something we try to avoid in order to simplify the life // of users trying to follow the manual and logback-examples from an IDE public void reset() throws JoranException { @@ -88,7 +90,7 @@ public void autoConfigFromSystemProperties() throws JoranException { doAutoConfigFromSystemProperties(ClassicTestConstants.INPUT_PREFIX + "autoConfig.xml"); doAutoConfigFromSystemProperties("autoConfigAsResource.xml"); // test passing a URL. note the relative path syntax with file:src/test/... - doAutoConfigFromSystemProperties("file:" + ClassicTestConstants.INPUT_PREFIX + "autoConfig.xml"); + doAutoConfigFromSystemProperties("file://./" + ClassicTestConstants.INPUT_PREFIX + "autoConfig.xml"); } public void doAutoConfigFromSystemProperties(String val) throws JoranException { @@ -99,12 +101,23 @@ public void doAutoConfigFromSystemProperties(String val) throws JoranException { assertNotNull(appender); } + // this test as constructed cannot run in a modular environment since + // ServiceLoader will not honor providers specified in a provider-configuration file (META-INF/..) + // if module-info.java in the same module declares a provider + // + // https://docs.oracle.com/en/java/javase/18/docs/api/java.base/java/util/ServiceLoader.html# + // + //In a provider-configuration file, any mention of a service provider that is deployed + // in a named module is ignored. This is to avoid duplicates that would otherwise arise + // when a named module has both a provides directive and a provider-configuration file + // that mention the same service provider. + @Disabled @Test public void autoConfigFromServiceLoaderJDK6andAbove() throws Exception { assumeTrue(!isJDK5()); - setupMockServiceLoader(); + ClassLoader mockClassLoader = buildMockServiceLoader(this.getClass().getClassLoader()); assertNull(MockConfigurator.context); - new ContextInitializer(loggerContext).autoConfig(); + new ContextInitializer(loggerContext).autoConfig(mockClassLoader); assertNotNull(MockConfigurator.context); assertSame(loggerContext, MockConfigurator.context); } @@ -112,9 +125,9 @@ public void autoConfigFromServiceLoaderJDK6andAbove() throws Exception { @Test public void autoConfigFromServiceLoaderJDK5() throws Exception { assumeTrue(isJDK5()); - setupMockServiceLoader(); + ClassLoader mockClassLoader = buildMockServiceLoader(this.getClass().getClassLoader()); assertNull(MockConfigurator.context); - new ContextInitializer(loggerContext).autoConfig(); + new ContextInitializer(loggerContext).autoConfig(mockClassLoader); assertNull(MockConfigurator.context); } @@ -125,10 +138,10 @@ public void autoStatusListener() throws JoranException { assertEquals(0, statusListenerList.size()); doAutoConfigFromSystemProperties(ClassicTestConstants.INPUT_PREFIX + "autoConfig.xml"); statusListenerList = loggerContext.getStatusManager().getCopyOfStatusListenerList(); - assertTrue(statusListenerList.size() + " should be 1", statusListenerList.size() == 1); + assertTrue(statusListenerList.size() == 1, statusListenerList.size() + " should be 1"); // LOGBACK-767 TrivialStatusListener tsl = (TrivialStatusListener) statusListenerList.get(0); - assertTrue("expecting at least one event in list", tsl.list.size() > 0); + assertTrue(tsl.list.size() > 0, "expecting at least one event in list"); } @Test @@ -138,17 +151,18 @@ public void autoOnConsoleStatusListener() throws JoranException { assertEquals(0, sll.size()); doAutoConfigFromSystemProperties(ClassicTestConstants.INPUT_PREFIX + "autoConfig.xml"); sll = loggerContext.getStatusManager().getCopyOfStatusListenerList(); - assertTrue(sll.size() + " should be 1", sll.size() == 1); + assertTrue(sll.size() == 1, sll.size() + " should be 1"); } @Test public void shouldConfigureFromXmlFile() throws MalformedURLException, JoranException { - LoggerContext loggerContext = new LoggerContext(); - ContextInitializer initializer = new ContextInitializer(loggerContext); assertNull(loggerContext.getObject(CoreConstants.SAFE_JORAN_CONFIGURATION)); - URL configurationFileUrl = Loader.getResource("BOO_logback-test.xml", Thread.currentThread().getContextClassLoader()); - initializer.configureByResource(configurationFileUrl); + URL configurationFileUrl = Loader.getResource("BOO_logback-test.xml", + Thread.currentThread().getContextClassLoader()); + DefaultJoranConfigurator joranConfigurator = new DefaultJoranConfigurator(); + joranConfigurator.setContext(loggerContext); + joranConfigurator.configureByResource(configurationFileUrl); assertNotNull(loggerContext.getObject(CoreConstants.SAFE_JORAN_CONFIGURATION)); } @@ -167,12 +181,11 @@ public void shouldConfigureFromXmlFile() throws MalformedURLException, JoranExce @Test public void shouldThrowExceptionIfUnexpectedConfigurationFileExtension() throws JoranException { - LoggerContext loggerContext = new LoggerContext(); - ContextInitializer initializer = new ContextInitializer(loggerContext); - URL configurationFileUrl = Loader.getResource("README.txt", Thread.currentThread().getContextClassLoader()); try { - initializer.configureByResource(configurationFileUrl); + DefaultJoranConfigurator joranConfigurator = new DefaultJoranConfigurator(); + joranConfigurator.setContext(loggerContext); + joranConfigurator.configureByResource(configurationFileUrl); fail("Should throw LogbackException"); } catch (LogbackException expectedException) { // pass @@ -185,16 +198,25 @@ private static boolean isJDK5() { return jdk5; } - private void setupMockServiceLoader() { - final ClassLoader realLoader = EnvUtil.class.getClassLoader(); - EnvUtil.testServiceLoaderClassLoader = new WrappedClassLoader(realLoader) { + private ClassLoader buildMockServiceLoader(ClassLoader realLoader) { + + //final ClassLoader realLoader = ClassicEnvUtil.class.getClassLoader(); + ClassLoader wrapperClassLoader = new WrappedClassLoader(realLoader) { + + @Override + public String toString() { + return "wrapperClassLoader: " + super.toString(); + } @Override public Enumeration getResources(String name) throws IOException { final Enumeration r; - if (name.endsWith("META-INF/services/ch.qos.logback.classic.spi.Configurator")) { + if (name.endsWith(PATH_TO_META_INF_CONF_SERVICE)) { + System.out.println("Hit on " + PATH_TO_META_INF_CONF_SERVICE); Vector vs = new Vector(); - URL u = super.getResource("FAKE_META_INF_SERVICES_ch_qos_logback_classic_spi_Configurator"); + URL u = super.getResource(FAKE_META_INF_SERVICES); + Assertions.assertNotNull(u); + System.out.println("Found url: " + u); vs.add(u); return vs.elements(); } else { @@ -203,6 +225,8 @@ public Enumeration getResources(String name) throws IOException { return r; } }; + + return wrapperClassLoader; } static class WrappedClassLoader extends ClassLoader { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationIntegrationTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationIntegrationTest.java index 3098203b4f..68a426e436 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationIntegrationTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/InitializationIntegrationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,19 @@ */ package ch.qos.logback.classic.util; -import static org.junit.Assert.assertNotNull; - -import org.junit.Test; -import org.slf4j.LoggerFactory; - import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; +import org.slf4j.LoggerFactory; + +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * @author Ceki Gülcü */ +@Disabled public class InitializationIntegrationTest { @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/LevelToSyslogSeverityTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/LevelToSyslogSeverityTest.java index af14591271..cc31b7123b 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/LevelToSyslogSeverityTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/LevelToSyslogSeverityTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,13 @@ */ package ch.qos.logback.classic.util; -import static org.junit.Assert.assertEquals; - -import org.junit.Test; - import ch.qos.logback.classic.Level; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.core.net.SyslogConstants; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; public class LevelToSyslogSeverityTest { diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackListener1159.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackListener1159.java new file mode 100644 index 0000000000..8dc64cf499 --- /dev/null +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackListener1159.java @@ -0,0 +1,56 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.classic.util; + +import java.io.IOException; + +import ch.qos.logback.classic.issue.logback1159.LoggingError; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusListener; +import ch.qos.logback.core.status.ErrorStatus; + +/** + * This class should be in a folder relating to the issue being tested. However, we place it here for reaasons related + * to JMPS package access rules. + */ +public class LogbackListener1159 extends ContextAwareBase implements StatusListener, LifeCycle { + private boolean started; + + @Override + public void start() { + this.started = true; + } + + @Override + public void stop() { + this.started = false; + } + + @Override + public boolean isStarted() { + return this.started; + } + + @Override + public void addStatusEvent(final Status status) { + if (status instanceof ErrorStatus && status.getThrowable() instanceof IOException) { + System.out.println("*************************LogbackListener.addStatusEvent"); + throw new LoggingError(status.getMessage(), status.getThrowable()); + } + } + +} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackMDCAdapterTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackMDCAdapterTest.java index 5c6de129cc..314abe9f0d 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackMDCAdapterTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/LogbackMDCAdapterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,19 +13,15 @@ */ package ch.qos.logback.classic.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; +import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CountDownLatch; -import org.junit.Test; -import ch.qos.logback.core.testUtil.RandomUtil; - public class LogbackMDCAdapterTest { final static String A_SUFFIX = "A_SUFFIX"; @@ -36,21 +32,20 @@ public class LogbackMDCAdapterTest { private final LogbackMDCAdapter mdcAdapter = new LogbackMDCAdapter(); /** - * Test that CopyOnInheritThreadLocal does not barf when the - * MDC hashmap is null + * Test that CopyOnInheritThreadLocal does not barf when the MDC hashmap is null * * @throws InterruptedException */ @Test public void LOGBACK_442() throws InterruptedException { Map parentHM = getMapFromMDCAdapter(mdcAdapter); - assertNull(parentHM); + Assertions.assertNull(parentHM); ChildThreadForMDCAdapter childThread = new ChildThreadForMDCAdapter(mdcAdapter); childThread.start(); childThread.join(); - assertTrue(childThread.successul); - assertNull(childThread.childHM); + Assertions.assertTrue(childThread.successul); + Assertions.assertNull(childThread.childHM); } @Test @@ -64,14 +59,18 @@ public void removeInexistentKey() { } @Test + @Disabled public void sequenceWithGet() { mdcAdapter.put("k0", "v0"); - Map map0 = mdcAdapter.copyOnThreadLocal.get(); + Map map0 = mdcAdapter.getPropertyMap(); mdcAdapter.get("k0"); mdcAdapter.put("k1", "v1"); // no map copy required - // verify that map0 is the same instance and that value was updated - assertSame(map0, mdcAdapter.copyOnThreadLocal.get()); + Map witness = new HashMap<>(); + witness.put("k0", "v0"); + witness.put("k1", "v1"); + + Assertions.assertEquals(witness, mdcAdapter.getPropertyMap()); } @Test @@ -80,25 +79,39 @@ public void sequenceWithGetPropertyMap() { Map map0 = mdcAdapter.getPropertyMap(); // point 0 mdcAdapter.put("k0", "v1"); // new map should be created // verify that map0 is that in point 0 - assertEquals("v0", map0.get("k0")); + Assertions.assertEquals("v0", map0.get("k0")); + } + + @Test + public void basicGetPropertyMap() { + mdcAdapter.put("k0", "v0"); + mdcAdapter.put("k1", "v1"); + + Map map0 = mdcAdapter.getPropertyMap(); // point 0 + mdcAdapter.put("k0", "v1"); // new map should be created + // verify that map0 is that in point 0 + Assertions.assertEquals("v0", map0.get("k0")); + Assertions.assertEquals("v1", map0.get("k1")); + } @Test + @Disabled public void sequenceWithCopyContextMap() { mdcAdapter.put("k0", "v0"); - Map map0 = mdcAdapter.copyOnThreadLocal.get(); + Map map0 = mdcAdapter.getPropertyMap(); mdcAdapter.getCopyOfContextMap(); mdcAdapter.put("k1", "v1"); // no map copy required // verify that map0 is the same instance and that value was updated - assertSame(map0, mdcAdapter.copyOnThreadLocal.get()); + Assertions.assertSame(map0, mdcAdapter.getPropertyMap()); } // ================================================= /** - * Test that LogbackMDCAdapter does not copy its hashmap when a child - * thread inherits it. + * Test that LogbackMDCAdapter does not copy its hashmap when a child thread + * inherits it. * * @throws InterruptedException */ @@ -115,52 +128,53 @@ public void noCopyOnInheritenceTest() throws InterruptedException { mdcAdapter.put(firstKey, firstKey + B_SUFFIX); childThread.join(); - assertNull(mdcAdapter.get(secondKey)); - assertTrue(childThread.successful); + Assertions.assertNull(mdcAdapter.get(secondKey)); + Assertions.assertTrue(childThread.successful); Map parentHM = getMapFromMDCAdapter(mdcAdapter); - assertTrue(parentHM != childThread.childHM); + Assertions.assertTrue(parentHM != childThread.childHM); HashMap parentHMWitness = new HashMap(); parentHMWitness.put(firstKey, firstKey + B_SUFFIX); - assertEquals(parentHMWitness, parentHM); + Assertions.assertEquals(parentHMWitness, parentHM); HashMap childHMWitness = new HashMap(); childHMWitness.put(secondKey, secondKey + A_SUFFIX); - assertEquals(childHMWitness, childThread.childHM); + Assertions.assertEquals(childHMWitness, childThread.childHM); } - // see also http://jira.qos.ch/browse/LBCLASSIC-253 + // see also https://jira.qos.ch/browse/LOGBACK-325 @Test public void clearOnChildThreadShouldNotAffectParent() throws InterruptedException { String firstKey = "x" + diff; String secondKey = "o" + diff; mdcAdapter.put(firstKey, firstKey + A_SUFFIX); - assertEquals(firstKey + A_SUFFIX, mdcAdapter.get(firstKey)); + Assertions.assertEquals(firstKey + A_SUFFIX, mdcAdapter.get(firstKey)); Thread clearer = new ChildThread(mdcAdapter, firstKey, secondKey) { @Override public void run() { mdcAdapter.clear(); - assertNull(mdcAdapter.get(firstKey)); + Assertions.assertNull(mdcAdapter.get(firstKey)); } }; clearer.start(); clearer.join(); - assertEquals(firstKey + A_SUFFIX, mdcAdapter.get(firstKey)); + Assertions.assertEquals(firstKey + A_SUFFIX, mdcAdapter.get(firstKey)); } - // see http://jira.qos.ch/browse/LBCLASSIC-289 + // see https://jira.qos.ch/browse/LOGBACK-434 // this test used to fail without synchronization code in LogbackMDCAdapter @Test public void nearSimultaneousPutsShouldNotCauseConcurrentModificationException() throws InterruptedException { // For the weirdest reason, modifications to mdcAdapter must be done // before the definition anonymous ChildThread class below. Otherwise, the - // map in the child thread, the one contained in mdcAdapter.copyOnInheritThreadLocal, + // map in the child thread, the one contained in + // mdcAdapter.copyOnInheritThreadLocal, // is null. How strange is that? // let the map have lots of elements so that copying it takes time @@ -185,12 +199,12 @@ public void run() { mdcAdapter.put("K" + i, "V" + i); } childThread.join(); - assertTrue(childThread.successful); + Assertions.assertTrue(childThread.successful); } Map getMapFromMDCAdapter(LogbackMDCAdapter lma) { - ThreadLocal> copyOnThreadLocal = lma.copyOnThreadLocal; - return copyOnThreadLocal.get(); + ThreadLocal> tlMap = lma.readWriteThreadLocalMap; + return tlMap.get(); } // ========================== various thread classes @@ -229,7 +243,8 @@ class ChildThread extends Thread { this(logbackMDCAdapter, firstKey, secondKey, null); } - ChildThread(LogbackMDCAdapter logbackMDCAdapter, String firstKey, String secondKey, CountDownLatch countDownLatch) { + ChildThread(LogbackMDCAdapter logbackMDCAdapter, String firstKey, String secondKey, + CountDownLatch countDownLatch) { super("chil"); this.logbackMDCAdapter = logbackMDCAdapter; this.firstKey = firstKey; @@ -240,11 +255,11 @@ class ChildThread extends Thread { @Override public void run() { logbackMDCAdapter.put(secondKey, secondKey + A_SUFFIX); - assertNull(logbackMDCAdapter.get(firstKey)); + Assertions.assertNull(logbackMDCAdapter.get(firstKey)); if (countDownLatch != null) countDownLatch.countDown(); - assertNotNull(logbackMDCAdapter.get(secondKey)); - assertEquals(secondKey + A_SUFFIX, logbackMDCAdapter.get(secondKey)); + Assertions.assertNotNull(logbackMDCAdapter.get(secondKey)); + Assertions.assertEquals(secondKey + A_SUFFIX, logbackMDCAdapter.get(secondKey)); successful = true; childHM = getMapFromMDCAdapter(logbackMDCAdapter); diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/LoggerNameUtilTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/LoggerNameUtilTest.java index e4be285948..7af6b9c11a 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/LoggerNameUtilTest.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/LoggerNameUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,12 @@ */ package ch.qos.logback.classic.util; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.assertEquals; - +import static org.junit.jupiter.api.Assertions.assertEquals; public class LoggerNameUtilTest { @Test diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockConfigurator.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/MockConfigurator.java index 69b9fc8384..bb1b558a08 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockConfigurator.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/MockConfigurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,13 +15,17 @@ import ch.qos.logback.classic.LoggerContext; import ch.qos.logback.classic.spi.Configurator; +import ch.qos.logback.classic.spi.ConfiguratorRank; +import ch.qos.logback.core.Context; import ch.qos.logback.core.spi.ContextAwareBase; +@ConfiguratorRank(ConfiguratorRank.CUSTOM_LOW_PRIORITY) public class MockConfigurator extends ContextAwareBase implements Configurator { - static LoggerContext context = null; + static Context context = null; - public void configure(LoggerContext loggerContext) { - context = loggerContext; + public ExecutionStatus configure(LoggerContext aContext) { + context = aContext; + return ExecutionStatus.NEUTRAL; } } diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/PackageTest.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/PackageTest.java deleted file mode 100755 index af623d6bbc..0000000000 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.classic.util; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ContextInitializerTest.class, ContextInitializerAutoConfigTest.class, LogbackMDCAdapterTest.class, LevelToSyslogSeverityTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/TestHelper.java b/logback-classic/src/test/java/ch/qos/logback/classic/util/TestHelper.java index ec4368ca56..975b3d2770 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/TestHelper.java +++ b/logback-classic/src/test/java/ch/qos/logback/classic/util/TestHelper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,33 +13,8 @@ */ package ch.qos.logback.classic.util; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - public class TestHelper { - private static final Method ADD_SUPPRESSED_METHOD; - - static { - Method method = null; - try { - method = Throwable.class.getMethod("addSuppressed", Throwable.class); - } catch (NoSuchMethodException e) { - // ignore, will get thrown in Java < 7 - } - ADD_SUPPRESSED_METHOD = method; - } - - public static boolean suppressedSupported() { - return ADD_SUPPRESSED_METHOD != null; - } - - public static void addSuppressed(Throwable outer, Throwable suppressed) throws InvocationTargetException, IllegalAccessException { - if (suppressedSupported()) { - ADD_SUPPRESSED_METHOD.invoke(outer, suppressed); - } - } - static public Throwable makeNestedException(int level) { if (level == 0) { return new Exception("nesting level=" + level); @@ -50,6 +25,7 @@ static public Throwable makeNestedException(int level) { /** * Usage: + * *

      * String s = "123";
      * positionOf("1").in(s) < positionOf("3").in(s)
diff --git a/logback-classic/src/test/java/integrator/Activator.java b/logback-classic/src/test/java/integrator/Activator.java
index b4abb2bf72..5d420f2869 100644
--- a/logback-classic/src/test/java/integrator/Activator.java
+++ b/logback-classic/src/test/java/integrator/Activator.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -26,6 +26,7 @@
 
 /**
  * A BundleActivator which invokes slf4j loggers
+ * 
  * @author Ceki Gülcü
  *
  */
diff --git a/logback-classic/src/test/java/org/dummy/DummyLBAppender.java b/logback-classic/src/test/java/org/dummy/DummyLBAppender.java
index 1542a4f042..611dbca2db 100644
--- a/logback-classic/src/test/java/org/dummy/DummyLBAppender.java
+++ b/logback-classic/src/test/java/org/dummy/DummyLBAppender.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-classic/src/test/java/org/dummy/Log4jInvocation.java b/logback-classic/src/test/java/org/dummy/Log4jInvocation.java
index 9cd8beb23e..f8d67d7430 100644
--- a/logback-classic/src/test/java/org/dummy/Log4jInvocation.java
+++ b/logback-classic/src/test/java/org/dummy/Log4jInvocation.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,17 +13,16 @@
  */
 package org.dummy;
 
-import static org.junit.Assert.assertEquals;
-
-import org.apache.log4j.Logger;
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.LoggerFactory;
-
 import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.classic.PatternLayout;
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import org.apache.log4j.Logger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactory;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * Used to test log4j-over-slf4j
@@ -39,7 +38,7 @@ public class Log4jInvocation {
     LoggerContext lc;
     ch.qos.logback.classic.Logger rootLogger;
 
-    @Before
+    @BeforeEach
     public void fixture() {
         lc = (LoggerContext) LoggerFactory.getILoggerFactory();
         lc.reset();
@@ -85,6 +84,7 @@ public void callerData() {
         assertEquals(HELLO, event.getMessage());
 
         assertEquals(1, listAppender.stringList.size());
-        assertEquals("TRACE [" + Log4jInvocation.class.getName() + "] basic-test - Hello", listAppender.stringList.get(0));
+        assertEquals("TRACE [" + Log4jInvocation.class.getName() + "] basic-test - Hello",
+                listAppender.stringList.get(0));
     }
 }
diff --git a/logback-classic/src/test/java/org/slf4j/impl/PackageTest.java b/logback-classic/src/test/java/org/slf4j/impl/PackageTest.java
deleted file mode 100644
index 0a13e35a1b..0000000000
--- a/logback-classic/src/test/java/org/slf4j/impl/PackageTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package org.slf4j.impl;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-import ch.qos.logback.classic.LoggerPerfTest;
-
-@RunWith(Suite.class)
-@SuiteClasses({ RecursiveInitializationTest.class, LoggerPerfTest.class, InitializationOutputTest.class })
-public class PackageTest {
-
-}
\ No newline at end of file
diff --git a/logback-classic/src/test/java/org/slf4j/impl/InitializationOutputTest.java b/logback-classic/src/test/java/org/slf4j/implTest/InitializationOutputTest.java
similarity index 77%
rename from logback-classic/src/test/java/org/slf4j/impl/InitializationOutputTest.java
rename to logback-classic/src/test/java/org/slf4j/implTest/InitializationOutputTest.java
index 4be240c1b6..31bccd49f5 100644
--- a/logback-classic/src/test/java/org/slf4j/impl/InitializationOutputTest.java
+++ b/logback-classic/src/test/java/org/slf4j/implTest/InitializationOutputTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -11,16 +11,7 @@
  * under the terms of the GNU Lesser General Public License version 2.1
  * as published by the Free Software Foundation.
  */
-package org.slf4j.impl;
-
-import static org.junit.Assert.assertEquals;
-
-import java.io.PrintStream;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.LoggerFactoryFriend;
+package org.slf4j.implTest;
 
 import ch.qos.logback.classic.ClassicConstants;
 import ch.qos.logback.classic.ClassicTestConstants;
@@ -28,6 +19,14 @@
 import ch.qos.logback.core.status.NopStatusListener;
 import ch.qos.logback.core.testUtil.RandomUtil;
 import ch.qos.logback.core.testUtil.TeeOutputStream;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.LoggerFactoryFriend;
+
+import java.io.PrintStream;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * @author Ceki Gülcü
@@ -39,10 +38,10 @@ public class InitializationOutputTest {
     TeeOutputStream tee;
     PrintStream original;
 
-    @Before
+    @BeforeEach
     public void setUp() {
         original = System.out;
-        // tee will output bytes on System out but it will also
+        // tee will output bytes on System.out but it will also
         // collect them so that the output can be compared against
         // some expected output data
 
@@ -53,7 +52,7 @@ public void setUp() {
         System.setOut(new PrintStream(tee));
     }
 
-    @After
+    @AfterEach
     public void tearDown() {
         System.setOut(original);
         System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
@@ -62,7 +61,8 @@ public void tearDown() {
 
     @Test
     public void noOutputIfContextHasAStatusListener() {
-        System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, ClassicTestConstants.INPUT_PREFIX + "issue/logback292.xml");
+        System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY,
+                ClassicTestConstants.INPUT_PREFIX + "issue/logback292.xml");
         System.setProperty(CoreConstants.STATUS_LISTENER_CLASS_KEY, NopStatusListener.class.getName());
 
         LoggerFactoryFriend.reset();
diff --git a/logback-classic/src/test/java/org/slf4j/impl/MultithreadedInitializationTest.java b/logback-classic/src/test/java/org/slf4j/implTest/MultithreadedInitializationTest.java
old mode 100755
new mode 100644
similarity index 79%
rename from logback-classic/src/test/java/org/slf4j/impl/MultithreadedInitializationTest.java
rename to logback-classic/src/test/java/org/slf4j/implTest/MultithreadedInitializationTest.java
index 880b92e1d8..2a5e22a48c
--- a/logback-classic/src/test/java/org/slf4j/impl/MultithreadedInitializationTest.java
+++ b/logback-classic/src/test/java/org/slf4j/implTest/MultithreadedInitializationTest.java
@@ -1,7 +1,30 @@
-package org.slf4j.impl;
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package org.slf4j.implTest;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
+import ch.qos.logback.classic.ClassicConstants;
+import ch.qos.logback.classic.ClassicTestConstants;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.LoggerFactoryFriend;
+import org.slf4j.helpers.SubstituteLogger;
 
 import java.util.List;
 import java.util.Random;
@@ -9,18 +32,8 @@
 import java.util.concurrent.CyclicBarrier;
 import java.util.concurrent.atomic.AtomicLong;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.slf4j.LoggerFactoryFriend;
-import org.slf4j.helpers.SubstituteLogger;
-
-import ch.qos.logback.classic.ClassicConstants;
-import ch.qos.logback.classic.ClassicTestConstants;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.read.ListAppender;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 public class MultithreadedInitializationTest {
 
@@ -31,13 +44,14 @@ public class MultithreadedInitializationTest {
     int diff = new Random().nextInt(10000);
     String loggerName = "org.slf4j.impl.MultithreadedInitializationTest";
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
-        System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, ClassicTestConstants.INPUT_PREFIX + "listAppender.xml");
+        System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY,
+                ClassicTestConstants.INPUT_PREFIX + "listAppender.xml");
         LoggerFactoryFriend.reset();
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
         System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
     }
@@ -60,7 +74,8 @@ public void multiThreadedInitialization() throws InterruptedException, BrokenBar
     }
 
     private List getRecordedEvents() {
-        ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+        ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory
+                .getLogger(Logger.ROOT_LOGGER_NAME);
 
         ListAppender la = (ListAppender) root.getAppender("LIST");
         assertNotNull(la);
diff --git a/logback-classic/src/test/java/org/slf4j/impl/RecursiveInitializationTest.java b/logback-classic/src/test/java/org/slf4j/implTest/RecursiveInitializationTest.java
similarity index 80%
rename from logback-classic/src/test/java/org/slf4j/impl/RecursiveInitializationTest.java
rename to logback-classic/src/test/java/org/slf4j/implTest/RecursiveInitializationTest.java
index 741b063dee..6cc9657be5 100644
--- a/logback-classic/src/test/java/org/slf4j/impl/RecursiveInitializationTest.java
+++ b/logback-classic/src/test/java/org/slf4j/implTest/RecursiveInitializationTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -11,11 +11,11 @@
  * under the terms of the GNU Lesser General Public License version 2.1
  * as published by the Free Software Foundation.
  */
-package org.slf4j.impl;
+package org.slf4j.implTest;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactoryFriend;
@@ -23,20 +23,20 @@
 import ch.qos.logback.classic.ClassicConstants;
 import ch.qos.logback.classic.LoggerContext;
 import ch.qos.logback.core.testUtil.RandomUtil;
-import ch.qos.logback.core.testUtil.StatusChecker;
+import ch.qos.logback.core.status.testUtil.StatusChecker;
 import ch.qos.logback.core.util.StatusPrinter;
 
 public class RecursiveInitializationTest {
 
     int diff = RandomUtil.getPositiveInt();
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
         System.setProperty(ClassicConstants.CONFIG_FILE_PROPERTY, "recursiveInit.xml");
         LoggerFactoryFriend.reset();
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
         System.clearProperty(ClassicConstants.CONFIG_FILE_PROPERTY);
     }
diff --git a/logback-classic/src/test/java/org/slf4j/test_osgi/BundleTest.java b/logback-classic/src/test/java/org/slf4j/test_osgi/BundleTest.java
index 6aadd515f5..36d111652f 100644
--- a/logback-classic/src/test/java/org/slf4j/test_osgi/BundleTest.java
+++ b/logback-classic/src/test/java/org/slf4j/test_osgi/BundleTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,27 +13,32 @@
  */
 package org.slf4j.test_osgi;
 
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
 import java.io.File;
 
-import junit.framework.TestCase;
+import static org.junit.jupiter.api.Assertions.*;
 
-public class BundleTest extends TestCase {
+public class BundleTest  {
 
     FrameworkErrorListener fel = new FrameworkErrorListener();
     CheckingBundleListener mbl = new CheckingBundleListener();
 
     FelixHost felixHost = new FelixHost(fel, mbl);
 
-    protected void setUp() throws Exception {
-        super.setUp();
+    @BeforeEach
+    public void setUp() throws Exception {
         felixHost.doLaunch();
     }
 
+    @AfterEach
     protected void tearDown() throws Exception {
-        super.tearDown();
         felixHost.stop();
     }
 
+    @Test
     public void testSmoke() {
         System.out.println("===========" + new File(".").getAbsolutePath());
         mbl.dumpAll();
diff --git a/logback-classic/src/test/java/org/slf4j/test_osgi/CheckingBundleListener.java b/logback-classic/src/test/java/org/slf4j/test_osgi/CheckingBundleListener.java
index 9b80597594..5ec11a263f 100644
--- a/logback-classic/src/test/java/org/slf4j/test_osgi/CheckingBundleListener.java
+++ b/logback-classic/src/test/java/org/slf4j/test_osgi/CheckingBundleListener.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -29,7 +29,8 @@ public void bundleChanged(BundleEvent be) {
     }
 
     private void dump(BundleEvent be) {
-        System.out.println("BundleEvent:" + ", source " + be.getSource() + ", bundle=" + be.getBundle() + ", type=" + be.getType());
+        System.out.println("BundleEvent:" + ", source " + be.getSource() + ", bundle=" + be.getBundle() + ", type="
+                + be.getType());
 
     }
 
diff --git a/logback-classic/src/test/java/org/slf4j/test_osgi/FelixHost.java b/logback-classic/src/test/java/org/slf4j/test_osgi/FelixHost.java
index 35b730e84e..8cdac1e8db 100644
--- a/logback-classic/src/test/java/org/slf4j/test_osgi/FelixHost.java
+++ b/logback-classic/src/test/java/org/slf4j/test_osgi/FelixHost.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -54,7 +54,8 @@ public void doLaunch() {
         // configMap.put(FelixConstants.EMBEDDED_EXECUTION_PROP, "true");
         // Add core OSGi packages to be exported from the class path
         // via the system bundle.
-        configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES, "org.osgi.framework; version=1.3.0," + "org.osgi.service.packageadmin; version=1.2.0,"
+        configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES,
+                "org.osgi.framework; version=1.3.0," + "org.osgi.service.packageadmin; version=1.2.0,"
                         + "org.osgi.service.startlevel; version=1.0.0," + "org.osgi.service.url; version=1.0.0");
 
         configMap.put(Constants.FRAMEWORK_STORAGE_CLEAN, Constants.FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT);
@@ -68,7 +69,8 @@ public void doLaunch() {
             List list = new ArrayList();
 
             // list.add(new HostActivator());
-            configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "org.xml.sax, org.xml.sax.helpers, javax.xml.parsers, javax.naming");
+            configMap.put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
+                    "org.xml.sax, org.xml.sax.helpers, javax.xml.parsers, javax.naming");
             configMap.put(FelixConstants.SYSTEMBUNDLE_ACTIVATORS_PROP, list);
             configMap.put("felix.log.level", "4");
 
@@ -80,7 +82,8 @@ public void doLaunch() {
             // otherProps.put(Constants.FRAMEWORK_STORAGE, "bundles");
 
             otherProps.put(AutoProcessor.AUTO_DEPLOY_DIR_PROPERTY, AutoProcessor.AUTO_DEPLOY_DIR_VALUE);
-            otherProps.put(AutoProcessor.AUTO_DEPLOY_ACTION_PROPERTY, AutoProcessor.AUTO_DEPLOY_START_VALUE + "," + AutoProcessor.AUTO_DEPLOY_INSTALL_VALUE);
+            otherProps.put(AutoProcessor.AUTO_DEPLOY_ACTION_PROPERTY,
+                    AutoProcessor.AUTO_DEPLOY_START_VALUE + "," + AutoProcessor.AUTO_DEPLOY_INSTALL_VALUE);
 
             BundleContext felixBudleContext = felix.getBundleContext();
 
diff --git a/logback-classic/src/test/java/org/slf4j/test_osgi/FrameworkErrorListener.java b/logback-classic/src/test/java/org/slf4j/test_osgi/FrameworkErrorListener.java
index beaad11dd9..fdd86d2d16 100644
--- a/logback-classic/src/test/java/org/slf4j/test_osgi/FrameworkErrorListener.java
+++ b/logback-classic/src/test/java/org/slf4j/test_osgi/FrameworkErrorListener.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -35,7 +35,8 @@ private void dump(FrameworkEvent fe) {
         if (t != null) {
             tString = t.toString();
         }
-        System.out.println("Framework ERROR:" + ", source " + fe.getSource() + ", bundle=" + fe.getBundle() + ", ex=" + tString);
+        System.out.println(
+                "Framework ERROR:" + ", source " + fe.getSource() + ", bundle=" + fe.getBundle() + ", ex=" + tString);
         if (t != null) {
             t.printStackTrace();
         }
diff --git a/logback-classic/src/test/resources/recursiveInit.xml b/logback-classic/src/test/resources/recursiveInit.xml
index 2dc899d686..c7ef95ec0c 100644
--- a/logback-classic/src/test/resources/recursiveInit.xml
+++ b/logback-classic/src/test/resources/recursiveInit.xml
@@ -8,7 +8,7 @@
   
 
                                                
-  
+  
     
   
     
diff --git a/logback-core-blackbox/LICENSE.txt b/logback-core-blackbox/LICENSE.txt
new file mode 100644
index 0000000000..0d92689462
--- /dev/null
+++ b/logback-core-blackbox/LICENSE.txt
@@ -0,0 +1,15 @@
+Logback LICENSE
+---------------
+
+Logback: the reliable, generic, fast and flexible logging framework.
+Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+
+This program and the accompanying materials are dual-licensed under
+either the terms of the Eclipse Public License v2.0 as published by
+the Eclipse Foundation
+ 
+  or (per the licensee's choosing)
+ 
+under the terms of the GNU Lesser General Public License version 2.1
+as published by the Free Software Foundation.
+
diff --git a/logback-core-blackbox/pom.xml b/logback-core-blackbox/pom.xml
new file mode 100644
index 0000000000..d5882ab395
--- /dev/null
+++ b/logback-core-blackbox/pom.xml
@@ -0,0 +1,75 @@
+
+
+
+    4.0.0
+
+    
+        ch.qos.logback
+        logback-parent
+        1.5.28-SNAPSHOT
+    
+
+    logback-core-blackbox
+    jar
+    Logback Core Blackbox Testing
+    Logback Core Blackbox Testing Module
+
+    
+
+        
+            ch.qos.logback
+            logback-core
+        
+
+        
+            org.codehaus.janino
+            janino
+            compile
+        
+
+        
+            org.fusesource.jansi
+            jansi
+            compile
+        
+
+        
+            org.tukaani
+            xz
+            compile
+        
+
+    
+
+    
+        
+            
+                org.apache.maven.plugins
+                maven-surefire-plugin
+                
+                    
+                        default-test
+                        
+                            
+                            true
+                            plain
+                            false
+                        
+                    
+
+                
+            
+
+        
+    
+
diff --git a/logback-core/src/test/input/compress1.copy b/logback-core-blackbox/src/test/blackboxInput/compress1.original
similarity index 100%
rename from logback-core/src/test/input/compress1.copy
rename to logback-core-blackbox/src/test/blackboxInput/compress1.original
diff --git a/logback-core/src/test/input/compress2.copy b/logback-core-blackbox/src/test/blackboxInput/compress2.original
similarity index 100%
rename from logback-core/src/test/input/compress2.copy
rename to logback-core-blackbox/src/test/blackboxInput/compress2.original
diff --git a/logback-core-blackbox/src/test/blackboxInput/compress3.original b/logback-core-blackbox/src/test/blackboxInput/compress3.original
new file mode 100644
index 0000000000..885e4175d1
--- /dev/null
+++ b/logback-core-blackbox/src/test/blackboxInput/compress3.original
@@ -0,0 +1,34 @@
+
+
+
+  
+    LOGBack Home
+  
+
+
+
+
+  

LOGBack project

+ +

LOGBack is intended as a successor to the popular log4j + project. It was also designed by Ceki Gülcü, the founder + of the log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. +

+ +

LOGBack's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, LOGBack is + divided into three modules, Core, Classic and Access. +

+ +

The Core module lays the groundwork for the other two + modules. The Classic module can be assimilated to an improved + version of log4j. The Access module integrates with Servlet + containers to provide HTPP-access log functionality. Note that you + can easily build your own modules on top of the Core module. +

+ + + +
+ diff --git a/logback-core-blackbox/src/test/blackboxInput/compress4.original b/logback-core-blackbox/src/test/blackboxInput/compress4.original new file mode 100644 index 0000000000..885e4175d1 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/compress4.original @@ -0,0 +1,34 @@ + + + + + LOGBack Home + + + + + +

LOGBack project

+ +

LOGBack is intended as a successor to the popular log4j + project. It was also designed by Ceki Gülcü, the founder + of the log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. +

+ +

LOGBack's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, LOGBack is + divided into three modules, Core, Classic and Access. +

+ +

The Core module lays the groundwork for the other two + modules. The Classic module can be assimilated to an improved + version of log4j. The Access module integrates with Servlet + containers to provide HTPP-access log functionality. Note that you + can easily build your own modules on top of the Core module. +

+ + + +
+ diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0.xml new file mode 100644 index 0000000000..aed2ac367f --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0_NoJoran.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0_NoJoran.xml new file mode 100644 index 0000000000..f0af770984 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if0_NoJoran.xml @@ -0,0 +1,30 @@ + + + + + + ki1 + val1 + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifNew.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifNew.xml new file mode 100644 index 0000000000..fae14c7484 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifNew.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem.xml new file mode 100644 index 0000000000..609a51e010 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem.xml @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem_NoJoran.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem_NoJoran.xml new file mode 100644 index 0000000000..5d47fb2b78 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifSystem_NoJoran.xml @@ -0,0 +1,29 @@ + + + + + + + + + sysKey + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithExec.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithExec.xml new file mode 100644 index 0000000000..db927f07d4 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithExec.xml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse.xml new file mode 100644 index 0000000000..63a02edeb1 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse_NoJoran.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse_NoJoran.xml new file mode 100644 index 0000000000..60299b5d07 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/ifWithoutElse_NoJoran.xml @@ -0,0 +1,27 @@ + + + + + + ki1 + val1 + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty.xml new file mode 100644 index 0000000000..650f9fa629 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty.xml @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty_NoJoran.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty_NoJoran.xml new file mode 100644 index 0000000000..28a92500e4 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/if_localProperty_NoJoran.xml @@ -0,0 +1,31 @@ + + + + + + + Ki1 + Val1 + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedA.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedA.xml new file mode 100644 index 0000000000..ecb4360a1c --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedA.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedB.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedB.xml new file mode 100644 index 0000000000..84974b7158 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/includedB.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf.xml new file mode 100644 index 0000000000..0751237770 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf_NoJoran.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf_NoJoran.xml new file mode 100644 index 0000000000..e8f91afd2e --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedIf_NoJoran.xml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedInclude.xml b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedInclude.xml new file mode 100644 index 0000000000..4aa8617701 --- /dev/null +++ b/logback-core-blackbox/src/test/blackboxInput/joran/conditional/nestedInclude.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/BlackboxCoreTestConstants.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/BlackboxCoreTestConstants.java new file mode 100644 index 0000000000..379535455b --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/BlackboxCoreTestConstants.java @@ -0,0 +1,23 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox; + +public class BlackboxCoreTestConstants { + + public static final String TEST_SRC_PREFIX = "src/test/"; + public static final String TEST_INPUT_PREFIX = TEST_SRC_PREFIX + "blackboxInput/"; + public static final String JORAN_INPUT_PREFIX = TEST_INPUT_PREFIX + "joran/"; + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/COWArrayListConcurrencyTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/COWArrayListConcurrencyTest.java new file mode 100644 index 0000000000..c407a4b2dc --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/COWArrayListConcurrencyTest.java @@ -0,0 +1,188 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox; + +//import ch.qos.logback.core.Appender; +//import ch.qos.logback.core.AppenderBase; +//import ch.qos.logback.core.Context; +//import ch.qos.logback.core.ContextBase; +//import ch.qos.logback.core.spi.AppenderAttachableImpl; +import org.junit.jupiter.api.Disabled; +//import org.junit.jupiter.api.Test; +// +//import java.util.concurrent.ExecutionException; +//import java.util.concurrent.ExecutorService; +//import java.util.concurrent.Executors; +// +//import java.util.ArrayList; +//import java.util.Arrays; +//import java.util.List; +//import java.util.concurrent.Future; +//import java.util.concurrent.locks.ReentrantLock; +// +//import static org.assertj.core.api.Fail.fail; + +@Disabled +public class COWArrayListConcurrencyTest { +// +// //private static final int LIST_SIZE = 1_000_000; +// private static final int LOOP_LEN = 1_0; +// private static final int RECONFIGURE_DELAY = 1; +// +// ReentrantLock reconfigureLock = new ReentrantLock(true); +// ReentrantLock writeLock = new ReentrantLock(true); +// +// private static int THREAD_COUNT = 200; //Runtime.getRuntime().availableProcessors()*200; +// //private static int THREAD_COUNT = 5000; +// +// private final ExecutorService tasksExecutor = Executors.newVirtualThreadPerTaskExecutor(); +// LoopingRunnable[] loopingThreads = new LoopingRunnable[THREAD_COUNT]; +// ReconfiguringThread[] reconfiguringThreads = new ReconfiguringThread[THREAD_COUNT]; +// Future[] futures = new Future[THREAD_COUNT]; +// +// AppenderAttachableImpl aai = new AppenderAttachableImpl<>(); +// Context context = new ContextBase(); +// +// void reconfigureWithDelay(AppenderAttachableImpl aai) { +// try { +// reconfigureLock.lock(); +// aai.addAppender(makeNewNOPAppender()); +// aai.addAppender(makeNewNOPAppender()); +// delay(RECONFIGURE_DELAY); +// aai.detachAndStopAllAppenders(); +// } finally { +// reconfigureLock.unlock(); +// } +// } +// +// private Appender makeNewNOPAppender() { +// List longList = new ArrayList<>(); +//// for (int j = 0; j < LIST_SIZE; j++) { +//// longList.add(0L); +//// } +// Appender nopAppenderWithDelay = new NOPAppenderWithDelay<>(longList); +// nopAppenderWithDelay.setContext(context); +// nopAppenderWithDelay.start(); +// return nopAppenderWithDelay; +// } +// +// private void delay(int delay) { +// try { +// Thread.sleep(delay); +// } catch (InterruptedException e) { +// throw new RuntimeException(e); +// } +// } +// +// @Test +// void smoke() throws InterruptedException, ExecutionException { +// +// for (int i = 0; i < THREAD_COUNT; i++) { +// System.out.println("i="+i); +// ReconfiguringThread rt = new ReconfiguringThread(aai); +// futures[i] = tasksExecutor.submit(rt); +// reconfiguringThreads[i] = rt; +// } +// +// for (int i = 0; i < THREAD_COUNT; i++) { +// LoopingRunnable loopingThread = new LoopingRunnable(i, aai); +// tasksExecutor.submit(loopingThread); +// loopingThreads[i] = loopingThread; +// } +// +// for (int i = 0; i < THREAD_COUNT; i++) { +// futures[i].get(); +// } +// +// //reconfiguringThread.join(); +// Arrays.stream(loopingThreads).forEach(lt -> lt.active = false); +// +// } +// +// public class NOPAppenderWithDelay extends AppenderBase { +// +// List longList; +// +// NOPAppenderWithDelay(List longList) { +// this.longList = new ArrayList<>(longList); +// } +// +// int i = 0; +// +// @Override +// protected void append(E eventObject) { +// i++; +// try { +// writeLock.lock(); +// if ((i & 0xF) == 0) { +// delay(1); +// } else { +// //longList.stream().map(x-> x+1); +// } +// } finally { +// writeLock.unlock(); +// } +// +// } +// +// } +// +// class ReconfiguringThread extends Thread { +// +// AppenderAttachableImpl aai; +// +// ReconfiguringThread(AppenderAttachableImpl aai) { +// this.aai = aai; +// } +// +// public void run() { +// Thread.yield(); +// for (int i = 0; i < LOOP_LEN; i++) { +// reconfigureWithDelay(aai); +// } +// } +// +// +// } +// +// +// class LoopingRunnable implements Runnable { +// +// int num; +// AppenderAttachableImpl aai; +// public boolean active = true; +// +// LoopingRunnable(int num, AppenderAttachableImpl aai) { +// this.num = num; +// this.aai = aai; +// } +// +// public void run() { +// System.out.println("LoopingRunnable.run.num="+num); +// int i = 0; +// while (active) { +// if ((i & 0xFFFFF) == 0) { +// long id = Thread.currentThread().threadId(); +// System.out.println("thread=" + id + " reconfigure=" + i); +// } +// aai.appendLoopOnAppenders(Integer.toString(i)); +// i++; +// //Thread.yield(); +// } +// } +// } + + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/VersionUtilTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/VersionUtilTest.java new file mode 100644 index 0000000000..0e4060604f --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/VersionUtilTest.java @@ -0,0 +1,47 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox; + +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.util.VersionUtil; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +public class VersionUtilTest { + + + @Test + public void smoke() { + +// { +// long startTime = System.nanoTime(); +// String result = VersionUtil.getVersionOfArtifact(CoreConstants.class); +// long endTime = System.nanoTime(); +// System.out.println(result); +// System.out.println("Took " + (endTime - startTime)/1000L + " micros"); +// } + + + { + long startTime = System.nanoTime(); + String result = VersionUtil.getArtifactVersionBySelfDeclaredProperties(CoreConstants.class, "logback-core"); + long endTime = System.nanoTime(); + System.out.println("Took " + (endTime - startTime)/1000L + " micros"); + assertNotNull(result); + assertTrue(result.startsWith("1.5")); + } + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/appender/JansiConsoleAppenderTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/appender/JansiConsoleAppenderTest.java new file mode 100644 index 0000000000..d366b8df07 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/appender/JansiConsoleAppenderTest.java @@ -0,0 +1,122 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.blackbox.appender; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; + +import ch.qos.logback.core.testUtil.DummyEncoder; +import ch.qos.logback.core.testUtil.XTeeOutputStream; +import org.fusesource.jansi.AnsiConsole; +import org.fusesource.jansi.AnsiPrintStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.io.FilterOutputStream; +import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Field; + +/** + * Redirecting System.out is quite messy. Disable this test in Maven but not in + * Package.class + */ +public class JansiConsoleAppenderTest { + Context context = new ContextBase(); + ConsoleAppender ca = new ConsoleAppender(); + + XTeeOutputStream teeOut; + XTeeOutputStream teeErr; + PrintStream originalOut; + PrintStream originalErr; + + @BeforeEach + public void setUp() { + originalOut = System.out; + originalErr = System.err; + // teeOut will output bytes on System out but it will also + // collect them so that the output can be compared against + // some expected output data + // teeOut = new TeeOutputStream(originalOut); + + // keep the console quiet + //teeOut = new XTeeOutputStream(null); + //teeErr = new XTeeOutputStream(null); + + //System.setOut(new PrintStream(teeOut)); + //System.setErr(new PrintStream(teeErr)); + + // redirect System.out to teeOut and System.err to teeErr + //replace(originalOut, teeOut); + //replace(originalErr, teeErr); + } + + @AfterEach + public void tearDown() { + AnsiConsole.systemUninstall(); + System.setOut(originalOut); + //replace(AnsiConsole.out(), originalOut); + System.setErr(originalErr); + //replace(AnsiConsole.err(), originalErr); + + } + + private void replace(AnsiPrintStream ansiPrintStream, OutputStream os) { + try { + Field field = FilterOutputStream.class.getDeclaredField("out"); + field.setAccessible(true); + OutputStream oldOs = (OutputStream) field.get(ansiPrintStream); + field.set(ansiPrintStream, os); + } catch (Throwable t) { + throw new IllegalStateException("Unable to initialize Jansi for testing", t); + } + } + + public Appender getAppender() { + return new ConsoleAppender<>(); + } + + @Test + public void jansiSystemOut() { + + DummyEncoder dummyEncoder = new DummyEncoder<>(); + ca.setEncoder(dummyEncoder); + ca.setTarget("System.out"); + ca.setContext(context); + ca.setWithJansi(true); + ca.start(); + Assertions.assertTrue(ca.getOutputStream() instanceof AnsiPrintStream); + ca.doAppend(new Object()); + // broken in Jansi 2.x as it uses java.io.FileDescriptor instead of System.out + //Assertions.assertEquals("dummy", teeOut.toString().trim()); + } + + @Test + public void jansiSystemErr() { + DummyEncoder dummyEncoder = new DummyEncoder<>(); + ca.setEncoder(dummyEncoder); + ca.setTarget("System.err"); + ca.setContext(context); + ca.setWithJansi(true); + ca.start(); + Assertions.assertTrue(ca.getOutputStream() instanceof AnsiPrintStream); + ca.doAppend(new Object()); + // broken in Jansi 2.x as it uses java.io.FileDescriptor instead of System.err + //Assertions.assertEquals("dummy", teeErr.toString().trim()); + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysFalseCondition.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysFalseCondition.java new file mode 100644 index 0000000000..eabf5373a0 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysFalseCondition.java @@ -0,0 +1,25 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.boolex; + +import ch.qos.logback.core.boolex.PropertyConditionBase; + +public class AlwaysFalseCondition extends PropertyConditionBase { + + @Override + public boolean evaluate() { + return false; + } +} \ No newline at end of file diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysTrueCondition.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysTrueCondition.java new file mode 100644 index 0000000000..463a2b6623 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/boolex/AlwaysTrueCondition.java @@ -0,0 +1,25 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.boolex; + +import ch.qos.logback.core.boolex.PropertyConditionBase; + +public class AlwaysTrueCondition extends PropertyConditionBase { + + @Override + public boolean evaluate() { + return true; + } +} \ No newline at end of file diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/BlackboxSimpleConfigurator.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/BlackboxSimpleConfigurator.java new file mode 100644 index 0000000000..952598c81e --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/BlackboxSimpleConfigurator.java @@ -0,0 +1,52 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.joran; + +import ch.qos.logback.core.joran.GenericXMLConfigurator; +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.ImplicitModelAction; +import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; + +import java.util.HashMap; +import java.util.function.Supplier; + +public class BlackboxSimpleConfigurator extends GenericXMLConfigurator { + + + HashMap> rulesMap; + + public BlackboxSimpleConfigurator(HashMap> rules) { + this.rulesMap = rules; + } + + @Override + protected void setImplicitRuleSupplier(SaxEventInterpreter interpreter) { + interpreter.setImplicitActionSupplier(() -> new ImplicitModelAction()); + } + + public SaxEventInterpreter getInterpreter() { + return saxEventInterpreter; + } + + @Override + protected void addElementSelectorAndActionAssociations(RuleStore rs) { + for (ElementSelector elementSelector : rulesMap.keySet()) { + Supplier actionSupplier = rulesMap.get(elementSelector); + rs.addRule(elementSelector, actionSupplier); + } + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/CoreBlackboxStatusChecker.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/CoreBlackboxStatusChecker.java new file mode 100644 index 0000000000..b935a09769 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/CoreBlackboxStatusChecker.java @@ -0,0 +1,67 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.joran; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.status.StatusUtil; +import org.junit.jupiter.api.Assertions; + +public class CoreBlackboxStatusChecker extends StatusUtil { + + public CoreBlackboxStatusChecker(StatusManager sm) { + super(sm); + } + + + public CoreBlackboxStatusChecker(Context context) { + super(context); + } + + public void assertContainsMatch(int level, String regex) { + Assertions.assertTrue(containsMatch(level, regex)); + } + + public void assertNoMatch(String regex) { + Assertions.assertFalse(containsMatch(regex)); + } + + public void assertContainsMatch(String regex) { + Assertions.assertTrue(containsMatch(regex)); + } + + public void assertContainsException(Class scanExceptionClass) { + Assertions.assertTrue(containsException(scanExceptionClass)); + } + + public void assertContainsException(Class scanExceptionClass, String msg) { + Assertions.assertTrue(containsException(scanExceptionClass, msg)); + } + + public void assertIsErrorFree() { + Assertions.assertTrue(isErrorFree(0)); + } + + public void assertIsErrorFree(long treshhold) { + Assertions.assertTrue(isErrorFree(treshhold)); + } + + public void assertIsWarningOrErrorFree() { + Assertions.assertTrue(isWarningOrErrorFree(0)); + } + + public void assertErrorCount(int i) { + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/BlackboxTopElementAction.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/BlackboxTopElementAction.java new file mode 100644 index 0000000000..a326084497 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/BlackboxTopElementAction.java @@ -0,0 +1,38 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.joran.action; + +import ch.qos.logback.core.blackbox.model.BlackboxTopModel; +import ch.qos.logback.core.joran.action.BaseModelAction; +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; + +/** + * Add a Model instance at the top of the InterpretationContext stack + * + * @author Ceki Gulcu + */ +public class BlackboxTopElementAction extends BaseModelAction { + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + BlackboxTopModel topModel = new BlackboxTopModel(); + return topModel; + } + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/ext/BlackboxStackAction.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/ext/BlackboxStackAction.java new file mode 100644 index 0000000000..d9af190b8d --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/action/ext/BlackboxStackAction.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.blackbox.joran.action.ext; + +import ch.qos.logback.core.blackbox.model.BlackboxStackModel; +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; + +public class BlackboxStackAction extends BaseModelAction { + + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, ic, name, attributes); + validator.validateNameAttribute(); + return validator.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + BlackboxStackModel stackModel = new BlackboxStackModel(); + stackModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + return stackModel; + } + + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/IfThenElseTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/IfThenElseTest.java new file mode 100644 index 0000000000..d19d1b574e --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/IfThenElseTest.java @@ -0,0 +1,271 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.blackbox.joran.conditional; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.blackbox.BlackboxCoreTestConstants; +import ch.qos.logback.core.blackbox.joran.BlackboxSimpleConfigurator; +import ch.qos.logback.core.blackbox.joran.action.BlackboxTopElementAction; +import ch.qos.logback.core.blackbox.joran.action.ext.BlackboxStackAction; +import ch.qos.logback.core.blackbox.model.BlackboxStackModel; +import ch.qos.logback.core.blackbox.model.BlackboxTopModel; +import ch.qos.logback.core.blackbox.model.processor.BlackboxStackModelHandler; +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.PropertyAction; +import ch.qos.logback.core.joran.conditional.ByPropertiesConditionAction; +import ch.qos.logback.core.joran.conditional.ElseAction; +import ch.qos.logback.core.joran.conditional.IfAction; +import ch.qos.logback.core.joran.conditional.ThenAction; +import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.conditional.ByPropertiesConditionModel; +import ch.qos.logback.core.model.conditional.ElseModel; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.ThenModel; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ImplicitModelHandler; +import ch.qos.logback.core.model.processor.NOPModelHandler; +import ch.qos.logback.core.model.processor.PropertyModelHandler; +import ch.qos.logback.core.model.processor.conditional.ByPropertiesConditionModelHandler; +import ch.qos.logback.core.model.processor.conditional.ElseModelHandler; +import ch.qos.logback.core.model.processor.conditional.IfModelHandler; +import ch.qos.logback.core.model.processor.conditional.ThenModelHandler; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusUtil; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Stack; +import java.util.function.Supplier; + +public class IfThenElseTest { + + Context context = new ContextBase(); + StatusUtil checker = new StatusUtil(context); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + BlackboxSimpleConfigurator simpleConfigurator; + int diff = RandomUtil.getPositiveInt(); + static final String CONDITIONAL_DIR_PREFIX = BlackboxCoreTestConstants.JORAN_INPUT_PREFIX + "conditional/"; + + String ki1 = "ki1"; + String val1 = "val1"; + String sysKey = "sysKey"; + String dynaKey = "dynaKey"; + + @BeforeEach + public void setUp() throws Exception { + HashMap> rulesMap = new HashMap<>(); + rulesMap.put(new ElementSelector("x"), BlackboxTopElementAction::new); + rulesMap.put(new ElementSelector("x/stack"), BlackboxStackAction::new); + rulesMap.put(new ElementSelector("x/property"), PropertyAction::new); + rulesMap.put(new ElementSelector("*/condition"), ByPropertiesConditionAction::new); + rulesMap.put(new ElementSelector("*/if"), IfAction::new); + rulesMap.put(new ElementSelector("*/if/then"), ThenAction::new); + rulesMap.put(new ElementSelector("*/if/else"), ElseAction::new); + + simpleConfigurator = new BlackboxSimpleConfigurator(rulesMap) { + + @Override + protected void addElementSelectorAndActionAssociations(RuleStore rs) { + super.addElementSelectorAndActionAssociations(rs); + + rs.addTransparentPathPart("if"); + rs.addTransparentPathPart("then"); + rs.addTransparentPathPart("else"); + + } + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + defaultProcessor.addHandler(BlackboxTopModel.class, NOPModelHandler::makeInstance); + + defaultProcessor.addHandler(BlackboxStackModel.class, BlackboxStackModelHandler::makeInstance); + defaultProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance); + defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); + defaultProcessor.addHandler(ByPropertiesConditionModel.class, ByPropertiesConditionModelHandler::makeInstance); + defaultProcessor.addHandler(IfModel.class, IfModelHandler::makeInstance); + defaultProcessor.addHandler(ThenModel.class, ThenModelHandler::makeInstance); + defaultProcessor.addHandler(ElseModel.class, ElseModelHandler::makeInstance); + } + }; + + simpleConfigurator.setContext(context); + } + + @AfterEach + public void tearDown() throws Exception { + statusPrinter2.printIfErrorsOccured(context); + System.clearProperty(sysKey); + } + + @Test + public void ifWithExec() throws JoranException { + context.putProperty(ki1, val1); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithExec.xml"); + checker.containsException(org.codehaus.commons.compiler.CompileException.class); + checker.containsMatch(Status.ERROR, "Failed to parse condition"); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void whenContextPropertyIsSet_IfThenBranchIsEvaluated() throws JoranException { + context.putProperty(ki1, val1); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if0.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + + @Test + public void whenContextPropertyIsSet_IfThenBranchIsEvaluated_WithoutJoran() throws JoranException { + context.putProperty(ki1, val1); + + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if0_NoJoran.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void ifWithNew() throws JoranException { + context.putProperty(ki1, val1); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifNew.xml"); + checker.containsMatch(Status.ERROR, IfModelHandler.NEW_OPERATOR_DISALLOWED_MSG); + checker.containsMatch(Status.ERROR, IfModelHandler.NEW_OPERATOR_DISALLOWED_SEE); + verifyConfig(new String[] { "BEGIN", "END" }); + } + + // ---------------------------------------------------------------------------------------------------- + @Test + public void whenLocalPropertyIsSet_IfThenBranchIsEvaluated() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if_localProperty.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + + @Test + public void whenLocalPropertyIsSet_IfThenBranchIsEvaluated_NoJoran() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if_localProperty_NoJoran.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void whenNoPropertyIsDefined_ElseBranchIsEvaluated() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if0.xml"); + verifyConfig(new String[] { "BEGIN", "b", "END" }); + } + + @Test + public void whenNoPropertyIsDefined_ElseBranchIsEvaluated_NoJoran() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "if0_NoJoran.xml"); + verifyConfig(new String[] { "BEGIN", "b", "END" }); + } + // ---------------------------------------------------------------------------------------------------- + + @Test + public void whenContextPropertyIsSet_IfThenBranchIsEvaluated_NO_ELSE_DEFINED() throws JoranException { + context.putProperty(ki1, val1); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + + @Test + public void whenContextPropertyIsSet_IfThenBranchIsEvaluated_NO_ELSE_DEFINED_NoJoran() throws JoranException { + context.putProperty(ki1, val1); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse_NoJoran.xml"); + verifyConfig(new String[] { "BEGIN", "a", "END" }); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void whenNoPropertyIsDefined_IfThenBranchIsNotEvaluated_NO_ELSE_DEFINED() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse.xml"); + verifyConfig(new String[] { "BEGIN", "END" }); + Assertions.assertTrue(checker.isErrorFree(0)); + } + + @Test + public void whenNoPropertyIsDefined_IfThenBranchIsNotEvaluated_NO_ELSE_DEFINED_NoJoran() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse_NoJoran.xml"); + verifyConfig(new String[] { "BEGIN", "END" }); + Assertions.assertTrue(checker.isErrorFree(0)); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void nestedIf() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "nestedIf.xml"); + //StatusPrinter.print(context); + verifyConfig(new String[] { "BEGIN", "a", "c", "END" }); + Assertions.assertTrue(checker.isErrorFree(0)); + } + @Test + public void nestedIf_NoJoran() throws JoranException { + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "nestedIf_NoJoran.xml"); + //StatusPrinter.print(context); + verifyConfig(new String[] { "BEGIN", "a", "c", "END" }); + Assertions.assertTrue(checker.isErrorFree(0)); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void useNonExistenceOfSystemPropertyToDefineAContextProperty() throws JoranException { + Assertions.assertNull(System.getProperty(sysKey)); + Assertions.assertNull(context.getProperty(dynaKey)); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem.xml"); + //System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); + Assertions.assertNotNull(context.getProperty(dynaKey)); + } + @Test + public void useNonExistenceOfSystemPropertyToDefineAContextProperty_NoJoran() throws JoranException { + Assertions.assertNull(System.getProperty(sysKey)); + Assertions.assertNull(context.getProperty(dynaKey)); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem_NoJoran.xml"); + //System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); + Assertions.assertNotNull(context.getProperty(dynaKey)); + } + // ---------------------------------------------------------------------------------------------------- + @Test + public void noContextPropertyShouldBeDefinedIfSystemPropertyExists() throws JoranException { + System.setProperty(sysKey, "a"); + Assertions.assertNull(context.getProperty(dynaKey)); + System.out.println("before " + dynaKey + "=" + context.getProperty(dynaKey)); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem.xml"); + System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); + Assertions.assertNull(context.getProperty(dynaKey)); + } + + @Test + public void noContextPropertyShouldBeDefinedIfSystemPropertyExists_NoJoran() throws JoranException { + System.setProperty(sysKey, "a"); + Assertions.assertNull(context.getProperty(dynaKey)); + System.out.println("before " + dynaKey + "=" + context.getProperty(dynaKey)); + simpleConfigurator.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem_NoJoran.xml"); + System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); + Assertions.assertNull(context.getProperty(dynaKey)); + } + // ---------------------------------------------------------------------------------------------------- + + private void verifyConfig(String[] expected) { + Stack witness = new Stack<>(); + witness.addAll(Arrays.asList(expected)); + + @SuppressWarnings({ "unchecked", "rawtypes" }) + Stack aStack = (Stack) context.getObject(BlackboxStackModelHandler.STACK_TEST); + Assertions.assertEquals(witness, aStack); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilderTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/PropertyEvalScriptBuilderTest.java similarity index 77% rename from logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilderTest.java rename to logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/PropertyEvalScriptBuilderTest.java index 3b07427b9e..d60f00e9d7 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilderTest.java +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/PropertyEvalScriptBuilderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,24 +11,25 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.joran.conditional; +package ch.qos.logback.core.blackbox.joran.conditional; -import static org.junit.Assert.*; - -import ch.qos.logback.core.joran.spi.InterpretationContext; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.joran.conditional.Condition; +import ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; import ch.qos.logback.core.testUtil.RandomUtil; public class PropertyEvalScriptBuilderTest { Context context = new ContextBase(); - InterpretationContext localPropContainer = new InterpretationContext(context, null); + ModelInterpretationContext localPropContainer = new ModelInterpretationContext(context); PropertyEvalScriptBuilder pesb = new PropertyEvalScriptBuilder(localPropContainer); int diff = RandomUtil.getPositiveInt(); @@ -39,27 +40,27 @@ public class PropertyEvalScriptBuilderTest { String isNullScriptStr = "isNull(\"" + k + "\")"; String isDefiedScriptStr = "isDefined(\"" + k + "\")"; - @Before + @BeforeEach public void setUp() { context.setName("c" + diff); pesb.setContext(context); } - @After + @AfterEach public void tearDown() { System.clearProperty(k); } void buildAndAssertTrue(String scriptStr) throws Exception { Condition condition = pesb.build(scriptStr); - assertNotNull(condition); - assertTrue(condition.evaluate()); + Assertions.assertNotNull(condition); + Assertions.assertTrue(condition.evaluate()); } void buildAndAssertFalse(String scriptStr) throws Exception { Condition condition = pesb.build(scriptStr); - assertNotNull(condition); - assertFalse(condition.evaluate()); + Assertions.assertNotNull(condition); + Assertions.assertFalse(condition.evaluate()); } @Test diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/TrivialTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/TrivialTest.java new file mode 100644 index 0000000000..3ffd97fc0e --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/joran/conditional/TrivialTest.java @@ -0,0 +1,30 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.joran.conditional; + +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.fail; + +public class TrivialTest { + + + @Disabled + @Test + public void smoke() { + fail(); + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxStackModel.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxStackModel.java new file mode 100644 index 0000000000..0b8a2cd150 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxStackModel.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.blackbox.model; + +import ch.qos.logback.core.model.NamedModel; + +public class BlackboxStackModel extends NamedModel { + + private static final long serialVersionUID = -2623437394373933695L; + + @Override + protected BlackboxStackModel makeNewInstance() { + return new BlackboxStackModel(); + } +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxTopModel.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxTopModel.java new file mode 100644 index 0000000000..e2460c86e4 --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/BlackboxTopModel.java @@ -0,0 +1,28 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.model; + +import ch.qos.logback.core.model.Model; + +public class BlackboxTopModel extends Model { + + private static final long serialVersionUID = 6378962040610737208L; + + @Override + protected BlackboxTopModel makeNewInstance() { + return new BlackboxTopModel(); + } + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/processor/BlackboxStackModelHandler.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/processor/BlackboxStackModelHandler.java new file mode 100644 index 0000000000..b3d3d3776c --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/model/processor/BlackboxStackModelHandler.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.model.processor; + +import java.util.Stack; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.blackbox.model.BlackboxStackModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class BlackboxStackModelHandler extends ModelHandlerBase { + + static public final String STACK_TEST = "STACK_TEST"; + + public BlackboxStackModelHandler(Context context) { + super(context); + } + + static public BlackboxStackModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new BlackboxStackModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return BlackboxStackModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + BlackboxStackModel stackModel = (BlackboxStackModel) model; + + String name = stackModel.getName(); + + ContextBase contextBase = (ContextBase) context; + + @SuppressWarnings("unchecked") + Stack aStack = (Stack) context.getObject(STACK_TEST); + if(aStack == null) { + aStack = new Stack<>(); + contextBase.putObject(STACK_TEST, aStack); + } + aStack.push(name); + } + + @Override + public void postHandle(ModelInterpretationContext intercon, Model model) throws ModelHandlerException { + } + +} diff --git a/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/rolling/helper/BlackboxWithXZCompressTest.java b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/rolling/helper/BlackboxWithXZCompressTest.java new file mode 100644 index 0000000000..51f1531e0d --- /dev/null +++ b/logback-core-blackbox/src/test/java/ch/qos/logback/core/blackbox/rolling/helper/BlackboxWithXZCompressTest.java @@ -0,0 +1,132 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.blackbox.rolling.helper; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.blackbox.joran.CoreBlackboxStatusChecker; +import ch.qos.logback.core.rolling.helper.CompressionMode; +import ch.qos.logback.core.rolling.helper.Compressor; +//import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.io.*; + +import static ch.qos.logback.core.blackbox.BlackboxCoreTestConstants.TEST_SRC_PREFIX; +import static ch.qos.logback.core.testUtil.CoreTestConstants.OUTPUT_DIR_PREFIX; + +public class BlackboxWithXZCompressTest { + Context context = new ContextBase(); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); + + final String original1 = TEST_SRC_PREFIX + "blackboxInput/compress1.original"; + final String copy1 = TEST_SRC_PREFIX + "blackboxInput/compress1.txt"; + final String compressed1 = OUTPUT_DIR_PREFIX + "compress1.txt.gz"; + + final String original2 = TEST_SRC_PREFIX + "blackboxInput/compress2.original"; + final String copy2 = TEST_SRC_PREFIX + "blackboxInput/compress2.txt"; + final String compressed2 = OUTPUT_DIR_PREFIX + "compress2.txt.gz"; + + final String original3 = TEST_SRC_PREFIX + "blackboxInput/compress3.original"; + final String copy3 = TEST_SRC_PREFIX + "blackboxInput/compress3.txt"; + final String compressed3 = OUTPUT_DIR_PREFIX + "compress3.txt.zip"; + + final String original4 = TEST_SRC_PREFIX + "blackboxInput/compress4.original"; + final String copy4 = TEST_SRC_PREFIX + "blackboxInput/compress4.txt"; + final String compressed4 = OUTPUT_DIR_PREFIX + "compress4.txt.xz"; + + @BeforeEach + public void setUp() throws IOException { + + } + + protected void copySourceFilesAndDeleteCompressedOutputFiles(String originalPathStr, String copyPathStr, String compressedStr) throws IOException { + // Copy source files + // Delete output files + + File originalFile = new File(originalPathStr); + File copyFile = new File(copyPathStr); + copy(originalFile, copyFile); + File compressedFile = new File(compressedStr); + compressedFile.mkdirs(); + compressedFile.delete(); + } + + protected void copy(File src, File dst) throws IOException { + try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst);) { + byte[] buf = new byte[1024]; + int len; + while ((len = in.read(buf)) > 0) { + out.write(buf, 0, len); + } + } + } + + @Test + public void gzTest1() throws Exception { + copySourceFilesAndDeleteCompressedOutputFiles(original1, copy1, compressed1); + Compressor compressor = new Compressor(CompressionMode.GZ); + compressor.setContext(context); + compressor.compress(copy1, compressed1, null); + + + CoreBlackboxStatusChecker checker = new CoreBlackboxStatusChecker(context); + Assertions.assertTrue(checker.isErrorFree(0)); + //Assertions.assertTrue(Compare.gzCompare(compressed1, TEST_SRC_PREFIX + "witness/compress1.txt.gz")); + } + + @Test + public void gzTest2() throws Exception { + copySourceFilesAndDeleteCompressedOutputFiles(original2, copy2, compressed2); + Compressor compressor = new Compressor(CompressionMode.GZ); + compressor.setContext(context); + compressor.compress(copy2, compressed2, null); + + CoreBlackboxStatusChecker checker = new CoreBlackboxStatusChecker(context); + Assertions.assertTrue(checker.isErrorFree(0)); + + //Assertions.assertTrue(Compare.gzCompare(compressed2, TEST_SRC_PREFIX + "witness/compress2.txt.gz")); + } + + @Test + public void zipTest() throws Exception { + copySourceFilesAndDeleteCompressedOutputFiles(original3, copy3, compressed3); + Compressor compressor = new Compressor(CompressionMode.ZIP); + compressor.setContext(context); + compressor.compress(copy3, compressed3, "compress3.txt"); + CoreBlackboxStatusChecker checker = new CoreBlackboxStatusChecker(context); + Assertions.assertTrue(checker.isErrorFree(0)); + + // we don't know how to compare .zip files + // Assertions.assertTrue(Compare.compare(CoreTestConstants.OUTPUT_DIR_PREFIX + // + "compress3.txt.zip", CoreTestConstants.TEST_SRC_PREFIX + // + "witness/compress3.txt.zip")); + } + + @Test + public void xzTest() throws Exception { + copySourceFilesAndDeleteCompressedOutputFiles(original4, copy4, compressed4); + Compressor compressor = new Compressor(CompressionMode.XZ); + compressor.setContext(context); + compressor.compress(copy4, compressed4, null); + statusPrinter2.print(context); + CoreBlackboxStatusChecker checker = new CoreBlackboxStatusChecker(context); + Assertions.assertTrue(checker.isErrorFree(0)); + } +} diff --git a/logback-core-blackbox/src/test/java/module-info.java b/logback-core-blackbox/src/test/java/module-info.java new file mode 100644 index 0000000000..7fed09c129 --- /dev/null +++ b/logback-core-blackbox/src/test/java/module-info.java @@ -0,0 +1,22 @@ +module ch.qos.logback.core.blackbox { + + requires ch.qos.logback.core; + + requires org.junit.jupiter.api; + requires org.junit.jupiter.engine; + + requires janino; + requires commons.compiler; + + requires org.fusesource.jansi; + + requires org.tukaani.xz; + + exports ch.qos.logback.core.blackbox; + exports ch.qos.logback.core.blackbox.boolex; + exports ch.qos.logback.core.blackbox.joran.conditional; + exports ch.qos.logback.core.blackbox.joran; + exports ch.qos.logback.core.blackbox.appender; + exports ch.qos.logback.core.blackbox.rolling.helper; + +} \ No newline at end of file diff --git a/logback-core/.gitignore b/logback-core/.gitignore new file mode 100755 index 0000000000..ae3c172604 --- /dev/null +++ b/logback-core/.gitignore @@ -0,0 +1 @@ +/bin/ diff --git a/logback-core/LICENSE.txt b/logback-core/LICENSE.txt new file mode 100644 index 0000000000..b87c4d056e --- /dev/null +++ b/logback-core/LICENSE.txt @@ -0,0 +1,15 @@ +Logback LICENSE +--------------- + +Logback: the reliable, generic, fast and flexible logging framework. +Copyright (C) 1999-2026, QOS.ch. All rights reserved. + +This program and the accompanying materials are dual-licensed under +either the terms of the Eclipse Public License v2.0 as published by +the Eclipse Foundation + + or (per the licensee's choosing) + +under the terms of the GNU Lesser General Public License version 2.1 +as published by the Free Software Foundation. + diff --git a/logback-core/pom.xml b/logback-core/pom.xml index 81cb60fe38..2ef301606c 100755 --- a/logback-core/pom.xml +++ b/logback-core/pom.xml @@ -3,138 +3,231 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - 4.0.0 - - - ch.qos.logback - logback-parent - 1.3.0-alpha5-SNAPSHOT - - - logback-core - jar - Logback Core Module - logback-core module - - - - org.codehaus.janino - janino - compile - true - - - org.fusesource.jansi - jansi - true - - - javax.mail - javax.mail-api - compile - true - - - com.sun.mail - javax.mail - runtime - true - - - - org.mockito - mockito-core - test - - - javax.servlet - javax.servlet-api - compile - true - - - joda-time - joda-time - test - - - - - - - org.apache.maven.plugins - maven-surefire-plugin - - - none - false - 2 - true - plain - false - - true - - **/All*Test.java - **/PackageTest.java - - **/ConsoleAppenderTest.java - - - - - org.apache.maven.plugins - maven-jar-plugin - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - - - - - bundle-test-jar - package - - test-jar - - - - - - - org.apache.felix - maven-bundle-plugin - true - - - - bundle-manifest - process-classes - - manifest - - - - - - <_noee>true - <_failok>true - ch.qos.logback.core.* - - javax.*;resolution:=optional, - org.xml.*;resolution:=optional, - org.fusesource.jansi;resolution:=optional, - org.codehaus.janino;resolution:=optional, - org.codehaus.commons.compiler;resolution:=optional, - * - - JavaSE-1.6 - - - - - - + 4.0.0 + + + ch.qos.logback + logback-parent + 1.5.28-SNAPSHOT + + + logback-core + jar + Logback Core Module + logback-core module + + + ch.qos.logback.core + + + + + + org.codehaus.janino + janino + compile + true + + + org.codehaus.janino + commons-compiler + compile + true + + + org.fusesource.jansi + jansi + true + + + + org.tukaani + xz + + provided + true + + + + jakarta.mail + jakarta.mail-api + compile + true + + + + org.eclipse.angus + angus-mail + test + + + + jakarta.servlet + jakarta.servlet-api + compile + true + + + + + org.mockito + mockito-core + test + + + + + + + + src/main/java/ + true + + ch/qos/logback/core/logback-core-version.properties + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + + + default-compile + + compile + + + ${jdk.version} + + + + + java21-compile + compile + + compile + + + 21 + + ${project.basedir}/src/main/java21 + + true + + + + + default-testCompile + test-compile + + + **/COWArrayListConcurrencyTest.java + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + + default-test + + + + + --add-opens ch.qos.logback.core/ch.qos.logback.core.testUtil=java.naming + + --add-reads ch.qos.logback.core=ALL-UNNAMED + + classes + 20 + + 5 + true + plain + false + + true + + **/All*Test.java + **/PackageTest.java + + **/ConsoleAppenderTest.java + ch.qos.logback.core.blackbox.rolling.helper.JDKOnlyCompressTest + + + + org.tukaani:xz + + + + + + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + + ${project.build.outputDirectory}/META-INF/MANIFEST.MF + + + + + + bundle-test-jar + package + + test-jar + + + + + + + org.apache.felix + maven-bundle-plugin + + + + bundle-manifest + process-classes + + manifest + + + + + + true + ch.qos.logback.core* + + ch.qos.logback.core*;version="${range;[==,+);${version_cleanup;${project.version}}}", + jakarta.*;resolution:=optional, + org.xml.*;resolution:=optional, + org.fusesource.jansi;resolution:=optional, + org.codehaus.janino;resolution:=optional, + org.codehaus.commons.compiler;resolution:=optional, + org.tukaani.xz;resolution:=optional, + * + + + + + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/Appender.java b/logback-core/src/main/java/ch/qos/logback/core/Appender.java index 581fd8a132..3ecc6757e0 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/Appender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/Appender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,24 +17,53 @@ import ch.qos.logback.core.spi.FilterAttachable; import ch.qos.logback.core.spi.LifeCycle; +/** + * Contract for components responsible for delivering logging events to their + * final destination (console, file, remote server, etc.). + * + *

Implementations are typically configured and managed by a LoggerContext. + * The type parameter E represents the event type the appender consumes (for + * example a log event object). Implementations should honor lifecycle methods + * from {@link LifeCycle} and may be {@link ContextAware} and + * {@link FilterAttachable} to support contextual information and filtering.

+ * + *

Concurrency: appenders are generally invoked by multiple threads. Implementations + * must ensure thread-safety where applicable (for example when writing to shared + * resources). The {@link #doAppend(Object)} method may be called concurrently.

+ * + * @param the event type accepted by this appender + */ public interface Appender extends LifeCycle, ContextAware, FilterAttachable { /** - * Get the name of this appender. The name uniquely identifies the appender. + * Get the name of this appender. The name uniquely identifies the appender + * within its context and is used for configuration and lookup. + * + * @return the appender name, or {@code null} if not set */ String getName(); /** - * This is where an appender accomplishes its work. Note that the argument - * is of type Object. - * @param event + * This is where an appender accomplishes its work: format and deliver the + * provided event to the appender's destination. + * + *

Implementations should apply any configured filters before outputting + * the event. Implementations should avoid throwing runtime exceptions; + * if an error occurs that cannot be handled internally, a {@link LogbackException} + * (or a subtype) may be thrown to indicate a failure during append.

+ * + * @param event the event to append; may not be {@code null} + * @throws LogbackException if the append fails in a way that needs to be + * propagated to the caller */ void doAppend(E event) throws LogbackException; /** * Set the name of this appender. The name is used by other components to - * identify this appender. - * + * identify and reference this appender (for example in configuration or for + * status messages). + * + * @param name the new name for this appender; may be {@code null} to unset */ void setName(String name); diff --git a/logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java index f5a27b55a9..bec03b838f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,10 +22,13 @@ import ch.qos.logback.core.status.WarnStatus; /** - * Sets a skeleton implementation for appenders. + *

Sets a skeleton implementation for appenders.

+ * + *

It is coarsely synchronized in its {@link #doAppend(E)} method.

* - *

For more information about this appender, please refer to the online - * manual at http://logback.qos.ch/manual/appenders.html#AppenderBase + *

+ * For more information about this appender, please refer to the online manual + * at http://logback.qos.ch/manual/appenders.html#AppenderBase * * @author Ceki Gülcü */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/AsyncAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/AsyncAppenderBase.java index c9e4fb6dc4..d0ef05e1e3 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/AsyncAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/AsyncAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,20 +17,26 @@ import ch.qos.logback.core.spi.AppenderAttachableImpl; import ch.qos.logback.core.util.InterruptUtil; +import java.util.ArrayList; import java.util.Iterator; +import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; /** - * This appender and derived classes, log events asynchronously. In order to avoid loss of logging events, this - * appender should be closed. It is the user's responsibility to close appenders, typically at the end of the + * This appender and derived classes, log events asynchronously. In order to + * avoid loss of logging events, this appender should be closed. It is the + * user's responsibility to close appenders, typically at the end of the * application lifecycle. *

- * This appender buffers events in a {@link BlockingQueue}. {@link Worker} thread created by this appender takes - * events from the head of the queue, and dispatches them to the single appender attached to this appender. + * This appender buffers events in a {@link BlockingQueue}. {@link Worker} + * thread created by this appender takes events from the head of the queue, and + * dispatches them to the single appender attached to this appender. *

- * Please refer to the logback manual for - * further information about this appender.

+ * Please refer to the + * logback + * manual for further information about this appender. + *

* * @param * @author Ceki Gülcü @@ -57,19 +63,20 @@ public class AsyncAppenderBase extends UnsynchronizedAppenderBase implemen Worker worker = new Worker(); /** - * The default maximum queue flush time allowed during appender stop. If the - * worker takes longer than this time it will exit, discarding any remaining + * The default maximum queue flush time allowed during appender stop. If the + * worker takes longer than this time it will exit, discarding any remaining * items in the queue */ public static final int DEFAULT_MAX_FLUSH_TIME = 1000; int maxFlushTime = DEFAULT_MAX_FLUSH_TIME; /** - * Is the eventObject passed as parameter discardable? The base class's implementation of this method always returns - * 'false' but sub-classes may (and do) override this method. + * Is the eventObject passed as parameter discardable? The base class's + * implementation of this method always returns 'false' but sub-classes may (and + * do) override this method. *

- * Note that only if the buffer is nearly full are events discarded. Otherwise, when the buffer is "not full" - * all events are logged. + * Note that only if the buffer is nearly full are events discarded. Otherwise, + * when the buffer is "not full" all events are logged. * * @param eventObject * @return - true if the event can be discarded, false otherwise @@ -79,8 +86,8 @@ protected boolean isDiscardable(E eventObject) { } /** - * Pre-process the event prior to queueing. The base class does no pre-processing but sub-classes can - * override this behavior. + * Pre-process the event prior to queueing. The base class does no + * pre-processing but subclasses can override this behavior. * * @param eventObject */ @@ -106,7 +113,8 @@ public void start() { addInfo("Setting discardingThreshold to " + discardingThreshold); worker.setDaemon(true); worker.setName("AsyncAppender-Worker-" + getName()); - // make sure this instance is marked as "started" before staring the worker Thread + // make sure this instance is marked as "started" before starting the + // worker Thread super.start(); worker.start(); } @@ -116,13 +124,14 @@ public void stop() { if (!isStarted()) return; - // mark this appender as stopped so that Worker can also processPriorToRemoval if it is invoking + // mark this appender as stopped so that Worker can also processPriorToRemoval + // if it is invoking // aii.appendLoopOnAppenders // and sub-appenders consume the interruption super.stop(); - // interrupt the worker thread so that it can terminate. Note that the interruption can be consumed - // by sub-appenders + // interrupt the worker thread so that it can terminate. Note that the + // interruption can be consumed by sub-appenders worker.interrupt(); InterruptUtil interruptUtil = new InterruptUtil(context); @@ -134,8 +143,8 @@ public void stop() { // check to see if the thread ended and if not add a warning message if (worker.isAlive()) { - addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately " + blockingQueue.size() - + " queued events were possibly discarded."); + addWarn("Max queue flush timeout (" + maxFlushTime + " ms) exceeded. Approximately " + + blockingQueue.size() + " queued events were possibly discarded."); } else { addInfo("Queue flush finished successfully within timeout."); } @@ -148,10 +157,6 @@ public void stop() { } } - - - - @Override protected void append(E eventObject) { if (isQueueBelowDiscardingThreshold() && isDiscardable(eventObject)) { @@ -161,7 +166,7 @@ protected void append(E eventObject) { put(eventObject); } - private boolean isQueueBelowDiscardingThreshold() { + public boolean isQueueBelowDiscardingThreshold() { return (blockingQueue.remainingCapacity() < discardingThreshold); } @@ -235,10 +240,11 @@ public boolean isNeverBlock() { /** * The remaining capacity available in the blocking queue. *

- * See also {@link java.util.concurrent.BlockingQueue#remainingCapacity() BlockingQueue#remainingCapacity()} + * See also {@link java.util.concurrent.BlockingQueue#remainingCapacity() + * BlockingQueue#remainingCapacity()} * * @return the remaining capacity - * + * */ public int getRemainingCapacity() { return blockingQueue.remainingCapacity(); @@ -288,9 +294,15 @@ public void run() { // loop while the parent is started while (parent.isStarted()) { try { - E e = parent.blockingQueue.take(); - aai.appendLoopOnAppenders(e); - } catch (InterruptedException ie) { + List elements = new ArrayList(); + E e0 = parent.blockingQueue.take(); + elements.add(e0); + parent.blockingQueue.drainTo(elements); + for (E e : elements) { + aai.appendLoopOnAppenders(e); + } + } catch (InterruptedException e1) { + // exit if interrupted break; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java b/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java index dc724808da..9c64497e31 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java +++ b/logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -51,8 +51,7 @@ public class BasicStatusManager implements StatusManager { /** * Add a new status object. * - * @param newStatus - * the status message to add + * @param newStatus the status message to add */ public void add(Status newStatus) { // LBCORE-72: fire event before the count check @@ -106,7 +105,9 @@ public int getCount() { } /** - * This implementation does not allow duplicate installations of OnConsoleStatusListener + * This implementation does not allow duplicate installations of + * OnConsoleStatusListener + * * @param listener */ public boolean add(StatusListener listener) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java b/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java index 53b258335c..4844752ec9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/ConsoleAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,19 +14,27 @@ package ch.qos.logback.core; import java.io.OutputStream; +import java.io.PrintStream; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; import java.util.Arrays; +import java.util.NoSuchElementException; +import java.util.Optional; import ch.qos.logback.core.joran.spi.ConsoleTarget; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.WarnStatus; -import ch.qos.logback.core.util.EnvUtil; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.ReentryGuard; +import ch.qos.logback.core.util.ReentryGuardFactory; /** * ConsoleAppender appends log events to System.out or * System.err using a layout specified by the user. The default * target is System.out. - *

 

+ *

+ *   + *

* For more information about this appender, please refer to the online manual * at http://logback.qos.ch/manual/appenders.html#ConsoleAppender * @@ -40,7 +48,15 @@ public class ConsoleAppender extends OutputStreamAppender { protected ConsoleTarget target = ConsoleTarget.SystemOut; protected boolean withJansi = false; - private final static String WindowsAnsiOutputStream_CLASS_NAME = "org.fusesource.jansi.WindowsAnsiOutputStream"; + private final static String AnsiConsole_CLASS_NAME = "org.fusesource.jansi.AnsiConsole"; + private final static String JANSI2_OUT_METHOD_NAME = "out"; + private final static String JANSI2_ERR_METHOD_NAME = "err"; + private final static String WRAP_SYSTEM_OUT_METHOD_NAME = "wrapSystemOut"; + private final static String WRAP_SYSTEM_ERR_METHOD_NAME = "wrapSystemErr"; + private final static String SYSTEM_INSTALL_METHOD_NAME = "systemInstall"; + private final static Class[] ARGUMENT_TYPES = { PrintStream.class }; + + private final static String CONSOLE_APPENDER_WARNING_URL = CoreConstants.CODES_URL+"#slowConsole"; /** * Sets the value of the Target option. Recognized values are @@ -56,8 +72,8 @@ public void setTarget(String value) { } /** - * Returns the current value of the target property. The default value - * of the option is "System.out". + * Returns the current value of the target property. The default value of + * the option is "System.out". *

* See also {@link #setTarget}. */ @@ -66,45 +82,92 @@ public String getTarget() { } private void targetWarn(String val) { - Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()), this); + Status status = new WarnStatus("[" + val + "] should be one of " + Arrays.toString(ConsoleTarget.values()), + this); status.add(new WarnStatus("Using previously set target, System.out by default.", this)); addStatus(status); } @Override public void start() { + addInfo("NOTE: Writing to the console can be slow. Try to avoid logging to the "); + addInfo("console in production environments, especially in high volume systems."); + addInfo("See also "+CONSOLE_APPENDER_WARNING_URL); OutputStream targetStream = target.getStream(); - // enable jansi only on Windows and only if withJansi set to true - if (EnvUtil.isWindows() && withJansi) { - targetStream = getTargetStreamForWindows(targetStream); + // enable jansi only if withJansi set to true + if (withJansi) { + targetStream = wrapWithJansi(targetStream); } setOutputStream(targetStream); super.start(); } - private OutputStream getTargetStreamForWindows(OutputStream targetStream) { + /** + * Create a ThreadLocal ReentryGuard to prevent recursive appender invocations. + * @return a ReentryGuard instance of type {@link ReentryGuardFactory.GuardType#THREAD_LOCAL THREAD_LOCAL}. + */ + protected ReentryGuard buildReentryGuard() { + return ReentryGuardFactory.makeGuard(ReentryGuardFactory.GuardType.THREAD_LOCAL); + } + + private OutputStream wrapWithJansi(OutputStream targetStream) { try { - addInfo("Enabling JANSI WindowsAnsiOutputStream for the console."); - Object windowsAnsiOutputStream = OptionHelper.instantiateByClassNameAndParameter(WindowsAnsiOutputStream_CLASS_NAME, Object.class, context, - OutputStream.class, targetStream); - return (OutputStream) windowsAnsiOutputStream; + addInfo("Enabling JANSI AnsiPrintStream for the console."); + ClassLoader classLoader = Loader.getClassLoaderOfObject(context); + Class classObj = classLoader.loadClass(AnsiConsole_CLASS_NAME); + + Method systemInstallMethod = classObj.getMethod(SYSTEM_INSTALL_METHOD_NAME); + if(systemInstallMethod != null) { + systemInstallMethod.invoke(null); + } + +// final Optional optSystemInstallMethod = Arrays.stream(classObj.getMethods()) +// .filter(m -> m.getName().equals(SYSTEM_INSTALL_METHOD_NAME)) +// .filter(m -> m.getParameters().length == 0) +// .filter(m -> Modifier.isStatic(m.getModifiers())) +// .findAny(); +// +// if (optSystemInstallMethod.isPresent()) { +// final Method systemInstallMethod = optSystemInstallMethod.orElseThrow(() -> new NoSuchElementException("No systemInstall method present")); +// systemInstallMethod.invoke(null); +// } + + // check for JAnsi 2 + String methodNameJansi2 = target == ConsoleTarget.SystemOut ? JANSI2_OUT_METHOD_NAME + : JANSI2_ERR_METHOD_NAME; + final Optional optOutMethod = Arrays.stream(classObj.getMethods()) + .filter(m -> m.getName().equals(methodNameJansi2)) + .filter(m -> m.getParameters().length == 0) + .filter(m -> Modifier.isStatic(m.getModifiers())) + .filter(m -> PrintStream.class.isAssignableFrom(m.getReturnType())) + .findAny(); + if (optOutMethod.isPresent()) { + final Method outMethod = optOutMethod.orElseThrow(() -> new NoSuchElementException("No out/err method present")); + return (PrintStream) outMethod.invoke(null); + } + + // JAnsi 1 + String methodName = target == ConsoleTarget.SystemOut ? WRAP_SYSTEM_OUT_METHOD_NAME + : WRAP_SYSTEM_ERR_METHOD_NAME; + Method method = classObj.getMethod(methodName, ARGUMENT_TYPES); + return (OutputStream) method.invoke(null, new PrintStream(targetStream)); } catch (Exception e) { - addWarn("Failed to create WindowsAnsiOutputStream. Falling back on the default stream.", e); + addWarn("Failed to create AnsiPrintStream. Falling back on the default stream.", e); } return targetStream; } /** - * @return + * @return whether to use JANSI or not. */ public boolean isWithJansi() { return withJansi; } /** - * If true, this appender will output to a stream which + * If true, this appender will output to a stream provided by the JANSI library. * - * @param withJansi + * @param withJansi whether to use JANSI or not. * @since 1.0.5 */ public void setWithJansi(boolean withJansi) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/Context.java b/logback-core/src/main/java/ch/qos/logback/core/Context.java index a310cb34a8..26016258fb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/Context.java +++ b/logback-core/src/main/java/ch/qos/logback/core/Context.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,11 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.locks.ReentrantLock; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; import ch.qos.logback.core.spi.LifeCycle; import ch.qos.logback.core.spi.PropertyContainer; import ch.qos.logback.core.spi.SequenceNumberGenerator; @@ -39,8 +43,7 @@ public interface Context extends PropertyContainer { StatusManager getStatusManager(); /** - * A Context can act as a store for various objects used by LOGBack - * components. + * A Context can act as a store for various objects used by LOGBack components. * * @return The object stored under 'key'. */ @@ -55,9 +58,9 @@ public interface Context extends PropertyContainer { void putObject(String key, Object value); /** - * Get all the properties for this context as a Map. Note that the returned - * cop might be a copy not the original. Thus, modifying the returned Map will - * have no effect (on the original.) + * Get all the properties for this context as a Map. Note that the returned cop + * might be a copy not the original. Thus, modifying the returned Map will have + * no effect (on the original.) * * @return */ @@ -74,6 +77,7 @@ public interface Context extends PropertyContainer { /** * Get a copy of the property map + * * @return * @since 0.9.20 */ @@ -94,28 +98,28 @@ public interface Context extends PropertyContainer { void setName(String name); /** - * The time at which this context was created, expressed in - * millisecond elapsed since the epoch (1.1.1970). + * The time at which this context was created, expressed in millisecond elapsed + * since the epoch (1.1.1970). * * @return The time as measured when this class was created. */ long getBirthTime(); /** - * Object used for synchronization purposes. - * INTENDED FOR INTERNAL USAGE. + * Object used for synchronization purposes. INTENDED FOR INTERNAL USAGE. */ - Object getConfigurationLock(); - + ReentrantLock getConfigurationLock(); /** * Returns the ScheduledExecutorService for this context. - * @return + * + * @return ScheduledExecutorService * @since 1.1.7 */ - // Apparently ScheduledThreadPoolExecutor has limitation where a task cannot be submitted from - // within a running task. ThreadPoolExecutor does not have this limitation. - // This causes tests failures in SocketReceiverTest.testDispatchEventForEnabledLevel and + // Apparently ScheduledThreadPoolExecutor has limitation where a task cannot be + // submitted from within a running task. ThreadPoolExecutor does not have this limitation. + // This causes tests failures in + // SocketReceiverTest.testDispatchEventForEnabledLevel and // ServerSocketReceiverFunctionalTest.testLogEventFromClient. ScheduledExecutorService getScheduledExecutorService(); @@ -124,24 +128,68 @@ public interface Context extends PropertyContainer { * tasks in a separate thread. * * @return the executor for this context. - * @since 1.0.0 - * @deprecated use {@link#getScheduledExecutorService()} instead + * @since 1.0.00 (undeprecated in 1.4.7) + * */ ExecutorService getExecutorService(); + + /** + * Return an alternate {@link ExecutorService} used for one task per thread execution. + * @return ExecutorService + */ + default ExecutorService getAlternateExecutorService() { + return getExecutorService(); + } + /** * Register a component that participates in the context's life cycle. *

- * All components registered via this method will be stopped and removed - * from the context when the context is reset. + * All components registered via this method will be stopped and removed from + * the context when the context is reset. * * @param component the subject component */ void register(LifeCycle component); + /** + * Add scheduledFuture parameter to the list of known futures. + * + * @param scheduledFuture + */ void addScheduledFuture(ScheduledFuture scheduledFuture); SequenceNumberGenerator getSequenceNumberGenerator(); + void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGenerator); - + + /** + * Add a {@link ConfigurationEventListener} to this context. + * + *

Configuration events are supposed to be rare and listeners to such events rarer still.

+ * + *

The propagation of {@link ConfigurationEvent configuration events} is intended for internal testing + * as well as some coordination between configurators.

+ * + * @param listener + * @since 1.3.6/1.4.6 + */ + void addConfigurationEventListener(ConfigurationEventListener listener); + + /** + * Remove an existing ConfigurationEventListener + * @param listener + * @since 1.5.7 + */ + default void removeConfigurationEventListener(ConfigurationEventListener listener) {}; + /** + * Fire {@link ConfigurationEvent} by invoking {@link #addConfigurationEventListener registered listeners}. + * + *

Note that it is the role of configurators to invoke this method as a context does + * not necessarily know when it is being configured.

+ * + * @param configurationEvent + * @since 1.3.6/1.4.6 + */ + void fireConfigurationEvent(ConfigurationEvent configurationEvent); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/ContextBase.java b/logback-core/src/main/java/ch/qos/logback/core/ContextBase.java index d24ad4c9c9..e8acc764eb 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/ContextBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/ContextBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,22 +14,21 @@ package ch.qos.logback.core; import static ch.qos.logback.core.CoreConstants.CONTEXT_NAME_KEY; -import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP; import static ch.qos.logback.core.CoreConstants.HOSTNAME_KEY; -import static ch.qos.logback.core.CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.*; +import java.util.concurrent.locks.ReentrantLock; import ch.qos.logback.core.rolling.helper.FileNamePattern; +import ch.qos.logback.core.spi.ConfigurationEvent; +import ch.qos.logback.core.spi.ConfigurationEventListener; import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.spi.LogbackLock; import ch.qos.logback.core.spi.SequenceNumberGenerator; +import ch.qos.logback.core.status.InfoStatus; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.util.ExecutorServiceUtil; import ch.qos.logback.core.util.NetworkAddressUtil; @@ -44,20 +43,26 @@ public class ContextBase implements Context, LifeCycle { // when it changes so that a new instance of propertyMap can be // serialized. For the time being, we ignore this shortcoming. Map propertyMap = new HashMap(); - Map objectMap = new HashMap(); + Map objectMap = new ConcurrentHashMap<>(); - LogbackLock configurationLock = new LogbackLock(); + protected ReentrantLock configurationLock = new ReentrantLock(); + + final private List configurationEventListenerList = new CopyOnWriteArrayList<>(); private ScheduledExecutorService scheduledExecutorService; + + private ThreadPoolExecutor threadPoolExecutor; + private ExecutorService alternateExecutorService; + + protected List> scheduledFutures = new ArrayList>(1); private LifeCycleManager lifeCycleManager; private SequenceNumberGenerator sequenceNumberGenerator; - - private boolean started; + // 'started' is used to mitigate race conditions in stop() and possibly other methods, hence the volatile qualifier. + private volatile boolean started; public ContextBase() { - initCollisionMaps(); } public StatusManager getStatusManager() { @@ -69,7 +74,8 @@ public StatusManager getStatusManager() { * context is initialized with a {@link BasicStatusManager}. A null value for * the 'statusManager' argument is not allowed. * - *

A malicious attacker can set the status manager to a dummy instance, + *

+ * A malicious attacker can set the status manager to a dummy instance, * disabling internal error reporting. * * @param statusManager the new status manager @@ -94,14 +100,19 @@ public void putProperty(String key, String val) { } } - protected void initCollisionMaps() { - putObject(FA_FILENAME_COLLISION_MAP, new HashMap()); - putObject(RFA_FILENAME_PATTERN_COLLISION_MAP, new HashMap()); + @Override + public void addSubstitutionProperty(String key, String value) { + if (key == null || value == null) { + return; + } + // values with leading or trailing spaces are bad. We remove them now. + value = value.trim(); + propertyMap.put(key, value); } /** - * Given a key, return the corresponding property value. If invoked with - * the special key "CONTEXT_NAME", the name of the context is returned. + * Given a key, return the corresponding property value. If invoked with the + * special key "CONTEXT_NAME", the name of the context is returned. * * @param key * @return @@ -150,6 +161,7 @@ public String getName() { return name; } + @Override public void start() { // We'd like to create the executor service here, but we can't; // ContextBase has not always implemented LifeCycle and there are *many* @@ -158,9 +170,9 @@ public void start() { } public void stop() { - // We don't check "started" here, because the executor service uses + // We don't check "started" here, because the executor services use // lazy initialization, rather than being created in the start method - stopExecutorService(); + stopExecutorServices(); started = false; } @@ -182,11 +194,12 @@ public void reset() { } /** - * The context name can be set only if it is not already set, or if the - * current name is the default context name, namely "default", or if the - * current name and the old name are the same. + * The context name can be set only if it is not already set, or if the current + * name is the default context name, namely "default", or if the current name + * and the old name are the same. * - * @throws IllegalStateException if the context already has a name, other than "default". + * @throws IllegalStateException if the context already has a name, other than + * "default". */ public void setName(String name) throws IllegalStateException { if (name != null && name.equals(this.name)) { @@ -203,19 +216,29 @@ public long getBirthTime() { return birthTime; } - public Object getConfigurationLock() { + public ReentrantLock getConfigurationLock() { return configurationLock; } + + @Override - /** - * @deprecated - */ public synchronized ExecutorService getExecutorService() { - return getScheduledExecutorService(); + if (threadPoolExecutor == null) { + threadPoolExecutor = (ThreadPoolExecutor) ExecutorServiceUtil.newThreadPoolExecutor(); + } + return threadPoolExecutor; } @Override + public synchronized ExecutorService getAlternateExecutorService() { + if(alternateExecutorService == null) { + alternateExecutorService = ExecutorServiceUtil.newAlternateThreadPoolExecutor(); + } + return alternateExecutorService; + } + + @Override public synchronized ScheduledExecutorService getScheduledExecutorService() { if (scheduledExecutorService == null) { scheduledExecutorService = ExecutorServiceUtil.newScheduledExecutorService(); @@ -223,19 +246,24 @@ public synchronized ScheduledExecutorService getScheduledExecutorService() { return scheduledExecutorService; } - private synchronized void stopExecutorService() { - if (scheduledExecutorService != null) { - ExecutorServiceUtil.shutdown(scheduledExecutorService); - scheduledExecutorService = null; - } + private synchronized void stopExecutorServices() { + ExecutorServiceUtil.shutdown(scheduledExecutorService); + scheduledExecutorService = null; + + ExecutorServiceUtil.shutdown(threadPoolExecutor); + threadPoolExecutor = null; } private void removeShutdownHook() { Thread hook = (Thread) getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); if (hook != null) { removeObject(CoreConstants.SHUTDOWN_HOOK_THREAD); + try { - Runtime.getRuntime().removeShutdownHook(hook); + sm.add(new InfoStatus("Removing shutdownHook " + hook, this)); + Runtime runtime = Runtime.getRuntime(); + boolean result = runtime.removeShutdownHook(hook); + sm.add(new InfoStatus("ShutdownHook removal result: " + result, this)); } catch (IllegalStateException e) { // if JVM is already shutting down, ISE is thrown // no need to do anything else @@ -251,13 +279,13 @@ public void register(LifeCycle component) { * Gets the life cycle manager for this context. *

* The default implementation lazily initializes an instance of - * {@link LifeCycleManager}. Subclasses may override to provide a custom - * manager implementation, but must take care to return the same manager - * object for each call to this method. + * {@link LifeCycleManager}. Subclasses may override to provide a custom manager + * implementation, but must take care to return the same manager object for each + * call to this method. *

* This is exposed primarily to support instrumentation for unit testing. * - * @return manager object + * @return manager object */ synchronized LifeCycleManager getLifeCycleManager() { if (lifeCycleManager == null) { @@ -276,10 +304,18 @@ public void addScheduledFuture(ScheduledFuture scheduledFuture) { scheduledFutures.add(scheduledFuture); } + /** + * @deprecated replaced by getCopyOfScheduledFutures + */ + @Deprecated public List> getScheduledFutures() { + return getCopyOfScheduledFutures(); + } + + public List> getCopyOfScheduledFutures() { return new ArrayList>(scheduledFutures); } - + public SequenceNumberGenerator getSequenceNumberGenerator() { return sequenceNumberGenerator; } @@ -288,4 +324,18 @@ public void setSequenceNumberGenerator(SequenceNumberGenerator sequenceNumberGen this.sequenceNumberGenerator = sequenceNumberGenerator; } + @Override + public void addConfigurationEventListener(ConfigurationEventListener listener) { + configurationEventListenerList.add(listener); + } + + @Override + public void removeConfigurationEventListener(ConfigurationEventListener listener) { + configurationEventListenerList.remove(listener); + } + + @Override + public void fireConfigurationEvent(ConfigurationEvent configurationEvent) { + configurationEventListenerList.forEach( l -> l.listen(configurationEvent)); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java b/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java index e866d6b818..6a95c371c9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java +++ b/logback-core/src/main/java/ch/qos/logback/core/CoreConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,9 @@ */ package ch.qos.logback.core; +import java.io.File; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; public class CoreConstants { @@ -20,25 +23,21 @@ public class CoreConstants { final public static String STATUS_LISTENER_CLASS_KEY = "logback.statusListenerClass"; final public static String SYSOUT = "SYSOUT"; + final public static String STDOUT = "STDOUT"; + /** * Number of idle threads to retain in a context's executor service. */ - public static final int CORE_POOL_SIZE = 0; + public static final int CORE_POOL_SIZE = 4; - // Apparently ScheduledThreadPoolExecutor has limitation where a task cannot be submitted from - // within a running task unless the pool has worker threads already available. ThreadPoolExecutor - // does not have this limitation. - // This causes tests failures in SocketReceiverTest.testDispatchEventForEnabledLevel and - // ServerSocketReceiverFunctionalTest.testLogEventFromClient. - // We thus set a pool size > 0 for tests to pass. - public static final int SCHEDULED_EXECUTOR_POOL_SIZE = 1; + // In Java 21 and later the actual threads are assumed to be virtual + public static final int SCHEDULED_EXECUTOR_POOL_SIZE = 4; - /** * Maximum number of threads to allow in a context's executor service. + * @deprecated no longer used + * */ - // if you need a different MAX_POOL_SIZE, please file create a jira issue - // asking to make MAX_POOL_SIZE a parameter. public static final int MAX_POOL_SIZE = 32; // Note that the line.separator property can be looked up even by @@ -46,52 +45,63 @@ public class CoreConstants { public static final String LINE_SEPARATOR = System.getProperty("line.separator"); public static final int LINE_SEPARATOR_LEN = LINE_SEPARATOR.length(); - public static final String CODES_URL = "http://logback.qos.ch/codes.html"; - public static final String MANUAL_URL_PREFIX = "http://logback.qos.ch/manual/"; + public static final String CODES_URL = "https://logback.qos.ch/codes.html"; + public static final String MANUAL_URL_PREFIX = "https://logback.qos.ch/manual/"; public static final String MORE_INFO_PREFIX = "For more information, please visit "; /** * The default context name. */ public static final String DEFAULT_CONTEXT_NAME = "default"; + + /** + * Customized pattern conversion rules are stored under this key in the + * {@link Context} object store. + * + * @since 1.5.14 + */ + public static final String PATTERN_RULE_REGISTRY_FOR_SUPPLIERS = "PATTERN_RULE_REGISTRY_FOR_SUPPLIERS"; + /** * Customized pattern conversion rules are stored under this key in the * {@link Context} object store. */ public static final String PATTERN_RULE_REGISTRY = "PATTERN_RULE_REGISTRY"; + + public static final String ISO8601_STR = "ISO8601"; public static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS"; - public static final String DAILY_DATE_PATTERN = "yyyy-MM-dd"; /** - * Time format used in Common Log Format + * Keyword for setting a strict ISO8601 pattern which includes 'T' between the date and the time + * + * @since 1.5.7 */ - public static final String CLF_DATE_PATTERN = "dd/MMM/yyyy:HH:mm:ss Z"; - + public static final String STRICT_STR = "STRICT"; /** - * The key used in locating the evaluator map in context's object map. + * Strict ISO8601 pattern which includes 'T' between the date and the time + * @since 15.7 */ - public static final String EVALUATOR_MAP = "EVALUATOR_MAP"; + public static final String STRICT_ISO8601_PATTERN = "yyyy-MM-dd'T'HH:mm:ss,SSS"; + + + public static final String FILE_TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HHmm"; + public static final String DAILY_DATE_PATTERN = "yyyy-MM-dd"; /** - * Key used to locate a map Files used by FileAppender instances in context's object map. - * - * Said map consists of entries of the type (appender name, File option) + * Time format used in Common Log Format */ - public static final String FA_FILENAME_COLLISION_MAP = "FA_FILENAMES_MAP"; + public static final String CLF_DATE_PATTERN = "dd/MMM/yyyy:HH:mm:ss Z"; /** - * Key used to locate a collision map for RollingFileAppender instances in context's object map. - * - * The collision map consists of entities of the type (appender name, FileNamePattern option) + * The key used in locating the evaluator map in context's object map. */ - public static final String RFA_FILENAME_PATTERN_COLLISION_MAP = "RFA_FILENAME_PATTERN_COLLISION_MAP"; + public static final String EVALUATOR_MAP = "EVALUATOR_MAP"; /** - * By convention, we assume that the static method named "valueOf" taking - * a string argument can restore a given object from its string - * representation. + * By convention, we assume that the static method named "valueOf" taking a + * string argument can restore a given object from its string representation. */ public static final String VALUE_OF = "valueOf"; @@ -100,15 +110,29 @@ public class CoreConstants { */ public static final String EMPTY_STRING = ""; + /** + * String value returned in case data is not available. + * + * For example, when caller information is not available this constant is used for file name, + * method name, etc. + */ + public static final String NA = "?"; + /** * An empty string array. */ public static final String[] EMPTY_STRING_ARRAY = new String[] {}; + public static final Charset UTF_8_CHARSET = StandardCharsets.UTF_8; + /** * An empty Class array. */ public static final Class[] EMPTY_CLASS_ARRAY = new Class[] {}; + + + public static final File[] EMPTY_FILE_ARRAY = new File[0]; + public static final String CAUSED_BY = "Caused by: "; public static final String SUPPRESSED = "Suppressed: "; public static final String WRAPPED_BY = "Wrapped by: "; @@ -125,23 +149,24 @@ public class CoreConstants { public static final char SINGLE_QUOTE_CHAR = '\''; public static final char COLON_CHAR = ':'; public static final char DASH_CHAR = '-'; + public static final char EQUALS_CHAR = '='; + public static final String DEFAULT_VALUE_SEPARATOR = ":-"; + public static final String NULL_STR = "null"; /** - * Number of rows before in an HTML table before, - * we close the table and create a new one + * Number of rows before in an HTML table before, we close the table and create + * a new one */ public static final int TABLE_ROW_LIMIT = 10000; // reset the ObjectOutputStream every OOS_RESET_FREQUENCY calls - // this avoid serious memory leaks + // this avoids serious memory leaks public static final int OOS_RESET_FREQUENCY = 70; - /** - * The reference bogo instructions per second on - * Ceki's machine (Orion) - */ - public static long REFERENCE_BIPS = 9000; + // See https://jakarta.ee/specifications/platform/8/platform-spec-8.html#a616 + // there are the java:comp, java:module, java:app, java:global namespaces + public static final String JNDI_JAVA_NAMESPACE = "java:"; // the max number of times an error should be reported public static final int MAX_ERROR_COUNT = 4; @@ -150,8 +175,8 @@ public class CoreConstants { public static final char TAB = '\t'; public static final char DOLLAR = '$'; - public static final String SEE_FNP_NOT_SET = "See also "+CODES_URL+"#tbr_fnp_not_set"; - public static final String SEE_MISSING_INTEGER_TOKEN = "See also "+CODES_URL+"#sat_missing_integer_token"; + public static final String SEE_FNP_NOT_SET = "See also " + CODES_URL + "#tbr_fnp_not_set"; + public static final String SEE_MISSING_INTEGER_TOKEN = "See also " + CODES_URL + "#sat_missing_integer_token"; public static final String CONFIGURATION_WATCH_LIST = "CONFIGURATION_WATCH_LIST"; public static final String CONFIGURATION_WATCH_LIST_RESET_X = "CONFIGURATION_WATCH_LIST_RESET"; @@ -163,8 +188,7 @@ public class CoreConstants { public static final String SHUTDOWN_HOOK_THREAD = "SHUTDOWN_HOOK"; /** - * The key under which the local host name is registered in the logger - * context. + * The key under which the local host name is registered in the logger context. */ public static final String HOSTNAME_KEY = "HOSTNAME"; @@ -199,9 +223,29 @@ public class CoreConstants { public static final String LEFT_ACCOLADE = new String(new char[] { CURLY_LEFT }); public static final String RIGHT_ACCOLADE = new String(new char[] { CURLY_RIGHT }); public static final long UNBOUNDED_TOTAL_SIZE_CAP = 0; - public static final int UNBOUND_HISTORY = 0; + + /** + * If Rolling + */ + public static final int UNBOUNDED_HISTORY = 0; + + /** + * Replaced by {@link CoreConstants#UNBOUNDED_HISTORY} with the same identical value. + + * @deprecated + * @see #UNBOUNDED_HISTORY + */ + public static final int UNBOUND_HISTORY = UNBOUNDED_HISTORY; - public static final String RECONFIGURE_ON_CHANGE_TASK = "RECONFIGURE_ON_CHANGE_TASK"; - public static final String SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED = "SizeAndTimeBasedFNATP is deprecated. Use SizeAndTimeBasedRollingPolicy instead"; + //public static final String RECONFIGURE_ON_CHANGE_TASK = "RECONFIGURE_ON_CHANGE_TASK"; + public static final String SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED = "Direct use of either SizeAndTimeBasedFNATP or SizeAndTimeBasedFileNamingAndTriggeringPolicy "; + public static final String SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED_BIS = "is deprecated. Please use SizeAndTimeBasedRollingPolicy instead."; + public static final char JSON_LINE_SEPARATOR = '\n'; + final public static String MODEL_CONFIG_FILE_EXTENSION = ".scmo"; + /** + * since 1.5.8 + */ + final public static String PROPERTIES_FILE_EXTENSION = ".properties"; + public static final String LOGBACK_CORE_VERSION_MESSAGE = "This is logback-core version "; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java b/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java index 907a10ec73..26940c6c36 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/FileAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -30,10 +30,10 @@ /** * FileAppender appends log events to a file. - * + * * For more information about this appender, please refer to the online manual * at http://logback.qos.ch/manual/appenders.html#FileAppender - * + * * @author Ceki Gülcü */ public class FileAppender extends OutputStreamAppender { @@ -59,8 +59,8 @@ public class FileAppender extends OutputStreamAppender { private FileSize bufferSize = new FileSize(DEFAULT_BUFFER_SIZE); /** - * The File property takes a string value which should be the name of - * the file to append to. + * The File property takes a string value which should be the name of the + * file to append to. */ public void setFile(String file) { if (file == null) { @@ -82,7 +82,7 @@ public boolean isAppend() { /** * This method is used by derived classes to obtain the raw file property. * Regular users should not be calling this method. - * + * * @return the value of the file property */ final public String rawFileProperty() { @@ -91,19 +91,18 @@ final public String rawFileProperty() { /** * Returns the value of the File property. - * + * *

* This method may be overridden by derived classes. - * + * */ public String getFile() { return fileName; } /** - * If the value of File is not null, then - * {@link #openFile} is called with the values of File and - * Append properties. + * If the value of File is not null, then {@link #openFile} + * is called with the values of File and Append properties. */ public void start() { int errors = 0; @@ -117,18 +116,11 @@ public void start() { } } - if (checkForFileCollisionInPreviousFileAppenders()) { - addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting."); - addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL); + try { + openFile(getFile()); + } catch (java.io.IOException e) { errors++; - } else { - // file should be opened only if collision free - try { - openFile(getFile()); - } catch (java.io.IOException e) { - errors++; - addError("openFile(" + fileName + "," + append + ") call failed.", e); - } + addError("openFile(" + fileName + "," + append + ") call failed.", e); } } else { errors++; @@ -141,59 +133,28 @@ public void start() { @Override public void stop() { - super.stop(); - - Map map = ContextUtil.getFilenameCollisionMap(context); - if (map == null || getName() == null) + if (!isStarted()) return; - map.remove(getName()); - } - - protected boolean checkForFileCollisionInPreviousFileAppenders() { - boolean collisionsDetected = false; - if (fileName == null) { - return false; - } - @SuppressWarnings("unchecked") - Map previousFilesMap = (Map) context.getObject(CoreConstants.FA_FILENAME_COLLISION_MAP); - if (previousFilesMap == null) { - return collisionsDetected; - } - for (Entry entry : previousFilesMap.entrySet()) { - if (fileName.equals(entry.getValue())) { - addErrorForCollision("File", entry.getValue(), entry.getKey()); - collisionsDetected = true; - } - } - if (name != null) { - previousFilesMap.put(getName(), fileName); - } - return collisionsDetected; - } - - protected void addErrorForCollision(String optionName, String optionValue, String appenderName) { - addError("'" + optionName + "' option has the same value \"" + optionValue + "\" as that given for appender [" + appenderName + "] defined earlier."); + super.stop(); } /** *

* Sets and opens the file where the log output will go. The specified * file must be writable. - * + * *

- * If there was already an opened file, then the previous file is closed - * first. - * + * If there was already an opened file, then the previous file is closed first. + * *

- * Do not use this method directly. To configure a FileAppender or one of - * its subclasses, set its properties one by one and then call start(). - * - * @param file_name - * The path to the log file. + * Do not use this method directly. To configure a FileAppender or one of its + * subclasses, set its properties one by one and then call start(). + * + * @param file_name The path to the log file. */ public void openFile(String file_name) throws IOException { - lock.lock(); + streamWriteLock.lock(); try { File file = new File(file_name); boolean result = FileUtil.createMissingParentDirectories(file); @@ -205,13 +166,13 @@ public void openFile(String file_name) throws IOException { resilientFos.setContext(context); setOutputStream(resilientFos); } finally { - lock.unlock(); + streamWriteLock.unlock(); } } /** * @see #setPrudent(boolean) - * + * * @return true if in prudent mode */ public boolean isPrudent() { @@ -221,7 +182,7 @@ public boolean isPrudent() { /** * When prudent is set to true, file appenders from multiple JVMs can safely * write to the same file. - * + * * @param prudent */ public void setPrudent(boolean prudent) { @@ -231,13 +192,35 @@ public void setPrudent(boolean prudent) { public void setAppend(boolean append) { this.append = append; } - + public void setBufferSize(FileSize bufferSize) { - addInfo("Setting bufferSize to ["+bufferSize.toString()+"]"); + addInfo("Setting bufferSize to [" + bufferSize.toString() + "]"); this.bufferSize = bufferSize; } - private void safeWrite(E event) throws IOException { + @Override + protected void writeOut(E event) throws IOException { + if (prudent) { + safeWriteOut(event); + } else { + super.writeOut(event); + } + } + + private void safeWriteOut(E event) { + byte[] byteArray = this.encoder.encode(event); + if (byteArray == null || byteArray.length == 0) + return; + + streamWriteLock.lock(); + try { + safeWriteBytes(byteArray); + } finally { + streamWriteLock.unlock(); + } + } + + private void safeWriteBytes(byte[] byteArray) { ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream(); FileChannel fileChannel = resilientFOS.getChannel(); if (fileChannel == null) { @@ -255,14 +238,12 @@ private void safeWrite(E event) throws IOException { if (size != position) { fileChannel.position(size); } - super.writeOut(event); + writeByteArrayToOutputStreamWithPossibleFlush(byteArray); } catch (IOException e) { // Mainly to catch FileLockInterruptionExceptions (see LOGBACK-875) resilientFOS.postIOFailure(e); } finally { - if (fileLock != null && fileLock.isValid()) { - fileLock.release(); - } + releaseFileLock(fileLock); // Re-interrupt if we started in an interrupted state (see LOGBACK-875) if (interrupted) { @@ -271,12 +252,13 @@ private void safeWrite(E event) throws IOException { } } - @Override - protected void writeOut(E event) throws IOException { - if (prudent) { - safeWrite(event); - } else { - super.writeOut(event); + private void releaseFileLock(FileLock fileLock) { + if (fileLock != null && fileLock.isValid()) { + try { + fileLock.release(); + } catch (IOException e) { + addError("failed to release lock", e); + } } } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/Layout.java b/logback-core/src/main/java/ch/qos/logback/core/Layout.java index 20be04048f..d43f624767 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/Layout.java +++ b/logback-core/src/main/java/ch/qos/logback/core/Layout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,11 +19,12 @@ public interface Layout extends ContextAware, LifeCycle { /** - * Transform an event (of type Object) and return it as a String after + * Transform an event (of type Object) and return it as a String after * appropriate formatting. * - *

Taking in an object and returning a String is the least sophisticated - * way of formatting events. However, it is remarkably CPU-effective. + *

+ * Taking in an object and returning a String is the least sophisticated way of + * formatting events. However, it is remarkably CPU-effective. *

* * @param event The event to format @@ -33,21 +34,22 @@ public interface Layout extends ContextAware, LifeCycle { /** * Return the file header for this layout. The returned value may be null. + * * @return The header. */ String getFileHeader(); /** - * Return the header of the logging event formatting. The returned value - * may be null. + * Return the header of the logging event formatting. The returned value may be + * null. * * @return The header. */ String getPresentationHeader(); /** - * Return the footer of the logging event formatting. The returned value - * may be null. + * Return the footer of the logging event formatting. The returned value may be + * null. * * @return The footer. */ @@ -56,13 +58,14 @@ public interface Layout extends ContextAware, LifeCycle { /** * Return the file footer for this layout. The returned value may be null. + * * @return The footer. */ String getFileFooter(); /** * Returns the content type as appropriate for the implementation. - * + * * @return */ String getContentType(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/LayoutBase.java b/logback-core/src/main/java/ch/qos/logback/core/LayoutBase.java index 2a344b9f62..405f0e59ec 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/LayoutBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/LayoutBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/LifeCycleManager.java b/logback-core/src/main/java/ch/qos/logback/core/LifeCycleManager.java index 340103a009..8518dd9ae6 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/LifeCycleManager.java +++ b/logback-core/src/main/java/ch/qos/logback/core/LifeCycleManager.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ /** * An object that manages a collection of components that implement the - * {@link LifeCycle} interface. Each component that is added to the manager - * will be stopped and removed from the manager when the manager is reset. + * {@link LifeCycle} interface. Each component that is added to the manager will + * be stopped and removed from the manager when the manager is reset. * * @author Carl Harris */ @@ -30,8 +30,9 @@ public class LifeCycleManager { private final Set components = new HashSet(); /** - * Registers a component with this manager. + * Registers a component with this manager. *

+ * * @param component the component whose life cycle is to be managed */ public void register(LifeCycle component) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/LogbackException.java b/logback-core/src/main/java/ch/qos/logback/core/LogbackException.java index 97ba937c7e..f83c87f2ac 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/LogbackException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/LogbackException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/OutputStreamAppender.java b/logback-core/src/main/java/ch/qos/logback/core/OutputStreamAppender.java index 6410634acb..05c2bfc29c 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/OutputStreamAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/OutputStreamAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,6 +21,7 @@ import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; +import ch.qos.logback.core.rolling.LengthCounter; import ch.qos.logback.core.spi.DeferredProcessingAware; import ch.qos.logback.core.status.ErrorStatus; @@ -36,15 +37,15 @@ public class OutputStreamAppender extends UnsynchronizedAppenderBase { /** - * It is the encoder which is ultimately responsible for writing the event to - * an {@link OutputStream}. + * It is the encoder which is ultimately responsible for writing the event to an + * {@link OutputStream}. */ protected Encoder encoder; /** * All synchronization in this class is done via the lock object. */ - protected final ReentrantLock lock = new ReentrantLock(false); + protected final ReentrantLock streamWriteLock = new ReentrantLock(false); /** * This is the {@link OutputStream outputStream} where output will be written. @@ -54,10 +55,10 @@ public class OutputStreamAppender extends UnsynchronizedAppenderBase { boolean immediateFlush = true; /** - * The underlying output stream used by this appender. - * - * @return - */ + * The underlying output stream used by this appender. + * + * @return + */ public OutputStream getOutputStream() { return outputStream; } @@ -77,9 +78,17 @@ public void start() { addStatus(new ErrorStatus("No output stream set for the appender named \"" + name + "\".", this)); errors++; } + + if (encoder == null) { + addWarn("Encoder has not been set. Cannot invoke its init method."); + errors++; + } + + // only error free appenders should be activated if (errors == 0) { super.start(); + encoderInit(); } } @@ -103,19 +112,21 @@ protected void append(E eventObject) { } /** - * Stop this appender instance. The underlying stream or writer is also - * closed. + * Stop this appender instance. The underlying stream or writer is also closed. * *

* Stopped appenders cannot be reused. */ public void stop() { - lock.lock(); + if(!isStarted()) + return; + + streamWriteLock.lock(); try { closeOutputStream(); super.stop(); } finally { - lock.unlock(); + streamWriteLock.unlock(); } } @@ -125,7 +136,7 @@ public void stop() { protected void closeOutputStream() { if (this.outputStream != null) { try { - // before closing we have to output out layout's footer + // before closing we have to output out encooder's footer encoderClose(); this.outputStream.close(); this.outputStream = null; @@ -154,23 +165,24 @@ void encoderClose() { * OutputStream will be closed when the appender instance is * closed. * - * @param outputStream - * An already opened OutputStream. + * @param outputStream An already opened OutputStream. */ public void setOutputStream(OutputStream outputStream) { - lock.lock(); + streamWriteLock.lock(); try { // close any previously opened output stream closeOutputStream(); this.outputStream = outputStream; - if (encoder == null) { - addWarn("Encoder has not been set. Cannot invoke its init method."); - return; - } - encoderInit(); + // the first call to setOutputStream() is made on a non started appender + // However, in subsequent calls to setOutputStream() the appender will be in + // started state. In subsequent calls, in particular when opening a file after rollover, + // we have to output the header hence the call to encoderInit(). + if(isStarted()) { + encoderInit(); + } } finally { - lock.unlock(); + streamWriteLock.unlock(); } } @@ -181,27 +193,47 @@ void encoderInit() { writeBytes(header); } catch (IOException ioe) { this.started = false; - addStatus(new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe)); + addStatus( + new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe)); } } } + protected void writeOut(E event) throws IOException { byte[] byteArray = this.encoder.encode(event); writeBytes(byteArray); } private void writeBytes(byte[] byteArray) throws IOException { - if(byteArray == null || byteArray.length == 0) + if (byteArray == null || byteArray.length == 0) return; - - lock.lock(); + + streamWriteLock.lock(); + try { - this.outputStream.write(byteArray); - if (immediateFlush) { - this.outputStream.flush(); + // guard against appender having been stop() in parallel + // note that the encoding step is performed outside the protection of the streamWriteLock + if(isStarted()) { + writeByteArrayToOutputStreamWithPossibleFlush(byteArray); + updateByteCount(byteArray); } } finally { - lock.unlock(); + streamWriteLock.unlock(); + } + } + + protected void updateByteCount(byte[] byteArray) { + } + + /** + * A simple method to write to an outputStream and flush the stream if immediateFlush is set to true. + * + * @since 1.3.9/1.4.9 + */ + protected final void writeByteArrayToOutputStreamWithPossibleFlush(byte[] byteArray) throws IOException { + this.outputStream.write(byteArray); + if (immediateFlush) { + this.outputStream.flush(); } } @@ -222,13 +254,7 @@ protected void subAppend(E event) { if (event instanceof DeferredProcessingAware) { ((DeferredProcessingAware) event).prepareForDeferredProcessing(); } - // the synchronization prevents the OutputStream from being closed while we - // are writing. It also prevents multiple threads from entering the same - // converter. Converters assume that they are in a synchronized block. - // lock.lock(); - - byte[] byteArray = this.encoder.encode(event); - writeBytes(byteArray); + writeOut(event); } catch (IOException ioe) { // as soon as an exception occurs, move to non-started state diff --git a/logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java b/logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java index 50bed62086..b93e0c4e92 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/PropertyDefinerBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,9 @@ import ch.qos.logback.core.spi.PropertyDefiner; /** - * A skeleton implementation for property definers so that they derive from {@link ContextAwareBase}. - * + * A skeleton implementation for property definers so that they derive from + * {@link ContextAwareBase}. + * * @author Aleksey Didik */ public abstract class PropertyDefinerBase extends ContextAwareBase implements PropertyDefiner { diff --git a/logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java index 0b5d28b91f..a991bb5524 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/UnsynchronizedAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,26 +20,27 @@ import ch.qos.logback.core.spi.FilterAttachableImpl; import ch.qos.logback.core.spi.FilterReply; import ch.qos.logback.core.status.WarnStatus; +import ch.qos.logback.core.util.ReentryGuard; +import ch.qos.logback.core.util.ReentryGuardFactory; +import ch.qos.logback.core.util.SimpleTimeBasedGuard; /** - * Similar to AppenderBase except that derived appenders need to handle - * thread synchronization on their own. + * Similar to {@link AppenderBase} except that derived appenders need to handle thread + * synchronization on their own. * * @author Ceki Gülcü * @author Ralph Goers */ abstract public class UnsynchronizedAppenderBase extends ContextAwareBase implements Appender { - protected boolean started = false; - - // using a ThreadLocal instead of a boolean add 75 nanoseconds per - // doAppend invocation. This is tolerable as doAppend takes at least a few microseconds - // on a real appender + protected volatile boolean started = false; /** * The guard prevents an appender from repeatedly calling its own doAppend * method. + * + * @since 1.5.21 */ - private ThreadLocal guard = new ThreadLocal(); + private ReentryGuard reentryGuard; /** * Appenders are named. @@ -52,29 +53,26 @@ public String getName() { return name; } - private int statusRepeatCount = 0; - private int exceptionCount = 0; + private SimpleTimeBasedGuard notStartedGuard = new SimpleTimeBasedGuard(); + private SimpleTimeBasedGuard exceptionGuard = new SimpleTimeBasedGuard(); - static final int ALLOWED_REPEATS = 3; public void doAppend(E eventObject) { - // WARNING: The guard check MUST be the first statement in the - // doAppend() method. + if (!this.started) { + + if (notStartedGuard.allow()) { + addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this)); + } + return; + } // prevent re-entry. - if (Boolean.TRUE.equals(guard.get())) { + if (reentryGuard.isLocked()) { return; } try { - guard.set(Boolean.TRUE); - - if (!this.started) { - if (statusRepeatCount++ < ALLOWED_REPEATS) { - addStatus(new WarnStatus("Attempted to append to non started appender [" + name + "].", this)); - } - return; - } + reentryGuard.lock(); if (getFilterChainDecision(eventObject) == FilterReply.DENY) { return; @@ -84,11 +82,11 @@ public void doAppend(E eventObject) { this.append(eventObject); } catch (Exception e) { - if (exceptionCount++ < ALLOWED_REPEATS) { + if (exceptionGuard.allow()) { addError("Appender [" + name + "] failed to append.", e); } } finally { - guard.set(Boolean.FALSE); + reentryGuard.unlock(); } } @@ -102,9 +100,37 @@ public void setName(String name) { } public void start() { + this.reentryGuard = buildReentryGuard(); started = true; } + /** + * Create a {@link ReentryGuard} instance used by this appender to prevent + * recursive/re-entrant calls to {@link #doAppend(Object)}. + * + *

The default implementation returns a no-op guard produced by + * {@link ReentryGuardFactory#makeGuard(ch.qos.logback.core.util.ReentryGuardFactory.GuardType)} + * using {@code GuardType.NOP}. Subclasses that require actual re-entry + * protection (for example using a thread-local or lock-based guard) should + * override this method to return an appropriate {@link ReentryGuard} + * implementation.

+ * + *

Contract/expectations: + *

    + *
  • Called from {@link #start()} to initialize the appender's guard.
  • + *
  • Implementations should be lightweight and thread-safe.
  • + *
  • Return value must not be {@code null}.
  • + *
+ *

+ * + * @return a non-null {@link ReentryGuard} used to detect and prevent + * re-entrant appends. By default, this is a no-op guard. + * @since 1.5.21 + */ + protected ReentryGuard buildReentryGuard() { + return ReentryGuardFactory.makeGuard(ReentryGuardFactory.GuardType.NOP); + } + public void stop() { started = false; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/EvaluationException.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/EvaluationException.java index f142b89cba..b1b7dfd4af 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/EvaluationException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/EvaluationException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluator.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluator.java index 6d92f1a0ed..f48f1989ab 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -36,25 +36,22 @@ public interface EventEvaluator extends ContextAware, LifeCycle { * The Evaluator is free to evaluate the event as it pleases. In * particular, the evaluation results may depend on previous events. * - * @param event - * The event to evaluate + * @param event The event to evaluate * @return true if there is a match, false otherwise. - * @throws NullPointerException - * can be thrown in presence of null values - * @throws EvaluationException - * may be thrown during faulty evaluation + * @throws NullPointerException can be thrown in presence of null values + * @throws EvaluationException may be thrown during faulty evaluation */ boolean evaluate(E event) throws NullPointerException, EvaluationException; /** - * Evaluators are named entities. + * An evaluator may optionally have a name. * * @return The name of this evaluator. */ String getName(); /** - * Evaluators are named entities. + * An evaluator may optionally have a name. */ void setName(String name); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluatorBase.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluatorBase.java index 7c7a0dcc01..851345a403 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluatorBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/EventEvaluatorBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/ExpressionPropertyCondition.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/ExpressionPropertyCondition.java new file mode 100644 index 0000000000..2947794728 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/ExpressionPropertyCondition.java @@ -0,0 +1,464 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +import ch.qos.logback.core.util.IntHolder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * This class evaluates boolean expressions based on property lookups. + *

It supports logical operators (NOT, AND, OR) and functions like isNull, isDefined, + * propertyEquals, and propertyContains. Expressions are parsed using the Shunting-Yard + * algorithm into Reverse Polish Notation (RPN) for evaluation. + *

+ * + *

Example expression: {@code isDefined("key1") && propertyEquals("key2", "value")}

+ * + *

Properties are resolved via {@link PropertyConditionBase#property(String)}.

+ * + * @since 1.5.24 + */ +public class ExpressionPropertyCondition extends PropertyConditionBase { + + + /** + * A map that associates a string key with a function for evaluating boolean conditions. + * + *

This map defines the known functions. It can be overridden by subclasses to define + * new functions.

+ * + *

In the context of this class, a function is a function that takes a String + * argument and returns a boolean.

+ */ + protected Map> functionMap = new HashMap<>(); + + + /** + * A map that associates a string key with a bi-function for evaluating boolean conditions. + * + *

This map defines the known bi-functions. It can be overridden by subclasses to define + * new bi-functions.

+ * + *

In the context of this class, a bi-function is a function that takes two String + * arguments and returns a boolean.

+ */ + protected Map> biFunctionMap = new HashMap<>(); + + private static final String IS_NULL_FUNCTION_KEY = "isNull"; + private static final String IS_DEFINEDP_FUNCTION_KEY = "isDefined"; + + private static final String PROPERTY_EQUALS_FUNCTION_KEY = "propertyEquals"; + private static final String PROPERTY_CONTAINS_FUNCTION_KEY = "propertyContains"; + + private static final char QUOTE = '"'; + private static final char COMMA = ','; + private static final char LEFT_PAREN = '('; + private static final char RIGHT_PAREN = ')'; + + + private static final char NOT_CHAR = '!'; + private static final char AMPERSAND_CHAR = '&'; + private static final char OR_CHAR = '|'; + + enum Associativity { + LEFT, RIGHT; + } + + enum TokenType { + NOT, AND, OR, FUNCTION, BI_FUNCTION, LEFT_PAREN, RIGHT_PAREN; + + boolean isLogicalOperator() { + return this == NOT || this == AND || this == OR; + } + } + + + static class Token { + TokenType tokenType; + String functionName; + String param0; + String param1; + + Token(TokenType tokenType) { + this.tokenType = tokenType; + + switch (tokenType) { + case LEFT_PAREN: + case RIGHT_PAREN: + case NOT: + case AND: + case OR: + break; + default: + throw new IllegalStateException("Unexpected value: " + tokenType); + } + } + + Token(TokenType tokenType, String functionName, String propertyKey, String value) { + this.tokenType = tokenType; + this.functionName = functionName; + this.param0 = propertyKey; + this.param1 = value; + } + + public static Token valueOf(char c) { + + if (c == LEFT_PAREN) + return new Token(TokenType.LEFT_PAREN); + if (c == RIGHT_PAREN) + return new Token(TokenType.RIGHT_PAREN); + throw new IllegalArgumentException("Unexpected char: " + c); + } + } + + + String expression; + List rpn; + + /** + * Constructs an ExpressionPropertyCondition and initializes the function maps + * with supported unary and binary functions. + */ + public ExpressionPropertyCondition() { + functionMap.put(IS_NULL_FUNCTION_KEY, this::isNull); + functionMap.put(IS_DEFINEDP_FUNCTION_KEY, this::isDefined); + biFunctionMap.put(PROPERTY_EQUALS_FUNCTION_KEY, this::propertyEquals); + biFunctionMap.put(PROPERTY_CONTAINS_FUNCTION_KEY, this::propertyContains); + } + + /** + * Starts the condition by parsing the expression into tokens and converting + * them to Reverse Polish Notation (RPN) for evaluation. + * + *

In case of malformed expression, the instance will not enter the "started" state.

+ */ + public void start() { + if (expression == null || expression.isEmpty()) { + addError("Empty expression"); + return; + } + + try { + List tokens = tokenize(expression.trim()); + this.rpn = infixToReversePolishNotation(tokens); + } catch (IllegalArgumentException|IllegalStateException e) { + addError("Malformed expression: " + e.getMessage()); + return; + } + super.start(); + } + + /** + * Returns the current expression string. + * + * @return the expression, or null if not set + */ + public String getExpression() { + return expression; + } + + /** + * Sets the expression to be evaluated. + * + * @param expression the boolean expression string + */ + public void setExpression(String expression) { + this.expression = expression; + } + + /** + * Evaluates the parsed expression against the current property context. + * + *

If the instance is not in started state, returns false.

+ * + * @return true if the expression evaluates to true, false otherwise + */ + @Override + public boolean evaluate() { + if (!isStarted()) { + return false; + } + return evaluateRPN(rpn); + } + + /** + * Tokenizes the input expression string into a list of tokens, handling + * functions, operators, and parentheses. + * + * @param expr the expression string to tokenize + * @return list of tokens + * @throws IllegalArgumentException if the expression is malformed + */ + private List tokenize(String expr) throws IllegalArgumentException, IllegalStateException { + List tokens = new ArrayList<>(); + + int i = 0; + while (i < expr.length()) { + char c = expr.charAt(i); + + if (Character.isWhitespace(c)) { + i++; + continue; + } + + if (c == LEFT_PAREN || c == RIGHT_PAREN) { + tokens.add(Token.valueOf(c)); + i++; + continue; + } + + if (c == NOT_CHAR) { + tokens.add(new Token(TokenType.NOT)); + i++; + continue; + } + + if (c == AMPERSAND_CHAR) { + i++; // consume '&' + c = expr.charAt(i); + if (c == AMPERSAND_CHAR) { + tokens.add(new Token(TokenType.AND)); + i++; // consume '&' + continue; + } else { + throw new IllegalArgumentException("Expected '&' after '&'"); + } + } + + if (c == OR_CHAR) { + i++; // consume '|' + c = expr.charAt(i); + if (c == OR_CHAR) { + tokens.add(new Token(TokenType.OR)); + i++; // consume '|' + continue; + } else { + throw new IllegalArgumentException("Expected '|' after '|'"); + } + } + + // Parse identifiers like isNull, isNotNull, etc. + if (Character.isLetter(c)) { + StringBuilder sb = new StringBuilder(); + while (i < expr.length() && Character.isLetter(expr.charAt(i))) { + sb.append(expr.charAt(i++)); + } + String functionName = sb.toString(); + + // Skip spaces + i = skipWhitespaces(i); + checkExpectedCharacter(LEFT_PAREN, i); + i++; // consume '(' + + IntHolder intHolder = new IntHolder(i); + String param0 = extractQuotedString(intHolder); + i = intHolder.value; + // Skip spaces + i = skipWhitespaces(i); + + + if (biFunctionMap.containsKey(functionName)) { + checkExpectedCharacter(COMMA, i); + i++; // consume ',' + intHolder.set(i); + String param1 = extractQuotedString(intHolder); + i = intHolder.get(); + i = skipWhitespaces(i); + tokens.add(new Token(TokenType.BI_FUNCTION, functionName, param0, param1)); + } else { + tokens.add(new Token(TokenType.FUNCTION, functionName, param0, null)); + } + + // Skip spaces and expect ')' + checkExpectedCharacter(RIGHT_PAREN, i); + i++; // consume ')' + + continue; + } + } + return tokens; + } + + private String extractQuotedString(IntHolder intHolder) { + int i = intHolder.get(); + i = skipWhitespaces(i); + + // Expect starting " + checkExpectedCharacter(QUOTE, i); + i++; // consume starting " + + int start = i; + i = findIndexOfClosingQuote(i); + String param = expression.substring(start, i); + i++; // consume closing " + intHolder.set(i); + return param; + } + + private int findIndexOfClosingQuote(int i) throws IllegalStateException{ + while (i < expression.length() && expression.charAt(i) != QUOTE) { + i++; + } + if (i >= expression.length()) { + throw new IllegalStateException("Missing closing quote"); + } + return i; + } + + void checkExpectedCharacter(char expectedChar, int i) throws IllegalArgumentException{ + if (i >= expression.length() || expression.charAt(i) != expectedChar) { + throw new IllegalArgumentException("In [" + expression + "] expecting '" + expectedChar + "' at position " + i); + } + } + + private int skipWhitespaces(int i) { + while (i < expression.length() && Character.isWhitespace(expression.charAt(i))) { + i++; + } + return i; + } + + /** + * Converts infix notation tokens to Reverse Polish Notation (RPN) using + * the Shunting-Yard algorithm. + * + * @param tokens list of infix tokens + * @return list of tokens in RPN + * @throws IllegalArgumentException if parentheses are mismatched + */ + private List infixToReversePolishNotation(List tokens) { + List output = new ArrayList<>(); + Stack operatorStack = new Stack<>(); + + for (Token token : tokens) { + TokenType tokenType = token.tokenType; + if (isPredicate(token)) { + output.add(token); + } else if (tokenType.isLogicalOperator()) { // one of NOT, AND, OR types + while (!operatorStack.isEmpty() && precedence(operatorStack.peek()) >= precedence(token) && + operatorAssociativity(token) == Associativity.LEFT) { + output.add(operatorStack.pop()); + } + operatorStack.push(token); + } else if (tokenType == TokenType.LEFT_PAREN) { + operatorStack.push(token); + } else if (tokenType == TokenType.RIGHT_PAREN) { + while (!operatorStack.isEmpty() && operatorStack.peek().tokenType != TokenType.LEFT_PAREN) { + output.add(operatorStack.pop()); + } + if (operatorStack.isEmpty()) + throw new IllegalArgumentException("Mismatched parentheses, expecting '('"); + operatorStack.pop(); // remove '(' + } + } + + while (!operatorStack.isEmpty()) { + Token token = operatorStack.pop(); + TokenType tokenType = token.tokenType; + if (tokenType == TokenType.LEFT_PAREN) + throw new IllegalArgumentException("Mismatched parentheses"); + output.add(token); + } + + return output; + } + + private boolean isPredicate(Token token) { + return token.tokenType == TokenType.FUNCTION || token.tokenType == TokenType.BI_FUNCTION; + } + + private int precedence(Token token) { + TokenType tokenType = token.tokenType; + switch (tokenType) { + case NOT: + return 3; + case AND: + return 2; + case OR: + return 1; + default: + return 0; + } + } + + private Associativity operatorAssociativity(Token token) { + TokenType tokenType = token.tokenType; + + return tokenType == TokenType.NOT ? Associativity.RIGHT : Associativity.LEFT; + } + + /** + * Evaluates the Reverse Polish Notation (RPN) expression. + * + * @param rpn list of tokens in RPN + * @return the boolean result of the evaluation + * @throws IllegalStateException if a function is not defined in the function map + */ + private boolean evaluateRPN(List rpn) throws IllegalStateException { + Stack resultStack = new Stack<>(); + + for (Token token : rpn) { + if (isPredicate(token)) { + boolean value = evaluateFunctions(token); + resultStack.push(value); + } else { + switch (token.tokenType) { + case NOT: + boolean a3 = resultStack.pop(); + resultStack.push(!a3); + break; + case AND: + boolean b2 = resultStack.pop(); + boolean a2 = resultStack.pop(); + resultStack.push(a2 && b2); + break; + + case OR: + boolean b1 = resultStack.pop(); + boolean a1 = resultStack.pop(); + resultStack.push(a1 || b1); + break; + } + } + } + + return resultStack.pop(); + } + + // Evaluate a single predicate like isNull("key1") + private boolean evaluateFunctions(Token token) throws IllegalStateException { + String functionName = token.functionName; + String param0 = token.param0; + String param1 = token.param1; + Function function = functionMap.get(functionName); + if (function != null) { + return function.apply(param0); + } + + BiFunction biFunction = biFunctionMap.get(functionName); + if (biFunction != null) { + return biFunction.apply(param0, param1); + } + + throw new IllegalStateException("Unknown function: " + token); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyDefinedCondition.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyDefinedCondition.java new file mode 100644 index 0000000000..b3b67491dd --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyDefinedCondition.java @@ -0,0 +1,77 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +/** + * Checks whether a named property is defined in the + * context (e.g. system properties, environment, or the configured + * property map used by the surrounding framework). + * + *

This condition expects a property name to be provided via + * {@link #setKey(String)}. When {@link #evaluate()} is called it returns + * {@code true} if the named property is defined and {@code false} + * otherwise. + */ +public class IsPropertyDefinedCondition extends PropertyConditionBase { + + /** + * The property name to check for definition. Must be set before + * starting this evaluator. + */ + String key; + + /** + * Start the evaluator. If the required {@link #key} is not set an + * error is reported and startup is aborted. + */ + public void start() { + if (key == null) { + addError("In IsPropertyDefinedEvaluator 'key' parameter cannot be null"); + return; + } + super.start(); + } + + /** + * Return the configured property name (key) that this evaluator will + * test for definition. + * + * @return the property key, or {@code null} if not set + */ + public String getKey() { + return key; + } + + /** + * Set the property name (key) to be checked by this evaluator. + * + * @param key the property name to check; must not be {@code null} + */ + public void setKey(String key) { + this.key = key; + } + + + /** + * Evaluate whether the configured property is defined. + * + * @return {@code true} if the property named by {@link #key} is + * defined, {@code false} otherwise + */ + @Override + public boolean evaluate() { + return isDefined(key); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyNullCondition.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyNullCondition.java new file mode 100644 index 0000000000..fa56182800 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/IsPropertyNullCondition.java @@ -0,0 +1,42 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +public class IsPropertyNullCondition extends PropertyConditionBase { + + String key; + + public void start() { + if (key == null) { + addError("In IsPropertyNullCondition 'key' parameter cannot be null"); + return; + } + super.start(); + } + + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + @Override + public boolean evaluate() { + return isNull(key); + } +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/JaninoEventEvaluatorBase.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/JaninoEventEvaluatorBase.java deleted file mode 100644 index 17bea06c88..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/JaninoEventEvaluatorBase.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.boolex; - -import java.util.ArrayList; -import java.util.List; - -import org.codehaus.janino.ScriptEvaluator; - -/** - * Abstract class which sets the groundwork for janino based evaluations. - * - * @author Ceki Gülcü - * - * @param event type - */ -abstract public class JaninoEventEvaluatorBase extends EventEvaluatorBase { - - static Class EXPRESSION_TYPE = boolean.class; - static Class[] THROWN_EXCEPTIONS = new Class[1]; - - static public final int ERROR_THRESHOLD = 4; - static { - THROWN_EXCEPTIONS[0] = EvaluationException.class; - } - - private String expression; - - ScriptEvaluator scriptEvaluator; - private int errorCount = 0; - - abstract protected String getDecoratedExpression(); - - abstract protected String[] getParameterNames(); - - abstract protected Class[] getParameterTypes(); - - abstract protected Object[] getParameterValues(E event); - - protected List matcherList = new ArrayList(); - - @Override - public void start() { - try { - assert context != null; - scriptEvaluator = new ScriptEvaluator(getDecoratedExpression(), EXPRESSION_TYPE, getParameterNames(), getParameterTypes(), THROWN_EXCEPTIONS); - super.start(); - } catch (Exception e) { - addError("Could not start evaluator with expression [" + expression + "]", e); - } - } - - public boolean evaluate(E event) throws EvaluationException { - if (!isStarted()) { - throw new IllegalStateException("Evaluator [" + name + "] was called in stopped state"); - } - try { - Boolean result = (Boolean) scriptEvaluator.evaluate(getParameterValues(event)); - return result.booleanValue(); - } catch (Exception ex) { - errorCount++; - if (errorCount >= ERROR_THRESHOLD) { - stop(); - } - throw new EvaluationException("Evaluator [" + name + "] caused an exception", ex); - } - } - - public String getExpression() { - return expression; - } - - public void setExpression(String expression) { - this.expression = expression; - } - - public void addMatcher(Matcher matcher) { - matcherList.add(matcher); - } - - public List getMatcherList() { - return matcherList; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/Matcher.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/Matcher.java index 9510617d53..e29a33f8c2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/Matcher.java +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/Matcher.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -76,7 +76,7 @@ public boolean isStarted() { // not require that the entire region (of the input) be matched. /** - * Checks whether the input matches the regular expression. + * Checks whether the input matches the regular expression. * * @param input * @return diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyCondition.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyCondition.java new file mode 100644 index 0000000000..4ddc7470b7 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyCondition.java @@ -0,0 +1,61 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.conditional.Condition; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.spi.PropertyContainer; + +/** + * Interface for evaluating conditions based on properties during the conditional processing + * of Logback configuration files. This interface is intended to provide an + * alternative to legacy Janino-based evaluation. + *

+ * Implementations of this interface can access both global properties from the {@link Context} + * and local properties specific to the embedding configurator instance. This allows for fine-grained + * and context-aware evaluation of configuration conditions. + *

+ * + *

+ * Typical usage involves implementing this interface to provide custom logic for evaluating + * whether certain configuration blocks should be included or excluded based on property values. + *

+ * + * @since 1.5.20 + * @author Ceki Gülcü + */ +public interface PropertyCondition extends Condition, ContextAware, LifeCycle { + + /** + * Returns the local {@link PropertyContainer} used for property lookups specific to the embedding configurator. + * This is distinct from the global {@link Context} property container. + * + * @return the local property container, or null if not set + */ + public PropertyContainer getLocalPropertyContainer(); + + /** + * Sets a {@link PropertyContainer} specific to the embedding configurator, which is used for property lookups + * in addition to the global {@link Context} properties. This allows for overriding or supplementing global properties + * with local values during evaluation. + * + * @param aPropertyContainer the local property container to use for lookups + */ + public void setLocalPropertyContainer(PropertyContainer aPropertyContainer); + + +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyConditionBase.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyConditionBase.java new file mode 100644 index 0000000000..f367e81771 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyConditionBase.java @@ -0,0 +1,218 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.PropertyContainer; +import ch.qos.logback.core.util.OptionHelper; + +import static ch.qos.logback.core.CoreConstants.EMPTY_STRING; + +/** + *

Abstract base class provides some scaffolding. It is intended to ease migration + * from legacy conditional processing in configuration files + * (e.g. <if>, <then>, <else>) using the Janino library. Nevertheless, + * it should also be useful in newly written code.

+ * + *

Properties are looked up in the following order:

+ * + *
    + *
  1. In the local property container, usually the {@link ModelInterpretationContext}
  2. + *
  3. in the logger context
  4. + *
  5. system properties
  6. + *
  7. environment variables
  8. + *
+ * + * @author Ceki Gülcü + * @see OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer) + * @since 1.5.20 + */ +abstract public class PropertyConditionBase extends ContextAwareBase implements PropertyCondition { + + /** + * Indicates whether this evaluator has been started. + */ + boolean started; + /** + *

The local property container used for property lookups.

+ * + *

Local properties correspond to the properties in the embedding + * configurator, i.e. usually the {@link ModelInterpretationContext} instance.

+ */ + PropertyContainer localPropertyContainer; + + /** + * Returns the local property container used by this evaluator. + * + *

Local properties correspond to the properties in the embedding + * configurator, i.e. usually the {@link ModelInterpretationContext} instance.

+ * + * @return the local property container + */ + @Override + public PropertyContainer getLocalPropertyContainer() { + return localPropertyContainer; + } + + /** + * Sets the local property container for this evaluator. + * + *

Local properties correspond to the properties in the embedding + * configurator, i.e. usually the {@link ModelInterpretationContext} instance.

+ * + * @param aLocalPropertyContainer the local property container to set + */ + @Override + public void setLocalPropertyContainer(PropertyContainer aLocalPropertyContainer) { + this.localPropertyContainer = aLocalPropertyContainer; + } + + /** + * Checks if the property with the given key is null. + * + *

The property is looked up via the + * {@link OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer)} method. + * See above for the lookup order.

+ * + * @param k the property key + * @return true if the property is null, false otherwise + */ + public boolean isNull(String k) { + String val = OptionHelper.propertyLookup(k, localPropertyContainer, getContext()); + return (val == null); + } + + /** + * Checks if the property with the given key is defined (not null). + * + *

The property is looked up via the + * {@link OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer)} method. + * See above for the lookup order.

+ * + * @param k the property key + * @return true if the property is defined, false otherwise + */ + public boolean isDefined(String k) { + String val = OptionHelper.propertyLookup(k, localPropertyContainer, getContext()); + return (val != null); + } + + /** + * Retrieves the property value for the given key, returning an empty string if null. + * This is a shorthand for {@link #property(String)}. + * + * @param k the property key + * @return the property value or an empty string + */ + public String p(String k) { + return property(k); + } + + /** + * Retrieves the property value for the given key, returning an empty string if null. + * + *

The property is looked up via the + * {@link OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer)} method. + * See above for the lookup order.

+ * + * @param k the property key + * @return the property value or an empty string + */ + public String property(String k) { + String val = OptionHelper.propertyLookup(k, localPropertyContainer, getContext()); + if (val != null) + return val; + else + return EMPTY_STRING; + } + + /** + * Compare the resolved property value with the provided expected value. + * + *

The property is looked up via the + * {@link OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer)} method. + * See above for the lookup order.

+ * + *

Returns {@code true} if the resolved property value is equal to {@code val} + * according to {@link String#equals(Object)}. If the resolved property value or {@code val} is null, + * then false is returned.

+ * + * @param propertyKey the property key to look up + * @param value expected string value to compare against; must be non-null + * @return {@code true} if the resolved property equals {@code value}, + * {@code false} otherwise or if either the resolved property or {@code value} is null. + * @since 1.5.24 + */ + public boolean propertyEquals(String propertyKey, String value) { + String actual = OptionHelper.propertyLookup(propertyKey, localPropertyContainer, getContext()); + if (actual == null || value == null) { + return false; + } + return actual.equals(value); + } + + + /** + * Determine whether the resolved property value contains the given substring. + *

+ * + *

The property is looked up via the + * {@link OptionHelper#propertyLookup(String, PropertyContainer, PropertyContainer)} method. + * See above for the lookup order.

+ * + *

This method returns {@code true} if the resolved property value's + * {@link String#contains(CharSequence)} returns {@code true} for the supplied + * {@code inclusion}. False is returned if either the resolved property value or + * {@code inclusion} parameter is null.

+ * + * @param k the property key to look up + * @param inclusion substring to search for in the resolved property value; must be non-null + * @return {@code true} if the property value contains {@code inclusion}, false otherwise or + * if either the resolved property value or {@code inclusion} is null + * + * @since 1.5.24 + */ + public boolean propertyContains(String k, String inclusion) { + String actual = OptionHelper.propertyLookup(k, localPropertyContainer, getContext()); + if (actual == null || inclusion == null) + return false; + + return actual.contains(inclusion); + } + + /** + * Checks if this evaluator has been started. + * + * @return true if started, false otherwise + */ + public boolean isStarted() { + return started; + } + + /** + * Starts this evaluator. + */ + public void start() { + started = true; + } + + /** + * Stops this evaluator. + */ + public void stop() { + started = false; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyEqualityCondition.java b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyEqualityCondition.java new file mode 100644 index 0000000000..d1b7efc247 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/PropertyEqualityCondition.java @@ -0,0 +1,117 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +/** + * Condition that evaluates to {@code true} when a property + * equals a specified expected value. + * + *

The property named by {@link #key} is resolved using the + * inherited property lookup mechanism (see {@code PropertyConditionBase}). + * If the resolved property value equals {@link #value} (using + * {@link String#equals(Object)}), this condition evaluates to {@code true}. + * + * @since 1.5.20 + */ +public class PropertyEqualityCondition extends PropertyConditionBase { + + /** + * The property name (key) to look up. Must be set before starting. + */ + String key; + + /** + * The expected value to compare the resolved property against. + */ + String value; + + /** + * Start the component and validate required parameters. + * If either {@link #key} or {@link #value} is {@code null}, an error + * is reported and the component does not start. + */ + public void start() { + if (key == null) { + addError("In PropertyEqualsValue 'key' parameter cannot be null"); + return; + } + if (value == null) { + addError("In PropertyEqualsValue 'value' parameter cannot be null"); + return; + } + super.start(); + } + + /** + * Return the configured expected value. + * + * @return the expected value, or {@code null} if not set + */ + public String getValue() { + return value; + } + + /** + * Set the expected value that the resolved property must equal for + * this condition to evaluate to {@code true}. + * + * @param value the expected value + */ + public void setValue(String value) { + this.value = value; + } + + /** + * Return the property key that will be looked up when evaluating the + * condition. + * + * @return the property key, or {@code null} if not set + */ + public String getKey() { + return key; + } + + /** + * Set the property key to resolve during evaluation. + * + * @param key the property key + */ + public void setKey(String key) { + this.key = key; + } + + /** + * Evaluate the condition: resolve the property named by {@link #key} + * and compare it to {@link #value}. + * + * @return {@code true} if the resolved property equals the expected + * value; {@code false} otherwise + */ + @Override + public boolean evaluate() { + if (key == null) { + addError("key cannot be null"); + return false; + } + + String val = p(key); + if (val == null) + return false; + else { + return val.equals(value); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/boolex/package.html b/logback-core/src/main/java/ch/qos/logback/core/boolex/package.html index 567de464e2..10b5ff986f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/boolex/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/boolex/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/BindDataSourceToJNDIAction.java b/logback-core/src/main/java/ch/qos/logback/core/db/BindDataSourceToJNDIAction.java deleted file mode 100644 index d3fa3dc42f..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/BindDataSourceToJNDIAction.java +++ /dev/null @@ -1,91 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.sql.DataSource; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; -import ch.qos.logback.core.util.OptionHelper; - -/** - * - * @author Ceki Gulcu - * - */ -public class BindDataSourceToJNDIAction extends Action { - - static final String DATA_SOURCE_CLASS = "dataSourceClass"; - static final String URL = "url"; - static final String USER = "user"; - static final String PASSWORD = "password"; - private final BeanDescriptionCache beanDescriptionCache; - - public BindDataSourceToJNDIAction(BeanDescriptionCache beanDescriptionCache) { - this.beanDescriptionCache = beanDescriptionCache; - } - - /** - * Instantiates an a data source and bind it to JNDI - * Most of the required parameters are placed in the ec.substitutionProperties - */ - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - String dsClassName = ec.getProperty(DATA_SOURCE_CLASS); - - if (OptionHelper.isEmpty(dsClassName)) { - addWarn("dsClassName is a required parameter"); - ec.addError("dsClassName is a required parameter"); - - return; - } - - String urlStr = ec.getProperty(URL); - String userStr = ec.getProperty(USER); - String passwordStr = ec.getProperty(PASSWORD); - - try { - DataSource ds = (DataSource) OptionHelper.instantiateByClassName(dsClassName, DataSource.class, context); - - PropertySetter setter = new PropertySetter(beanDescriptionCache,ds); - setter.setContext(context); - - if (!OptionHelper.isEmpty(urlStr)) { - setter.setProperty("url", urlStr); - } - - if (!OptionHelper.isEmpty(userStr)) { - setter.setProperty("user", userStr); - } - - if (!OptionHelper.isEmpty(passwordStr)) { - setter.setProperty("password", passwordStr); - } - - Context ctx = new InitialContext(); - ctx.rebind("dataSource", ds); - } catch (Exception oops) { - addError("Could not bind datasource. Reported error follows.", oops); - ec.addError("Could not not bind datasource of type [" + dsClassName + "]."); - } - } - - public void end(InterpretationContext ec, String name) { - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java b/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java deleted file mode 100644 index 3e11d20c1f..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSource.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.SQLException; - -import ch.qos.logback.core.db.dialect.SQLDialectCode; -import ch.qos.logback.core.spi.LifeCycle; - -/** - * The ConnectionSource interface provides a pluggable means of - * transparently obtaining JDBC {@link java.sql.Connection}s for logback classes - * that require the use of a {@link java.sql.Connection}. - * - * For more information about this component, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#DBAppender - * - * @author Ray DeCampo - */ -public interface ConnectionSource extends LifeCycle { - - /** - * Obtain a {@link java.sql.Connection} for use. The client is - * responsible for closing the {@link java.sql.Connection} when it is no - * longer required. - * - * @throws SQLException if a {@link java.sql.Connection} could not be - * obtained - */ - Connection getConnection() throws SQLException; - - /** - * Get the SQL dialect that should be used for this connection. Note that the - * dialect is not needed if the JDBC driver supports the getGeneratedKeys - * method. - */ - SQLDialectCode getSQLDialectCode(); - - /** - * If the connection supports the JDBC 3.0 getGeneratedKeys method, then - * we do not need any specific dialect support. - */ - boolean supportsGetGeneratedKeys(); - - /** - * If the connection does not support batch updates, we will avoid using them. - */ - boolean supportsBatchUpdates(); -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java b/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java deleted file mode 100644 index ccf1e4cb9d..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/ConnectionSourceBase.java +++ /dev/null @@ -1,128 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -import ch.qos.logback.core.db.dialect.DBUtil; -import ch.qos.logback.core.db.dialect.SQLDialectCode; -import ch.qos.logback.core.spi.ContextAwareBase; - -/** - * @author Ceki Gülcü - */ -public abstract class ConnectionSourceBase extends ContextAwareBase implements ConnectionSource { - - private boolean started; - - private String user = null; - private String password = null; - - // initially we have an unknown dialect - private SQLDialectCode dialectCode = SQLDialectCode.UNKNOWN_DIALECT; - private boolean supportsGetGeneratedKeys = false; - private boolean supportsBatchUpdates = false; - - /** - * Learn relevant information about this connection source. - * - */ - public void discoverConnectionProperties() { - Connection connection = null; - try { - connection = getConnection(); - if (connection == null) { - addWarn("Could not get a connection"); - return; - } - DatabaseMetaData meta = connection.getMetaData(); - DBUtil util = new DBUtil(); - util.setContext(getContext()); - supportsGetGeneratedKeys = util.supportsGetGeneratedKeys(meta); - supportsBatchUpdates = util.supportsBatchUpdates(meta); - dialectCode = DBUtil.discoverSQLDialect(meta); - addInfo("Driver name=" + meta.getDriverName()); - addInfo("Driver version=" + meta.getDriverVersion()); - addInfo("supportsGetGeneratedKeys=" + supportsGetGeneratedKeys); - - } catch (SQLException se) { - addWarn("Could not discover the dialect to use.", se); - } finally { - DBHelper.closeConnection(connection); - } - } - - /** - * Does this connection support the JDBC Connection.getGeneratedKeys method? - */ - public final boolean supportsGetGeneratedKeys() { - return supportsGetGeneratedKeys; - } - - public final SQLDialectCode getSQLDialectCode() { - return dialectCode; - } - - /** - * Get the password for this connection source. - */ - public final String getPassword() { - return password; - } - - /** - * Sets the password. - * @param password The password to set - */ - public final void setPassword(final String password) { - this.password = password; - } - - /** - * Get the user for this connection source. - */ - public final String getUser() { - return user; - } - - /** - * Sets the username. - * @param username The username to set - */ - public final void setUser(final String username) { - this.user = username; - } - - /** - * Does this connection support batch updates? - */ - public final boolean supportsBatchUpdates() { - return supportsBatchUpdates; - } - - public boolean isStarted() { - return started; - } - - public void start() { - started = true; - } - - public void stop() { - started = false; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java deleted file mode 100644 index 895fee3259..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/DBAppenderBase.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; - -import ch.qos.logback.core.UnsynchronizedAppenderBase; -import ch.qos.logback.core.db.dialect.DBUtil; -import ch.qos.logback.core.db.dialect.SQLDialect; -import ch.qos.logback.core.db.dialect.SQLDialectCode; - -/** - * @author Ceki Gülcü - * @author Ray DeCampo - * @author Sébastien Pennec - */ -public abstract class DBAppenderBase extends UnsynchronizedAppenderBase { - - protected ConnectionSource connectionSource; - protected boolean cnxSupportsGetGeneratedKeys = false; - protected boolean cnxSupportsBatchUpdates = false; - protected SQLDialect sqlDialect; - - protected abstract Method getGeneratedKeysMethod(); - - protected abstract String getInsertSQL(); - - @Override - public void start() { - - if (connectionSource == null) { - throw new IllegalStateException("DBAppender cannot function without a connection source"); - } - - sqlDialect = DBUtil.getDialectFromCode(connectionSource.getSQLDialectCode()); - if (getGeneratedKeysMethod() != null) { - cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys(); - } else { - cnxSupportsGetGeneratedKeys = false; - } - cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates(); - if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) { - throw new IllegalStateException( - "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect"); - } - - // all nice and dandy on the eastern front - super.start(); - } - - /** - * @return Returns the connectionSource. - */ - public ConnectionSource getConnectionSource() { - return connectionSource; - } - - /** - * @param connectionSource - * The connectionSource to set. - */ - public void setConnectionSource(ConnectionSource connectionSource) { - this.connectionSource = connectionSource; - } - - @Override - public void append(E eventObject) { - Connection connection = null; - PreparedStatement insertStatement = null; - try { - connection = connectionSource.getConnection(); - connection.setAutoCommit(false); - - if (cnxSupportsGetGeneratedKeys) { - String EVENT_ID_COL_NAME = "EVENT_ID"; - // see - if (connectionSource.getSQLDialectCode() == SQLDialectCode.POSTGRES_DIALECT) { - EVENT_ID_COL_NAME = EVENT_ID_COL_NAME.toLowerCase(); - } - insertStatement = connection.prepareStatement(getInsertSQL(), new String[] { EVENT_ID_COL_NAME }); - } else { - insertStatement = connection.prepareStatement(getInsertSQL()); - } - - long eventId; - // inserting an event and getting the result must be exclusive - synchronized (this) { - subAppend(eventObject, connection, insertStatement); - eventId = selectEventId(insertStatement, connection); - } - secondarySubAppend(eventObject, connection, eventId); - - connection.commit(); - } catch (Throwable sqle) { - addError("problem appending event", sqle); - } finally { - DBHelper.closeStatement(insertStatement); - DBHelper.closeConnection(connection); - } - } - - protected abstract void subAppend(E eventObject, Connection connection, PreparedStatement statement) throws Throwable; - - protected abstract void secondarySubAppend(E eventObject, Connection connection, long eventId) throws Throwable; - - @SuppressWarnings("resource") - protected long selectEventId(PreparedStatement insertStatement, Connection connection) throws SQLException, InvocationTargetException { - ResultSet rs = null; - Statement idStatement = null; - try { - boolean gotGeneratedKeys = false; - if (cnxSupportsGetGeneratedKeys) { - try { - rs = (ResultSet) getGeneratedKeysMethod().invoke(insertStatement, (Object[]) null); - gotGeneratedKeys = true; - } catch (InvocationTargetException ex) { - Throwable target = ex.getTargetException(); - if (target instanceof SQLException) { - throw (SQLException) target; - } - throw ex; - } catch (IllegalAccessException ex) { - addWarn("IllegalAccessException invoking PreparedStatement.getGeneratedKeys", ex); - } - } - - if (!gotGeneratedKeys) { - idStatement = connection.createStatement(); - idStatement.setMaxRows(1); - String selectInsertIdStr = sqlDialect.getSelectInsertId(); - rs = idStatement.executeQuery(selectInsertIdStr); - } - - // A ResultSet cursor is initially positioned before the first row; - // the first call to the method next makes the first row the current row - rs.next(); - long eventId = rs.getLong(1); - return eventId; - } finally { - if (rs != null) { - try { - rs.close(); - } catch (SQLException e) { - } - } - DBHelper.closeStatement(idStatement); - } - } - - @Override - public void stop() { - super.stop(); - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/DBHelper.java b/logback-core/src/main/java/ch/qos/logback/core/db/DBHelper.java deleted file mode 100644 index 61a16e962a..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/DBHelper.java +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.SQLException; -import java.sql.Statement; - -/** - * @author Ceki Gülcü - * - */ -public class DBHelper { - - static public void closeConnection(Connection connection) { - if (connection != null) { - try { - connection.close(); - } catch (SQLException sqle) { - // static utility classes should not log without an explicit repository - // reference - } - } - } - - public static void closeStatement(Statement statement) { - if (statement != null) { - try { - statement.close(); - } catch (SQLException sqle) { - } - } - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java b/logback-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java deleted file mode 100644 index 9e05cd40fd..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/DataSourceConnectionSource.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.SQLException; - -import javax.sql.DataSource; - -import ch.qos.logback.core.db.dialect.SQLDialectCode; - -/** - * The DataSourceConnectionSource is an implementation of - * {@link ConnectionSource} that obtains the Connection in the recommended JDBC - * manner based on a {@link javax.sql.DataSource DataSource}. - *

- * - * For more information about this component, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#DBAppender - * - * @author Ray DeCampo - * @author Ceki Gülcü - */ -public class DataSourceConnectionSource extends ConnectionSourceBase { - - private DataSource dataSource; - - @Override - public void start() { - if (dataSource == null) { - addWarn("WARNING: No data source specified"); - } else { - discoverConnectionProperties(); - if (!supportsGetGeneratedKeys() && getSQLDialectCode() == SQLDialectCode.UNKNOWN_DIALECT) { - addWarn("Connection does not support GetGeneratedKey method and could not discover the dialect."); - } - } - super.start(); - } - - /** - * @see ch.qos.logback.core.db.ConnectionSource#getConnection() - */ - public Connection getConnection() throws SQLException { - if (dataSource == null) { - addError("WARNING: No data source specified"); - return null; - } - - if (getUser() == null) { - return dataSource.getConnection(); - } else { - return dataSource.getConnection(getUser(), getPassword()); - } - } - - public DataSource getDataSource() { - return dataSource; - } - - public void setDataSource(DataSource dataSource) { - this.dataSource = dataSource; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java b/logback-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java deleted file mode 100644 index 43733d6d66..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/DriverManagerConnectionSource.java +++ /dev/null @@ -1,95 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.DriverManager; -import java.sql.SQLException; - -/** - * The DriverManagerConnectionSource is an implementation of - * {@link ConnectionSource} that obtains the Connection in the traditional JDBC - * manner based on the connection URL. - *

- * For more information about this component, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#DBAppender - * - * @author Ray DeCampo - */ -public class DriverManagerConnectionSource extends ConnectionSourceBase { - private String driverClass = null; - private String url = null; - - public void start() { - try { - if (driverClass != null) { - Class.forName(driverClass); - discoverConnectionProperties(); - } else { - addError("WARNING: No JDBC driver specified for logback DriverManagerConnectionSource."); - } - } catch (final ClassNotFoundException cnfe) { - addError("Could not load JDBC driver class: " + driverClass, cnfe); - } - } - - /** - * @see ch.qos.logback.core.db.ConnectionSource#getConnection() - */ - public Connection getConnection() throws SQLException { - if (getUser() == null) { - return DriverManager.getConnection(url); - } else { - return DriverManager.getConnection(url, getUser(), getPassword()); - } - } - - /** - * Returns the url. - * - * @return String - */ - public String getUrl() { - return url; - } - - /** - * Sets the url. - * - * @param url - * The url to set - */ - public void setUrl(String url) { - this.url = url; - } - - /** - * Returns the name of the driver class. - * - * @return String - */ - public String getDriverClass() { - return driverClass; - } - - /** - * Sets the driver class. - * - * @param driverClass - * The driver class to set - */ - public void setDriverClass(String driverClass) { - this.driverClass = driverClass; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java b/logback-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java deleted file mode 100644 index 97189ed0de..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/JNDIConnectionSource.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db; - -import java.sql.Connection; -import java.sql.SQLException; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; - -// PortableRemoteObject was introduced in JDK 1.3. We won't use it. -// import javax.rmi.PortableRemoteObject; -import javax.sql.DataSource; - -/** - * The JNDIConnectionSource is an implementation of - * {@link ConnectionSource} that obtains a {@link javax.sql.DataSource} from a - * JNDI provider and uses it to obtain a {@link java.sql.Connection}. It is - * primarily designed to be used inside of J2EE application servers or - * application server clients, assuming the application server supports remote - * access of {@link javax.sql.DataSource}s. In this way one can take advantage - * of connection pooling and whatever other goodies the application server - * provides. - *

- * For more information about this component, please refer to the online manual at - * http://logback.qos.ch/manual/appenders.html#DBAppender - * - * @author Ray DeCampo - */ -public class JNDIConnectionSource extends ConnectionSourceBase { - private String jndiLocation = null; - private DataSource dataSource = null; - - public void start() { - if (jndiLocation == null) { - addError("No JNDI location specified for JNDIConnectionSource."); - } - discoverConnectionProperties(); - } - - public Connection getConnection() throws SQLException { - Connection conn = null; - try { - if (dataSource == null) { - dataSource = lookupDataSource(); - } - if (getUser() != null) { - addWarn("Ignoring property [user] with value [" + getUser() + "] for obtaining a connection from a DataSource."); - } - conn = dataSource.getConnection(); - } catch (final NamingException ne) { - addError("Error while getting data source", ne); - throw new SQLException("NamingException while looking up DataSource: " + ne.getMessage()); - } catch (final ClassCastException cce) { - addError("ClassCastException while looking up DataSource.", cce); - throw new SQLException("ClassCastException while looking up DataSource: " + cce.getMessage()); - } - - return conn; - } - - /** - * Returns the jndiLocation. - * - * @return String - */ - public String getJndiLocation() { - return jndiLocation; - } - - /** - * Sets the jndiLocation. - * - * @param jndiLocation - * The jndiLocation to set - */ - public void setJndiLocation(String jndiLocation) { - this.jndiLocation = jndiLocation; - } - - private DataSource lookupDataSource() throws NamingException, SQLException { - addInfo("Looking up [" + jndiLocation + "] in JNDI"); - DataSource ds; - Context initialContext = new InitialContext(); - Object obj = initialContext.lookup(jndiLocation); - - // PortableRemoteObject was introduced in JDK 1.3. We won't use it. - // ds = (DataSource)PortableRemoteObject.narrow(obj, DataSource.class); - ds = (DataSource) obj; - - if (ds == null) { - throw new SQLException("Failed to obtain data source from JNDI location " + jndiLocation); - } else { - return ds; - } - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java deleted file mode 100644 index f8e7e3f567..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/DBUtil.java +++ /dev/null @@ -1,143 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -import java.sql.DatabaseMetaData; -import java.sql.SQLException; - -import ch.qos.logback.core.spi.ContextAwareBase; - -/** - * - * @author Ceki Gulcu - * - */ -public class DBUtil extends ContextAwareBase { - private static final String POSTGRES_PART = "postgresql"; - private static final String MYSQL_PART = "mysql"; - private static final String ORACLE_PART = "oracle"; - // private static final String MSSQL_PART = "mssqlserver4"; - private static final String MSSQL_PART = "microsoft"; - private static final String HSQL_PART = "hsql"; - private static final String H2_PART = "h2"; - private static final String SYBASE_SQLANY_PART = "sql anywhere"; - private static final String SQLITE_PART = "sqlite"; - - public static SQLDialectCode discoverSQLDialect(DatabaseMetaData meta) { - SQLDialectCode dialectCode = SQLDialectCode.UNKNOWN_DIALECT; - - try { - - String dbName = meta.getDatabaseProductName().toLowerCase(); - - if (dbName.indexOf(POSTGRES_PART) != -1) { - return SQLDialectCode.POSTGRES_DIALECT; - } else if (dbName.indexOf(MYSQL_PART) != -1) { - return SQLDialectCode.MYSQL_DIALECT; - } else if (dbName.indexOf(ORACLE_PART) != -1) { - return SQLDialectCode.ORACLE_DIALECT; - } else if (dbName.indexOf(MSSQL_PART) != -1) { - return SQLDialectCode.MSSQL_DIALECT; - } else if (dbName.indexOf(HSQL_PART) != -1) { - return SQLDialectCode.HSQL_DIALECT; - } else if (dbName.indexOf(H2_PART) != -1) { - return SQLDialectCode.H2_DIALECT; - } else if (dbName.indexOf(SYBASE_SQLANY_PART) != -1) { - return SQLDialectCode.SYBASE_SQLANYWHERE_DIALECT; - } else if (dbName.indexOf(SQLITE_PART) != -1) { - return SQLDialectCode.SQLITE_DIALECT; - } else { - return SQLDialectCode.UNKNOWN_DIALECT; - } - } catch (SQLException sqle) { - // we can't do much here - } - - return dialectCode; - } - - public static SQLDialect getDialectFromCode(SQLDialectCode sqlDialectType) { - SQLDialect sqlDialect = null; - - switch (sqlDialectType) { - case POSTGRES_DIALECT: - sqlDialect = new PostgreSQLDialect(); - break; - - case MYSQL_DIALECT: - sqlDialect = new MySQLDialect(); - break; - - case ORACLE_DIALECT: - sqlDialect = new OracleDialect(); - break; - - case MSSQL_DIALECT: - sqlDialect = new MsSQLDialect(); - break; - - case HSQL_DIALECT: - sqlDialect = new HSQLDBDialect(); - break; - - case H2_DIALECT: - sqlDialect = new H2Dialect(); - break; - - case SYBASE_SQLANYWHERE_DIALECT: - sqlDialect = new SybaseSqlAnywhereDialect(); - break; - - case SQLITE_DIALECT: - sqlDialect = new SQLiteDialect(); - break; - - case UNKNOWN_DIALECT: - // nothing to do - - } - return sqlDialect; - } - - /** - * This method handles cases where the - * {@link DatabaseMetaData#supportsGetGeneratedKeys} method is missing in the - * JDBC driver implementation. - */ - public boolean supportsGetGeneratedKeys(DatabaseMetaData meta) { - try { - // - // invoking JDBC 1.4 method by reflection - // - return ((Boolean) DatabaseMetaData.class.getMethod("supportsGetGeneratedKeys", (Class[]) null).invoke(meta, (Object[]) null)).booleanValue(); - } catch (Throwable e) { - addInfo("Could not call supportsGetGeneratedKeys method. This may be recoverable"); - return false; - } - } - - /** - * This method handles cases where the - * {@link DatabaseMetaData#supportsBatchUpdates} method is missing in the JDBC - * driver implementation. - */ - public boolean supportsBatchUpdates(DatabaseMetaData meta) { - try { - return meta.supportsBatchUpdates(); - } catch (Throwable e) { - addInfo("Missing DatabaseMetaData.supportsBatchUpdates method."); - return false; - } - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/H2Dialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/H2Dialect.java deleted file mode 100644 index 3365736e41..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/H2Dialect.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * The H2 dialect. - * - * @author Ceki Gülcü - */ -public class H2Dialect implements SQLDialect { - public static final String SELECT_CURRVAL = "CALL IDENTITY()"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java deleted file mode 100644 index f918031319..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/HSQLDBDialect.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * The HSQLDB dialect. - * - * @author Ceki Gülcü - */ -public class HSQLDBDialect implements SQLDialect { - public static final String SELECT_CURRVAL = "CALL IDENTITY()"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java deleted file mode 100644 index c5b2934ea7..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MsSQLDialect.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** -* The MS SQL Server dialect is untested. -* -* Note that the dialect is not needed if your JDBC driver supports -* the getGeneratedKeys method introduced in JDBC 3.0 specification. -* -* @author James Stauffer -*/ -public class MsSQLDialect implements SQLDialect { - public static final String SELECT_CURRVAL = "SELECT @@identity id"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java deleted file mode 100644 index 12b475a041..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/MySQLDialect.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * - * - * @author Ceki - * - */ -public class MySQLDialect implements SQLDialect { - public static final String SELECT_LAST_INSERT_ID = "SELECT LAST_INSERT_ID()"; - - public String getSelectInsertId() { - return SELECT_LAST_INSERT_ID; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java deleted file mode 100644 index 813e478621..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/OracleDialect.java +++ /dev/null @@ -1,29 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * The Oracle dialect. Tested successfully on Oracle9i Release 9.2.0.3.0 by - * James Stauffer. - * - * @author Ceki Gülcü - */ -public class OracleDialect implements SQLDialect { - public static final String SELECT_CURRVAL = "SELECT logging_event_id_seq.currval from dual"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java deleted file mode 100644 index a5bb181478..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/PostgreSQLDialect.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * - * @author ceki - * - */ -public class PostgreSQLDialect implements SQLDialect { - public static final String SELECT_CURRVAL = "SELECT currval('logging_event_id_seq')"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java deleted file mode 100644 index b1ab16507d..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialect.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * @author Ceki Gülcü - * - */ -public interface SQLDialect { - String getSelectInsertId(); -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialectCode.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialectCode.java deleted file mode 100644 index dde41edeac..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLDialectCode.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -public enum SQLDialectCode { - UNKNOWN_DIALECT, POSTGRES_DIALECT, MYSQL_DIALECT, ORACLE_DIALECT, MSSQL_DIALECT, HSQL_DIALECT, H2_DIALECT, SYBASE_SQLANYWHERE_DIALECT, SQLITE_DIALECT; -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLiteDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLiteDialect.java deleted file mode 100644 index 6b6a9a59e5..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SQLiteDialect.java +++ /dev/null @@ -1,30 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -/** - * SQLite dialect - * - * Note that the dialect is not needed if your JDBC driver supports the - * getGeneratedKeys method introduced in JDBC 3.0 specification. - * - * @author Anthony Trinh - */ -public class SQLiteDialect implements SQLDialect { - public static final String SELECT_CURRVAL = "SELECT last_insert_rowid();"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SybaseSqlAnywhereDialect.java b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SybaseSqlAnywhereDialect.java deleted file mode 100644 index 1d68740c9c..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/SybaseSqlAnywhereDialect.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.db.dialect; - -public class SybaseSqlAnywhereDialect implements SQLDialect { - - /** - * The Sybase SQLAnywhere Dialect - * - * Note that the dialect is not needed if your JDBC driver supports - * the getGeneratedKeys method introduced in JDBC 3.0 specification. - * - * @author Michael Lynch - */ - - public static final String SELECT_CURRVAL = "SELECT @@identity id"; - - public String getSelectInsertId() { - return SELECT_CURRVAL; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/package.html b/logback-core/src/main/java/ch/qos/logback/core/db/dialect/package.html deleted file mode 100644 index d4f92b53c7..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/dialect/package.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - -

Contains the dialect classes used by logback to log to different databases, and the - SQL scripts to created the necessary tables.

- - - \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/db/package.html b/logback-core/src/main/java/ch/qos/logback/core/db/package.html deleted file mode 100644 index a3ff88dfcb..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/db/package.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - - - -

The ch.qos.logback.core.db package provides bases classes to append objects -into various databases. -

- -

Most popular database systems, such as PostgreSQL, MySQL, Oracle, DB2 and MsSQL -are supported. -

- -

Just as importantly, the way for obtaining JDBC connections is pluggable. Connections can -be obtained through the traditional way of DriverManager, or alternatively as a DataSource. -A DataSource can be instantiated directly or it can obtained through JNDI. -

- - \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/ByteArrayUtil.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/ByteArrayUtil.java index 5c6660d811..0b80a85ed6 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/ByteArrayUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/ByteArrayUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/EchoEncoder.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/EchoEncoder.java index ef61b77d0d..946f7a0b58 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/EchoEncoder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/EchoEncoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -25,14 +25,14 @@ public byte[] encode(E event) { return val.getBytes(); } - public byte[] footerBytes() { + public byte[] footerBytes() { if (fileFooter == null) { return null; } return fileFooter.getBytes(); } - public byte[] headerBytes() { + public byte[] headerBytes() { if (fileHeader == null) { return null; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/Encoder.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/Encoder.java index fb9909f8d1..cd901d21e2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/Encoder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/Encoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,15 +23,14 @@ * @author Joern Huxhorn * @author Maarten Bosteels * - * @param - * event type + * @param event type * @since 0.9.19 */ public interface Encoder extends ContextAware, LifeCycle { /** - * Get header bytes. This method is typically called upon opening of - * an output stream. + * Get header bytes. This method is typically called upon opening of an output + * stream. * * @return header bytes. Null values are allowed. */ @@ -39,14 +38,14 @@ public interface Encoder extends ContextAware, LifeCycle { /** * Encode an event as bytes. - * + * * @param event */ byte[] encode(E event); - + /** - * Get footer bytes. This method is typically called prior to the closing - * of the stream where events are written. + * Get footer bytes. This method is typically called prior to the closing of the + * stream where events are written. * * @return footer bytes. Null values are allowed. */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/EncoderBase.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/EncoderBase.java index 0daf74f50b..7cbecc38d7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/EncoderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/EncoderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/JsonEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/JsonEscapeUtil.java new file mode 100644 index 0000000000..8922fec07e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/JsonEscapeUtil.java @@ -0,0 +1,115 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.encoder; + +public class JsonEscapeUtil { + + protected final static char[] HEXADECIMALS_TABLE = "0123456789ABCDEF".toCharArray(); + + static final int ESCAPE_CODES_COUNT = 32; + + static final String[] ESCAPE_CODES = new String[ESCAPE_CODES_COUNT]; + + // From RFC-8259 page 5 + + // " quotation mark U+0022 -- escaped as \" + // \ reverse solidus U+005C -- escaped as \\ + // / solidus U+002F -- escaped as \/ + + // backspace U+0008 -- escaped as \b + // tab U+0009 -- escaped as \t + // line feed U+000A -- escaped as \n + // form feed U+000C -- escaped as \f + // carriage return U+000D -- escaped as \r + + static { + for (char c = 0; c < ESCAPE_CODES_COUNT; c++) { + + switch (c) { + case 0x08: + ESCAPE_CODES[c] = "\\b"; + break; + case 0x09: + ESCAPE_CODES[c] = "\\t"; + break; + case 0x0A: + ESCAPE_CODES[c] = "\\n"; + break; + case 0x0C: + ESCAPE_CODES[c] = "\\f"; + break; + case 0x0D: + ESCAPE_CODES[c] = "\\r"; + break; + default: + ESCAPE_CODES[c] = _computeEscapeCodeBelowASCII32(c); + } + } + } + + // this method should not be called by methods except the static initializer + private static String _computeEscapeCodeBelowASCII32(char c) { + if (c > 32) { + throw new IllegalArgumentException("input must be less than 32"); + } + + StringBuilder sb = new StringBuilder(6); + sb.append("\\u00"); + + int highPart = c >> 4; + sb.append(HEXADECIMALS_TABLE[highPart]); + + int lowPart = c & 0x0F; + sb.append(HEXADECIMALS_TABLE[lowPart]); + + return sb.toString(); + } + + // " quotation mark U+0022 -- escaped as \" + // \ reverse solidus U+005C -- escaped as \\ + // / solidus U+002F -- escaped as \/ + + static String getObligatoryEscapeCode(char c) { + if (c < 32) + return ESCAPE_CODES[c]; + if (c == 0x22) + return "\\\""; + if (c == 0x2F) + return "\\/"; + if (c == 0x5C) + return "\\\\"; + + return null; + } + + static public String jsonEscapeString(String input) { + int length = input.length(); + int lenthWithLeeway = (int) (length * 1.1); + + StringBuilder sb = new StringBuilder(lenthWithLeeway); + for (int i = 0; i < length; i++) { + final char c = input.charAt(i); + String escaped = getObligatoryEscapeCode(c); + if (escaped == null) + sb.append(c); + else { + sb.append(escaped); + } + } + + return sb.toString(); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/LayoutWrappingEncoder.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/LayoutWrappingEncoder.java index 45be7ef8c8..7a8e9ddf9e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/LayoutWrappingEncoder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/LayoutWrappingEncoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,10 +15,10 @@ import java.nio.charset.Charset; -import ch.qos.logback.core.Appender; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.Layout; import ch.qos.logback.core.OutputStreamAppender; +import ch.qos.logback.core.spi.ContextAware; public class LayoutWrappingEncoder extends EncoderBase { @@ -27,12 +27,12 @@ public class LayoutWrappingEncoder extends EncoderBase { /** * The charset to use when converting a String into bytes. *

- * By default this property has the value {@code null} which corresponds to - * the system's default charset. + * By default, this property has the value {@code null} which corresponds to the + * system's default charset. */ private Charset charset; - Appender parent; + ContextAware parent; Boolean immediateFlush = null; public Layout getLayout() { @@ -48,11 +48,11 @@ public Charset getCharset() { } /** - * Set the charset to use when converting the string returned by the layout - * into bytes. + * Set the charset to use when converting the string returned by the layout into + * bytes. *

- * By default this property has the value {@code null} which corresponds to - * the system's default charset. + * By default, this property has the value {@code null} which corresponds to the + * system's default charset. * * @param charset */ @@ -61,9 +61,10 @@ public void setCharset(Charset charset) { } /** - * Sets the immediateFlush option. The default value for immediateFlush is 'true'. If set to true, - * the doEncode() method will immediately flush the underlying OutputStream. Although immediate flushing - * is safer, it also significantly degrades logging throughput. + * Sets the immediateFlush option. The default value for immediateFlush is + * 'true'. If set to true, the doEncode() method will immediately flush the + * underlying OutputStream. Although immediate flushing is safer, it also + * significantly degrades logging throughput. * * @since 1.0.3 */ @@ -115,7 +116,7 @@ public byte[] encode(E event) { } public boolean isStarted() { - return false; + return started; } public void start() { @@ -148,7 +149,7 @@ private void appendIfNotNull(StringBuilder sb, String s) { * * @param parent */ - public void setParent(Appender parent) { + public void setParent(ContextAware parent) { this.parent = parent; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/encoder/NonClosableInputStream.java b/logback-core/src/main/java/ch/qos/logback/core/encoder/NonClosableInputStream.java index daa94e74c3..e38476a3eb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/encoder/NonClosableInputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/encoder/NonClosableInputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/filter/AbstractMatcherFilter.java b/logback-core/src/main/java/ch/qos/logback/core/filter/AbstractMatcherFilter.java index 70e12bb581..a4bd2da9e9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/filter/AbstractMatcherFilter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/filter/AbstractMatcherFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/filter/EvaluatorFilter.java b/logback-core/src/main/java/ch/qos/logback/core/filter/EvaluatorFilter.java index 23be430203..a3effdf6d8 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/filter/EvaluatorFilter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/filter/EvaluatorFilter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,16 +19,18 @@ /** * The value of the {@link #onMatch} and {@link #onMismatch} attributes is set - * to {@link FilterReply#NEUTRAL}, so that a badly configured evaluator filter does - * not disturb the functioning of the filter chain. + * to {@link FilterReply#NEUTRAL}, so that a badly configured evaluator filter + * does not disturb the functioning of the filter chain. * - *

It is expected that one of the two attributes will have its value changed - * to {@link FilterReply#ACCEPT} or {@link FilterReply#DENY}. That way, it is possible to - * decide if a given result must be returned after the evaluation either failed - * or succeeded. + *

+ * It is expected that one of the two attributes will have its value changed to + * {@link FilterReply#ACCEPT} or {@link FilterReply#DENY}. That way, it is + * possible to decide if a given result must be returned after the evaluation + * either failed or succeeded. * * - *

For more information about filters, please refer to the online manual at + *

+ * For more information about filters, please refer to the online manual at * http://logback.qos.ch/manual/filters.html * * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/filter/Filter.java b/logback-core/src/main/java/ch/qos/logback/core/filter/Filter.java index 0303e0d24f..856084c612 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/filter/Filter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/filter/Filter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,10 +20,12 @@ /** * Users should extend this class to implement customized event filtering. * - *

We suggest that you first try to use the built-in rules before rushing to + *

+ * We suggest that you first try to use the built-in rules before rushing to * write your own custom filters. * - *

For more information about filters, please refer to the online manual at + *

+ * For more information about filters, please refer to the online manual at * http://logback.qos.ch/manual/filters.html * * @author Ceki Gülcü @@ -47,14 +49,13 @@ public void stop() { } /** - * If the decision is {@link FilterReply#DENY}, then the event will be - * dropped. If the decision is {@link FilterReply#NEUTRAL}, then the next - * filter, if any, will be invoked. If the decision is + * If the decision is {@link FilterReply#DENY}, then the event will + * be dropped. If the decision is {@link FilterReply#NEUTRAL}, then + * the next filter, if any, will be invoked. If the decision is * {@link FilterReply#ACCEPT} then the event will be logged without * consulting with other filters in the chain. * - * @param event - * The event to decide upon. + * @param event The event to decide upon. */ public abstract FilterReply decide(E event); diff --git a/logback-core/src/main/java/ch/qos/logback/core/filter/package.html b/logback-core/src/main/java/ch/qos/logback/core/filter/package.html index 6d5ee0fee4..cc9a977f88 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/filter/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/filter/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java b/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java index 19c5e4cad8..cfd3759f60 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/helpers/CyclicBuffer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,9 @@ /** * CyclicBuffer holds values in a cyclic array. * - *

It allows read access to any element in the buffer not just the first or - * last element. + *

+ * It allows read access to any element in the buffer not just the first or last + * element. * * @author Ceki Gülcü */ @@ -37,8 +38,7 @@ public class CyclicBuffer { * * The maxSize argument must a positive integer. * - * @param maxSize - * The maximum number of elements in the buffer. + * @param maxSize The maximum number of elements in the buffer. */ public CyclicBuffer(int maxSize) throws IllegalArgumentException { if (maxSize < 1) { @@ -89,9 +89,9 @@ else if (++first == maxSize) } /** - * Get the ith oldest event currently in the buffer. If i - * is outside the range 0 to the number of elements currently in the buffer, - * then null is returned. + * Get the ith oldest event currently in the buffer. If i is + * outside the range 0 to the number of elements currently in the buffer, then + * null is returned. */ public E get(int i) { if (i < 0 || i >= numElems) @@ -129,8 +129,8 @@ public List asList() { } /** - * Get the number of elements in the buffer. This number is guaranteed to be - * in the range 0 to maxSize (inclusive). + * Get the number of elements in the buffer. This number is guaranteed to be in + * the range 0 to maxSize (inclusive). */ public int length() { return numElems; @@ -139,8 +139,7 @@ public int length() { /** * Resize the cyclic buffer to newSize. * - * @throws IllegalArgumentException - * if newSize is negative. + * @throws IllegalArgumentException if newSize is negative. */ @SuppressWarnings("unchecked") public void resize(int newSize) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/helpers/NOPAppender.java b/logback-core/src/main/java/ch/qos/logback/core/helpers/NOPAppender.java index 7b519ec089..49aba60a72 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/helpers/NOPAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/helpers/NOPAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java b/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java index a7735a7f3f..665f1632ec 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java +++ b/logback-core/src/main/java/ch/qos/logback/core/helpers/ThrowableToStringArray.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/helpers/Transform.java b/logback-core/src/main/java/ch/qos/logback/core/helpers/Transform.java index 33b3a47c7f..10faf09953 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/helpers/Transform.java +++ b/logback-core/src/main/java/ch/qos/logback/core/helpers/Transform.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -34,8 +34,7 @@ public class Transform { * <table>, etc) and replaces any '<','>' ... characters with * respective predefined entity references. * - * @param input - * The text to be converted. + * @param input The text to be converted. */ public static String escapeTags(final String input) { if (input == null || input.length() == 0 || !UNSAFE_XML_CHARS.matcher(input).find()) { @@ -49,6 +48,7 @@ public static String escapeTags(final String input) { * This method takes a StringBuilder which may contain HTML tags (ie, <b>, * <table>, etc) and replaces any '<' and '>' characters with * respective predefined entity references. + * * @param buf StringBuffer to transform * @return */ @@ -92,12 +92,11 @@ public static String escapeTags(final StringBuffer buf) { * Ensures that embedded CDEnd strings (]]>) are handled properly within * message, NDC and throwable tag text. * - * @param output - * Writer. The initial CDStart (<![CDATA[) and final CDEnd (]]>) of - * the CDATA section are the responsibility of the calling method. + * @param output Writer. The initial CDStart (<![CDATA[) and final CDEnd + * (]]>) of the CDATA section are the responsibility of the + * calling method. * - * @param str - * The String that is inserted into an existing CDATA Section. + * @param str The String that is inserted into an existing CDATA Section. */ public static void appendEscapingCDATA(StringBuilder output, String str) { if (str == null) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/helpers/package.html b/logback-core/src/main/java/ch/qos/logback/core/helpers/package.html index b2ecf85c55..64f204399a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/helpers/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/helpers/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/hook/DefaultShutdownHook.java b/logback-core/src/main/java/ch/qos/logback/core/hook/DefaultShutdownHook.java index 2f3ffbae5d..251204e08f 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/hook/DefaultShutdownHook.java +++ b/logback-core/src/main/java/ch/qos/logback/core/hook/DefaultShutdownHook.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,10 +16,11 @@ import ch.qos.logback.core.util.Duration; /** - * ShutdownHook implementation that stops the Logback context after a specified - * delay. The default delay is 0 ms (zero). + * {@link ShutdownHook} implementation that stops the Logback context + * after a specified delay. The default delay is 0 ms (zero). * *

Stopping the logback context + *

* * @author Mike Reinhold */ @@ -34,9 +35,14 @@ public class DefaultShutdownHook extends ShutdownHookBase { */ private Duration delay = DEFAULT_DELAY; + + /** + * Creates a DefaultShutdownHook using the default delay ({@link #DEFAULT_DELAY}). + */ public DefaultShutdownHook() { } + public Duration getDelay() { return delay; } @@ -60,4 +66,5 @@ public void run() { } super.stop(); } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHook.java b/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHook.java index 973e7d74d6..0fc7999989 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHook.java +++ b/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHook.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHookBase.java b/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHookBase.java index 2802cce816..7fd6eea230 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHookBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/hook/ShutdownHookBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.ContextAwareBase; /** @@ -39,4 +40,5 @@ protected void stop() { context.stop(); } } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/hook/package.html b/logback-core/src/main/java/ch/qos/logback/core/hook/package.html index 3382a3d11c..1d8ffa5a96 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/hook/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/hook/package.html @@ -1,3 +1,17 @@ + + @@ -10,7 +24,7 @@

Contains the shutdown hook functionality of logback, including the interface and base class for implementing hooks as well as some default implementations, such as - {@link ch.qos.logback.core.hook.DelayingShutdownHook DelayingShutdownHook} + {@link ch.qos.logback.core.hook.DefaultShutdownHook DefaultShutdownHook}

diff --git a/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java b/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java index c4fe36dc2c..95c4bd60fc 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/html/CssBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java b/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java index 573a1a59ad..20f24e9cb5 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/html/HTMLLayoutBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.Context; import ch.qos.logback.core.CoreConstants; @@ -22,13 +23,14 @@ import ch.qos.logback.core.LayoutBase; import ch.qos.logback.core.pattern.Converter; import ch.qos.logback.core.pattern.ConverterUtil; +import ch.qos.logback.core.pattern.DynamicConverter; import ch.qos.logback.core.pattern.parser.Node; import ch.qos.logback.core.pattern.parser.Parser; import ch.qos.logback.core.spi.ScanException; /** - * This class is a base class for HTMLLayout classes part of - * other logback modules such as logback-classic and logback-access. + * This class is a base class for HTMLLayout classes part of other logback + * modules such as logback-classic and logback-access. * * * @author Sébastien Pennec @@ -95,26 +97,27 @@ public void start() { } } - protected abstract Map getDefaultConverterMap(); + protected abstract Map> getDefaultConverterSupplierMap(); /** * Returns a map where the default converter map is merged with the map * contained in the context. */ - public Map getEffectiveConverterMap() { - Map effectiveMap = new HashMap(); + public Map> getEffectiveConverterMap() { + Map> effectiveMap = new HashMap<>(); // add the least specific map fist - Map defaultMap = getDefaultConverterMap(); - if (defaultMap != null) { - effectiveMap.putAll(defaultMap); + Map> defaultSupplierMap = getDefaultConverterSupplierMap(); + if (defaultSupplierMap != null) { + effectiveMap.putAll(defaultSupplierMap); } // contextMap is more specific than the default map Context context = getContext(); if (context != null) { @SuppressWarnings("unchecked") - Map contextMap = (Map) context.getObject(CoreConstants.PATTERN_RULE_REGISTRY); + Map> contextMap = (Map>) context + .getObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS); if (contextMap != null) { effectiveMap.putAll(contextMap); } @@ -123,8 +126,8 @@ public Map getEffectiveConverterMap() { } /** - * The Title option takes a String value. This option sets the - * document title of the generated HTML document. + * The Title option takes a String value. This option sets the document + * title of the generated HTML document. * *

* Defaults to 'Logback Log Messages'. diff --git a/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java b/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java index 84dea25550..e97fad2bd3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/html/IThrowableRenderer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java b/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java index c0b6cdaf88..6f1de7386e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/html/NOPThrowableRenderer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,6 @@ */ package ch.qos.logback.core.html; -import ch.qos.logback.core.html.IThrowableRenderer; - public class NOPThrowableRenderer implements IThrowableRenderer { public void render(StringBuilder sbuf, Object event) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/html/package.html b/logback-core/src/main/java/ch/qos/logback/core/html/package.html index 01a5548e9e..2069e39096 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/html/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/html/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java b/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java deleted file mode 100755 index 7535feba7c..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java +++ /dev/null @@ -1,186 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.event.SaxEventRecorder; -import ch.qos.logback.core.joran.spi.*; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.status.StatusUtil; - -import org.xml.sax.InputSource; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.net.URLConnection; -import java.util.List; - -import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION; - -public abstract class GenericConfigurator extends ContextAwareBase { - - private BeanDescriptionCache beanDescriptionCache; - - protected Interpreter interpreter; - - public final void doConfigure(URL url) throws JoranException { - InputStream in = null; - try { - informContextOfURLUsedForConfiguration(getContext(), url); - URLConnection urlConnection = url.openConnection(); - // per http://jira.qos.ch/browse/LBCORE-105 - // per http://jira.qos.ch/browse/LBCORE-127 - urlConnection.setUseCaches(false); - - in = urlConnection.getInputStream(); - doConfigure(in, url.toExternalForm()); - } catch (IOException ioe) { - String errMsg = "Could not open URL [" + url + "]."; - addError(errMsg, ioe); - throw new JoranException(errMsg, ioe); - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException ioe) { - String errMsg = "Could not close input stream"; - addError(errMsg, ioe); - throw new JoranException(errMsg, ioe); - } - } - } - } - - public final void doConfigure(String filename) throws JoranException { - doConfigure(new File(filename)); - } - - public final void doConfigure(File file) throws JoranException { - FileInputStream fis = null; - try { - URL url = file.toURI().toURL(); - informContextOfURLUsedForConfiguration(getContext(), url); - fis = new FileInputStream(file); - doConfigure(fis, url.toExternalForm()); - } catch (IOException ioe) { - String errMsg = "Could not open [" + file.getPath() + "]."; - addError(errMsg, ioe); - throw new JoranException(errMsg, ioe); - } finally { - if (fis != null) { - try { - fis.close(); - } catch (java.io.IOException ioe) { - String errMsg = "Could not close [" + file.getName() + "]."; - addError(errMsg, ioe); - throw new JoranException(errMsg, ioe); - } - } - } - } - - public static void informContextOfURLUsedForConfiguration(Context context, URL url) { - ConfigurationWatchListUtil.setMainWatchURL(context, url); - } - - public final void doConfigure(InputStream inputStream) throws JoranException { - doConfigure(new InputSource(inputStream)); - } - - public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { - InputSource inputSource = new InputSource(inputStream); - inputSource.setSystemId(systemId); - doConfigure(inputSource); - } - - protected BeanDescriptionCache getBeanDescriptionCache() { - if (beanDescriptionCache == null) { - beanDescriptionCache = new BeanDescriptionCache(getContext()); - } - return beanDescriptionCache; - } - - protected abstract void addInstanceRules(RuleStore rs); - - protected abstract void addImplicitRules(Interpreter interpreter); - - protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { - - } - - protected ElementPath initialElementPath() { - return new ElementPath(); - } - - protected void buildInterpreter() { - RuleStore rs = new SimpleRuleStore(context); - addInstanceRules(rs); - this.interpreter = new Interpreter(context, rs, initialElementPath()); - InterpretationContext interpretationContext = interpreter.getInterpretationContext(); - interpretationContext.setContext(context); - addImplicitRules(interpreter); - addDefaultNestedComponentRegistryRules(interpretationContext.getDefaultNestedComponentRegistry()); - } - - // this is the most inner form of doConfigure whereto other doConfigure - // methods ultimately delegate - public final void doConfigure(final InputSource inputSource) throws JoranException { - - long threshold = System.currentTimeMillis(); - // if (!ConfigurationWatchListUtil.wasConfigurationWatchListReset(context)) { - // informContextOfURLUsedForConfiguration(getContext(), null); - // } - SaxEventRecorder recorder = new SaxEventRecorder(context); - recorder.recordEvents(inputSource); - doConfigure(recorder.saxEventList); - // no exceptions a this level - StatusUtil statusUtil = new StatusUtil(context); - if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { - addInfo("Registering current configuration as safe fallback point"); - registerSafeConfiguration(recorder.saxEventList); - } - } - - public void doConfigure(final List eventList) throws JoranException { - buildInterpreter(); - // disallow simultaneous configurations of the same context - synchronized (context.getConfigurationLock()) { - interpreter.getEventPlayer().play(eventList); - } - } - - /** - * Register the current event list in currently in the interpreter as a safe - * configuration point. - * - * @since 0.9.30 - */ - public void registerSafeConfiguration(List eventList) { - context.putObject(SAFE_JORAN_CONFIGURATION, eventList); - } - - /** - * Recall the event list previously registered as a safe point. - */ - @SuppressWarnings("unchecked") - public List recallSafeConfiguration() { - return (List) context.getObject(SAFE_JORAN_CONFIGURATION); - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java b/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java new file mode 100755 index 0000000000..cd4fac1741 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/GenericXMLConfigurator.java @@ -0,0 +1,277 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. + * + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran; + +import static ch.qos.logback.core.CoreConstants.SAFE_JORAN_CONFIGURATION; +import static ch.qos.logback.core.spi.ConfigurationEvent.*; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.net.URLConnection; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +import org.xml.sax.InputSource; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.event.SaxEventRecorder; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.spi.ElementPath; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; +import ch.qos.logback.core.joran.spi.SimpleRuleStore; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ErrorCodes; +import ch.qos.logback.core.status.StatusUtil; + +public abstract class GenericXMLConfigurator extends ContextAwareBase { + + protected SaxEventInterpreter saxEventInterpreter; + protected ModelInterpretationContext modelInterpretationContext; + + public ModelInterpretationContext getModelInterpretationContext() { + return this.modelInterpretationContext; + } + private RuleStore ruleStore; + + public URL getTopURL() { + return topURL; + } + + public void setTopURL(URL topURL) { + this.topURL = topURL; + } + + URL topURL; + + public final void doConfigure(URL url) throws JoranException { + InputStream in = null; + try { + topURL = url; + URLConnection urlConnection = url.openConnection(); + // per http://jira.qos.ch/browse/LOGBACK-117 LBCORE-105 + // per http://jira.qos.ch/browse/LOGBACK-163 LBCORE-127 + urlConnection.setUseCaches(false); + + in = urlConnection.getInputStream(); + doConfigure(in, url.toExternalForm()); + } catch (IOException ioe) { + String errMsg = "Could not open URL [" + url + "]."; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException ioe) { + String errMsg = "Could not close input stream"; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } + } + } + } + + public final void doConfigure(String filename) throws JoranException { + doConfigure(new File(filename)); + } + + public final void doConfigure(File file) throws JoranException { + FileInputStream fis = null; + try { + URL url = file.toURI().toURL(); + topURL = url; + fis = new FileInputStream(file); + doConfigure(fis, url.toExternalForm()); + } catch (IOException ioe) { + String errMsg = "Could not open [" + file.getPath() + "]."; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } finally { + if (fis != null) { + try { + fis.close(); + } catch (java.io.IOException ioe) { + String errMsg = "Could not close [" + file.getName() + "]."; + addError(errMsg, ioe); + throw new JoranException(errMsg, ioe); + } + } + } + } + + /** + * Removed in 1.5.27 with no replacement. + * + * @deprecated + */ + @Deprecated + public static void informContextOfURLUsedForConfiguration(Context context, URL url) { + // + } + + public final void doConfigure(InputStream inputStream) throws JoranException { + doConfigure(new InputSource(inputStream)); + } + + public final void doConfigure(InputStream inputStream, String systemId) throws JoranException { + InputSource inputSource = new InputSource(inputStream); + inputSource.setSystemId(systemId); + doConfigure(inputSource); + } + + protected abstract void addElementSelectorAndActionAssociations(RuleStore rs); + + protected abstract void setImplicitRuleSupplier(SaxEventInterpreter interpreter); + + protected void addDefaultNestedComponentRegistryRules(DefaultNestedComponentRegistry registry) { + // nothing by default + } + + protected ElementPath initialElementPath() { + return new ElementPath(); + } + + protected void buildSaxEventInterpreter(List saxEvents) { + RuleStore rs = getRuleStore(); + addElementSelectorAndActionAssociations(rs); + this.saxEventInterpreter = new SaxEventInterpreter(context, rs, initialElementPath(), saxEvents); + SaxEventInterpretationContext interpretationContext = saxEventInterpreter.getSaxEventInterpretationContext(); + interpretationContext.setContext(context); + setImplicitRuleSupplier(saxEventInterpreter); + } + + public RuleStore getRuleStore() { + if(this.ruleStore == null) { + this.ruleStore = new SimpleRuleStore(context); + } + return this.ruleStore; + } + + protected void buildModelInterpretationContext() { + this.modelInterpretationContext = new ModelInterpretationContext(context); + this.modelInterpretationContext.setTopURL(topURL); + addDefaultNestedComponentRegistryRules(modelInterpretationContext.getDefaultNestedComponentRegistry()); + } + + // this is the most inner form of doConfigure whereto other doConfigure + // methods ultimately delegate + public final void doConfigure(final InputSource inputSource) throws JoranException { + + context.fireConfigurationEvent(newConfigurationStartedEvent(this)); + long threshold = System.currentTimeMillis(); + + SaxEventRecorder recorder = populateSaxEventRecorder(inputSource); + List saxEvents = recorder.getSaxEventList(); + if (saxEvents.isEmpty()) { + addWarn("Empty sax event list"); + return; + } + Model top = buildModelFromSaxEventList(recorder.getSaxEventList()); + if (top == null) { + addError(ErrorCodes.EMPTY_MODEL_STACK); + return; + } + sanityCheck(top); + processModel(top); + + // no exceptions at this level + StatusUtil statusUtil = new StatusUtil(context); + if (statusUtil.noXMLParsingErrorsOccurred(threshold)) { + addInfo("Registering current configuration as safe fallback point"); + registerSafeConfiguration(top); + context.fireConfigurationEvent(newConfigurationEndedSuccessfullyEvent(this)); + } else { + context.fireConfigurationEvent(newConfigurationEndedWithXMLParsingErrorsEvent(this)); + } + + + } + + public SaxEventRecorder populateSaxEventRecorder(final InputSource inputSource) throws JoranException { + SaxEventRecorder recorder = new SaxEventRecorder(context); + recorder.recordEvents(inputSource); + return recorder; + } + + public Model buildModelFromSaxEventList(List saxEvents) throws JoranException { + buildSaxEventInterpreter(saxEvents); + playSaxEvents(); + Model top = saxEventInterpreter.getSaxEventInterpretationContext().peekModel(); + return top; + } + + private void playSaxEvents() throws JoranException { + saxEventInterpreter.getEventPlayer().play(); + } + + public void processModel(Model model) { + buildModelInterpretationContext(); + this.modelInterpretationContext.setTopModel(model); + modelInterpretationContext.setConfiguratorHint(this); + DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); + addModelHandlerAssociations(defaultProcessor); + + // disallow simultaneous configurations of the same context + ReentrantLock configurationLock = context.getConfigurationLock(); + + try { + configurationLock.lock(); + defaultProcessor.process(model); + } finally { + configurationLock.unlock(); + } + } + + /** + * Perform sanity check and issue warning if necessary. + * + * Default implementation does nothing. + * + * @param topModel + * @since 1.3.2 and 1.4.2 + */ + protected void sanityCheck(Model topModel) { + + } + + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + } + + /** + * Register the current event list in currently in the interpreter as a safe + * configuration point. + * + * @since 0.9.30 + */ + public void registerSafeConfiguration(Model top) { + context.putObject(SAFE_JORAN_CONFIGURATION, top); + } + + /** + * Recall the event list previously registered as a safe point. + */ + public Model recallSafeConfiguration() { + return (Model) context.getObject(SAFE_JORAN_CONFIGURATION); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java b/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java index 327940090d..bcac0d2af8 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConfiguratorBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,28 +13,17 @@ */ package ch.qos.logback.core.joran; -import java.util.HashMap; -import java.util.Map; - -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.joran.action.ActionConst; -import ch.qos.logback.core.joran.action.AppenderAction; -import ch.qos.logback.core.joran.action.AppenderRefAction; -import ch.qos.logback.core.joran.action.ContextPropertyAction; -import ch.qos.logback.core.joran.action.ConversionRuleAction; -import ch.qos.logback.core.joran.action.DefinePropertyAction; -import ch.qos.logback.core.joran.action.NestedBasicPropertyIA; -import ch.qos.logback.core.joran.action.NestedComplexPropertyIA; -import ch.qos.logback.core.joran.action.NewRuleAction; -import ch.qos.logback.core.joran.action.ParamAction; -import ch.qos.logback.core.joran.action.PropertyAction; -import ch.qos.logback.core.joran.action.ShutdownHookAction; -import ch.qos.logback.core.joran.action.StatusListenerAction; -import ch.qos.logback.core.joran.action.TimestampAction; +import ch.qos.logback.core.joran.action.*; +import ch.qos.logback.core.joran.conditional.*; +import ch.qos.logback.core.joran.sanity.AppenderWithinAppenderSanityChecker; +import ch.qos.logback.core.joran.sanity.SanityChecker; import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.Interpreter; import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; +import ch.qos.logback.core.model.*; +import ch.qos.logback.core.model.processor.*; +import ch.qos.logback.core.spi.ContextAware; // Based on 310985 revision 310985 as attested by http://tinyurl.com/8njps // see also http://tinyurl.com/c2rp5 @@ -42,63 +31,97 @@ /** * A JoranConfiguratorBase lays most of the groundwork for concrete * configurators derived from it. Concrete configurators only need to implement - * the {@link #addInstanceRules} method. + * the {@link #addElementSelectorAndActionAssociations} method. *

* A JoranConfiguratorBase instance should not be used more than once to * configure a Context. * * @author Ceki Gülcü */ -abstract public class JoranConfiguratorBase extends GenericConfigurator { +abstract public class JoranConfiguratorBase extends GenericXMLConfigurator { @Override - protected void addInstanceRules(RuleStore rs) { + protected void addElementSelectorAndActionAssociations(RuleStore rs) { + + // is "*/variable" referenced in the docs? + rs.addRule(new ElementSelector("*/variable"), PropertyAction::new); + rs.addRule(new ElementSelector("*/property"), PropertyAction::new); + // substitutionProperty is deprecated + rs.addRule(new ElementSelector("*/substitutionProperty"), PropertyAction::new); - // is "configuration/variable" referenced in the docs? - rs.addRule(new ElementSelector("configuration/variable"), new PropertyAction()); - rs.addRule(new ElementSelector("configuration/property"), new PropertyAction()); + rs.addRule(new ElementSelector("configuration/import"), ImportAction::new); + - rs.addRule(new ElementSelector("configuration/substitutionProperty"), new PropertyAction()); + rs.addRule(new ElementSelector("configuration/timestamp"), TimestampAction::new); + rs.addRule(new ElementSelector("configuration/shutdownHook"), ShutdownHookAction::new); + rs.addRule(new ElementSelector("configuration/sequenceNumberGenerator"), SequenceNumberGeneratorAction::new); + rs.addRule(new ElementSelector("configuration/serializeModel"), SerializeModelAction::new); - rs.addRule(new ElementSelector("configuration/timestamp"), new TimestampAction()); - rs.addRule(new ElementSelector("configuration/shutdownHook"), new ShutdownHookAction()); - rs.addRule(new ElementSelector("configuration/define"), new DefinePropertyAction()); + rs.addRule(new ElementSelector("configuration/define"), DefinePropertyAction::new); + rs.addRule(new ElementSelector("configuration/evaluator"), EventEvaluatorAction::new); // the contextProperty pattern is deprecated. It is undocumented // and will be dropped in future versions of logback - rs.addRule(new ElementSelector("configuration/contextProperty"), new ContextPropertyAction()); - - rs.addRule(new ElementSelector("configuration/conversionRule"), new ConversionRuleAction()); + rs.addRule(new ElementSelector("configuration/contextProperty"), ContextPropertyAction::new); + + rs.addRule(new ElementSelector("configuration/conversionRule"), ConversionRuleAction::new); + + rs.addRule(new ElementSelector("configuration/statusListener"), StatusListenerAction::new); + + rs.addRule(new ElementSelector("*/appender"), AppenderAction::new); + rs.addRule(new ElementSelector("configuration/appender/appender-ref"), AppenderRefAction::new); + rs.addRule(new ElementSelector("configuration/newRule"), NewRuleAction::new); + + rs.addRule(new ElementSelector("*/param"), ParamAction::new); + + // add if-then-else support + rs.addRule(new ElementSelector("*/condition"), ByPropertiesConditionAction::new); + rs.addRule(new ElementSelector("*/if"), IfAction::new); + rs.addTransparentPathPart("if"); + rs.addRule(new ElementSelector("*/if/then"), ThenAction::new); + rs.addTransparentPathPart("then"); + rs.addRule(new ElementSelector("*/if/else"), ElseAction::new); + rs.addTransparentPathPart("else"); + + rs.addRule(new ElementSelector("*/appender/sift"), SiftAction::new); + rs.addTransparentPathPart("sift"); + + + } - rs.addRule(new ElementSelector("configuration/statusListener"), new StatusListenerAction()); + /** + * Perform sanity check and issue warning if necessary. + * + * @param topModel + */ + protected void sanityCheck(Model topModel) { + performCheck(new AppenderWithinAppenderSanityChecker(), topModel); + } - rs.addRule(new ElementSelector("configuration/appender"), new AppenderAction()); - rs.addRule(new ElementSelector("configuration/appender/appender-ref"), new AppenderRefAction()); - rs.addRule(new ElementSelector("configuration/newRule"), new NewRuleAction()); - rs.addRule(new ElementSelector("*/param"), new ParamAction(getBeanDescriptionCache())); + protected void performCheck(SanityChecker sc, Model model) { + if(sc instanceof ContextAware) + ((ContextAware) sc).setContext(context); + sc.check(model); } @Override - protected void addImplicitRules(Interpreter interpreter) { - // The following line adds the capability to parse nested components - NestedComplexPropertyIA nestedComplexPropertyIA = new NestedComplexPropertyIA(getBeanDescriptionCache()); - nestedComplexPropertyIA.setContext(context); - interpreter.addImplicitAction(nestedComplexPropertyIA); - - NestedBasicPropertyIA nestedBasicIA = new NestedBasicPropertyIA(getBeanDescriptionCache()); - nestedBasicIA.setContext(context); - interpreter.addImplicitAction(nestedBasicIA); + protected void setImplicitRuleSupplier(SaxEventInterpreter interpreter) { + interpreter.setImplicitActionSupplier( ImplicitModelAction::new ); } @Override - protected void buildInterpreter() { - super.buildInterpreter(); - Map omap = interpreter.getInterpretationContext().getObjectMap(); - omap.put(ActionConst.APPENDER_BAG, new HashMap>()); - //omap.put(ActionConst.FILTER_CHAIN_BAG, new HashMap()); + public void buildModelInterpretationContext() { + super.buildModelInterpretationContext(); + modelInterpretationContext.createAppenderBags(); } - public InterpretationContext getInterpretationContext() { - return interpreter.getInterpretationContext(); + public SaxEventInterpretationContext getInterpretationContext() { + return saxEventInterpreter.getSaxEventInterpretationContext(); } + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + // Please note that code previously here moved to ModelClassToModelHandlerLinkerBase + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConstants.java b/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConstants.java new file mode 100644 index 0000000000..3b4ba19fff --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/JoranConstants.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran; + +import ch.qos.logback.core.CoreConstants; + +/** + * + * This class contains constants used by Joran components. + * + * @author Ceki Gülcü + * + */ +public abstract class JoranConstants { + public static final String INCLUDED_TAG = "included"; + public static final String CONFIGURATION_TAG = "configuration"; + + public static final String INCLUDE_TAG = "include"; + + public static final String APPENDER_TAG = "appender"; + public static final String REF_ATTRIBUTE = "ref"; + public static final String ADDITIVITY_ATTRIBUTE = "additivity"; + public static final String LEVEL_ATTRIBUTE = "level"; + public static final String CONVERTER_CLASS_ATTRIBUTE = "converterClass"; + public static final String CONVERSION_WORD_ATTRIBUTE = "conversionWord"; + public static final String PATTERN_ATTRIBUTE = "pattern"; + public static final String VALUE_ATTR = "value"; + public static final String ACTION_CLASS_ATTRIBUTE = "actionClass"; + + public static final String INHERITED = "INHERITED"; + // all usages in the project are case-insensitive. Elsewhere this might not be the case hence the toUpperCase call + public static final String NULL = CoreConstants.NULL_STR.toUpperCase(); + static final Class[] ONE_STRING_PARAM = new Class[] { String.class }; + + public static final String APPENDER_BAG = "APPENDER_BAG"; + public static final String APPENDER_REF_BAG = "APPENDER_REF_BAG"; + // public static final String FILTER_CHAIN_BAG = "FILTER_CHAIN_BAG"; +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/ModelClassToModelHandlerLinkerBase.java b/logback-core/src/main/java/ch/qos/logback/core/joran/ModelClassToModelHandlerLinkerBase.java new file mode 100644 index 0000000000..900cc822ee --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/ModelClassToModelHandlerLinkerBase.java @@ -0,0 +1,84 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.*; +import ch.qos.logback.core.model.conditional.ByPropertiesConditionModel; +import ch.qos.logback.core.model.conditional.ElseModel; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.ThenModel; +import ch.qos.logback.core.model.processor.*; +import ch.qos.logback.core.model.processor.conditional.ByPropertiesConditionModelHandler; +import ch.qos.logback.core.model.processor.conditional.ElseModelHandler; +import ch.qos.logback.core.model.processor.conditional.IfModelHandler; +import ch.qos.logback.core.model.processor.conditional.ThenModelHandler; +import ch.qos.logback.core.sift.SiftModelHandler; + +/** + * For a given DefaultProcessor instance link a {@link ch.qos.logback.core.model.Model Model} class to + * a {@link ch.qos.logback.core.model.processor.ModelHandlerBase ModelHandler} instance in logback-core. + * + *

Derived classes are likely to add further links.

+ * + * @since 1.3.9/1.4.9 + */ +public class ModelClassToModelHandlerLinkerBase { + + + protected Context context; + + public ModelClassToModelHandlerLinkerBase(Context context) { + this.context = context; + } + + public void link(DefaultProcessor defaultProcessor) { + defaultProcessor.addHandler(ImportModel.class, ImportModelHandler::makeInstance); + + defaultProcessor.addHandler(ShutdownHookModel.class, ShutdownHookModelHandler::makeInstance); + defaultProcessor.addHandler(SequenceNumberGeneratorModel.class, SequenceNumberGeneratorModelHandler::makeInstance); + defaultProcessor.addHandler(SerializeModelModel.class, SerializeModelModelHandler::makeInstance); + + defaultProcessor.addHandler(EventEvaluatorModel.class, EventEvaluatorModelHandler::makeInstance); + defaultProcessor.addHandler(ConversionRuleModel.class, ConversionRuleModelHandler::makeInstance); + + defaultProcessor.addHandler(DefineModel.class, DefineModelHandler::makeInstance); + defaultProcessor.addHandler(IncludeModel.class, IncludeModelHandler::makeInstance); + + + defaultProcessor.addHandler(ParamModel.class, ParamModelHandler::makeInstance); + defaultProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance); + defaultProcessor.addHandler(TimestampModel.class, TimestampModelHandler::makeInstance); + defaultProcessor.addHandler(StatusListenerModel.class, StatusListenerModelHandler::makeInstance); + defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); + + + defaultProcessor.addHandler(ByPropertiesConditionModel.class, ByPropertiesConditionModelHandler::makeInstance); + defaultProcessor.addHandler(IfModel.class, IfModelHandler::makeInstance); + defaultProcessor.addHandler(ThenModel.class, ThenModelHandler::makeInstance); + defaultProcessor.addHandler(ElseModel.class, ElseModelHandler::makeInstance); + + defaultProcessor.addHandler(SiftModel.class, SiftModelHandler::makeInstance); + } + + // The final filters in the two filter chain are rather crucial. + // They ensure that only Models attached to the firstPhaseFilter will + // be handled in the first phase and all models not previously handled + // in the second phase will be handled in a catch-all fallback case. + protected void sealModelFilters(DefaultProcessor defaultProcessor) { + defaultProcessor.getPhaseOneFilter().denyAll(); + defaultProcessor.getPhaseTwoFilter().allowAll(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/ParamModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/joran/ParamModelHandler.java new file mode 100755 index 0000000000..3e5ba07bcb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/ParamModelHandler.java @@ -0,0 +1,61 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.util.PropertySetter; +import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ParamModel; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class ParamModelHandler extends ModelHandlerBase { + + private final BeanDescriptionCache beanDescriptionCache; + + public ParamModelHandler(Context context, BeanDescriptionCache beanDescriptionCache) { + super(context); + this.beanDescriptionCache = beanDescriptionCache; + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new ParamModelHandler(context, ic.getBeanDescriptionCache()); + } + + @Override + protected Class getSupportedModelClass() { + return ParamModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ParamModel paramModel = (ParamModel) model; + + String valueStr = mic.subst(paramModel.getValue()); + + Object o = mic.peekObject(); + + PropertySetter propSetter = new PropertySetter(beanDescriptionCache, o); + propSetter.setContext(context); + + // allow for variable substitution for name as well + String finalName = mic.subst(paramModel.getName()); + propSetter.setProperty(finalName, valueStr); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AbstractEventEvaluatorAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/AbstractEventEvaluatorAction.java deleted file mode 100644 index c4acd006f7..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AbstractEventEvaluatorAction.java +++ /dev/null @@ -1,117 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import java.util.Map; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.boolex.EventEvaluator; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.util.OptionHelper; - -abstract public class AbstractEventEvaluatorAction extends Action { - - EventEvaluator evaluator; - boolean inError = false; - - /** - * Instantiates an evaluator of the given class and sets its name. - */ - public void begin(InterpretationContext ec, String name, Attributes attributes) { - // Let us forget about previous errors (in this instance) - inError = false; - evaluator = null; - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - className = defaultClassName(); - addInfo("Assuming default evaluator class [" + className + "]"); - } - - if (OptionHelper.isEmpty(className)) { - className = defaultClassName(); - inError = true; - addError("Mandatory \"" + CLASS_ATTRIBUTE + "\" attribute not set for "); - return; - } - - String evaluatorName = attributes.getValue(Action.NAME_ATTRIBUTE); - if (OptionHelper.isEmpty(evaluatorName)) { - inError = true; - addError("Mandatory \"" + NAME_ATTRIBUTE + "\" attribute not set for "); - return; - } - try { - evaluator = (EventEvaluator) OptionHelper.instantiateByClassName(className, ch.qos.logback.core.boolex.EventEvaluator.class, context); - - evaluator.setContext(this.context); - evaluator.setName(evaluatorName); - - ec.pushObject(evaluator); - addInfo("Adding evaluator named [" + evaluatorName + "] to the object stack"); - - } catch (Exception oops) { - inError = true; - addError("Could not create evaluator of type " + className + "].", oops); - } - } - - /** - * Returns a default class name in case the class attribute is not specified - * - * @return - */ - abstract protected String defaultClassName(); - - /** - * Once the children elements are also parsed, now is the time to activate the - * evaluator options. - */ - @SuppressWarnings("unchecked") - public void end(InterpretationContext ec, String e) { - if (inError) { - return; - } - - if (evaluator instanceof LifeCycle) { - ((LifeCycle) evaluator).start(); - addInfo("Starting evaluator named [" + evaluator.getName() + "]"); - } - - Object o = ec.peekObject(); - - if (o != evaluator) { - addWarn("The object on the top the of the stack is not the evaluator pushed earlier."); - } else { - ec.popObject(); - - try { - Map> evaluatorMap = (Map>) context.getObject(CoreConstants.EVALUATOR_MAP); - if (evaluatorMap == null) { - addError("Could not find EvaluatorMap"); - } else { - evaluatorMap.put(evaluator.getName(), evaluator); - } - } catch (Exception ex) { - addError("Could not set evaluator named [" + evaluator + "].", ex); - } - } - } - - public void finish(InterpretationContext ec) { - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/Action.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/Action.java index a05db9282b..2075d9e4cc 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/Action.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/Action.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,17 +17,19 @@ import org.xml.sax.Locator; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.Interpreter; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; import ch.qos.logback.core.spi.ContextAwareBase; /** * * Most of the work for configuring logback is done by Actions. * - *

Action methods are invoked as the XML file is parsed. + *

+ * Action methods are invoked as the XML file is parsed. * - *

This class is largely inspired from the relevant class in the + *

+ * This class is largely inspired from the relevant class in the * commons-digester project of the Apache Software Foundation. * * @author Craig McClanahan @@ -51,47 +53,63 @@ public abstract class Action extends ContextAwareBase { * Called when the parser encounters an element matching a * {@link ch.qos.logback.core.joran.spi.ElementSelector Pattern}. */ - public abstract void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException; + public abstract void begin(SaxEventInterpretationContext intercon, String name, Attributes attributes) + throws ActionException; /** * Called to pass the body (as text) contained within an element. - * @param ic + * + * @param intercon * @param body * @throws ActionException */ - public void body(InterpretationContext ic, String body) throws ActionException { + public void body(SaxEventInterpretationContext intercon, String body) throws ActionException { // NOP } /* - * Called when the parser encounters an endElement event matching a {@link ch.qos.logback.core.joran.spi.Pattern - * Pattern}. + * Called when the parser encounters an endElement event matching a {@link + * ch.qos.logback.core.joran.spi.Pattern Pattern}. */ - public abstract void end(InterpretationContext ic, String name) throws ActionException; + public abstract void end(SaxEventInterpretationContext intercon, String name) throws ActionException; public String toString() { return this.getClass().getName(); } - protected int getColumnNumber(InterpretationContext ic) { - Interpreter ji = ic.getJoranInterpreter(); - Locator locator = ji.getLocator(); + protected int getColumnNumber(SaxEventInterpretationContext intercon) { + SaxEventInterpreter interpreter = intercon.getSaxEventInterpreter(); + if (interpreter == null) + return -1; + + Locator locator = interpreter.getLocator(); if (locator != null) { return locator.getColumnNumber(); } return -1; } - protected int getLineNumber(InterpretationContext ic) { - Interpreter ji = ic.getJoranInterpreter(); - Locator locator = ji.getLocator(); + // move to InterpretationContext + static public int getLineNumber(SaxEventInterpretationContext intercon) { + SaxEventInterpreter interpreter = intercon.getSaxEventInterpreter(); + if (interpreter == null) + return -1; + Locator locator = interpreter.getLocator(); if (locator != null) { return locator.getLineNumber(); } return -1; } - protected String getLineColStr(InterpretationContext ic) { - return "line: " + getLineNumber(ic) + ", column: " + getColumnNumber(ic); + protected String getLineColStr(SaxEventInterpretationContext intercon) { + return "line: " + getLineNumber(intercon) + ", column: " + getColumnNumber(intercon); + } + + protected String atLine(SaxEventInterpretationContext intercon) { + return "At line " + getLineNumber(intercon); + } + + protected String nearLine(SaxEventInterpretationContext intercon) { + return "Near line " + getLineNumber(intercon); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionConst.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionConst.java deleted file mode 100644 index e8739eee63..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionConst.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -/** - * - * This class contains costants used by other Actions. - * - * @author Ceki Gülcü - * - */ -public abstract class ActionConst { - - public static final String APPENDER_TAG = "appender"; - public static final String REF_ATTRIBUTE = "ref"; - public static final String ADDITIVITY_ATTRIBUTE = "additivity"; - public static final String LEVEL_ATTRIBUTE = "level"; - public static final String CONVERTER_CLASS_ATTRIBUTE = "converterClass"; - public static final String CONVERSION_WORD_ATTRIBUTE = "conversionWord"; - public static final String PATTERN_ATTRIBUTE = "pattern"; - public static final String VALUE_ATTR = "value"; - public static final String ACTION_CLASS_ATTRIBUTE = "actionClass"; - - public static final String INHERITED = "INHERITED"; - public static final String NULL = "NULL"; - static final Class[] ONE_STRING_PARAM = new Class[] { String.class }; - - public static final String APPENDER_BAG = "APPENDER_BAG"; - //public static final String FILTER_CHAIN_BAG = "FILTER_CHAIN_BAG"; -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionUtil.java index 0616002b0b..cf16af555d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ActionUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,8 @@ */ package ch.qos.logback.core.joran.action; -import java.util.Properties; - -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.ContextUtil; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; import ch.qos.logback.core.util.OptionHelper; public class ActionUtil { @@ -26,9 +24,10 @@ public enum Scope { }; /** - * Convert a string into a scope. Scole.LOCAL is returned by default. + * Convert a string into a scope. Scope.LOCAL is returned by default. + * * @param scopeStr - * @return a scope corresponding to the input string; Scope.LOCAL by default. + * @return a scope corresponding to the input string; Scope.LOCAL by default. */ static public Scope stringToScope(String scopeStr) { if (Scope.SYSTEM.toString().equalsIgnoreCase(scopeStr)) @@ -39,7 +38,8 @@ static public Scope stringToScope(String scopeStr) { return Scope.LOCAL; } - static public void setProperty(InterpretationContext ic, String key, String value, Scope scope) { + + static public void setProperty(ContextAwarePropertyContainer ic, String key, String value, Scope scope) { switch (scope) { case LOCAL: ic.addSubstitutionProperty(key, value); @@ -51,23 +51,4 @@ static public void setProperty(InterpretationContext ic, String key, String valu OptionHelper.setSystemProperty(ic, key, value); } } - - /** - * Add all the properties found in the argument named 'props' to an - * InterpretationContext. - */ - static public void setProperties(InterpretationContext ic, Properties props, Scope scope) { - switch (scope) { - case LOCAL: - ic.addSubstitutionProperties(props); - break; - case CONTEXT: - ContextUtil cu = new ContextUtil(ic.getContext()); - cu.addProperties(props); - break; - case SYSTEM: - OptionHelper.setSystemProperties(ic, props); - } - } - } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java old mode 100644 new mode 100755 index df7524943a..eb5042901e --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,91 +11,32 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.joran.action; -import java.util.HashMap; +package ch.qos.logback.core.joran.action; import org.xml.sax.Attributes; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.util.OptionHelper; - -public class AppenderAction extends Action { - Appender appender; - private boolean inError = false; - - /** - * Instantiates an appender of the given class and sets its name. - * - * The appender thus generated is placed in the {@link InterpretationContext}'s - * appender bag. - */ - @SuppressWarnings("unchecked") - public void begin(InterpretationContext ec, String localName, Attributes attributes) throws ActionException { - // We are just beginning, reset variables - appender = null; - inError = false; - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - addError("Missing class name for appender. Near [" + localName + "] line " + getLineNumber(ec)); - inError = true; - return; - } - - try { - addInfo("About to instantiate appender of type [" + className + "]"); - - appender = (Appender) OptionHelper.instantiateByClassName(className, ch.qos.logback.core.Appender.class, context); - - appender.setContext(context); +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; - String appenderName = ec.subst(attributes.getValue(NAME_ATTRIBUTE)); +public class AppenderAction extends BaseModelAction { - if (OptionHelper.isEmpty(appenderName)) { - addWarn("No appender name given for appender of type " + className + "]."); - } else { - appender.setName(appenderName); - addInfo("Naming appender as [" + appenderName + "]"); - } - - // The execution context contains a bag which contains the appenders - // created thus far. - HashMap> appenderBag = (HashMap>) ec.getObjectMap().get(ActionConst.APPENDER_BAG); - - // add the appender just created to the appender bag. - appenderBag.put(appenderName, appender); - - ec.pushObject(appender); - } catch (Exception oops) { - inError = true; - addError("Could not create an Appender of type [" + className + "].", oops); - throw new ActionException(oops); - } + @Override + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, ic, name, attributes); + validator.validateClassAttribute(); + validator.validateNameAttribute(); + return validator.isValid(); } - /** - * Once the children elements are also parsed, now is the time to activate the - * appender options. - */ - public void end(InterpretationContext ec, String name) { - if (inError) { - return; - } - - if (appender instanceof LifeCycle) { - ((LifeCycle) appender).start(); - } - - Object o = ec.peekObject(); - - if (o != appender) { - addWarn("The object at the of the stack is not the appender named [" + appender.getName() + "] pushed earlier."); - } else { - ec.popObject(); - } + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + AppenderModel appenderModel = new AppenderModel(); + appenderModel.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + appenderModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + return appenderModel; } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderRefAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderRefAction.java index cde4652820..8ea9800d13 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderRefAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/AppenderRefAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,62 +15,27 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.spi.AppenderAttachable; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.AppenderRefModel; +import ch.qos.logback.core.model.Model; -import java.util.HashMap; +public class AppenderRefAction extends BaseModelAction { -public class AppenderRefAction extends Action { - boolean inError = false; - - @SuppressWarnings("unchecked") - public void begin(InterpretationContext ec, String tagName, Attributes attributes) { - // Let us forget about previous errors (in this object) - inError = false; - - // logger.debug("begin called"); - - Object o = ec.peekObject(); - - if (!(o instanceof AppenderAttachable)) { - String errMsg = "Could not find an AppenderAttachable at the top of execution stack. Near [" + tagName + "] line " + getLineNumber(ec); - inError = true; - addError(errMsg); - return; - } - - AppenderAttachable appenderAttachable = (AppenderAttachable) o; - - String appenderName = ec.subst(attributes.getValue(ActionConst.REF_ATTRIBUTE)); - - if (OptionHelper.isEmpty(appenderName)) { - // print a meaningful error message and return - String errMsg = "Missing appender ref attribute in tag."; - inError = true; - addError(errMsg); - - return; - } - - HashMap> appenderBag = (HashMap>) ec.getObjectMap().get(ActionConst.APPENDER_BAG); - Appender appender = (Appender) appenderBag.get(appenderName); - - if (appender == null) { - String msg = "Could not find an appender named [" + appenderName + "]. Did you define it below instead of above in the configuration file?"; - inError = true; - addError(msg); - addError("See " + CoreConstants.CODES_URL + "#appender_order for more details."); - return; - } - - addInfo("Attaching appender named [" + appenderName + "] to " + appenderAttachable); - appenderAttachable.addAppender(appender); + @Override + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, intercon, name, attributes); + pv.validateRefAttribute(); + return pv.isValid(); } - public void end(InterpretationContext ec, String n) { + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + AppenderRefModel arm = new AppenderRefModel(); + String ref = attributes.getValue(JoranConstants.REF_ATTRIBUTE); + arm.setRef(ref); + return arm; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/BaseModelAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/BaseModelAction.java new file mode 100755 index 0000000000..148667ea2a --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/BaseModelAction.java @@ -0,0 +1,93 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; + +public abstract class BaseModelAction extends Action { + + Model parentModel; + Model currentModel; + boolean inError = false; + + @Override + public void begin(SaxEventInterpretationContext saxEventInterpretationContext, String name, Attributes attributes) + throws ActionException { + parentModel = null; + inError = false; + + if (!validPreconditions(saxEventInterpretationContext, name, attributes)) { + inError = true; + return; + } + + currentModel = buildCurrentModel(saxEventInterpretationContext, name, attributes); + currentModel.setTag(name); + if (!saxEventInterpretationContext.isModelStackEmpty()) { + parentModel = saxEventInterpretationContext.peekModel(); + } + final int lineNumber = getLineNumber(saxEventInterpretationContext); + currentModel.setLineNumber(lineNumber); + saxEventInterpretationContext.pushModel(currentModel); + } + + abstract protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes); + + /** + * Validate preconditions of this action. + * + * By default, true is returned. Subclasses should override appropriately. + * + * @param intercon + * @param name + * @param attributes + * @return + */ + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + return true; + } + + @Override + public void body(SaxEventInterpretationContext ec, String body) throws ActionException { + if(currentModel == null) { + throw new ActionException("current model is null. Is element missing?"); + } + currentModel.addText(body); + } + + @Override + public void end(SaxEventInterpretationContext saxEventInterpretationContext, String name) throws ActionException { + if (inError) + return; + + Model m = saxEventInterpretationContext.peekModel(); + + if (m != currentModel) { + addWarn("The object "+ m +"] at the top of the stack differs from the model [" + currentModel.idString() + + "] pushed earlier."); + addWarn("This is wholly unexpected."); + } + + // do not pop nor add to parent if there is no parent + if (parentModel != null) { + parentModel.addSubModel(currentModel); + saxEventInterpretationContext.popModel(); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ContextPropertyAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ContextPropertyAction.java index c4f8295ddb..a014898bb7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ContextPropertyAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ContextPropertyAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,7 +16,7 @@ import org.xml.sax.Attributes; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; /** * @author Ceki Gulcu @@ -24,12 +24,12 @@ public class ContextPropertyAction extends Action { @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - addError("The [contextProperty] element has been removed. Please use [substitutionProperty] element instead"); + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) throws ActionException { + addError("The [contextProperty] element has been removed. Please use [property] element instead"); } @Override - public void end(InterpretationContext ec, String name) throws ActionException { + public void end(SaxEventInterpretationContext ec, String name) throws ActionException { } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ConversionRuleAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ConversionRuleAction.java index 00eed11816..5ab92dc710 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ConversionRuleAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ConversionRuleAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,70 +13,124 @@ */ package ch.qos.logback.core.joran.action; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.ConversionRuleModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.util.OptionHelper; +import org.xml.sax.Attributes; + import java.util.HashMap; import java.util.Map; -import org.xml.sax.Attributes; +import static ch.qos.logback.core.joran.JoranConstants.CONVERSION_WORD_ATTRIBUTE; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +public class ConversionRuleAction extends BaseModelAction { -public class ConversionRuleAction extends Action { - boolean inError = false; + static public String CONVERTER_CLASS_ATTRIBUTE = "converterClass"; - /** - * Instantiates an layout of the given class and sets its name. - * - */ - @SuppressWarnings("unchecked") - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - // Let us forget about previous errors (in this object) - inError = false; - String errorMsg; - String conversionWord = attributes.getValue(ActionConst.CONVERSION_WORD_ATTRIBUTE); - String converterClass = attributes.getValue(ActionConst.CONVERTER_CLASS_ATTRIBUTE); + @Override + protected boolean validPreconditions(SaxEventInterpretationContext seic, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, seic, name, attributes); - if (OptionHelper.isEmpty(conversionWord)) { - inError = true; - errorMsg = "No 'conversionWord' attribute in "; - addError(errorMsg); + boolean invalidConverterClassAttribute = pv.isInvalidAttribute(CONVERTER_CLASS_ATTRIBUTE); + boolean invalidClassAttribute = pv.isInvalidAttribute(CLASS_ATTRIBUTE); - return; + if(!invalidConverterClassAttribute) { + pv.addWarn("["+CONVERTER_CLASS_ATTRIBUTE +"] attribute is deprecated and replaced by ["+CLASS_ATTRIBUTE+ + "]. "+pv.getLocationSuffix()); } - - if (OptionHelper.isEmpty(converterClass)) { - inError = true; - errorMsg = "No 'converterClass' attribute in "; - ec.addError(errorMsg); - - return; + boolean missingClass = invalidClassAttribute && invalidConverterClassAttribute; + if(missingClass) { + pv.addMissingAttributeError(CLASS_ATTRIBUTE); + return false; } - try { - Map ruleRegistry = (Map) context.getObject(CoreConstants.PATTERN_RULE_REGISTRY); - if (ruleRegistry == null) { - ruleRegistry = new HashMap(); - context.putObject(CoreConstants.PATTERN_RULE_REGISTRY, ruleRegistry); - } - // put the new rule into the rule registry - addInfo("registering conversion word " + conversionWord + " with class [" + converterClass + "]"); - ruleRegistry.put(conversionWord, converterClass); - } catch (Exception oops) { - inError = true; - errorMsg = "Could not add conversion rule to PatternLayout."; - addError(errorMsg); + boolean multipleClassAttributes = (!invalidClassAttribute) && (!invalidConverterClassAttribute); + if(multipleClassAttributes) { + pv.addWarn("Both ["+CONVERTER_CLASS_ATTRIBUTE+"] attribute and ["+CLASS_ATTRIBUTE+"] attribute specified. "); + pv.addWarn( "["+CLASS_ATTRIBUTE+"] attribute will override. "); } + pv.validateGivenAttribute(CONVERSION_WORD_ATTRIBUTE); + return pv.isValid(); } - /** - * Once the children elements are also parsed, now is the time to activate - * the appender options. - */ - public void end(InterpretationContext ec, String n) { - } - public void finish(InterpretationContext ec) { + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ConversionRuleModel conversionRuleModel = new ConversionRuleModel(); + conversionRuleModel.setConversionWord(attributes.getValue(CONVERSION_WORD_ATTRIBUTE)); + + String converterClassStr = attributes.getValue(CONVERTER_CLASS_ATTRIBUTE); + if(!OptionHelper.isNullOrEmpty(converterClassStr)) { + conversionRuleModel.setClassName(converterClassStr); + } + // if both converterClass and class are specified the latter overrides. + String classStr = attributes.getValue(CLASS_ATTRIBUTE); + if(!OptionHelper.isNullOrEmpty(classStr)) { + conversionRuleModel.setClassName(classStr); + } + return conversionRuleModel; } + +// /** +// * Instantiates a layout of the given class and sets its name. +// * +// */ +// @SuppressWarnings("unchecked") +// public void begin(SaxEventInterpretationContext ec, String localName, Attributes attributes) { +// // Let us forget about previous errors (in this object) +// inError = false; +// +// String errorMsg; +// String conversionWord = attributes.getValue(CONVERSION_WORD_ATTRIBUTE); +// String converterClass = attributes.getValue(JoranConstants.CONVERTER_CLASS_ATTRIBUTE); +// +// if (OptionHelper.isNullOrEmptyOrAllSpaces(conversionWord)) { +// inError = true; +// errorMsg = "No 'conversionWord' attribute in "; +// addError(errorMsg); +// +// return; +// } +// +// if (OptionHelper.isNullOrEmptyOrAllSpaces(converterClass)) { +// inError = true; +// errorMsg = "No 'converterClass' attribute in "; +// ec.addError(errorMsg); +// +// return; +// } +// +// try { +// Map ruleRegistry = (Map) context +// .getObject(CoreConstants.PATTERN_RULE_REGISTRY); +// if (ruleRegistry == null) { +// ruleRegistry = new HashMap(); +// context.putObject(CoreConstants.PATTERN_RULE_REGISTRY, ruleRegistry); +// } +// // put the new rule into the rule registry +// addInfo("registering conversion word " + conversionWord + " with class [" + converterClass + "]"); +// ruleRegistry.put(conversionWord, converterClass); +// } catch (Exception oops) { +// inError = true; +// errorMsg = "Could not add conversion rule to PatternLayout."; +// addError(errorMsg); +// } +// } +// +// +// /** +// * Once the children elements are also parsed, now is the time to activate the +// * appender options. +// */ +// public void end(SaxEventInterpretationContext ec, String n) { +// } +// +// public void finish(SaxEventInterpretationContext ec) { +// } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java index 46c4c375fe..6c7051344d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/DefinePropertyAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,94 +13,37 @@ */ package ch.qos.logback.core.joran.action; -import ch.qos.logback.core.joran.action.ActionUtil.Scope; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.util.OptionHelper; -import ch.qos.logback.core.spi.PropertyDefiner; import org.xml.sax.Attributes; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.DefineModel; +import ch.qos.logback.core.model.Model; + /** - * Instantiate class for define property value. Get future property name and - * property definer class from attributes. Some property definer properties - * could be used. After defining put new property to context. + * Creates {@link DefineModel} instance and populate its name, className and + * scope. * * @author Aleksey Didik + * @author Ceki G¨lc¨ */ -public class DefinePropertyAction extends Action { - - String scopeStr; - Scope scope; - String propertyName; - PropertyDefiner definer; - boolean inError; - - public void begin(InterpretationContext ec, String localName, Attributes attributes) throws ActionException { - // reset variables - scopeStr = null; - scope = null; - propertyName = null; - definer = null; - inError = false; - - // read future property name - propertyName = attributes.getValue(NAME_ATTRIBUTE); - scopeStr = attributes.getValue(SCOPE_ATTRIBUTE); - - scope = ActionUtil.stringToScope(scopeStr); - if (OptionHelper.isEmpty(propertyName)) { - addError("Missing property name for property definer. Near [" + localName + "] line " + getLineNumber(ec)); - inError = true; - return; - } - - // read property definer class name - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - addError("Missing class name for property definer. Near [" + localName + "] line " + getLineNumber(ec)); - inError = true; - return; - } - - // try to instantiate property definer - try { - addInfo("About to instantiate property definer of type [" + className + "]"); - definer = (PropertyDefiner) OptionHelper.instantiateByClassName(className, PropertyDefiner.class, context); - definer.setContext(context); - if (definer instanceof LifeCycle) { - ((LifeCycle) definer).start(); - } - ec.pushObject(definer); - } catch (Exception oops) { - inError = true; - addError("Could not create an PropertyDefiner of type [" + className + "].", oops); - throw new ActionException(oops); - } +public class DefinePropertyAction extends BaseModelAction { + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, ic, name, attributes); + validator.validateClassAttribute(); + validator.validateNameAttribute(); + return validator.isValid(); } - /** - * Now property definer is initialized by all properties and we can put - * property value to context - */ - public void end(InterpretationContext ec, String name) { - if (inError) { - return; - } - - Object o = ec.peekObject(); - - if (o != definer) { - addWarn("The object at the of the stack is not the property definer for property named [" + propertyName + "] pushed earlier."); - } else { - addInfo("Popping property definer for property named [" + propertyName + "] from the object stack"); - ec.popObject(); - // let's put defined property and value to context but only if it is - // not null - String propertyValue = definer.getPropertyValue(); - if (propertyValue != null) { - ActionUtil.setProperty(ec, propertyName, propertyValue, scope); - } - } + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + DefineModel defineModel = new DefineModel(); + defineModel.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + defineModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + defineModel.setScopeStr(attributes.getValue(SCOPE_ATTRIBUTE)); + return defineModel; } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/EventEvaluatorAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/EventEvaluatorAction.java new file mode 100644 index 0000000000..bdf15ed360 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/EventEvaluatorAction.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.EventEvaluatorModel; +import ch.qos.logback.core.model.Model; + +public class EventEvaluatorAction extends BaseModelAction { + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, intercon, name, attributes); + pv.validateNameAttribute(); + return pv.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + + EventEvaluatorModel eem = new EventEvaluatorModel(); + + eem.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + eem.setName(attributes.getValue(NAME_ATTRIBUTE)); + + return eem; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForBasicProperty.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForBasicProperty.java deleted file mode 100644 index 697227c22d..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForBasicProperty.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.util.AggregationType; - -/** - * Lump together several fields for use by {@link NestedBasicPropertyIA}. - * - * @author Ceki Gulcu - */ -class IADataForBasicProperty { - final PropertySetter parentBean; - final AggregationType aggregationType; - final String propertyName; - boolean inError; - - IADataForBasicProperty(PropertySetter parentBean, AggregationType aggregationType, String propertyName) { - this.parentBean = parentBean; - this.aggregationType = aggregationType; - this.propertyName = propertyName; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForComplexProperty.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForComplexProperty.java deleted file mode 100644 index ca346d5c48..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IADataForComplexProperty.java +++ /dev/null @@ -1,53 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.util.AggregationType; - -/** - * Lump together several fields for use by {@link NestedComplexPropertyIA}. - * - * @author Ceki - */ -public class IADataForComplexProperty { - final PropertySetter parentBean; - final AggregationType aggregationType; - final String complexPropertyName; - private Object nestedComplexProperty; - boolean inError; - - public IADataForComplexProperty(PropertySetter parentBean, AggregationType aggregationType, String complexPropertyName) { - this.parentBean = parentBean; - this.aggregationType = aggregationType; - this.complexPropertyName = complexPropertyName; - } - - public AggregationType getAggregationType() { - return aggregationType; - } - - public Object getNestedComplexProperty() { - return nestedComplexProperty; - } - - public String getComplexPropertyName() { - return complexPropertyName; - } - - public void setNestedComplexProperty(Object nestedComplexProperty) { - this.nestedComplexProperty = nestedComplexProperty; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImcplicitActionDataForBasicProperty.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImcplicitActionDataForBasicProperty.java new file mode 100644 index 0000000000..6db192bfdc --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImcplicitActionDataForBasicProperty.java @@ -0,0 +1,31 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.util.PropertySetter; +import ch.qos.logback.core.util.AggregationType; + +/** + * Lump together several fields for use by {@link ImcplicitActionDataForBasicProperty}. + * + * @author Ceki Gulcu + */ +public class ImcplicitActionDataForBasicProperty extends ImplicitModelData { + + public ImcplicitActionDataForBasicProperty(PropertySetter parentBean, AggregationType aggregationType, + String propertyName) { + super(parentBean, aggregationType, propertyName); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitAction.java deleted file mode 100644 index 25635cde08..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitAction.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import ch.qos.logback.core.joran.spi.ElementPath; -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.spi.InterpretationContext; - -/** - * ImplcitActions are like normal (explicit) actions except that are applied - * by the parser when no other pattern applies. Since there can be many implicit - * actions, each action is asked whether it applies in the given context. The - * first implicit action to respond positively is then applied. See also the - * {@link #isApplicable} method. - * - * @author Ceki Gülcü - */ -public abstract class ImplicitAction extends Action { - - /** - * Check whether this implicit action is appropriate in the current context. - * - * @param currentElementPath This pattern contains the tag name of the current - * element being parsed at the top of the stack. - * @param attributes The attributes of the current element to process. - * @param ec - * @return Whether the implicit action is applicable in the current context - */ - public abstract boolean isApplicable(ElementPath currentElementPath, Attributes attributes, InterpretationContext ec); - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelAction.java new file mode 100755 index 0000000000..1781955915 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelAction.java @@ -0,0 +1,78 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import java.util.Stack; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.ErrorCodes; + +/** + * + * Action dealing with elements corresponding to implicit rules. + * + * + * @author Ceki Gülcü + * + */ +// TODO: rename to DefaultImplicitRuleAction (after Model migration) +public class ImplicitModelAction extends Action { + + Stack currentImplicitModelStack = new Stack<>(); + + @Override + public void begin(SaxEventInterpretationContext interpretationContext, String name, Attributes attributes) + throws ActionException { + ImplicitModel currentImplicitModel = new ImplicitModel(); + currentImplicitModel.setTag(name); + + String className = attributes.getValue(CLASS_ATTRIBUTE); + currentImplicitModel.setClassName(className); + currentImplicitModelStack.push(currentImplicitModel); + interpretationContext.pushModel(currentImplicitModel); + } + + @Override + public void body(SaxEventInterpretationContext ec, String body) { + ImplicitModel implicitModel = currentImplicitModelStack.peek(); + implicitModel.addText(body); + } + + @Override + public void end(SaxEventInterpretationContext interpretationContext, String name) throws ActionException { + + ImplicitModel implicitModel = currentImplicitModelStack.peek(); + Model otherImplicitModel = interpretationContext.popModel(); + + if (implicitModel != otherImplicitModel) { + addError(implicitModel + " does not match " + otherImplicitModel); + return; + } + Model parentModel = interpretationContext.peekModel(); + if(parentModel != null) { + parentModel.addSubModel(implicitModel); + } else { + addWarn(ErrorCodes.PARENT_MODEL_NOT_FOUND); + addWarn(ErrorCodes.SKIPPING_IMPLICIT_MODEL_ADDITION); + } + currentImplicitModelStack.pop(); + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelData.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelData.java new file mode 100755 index 0000000000..d46df39f11 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelData.java @@ -0,0 +1,40 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.util.PropertySetter; +import ch.qos.logback.core.util.AggregationType; + +/** + * Lump together several fields for use by implicit action and co. + * + * @author Ceki + */ +public class ImplicitModelData { + public final PropertySetter parentBean; + public final AggregationType aggregationType; + public final String propertyName; + public boolean inError; + + public ImplicitModelData(PropertySetter parentBean, AggregationType aggregationType, String propertyName) { + this.parentBean = parentBean; + this.aggregationType = aggregationType; + this.propertyName = propertyName; + } + + public AggregationType getAggregationType() { + return aggregationType; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java new file mode 100644 index 0000000000..5b5cf85709 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImplicitModelDataForComplexProperty.java @@ -0,0 +1,48 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. + * + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.util.PropertySetter; +import ch.qos.logback.core.util.AggregationType; + +/** + * Lump together several fields for use by {@link ch.qos.logback.core.model.processor.ImplicitModelHandler ImplicitModelHandler}. + * + * @author Ceki + */ +public class ImplicitModelDataForComplexProperty extends ImplicitModelData { + + private Object nestedComplexProperty; + private Class expectedPropertyType; + + public ImplicitModelDataForComplexProperty(PropertySetter parentBean, AggregationType aggregationType, + String propertyName) { + super(parentBean, aggregationType, propertyName); + } + + public Object getNestedComplexProperty() { + return nestedComplexProperty; + } + + public Class getExpectedPropertyType() { + return expectedPropertyType; + } + + public void setExpectedPropertyType(Class expectedPropertyType) { + this.expectedPropertyType = expectedPropertyType; + } + + public void setNestedComplexProperty(Object nestedComplexProperty) { + this.nestedComplexProperty = nestedComplexProperty; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImportAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImportAction.java new file mode 100644 index 0000000000..351355bb6f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ImportAction.java @@ -0,0 +1,45 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.ImportModel; +import ch.qos.logback.core.model.Model; + +/** + * Populates {@link ImportModel} based on XML input. + * + * @author Ceki Gülcü + * @since 1.3.0 + */ +public class ImportAction extends BaseModelAction { + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, intercon, name, attributes); + pv.validateClassAttribute(); + return pv.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String localName, + Attributes attributes) { + ImportModel importModel = new ImportModel(); + importModel.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + return importModel; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IncludeAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/IncludeAction.java index 5e3bac655d..de66d4dcbd 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/IncludeAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/IncludeAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,212 +13,19 @@ */ package ch.qos.logback.core.joran.action; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.MalformedURLException; -import java.net.URI; -import java.net.URL; -import java.util.List; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.event.SaxEventRecorder; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; -import ch.qos.logback.core.util.Loader; -import ch.qos.logback.core.util.OptionHelper; - -public class IncludeAction extends Action { - - private static final String INCLUDED_TAG = "included"; - private static final String FILE_ATTR = "file"; - private static final String URL_ATTR = "url"; - private static final String RESOURCE_ATTR = "resource"; - private static final String OPTIONAL_ATTR = "optional"; - - private String attributeInUse; - private boolean optional; - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - - SaxEventRecorder recorder = new SaxEventRecorder(context); - - this.attributeInUse = null; - this.optional = OptionHelper.toBoolean(attributes.getValue(OPTIONAL_ATTR), false); - - if (!checkAttributes(attributes)) { - return; - } - - InputStream in = getInputStream(ec, attributes); - - try { - if (in != null) { - parseAndRecord(in, recorder); - // remove the tag from the beginning and from the end - trimHeadAndTail(recorder); - - // offset = 2, because we need to get past this element as well as the end element - ec.getJoranInterpreter().getEventPlayer().addEventsDynamically(recorder.saxEventList, 2); - } - } catch (JoranException e) { - addError("Error while parsing " + attributeInUse, e); - } finally { - close(in); - } - - } - - void close(InputStream in) { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - } - - private boolean checkAttributes(Attributes attributes) { - String fileAttribute = attributes.getValue(FILE_ATTR); - String urlAttribute = attributes.getValue(URL_ATTR); - String resourceAttribute = attributes.getValue(RESOURCE_ATTR); - - int count = 0; - - if (!OptionHelper.isEmpty(fileAttribute)) { - count++; - } - if (!OptionHelper.isEmpty(urlAttribute)) { - count++; - } - if (!OptionHelper.isEmpty(resourceAttribute)) { - count++; - } - - if (count == 0) { - addError("One of \"path\", \"resource\" or \"url\" attributes must be set."); - return false; - } else if (count > 1) { - addError("Only one of \"file\", \"url\" or \"resource\" attributes should be set."); - return false; - } else if (count == 1) { - return true; - } - throw new IllegalStateException("Count value [" + count + "] is not expected"); - } - - URL attributeToURL(String urlAttribute) { - try { - return new URL(urlAttribute); - } catch (MalformedURLException mue) { - String errMsg = "URL [" + urlAttribute + "] is not well formed."; - addError(errMsg, mue); - return null; - } - } - - InputStream openURL(URL url) { - try { - return url.openStream(); - } catch (IOException e) { - optionalWarning("Failed to open [" + url.toString() + "]"); - return null; - } - } - - URL resourceAsURL(String resourceAttribute) { - URL url = Loader.getResourceBySelfClassLoader(resourceAttribute); - if (url == null) { - optionalWarning("Could not find resource corresponding to [" + resourceAttribute + "]"); - return null; - } else - return url; - } - - private void optionalWarning(String msg) { - if (!optional) { - addWarn(msg); - } - } - - URL filePathAsURL(String path) { - URI uri = new File(path).toURI(); - try { - return uri.toURL(); - } catch (MalformedURLException e) { - // impossible to get here - e.printStackTrace(); - return null; - } - } - - URL getInputURL(InterpretationContext ec, Attributes attributes) { - String fileAttribute = attributes.getValue(FILE_ATTR); - String urlAttribute = attributes.getValue(URL_ATTR); - String resourceAttribute = attributes.getValue(RESOURCE_ATTR); - - if (!OptionHelper.isEmpty(fileAttribute)) { - this.attributeInUse = ec.subst(fileAttribute); - return filePathAsURL(attributeInUse); - } - - if (!OptionHelper.isEmpty(urlAttribute)) { - this.attributeInUse = ec.subst(urlAttribute); - return attributeToURL(attributeInUse); - } - - if (!OptionHelper.isEmpty(resourceAttribute)) { - this.attributeInUse = ec.subst(resourceAttribute); - return resourceAsURL(attributeInUse); - } - // given previous checkAttributes() check we cannot reach this line - throw new IllegalStateException("A URL stream should have been returned"); - - } - - InputStream getInputStream(InterpretationContext ec, Attributes attributes) { - URL inputURL = getInputURL(ec, attributes); - if (inputURL == null) - return null; +import ch.qos.logback.core.model.IncludeModel; +import ch.qos.logback.core.model.ResourceModel; - ConfigurationWatchListUtil.addToWatchList(context, inputURL); - return openURL(inputURL); - } - - private void trimHeadAndTail(SaxEventRecorder recorder) { - // Let's remove the two events before - // adding the events to the player. - - List saxEventList = recorder.saxEventList; - - if (saxEventList.size() == 0) { - return; - } - - SaxEvent first = saxEventList.get(0); - if (first != null && first.qName.equalsIgnoreCase(INCLUDED_TAG)) { - saxEventList.remove(0); - } - - SaxEvent last = saxEventList.get(recorder.saxEventList.size() - 1); - if (last != null && last.qName.equalsIgnoreCase(INCLUDED_TAG)) { - saxEventList.remove(recorder.saxEventList.size() - 1); - } - } - - private void parseAndRecord(InputStream inputSource, SaxEventRecorder recorder) throws JoranException { - recorder.setContext(context); - recorder.recordEvents(inputSource); - } +/** + * Build an {@link IncludeModel} instance from SAX events. + * + * @author Ceki Gülcü + * + */ +public class IncludeAction extends ResourceAction { - @Override - public void end(InterpretationContext ec, String name) throws ActionException { - // do nothing + protected ResourceModel makeNewResourceModel() { + return new IncludeModel(); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NOPAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NOPAction.java index dc6a888f80..8f70a06d86 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NOPAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NOPAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,21 +15,19 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; /** - * No operation (NOP) action that does strictly nothing. - * Setting a rule to this pattern is sometimes useful in order - * to prevent implicit actions to kick in. - * + * No operation (NOP) action that does strictly nothing. Setting a rule to this + * pattern is sometimes useful in order to prevent implicit actions to kick in. + * * @author Ceki Gülcü */ public class NOPAction extends Action { - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { } - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedBasicPropertyIA.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedBasicPropertyIA.java deleted file mode 100644 index 5a01741ed1..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedBasicPropertyIA.java +++ /dev/null @@ -1,109 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import java.util.Stack; - -import ch.qos.logback.core.joran.spi.ElementPath; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; -import ch.qos.logback.core.util.AggregationType; - -/** - * This action is responsible for tying together a parent object with one of its - * simple properties specified as an element but for which there is - * no explicit rule. - * - * @author Ceki Gülcü - */ -public class NestedBasicPropertyIA extends ImplicitAction { - - // We use a stack of IADataForBasicProperty objects in order to - // support nested elements which are handled by the same NestedBasicPropertyIA instance. - // We push a IADataForBasicProperty instance in the isApplicable method (if the - // action is applicable) and pop it in the end() method. - // The XML well-formedness property will guarantee that a push will eventually - // be followed by the corresponding pop. - Stack actionDataStack = new Stack(); - - private final BeanDescriptionCache beanDescriptionCache; - - public NestedBasicPropertyIA(BeanDescriptionCache beanDescriptionCache) { - this.beanDescriptionCache = beanDescriptionCache; - } - - public boolean isApplicable(ElementPath elementPath, Attributes attributes, InterpretationContext ec) { - // System.out.println("in NestedSimplePropertyIA.isApplicable [" + pattern + - // "]"); - String nestedElementTagName = elementPath.peekLast(); - - // no point in attempting if there is no parent object - if (ec.isEmpty()) { - return false; - } - - Object o = ec.peekObject(); - PropertySetter parentBean = new PropertySetter(beanDescriptionCache, o); - parentBean.setContext(context); - - AggregationType aggregationType = parentBean.computeAggregationType(nestedElementTagName); - - switch (aggregationType) { - case NOT_FOUND: - case AS_COMPLEX_PROPERTY: - case AS_COMPLEX_PROPERTY_COLLECTION: - return false; - - case AS_BASIC_PROPERTY: - case AS_BASIC_PROPERTY_COLLECTION: - IADataForBasicProperty ad = new IADataForBasicProperty(parentBean, aggregationType, nestedElementTagName); - actionDataStack.push(ad); - // addInfo("NestedSimplePropertyIA deemed applicable [" + pattern + "]"); - return true; - default: - addError("PropertySetter.canContainComponent returned " + aggregationType); - return false; - } - } - - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - // NOP - } - - public void body(InterpretationContext ec, String body) { - - String finalBody = ec.subst(body); - // get the action data object pushed in isApplicable() method call - IADataForBasicProperty actionData = (IADataForBasicProperty) actionDataStack.peek(); - switch (actionData.aggregationType) { - case AS_BASIC_PROPERTY: - actionData.parentBean.setProperty(actionData.propertyName, finalBody); - break; - case AS_BASIC_PROPERTY_COLLECTION: - actionData.parentBean.addBasicProperty(actionData.propertyName, finalBody); - break; - default: - addError("Unexpected aggregationType " + actionData.aggregationType); - } - } - - public void end(InterpretationContext ec, String tagName) { - // pop the action data object pushed in isApplicable() method call - actionDataStack.pop(); - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java deleted file mode 100644 index c9f6ce6a43..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.java +++ /dev/null @@ -1,185 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import java.util.Stack; - -import ch.qos.logback.core.joran.spi.ElementPath; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.NoAutoStartUtil; -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; -import ch.qos.logback.core.spi.ContextAware; -import ch.qos.logback.core.spi.LifeCycle; -import ch.qos.logback.core.util.AggregationType; -import ch.qos.logback.core.util.Loader; -import ch.qos.logback.core.util.OptionHelper; - -/** - * This action is responsible for tying together a parent object with a child - * element for which there is no explicit rule. - * - * @author Ceki Gülcü - */ -public class NestedComplexPropertyIA extends ImplicitAction { - - // actionDataStack contains ActionData instances - // We use a stack of ActionData objects in order to support nested - // elements which are handled by the same NestedComplexPropertyIA instance. - // We push a ActionData instance in the isApplicable method (if the - // action is applicable) and pop it in the end() method. - // The XML well-formedness property will guarantee that a push will eventually - // be followed by a corresponding pop. - Stack actionDataStack = new Stack(); - - private final BeanDescriptionCache beanDescriptionCache; - - public NestedComplexPropertyIA(BeanDescriptionCache beanDescriptionCache) { - this.beanDescriptionCache = beanDescriptionCache; - } - - public boolean isApplicable(ElementPath elementPath, Attributes attributes, InterpretationContext ic) { - - String nestedElementTagName = elementPath.peekLast(); - - // calling ic.peekObject with an empty stack will throw an exception - if (ic.isEmpty()) { - return false; - } - - Object o = ic.peekObject(); - PropertySetter parentBean = new PropertySetter(beanDescriptionCache, o); - parentBean.setContext(context); - - AggregationType aggregationType = parentBean.computeAggregationType(nestedElementTagName); - - switch (aggregationType) { - case NOT_FOUND: - case AS_BASIC_PROPERTY: - case AS_BASIC_PROPERTY_COLLECTION: - return false; - - // we only push action data if NestComponentIA is applicable - case AS_COMPLEX_PROPERTY_COLLECTION: - case AS_COMPLEX_PROPERTY: - IADataForComplexProperty ad = new IADataForComplexProperty(parentBean, aggregationType, nestedElementTagName); - actionDataStack.push(ad); - - return true; - default: - addError("PropertySetter.computeAggregationType returned " + aggregationType); - return false; - } - } - - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - // LogLog.debug("in NestComponentIA begin method"); - // get the action data object pushed in isApplicable() method call - IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack.peek(); - - String className = attributes.getValue(CLASS_ATTRIBUTE); - // perform variable name substitution - className = ec.subst(className); - - Class componentClass = null; - try { - - if (!OptionHelper.isEmpty(className)) { - componentClass = Loader.loadClass(className, context); - } else { - // guess class name via implicit rules - PropertySetter parentBean = actionData.parentBean; - componentClass = parentBean.getClassNameViaImplicitRules(actionData.getComplexPropertyName(), actionData.getAggregationType(), - ec.getDefaultNestedComponentRegistry()); - } - - if (componentClass == null) { - actionData.inError = true; - String errMsg = "Could not find an appropriate class for property [" + localName + "]"; - addError(errMsg); - return; - } - - if (OptionHelper.isEmpty(className)) { - addInfo("Assuming default type [" + componentClass.getName() + "] for [" + localName + "] property"); - } - - actionData.setNestedComplexProperty(componentClass.getConstructor().newInstance()); - - // pass along the repository - if (actionData.getNestedComplexProperty() instanceof ContextAware) { - ((ContextAware) actionData.getNestedComplexProperty()).setContext(this.context); - } - // addInfo("Pushing component [" + localName - // + "] on top of the object stack."); - ec.pushObject(actionData.getNestedComplexProperty()); - - } catch (Exception oops) { - actionData.inError = true; - String msg = "Could not create component [" + localName + "] of type [" + className + "]"; - addError(msg, oops); - } - - } - - public void end(InterpretationContext ec, String tagName) { - - // pop the action data object pushed in isApplicable() method call - // we assume that each this begin - IADataForComplexProperty actionData = (IADataForComplexProperty) actionDataStack.pop(); - - if (actionData.inError) { - return; - } - - PropertySetter nestedBean = new PropertySetter(beanDescriptionCache, actionData.getNestedComplexProperty()); - nestedBean.setContext(context); - - // have the nested element point to its parent if possible - if (nestedBean.computeAggregationType("parent") == AggregationType.AS_COMPLEX_PROPERTY) { - nestedBean.setComplexProperty("parent", actionData.parentBean.getObj()); - } - - // start the nested complex property if it implements LifeCycle and is not - // marked with a @NoAutoStart annotation - Object nestedComplexProperty = actionData.getNestedComplexProperty(); - if (nestedComplexProperty instanceof LifeCycle && NoAutoStartUtil.notMarkedWithNoAutoStart(nestedComplexProperty)) { - ((LifeCycle) nestedComplexProperty).start(); - } - - Object o = ec.peekObject(); - - if (o != actionData.getNestedComplexProperty()) { - addError("The object on the top the of the stack is not the component pushed earlier."); - } else { - ec.popObject(); - // Now let us attach the component - switch (actionData.aggregationType) { - case AS_COMPLEX_PROPERTY: - actionData.parentBean.setComplexProperty(tagName, actionData.getNestedComplexProperty()); - - break; - case AS_COMPLEX_PROPERTY_COLLECTION: - actionData.parentBean.addComplexProperty(tagName, actionData.getNestedComplexProperty()); - break; - default: - addError("Unexpected aggregationType " + actionData.aggregationType); - } - } - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NewRuleAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NewRuleAction.java index 157a5380b1..56e5970b16 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/NewRuleAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/NewRuleAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,7 +15,7 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; import ch.qos.logback.core.joran.spi.ElementSelector; import ch.qos.logback.core.util.OptionHelper; @@ -23,23 +23,23 @@ public class NewRuleAction extends Action { boolean inError = false; /** - * Instantiates an layout of the given class and sets its name. + * Instantiates a layout of the given class and sets its name. */ - public void begin(InterpretationContext ec, String localName, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String localName, Attributes attributes) { // Let us forget about previous errors (in this object) inError = false; String errorMsg; String pattern = attributes.getValue(Action.PATTERN_ATTRIBUTE); String actionClass = attributes.getValue(Action.ACTION_CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(pattern)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(pattern)) { inError = true; errorMsg = "No 'pattern' attribute in "; addError(errorMsg); return; } - if (OptionHelper.isEmpty(actionClass)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(actionClass)) { inError = true; errorMsg = "No 'actionClass' attribute in "; addError(errorMsg); @@ -48,7 +48,7 @@ public void begin(InterpretationContext ec, String localName, Attributes attribu try { addInfo("About to add new Joran parsing rule [" + pattern + "," + actionClass + "]."); - ec.getJoranInterpreter().getRuleStore().addRule(new ElementSelector(pattern), actionClass); + ec.getSaxEventInterpreter().getRuleStore().addRule(new ElementSelector(pattern), actionClass); } catch (Exception oops) { inError = true; errorMsg = "Could not add new Joran parsing rule [" + pattern + "," + actionClass + "]"; @@ -60,9 +60,9 @@ public void begin(InterpretationContext ec, String localName, Attributes attribu * Once the children elements are also parsed, now is the time to activate the * appender options. */ - public void end(InterpretationContext ec, String n) { + public void end(SaxEventInterpretationContext ec, String n) { } - public void finish(InterpretationContext ec) { + public void finish(SaxEventInterpretationContext ec) { } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ParamAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ParamAction.java index a41ef2740e..13efb5b6c1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ParamAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ParamAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,55 +15,32 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.util.PropertySetter; -import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ParamModel; -public class ParamAction extends Action { - static String NO_NAME = "No name attribute in element"; - static String NO_VALUE = "No value attribute in element"; - boolean inError = false; +public class ParamAction extends BaseModelAction { - private final BeanDescriptionCache beanDescriptionCache; - public ParamAction(BeanDescriptionCache beanDescriptionCache) { - this.beanDescriptionCache=beanDescriptionCache; - } + @Override + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, intercon, name, attributes); + pv.validateNameAttribute(); + pv.validateValueAttribute(); - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - String name = attributes.getValue(NAME_ATTRIBUTE); - String value = attributes.getValue(VALUE_ATTRIBUTE); + addWarn(" element is deprecated in favor of a more direct syntax." + atLine(intercon)); + addWarn("For details see " + CoreConstants.CODES_URL + "#param"); - if (name == null) { - inError = true; - addError(NO_NAME); - return; - } + return pv.isValid(); - if (value == null) { - inError = true; - addError(NO_VALUE); - return; - } - - // remove both leading and trailing spaces - value = value.trim(); - - Object o = ec.peekObject(); - PropertySetter propSetter = new PropertySetter(beanDescriptionCache,o); - propSetter.setContext(context); - value = ec.subst(value); - - // allow for variable substitution for name as well - name = ec.subst(name); - - // getLogger().debug( - // "In ParamAction setting parameter [{}] to value [{}].", name, value); - propSetter.setProperty(name, value); - } - - public void end(InterpretationContext ec, String localName) { } - public void finish(InterpretationContext ec) { + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ParamModel paramModel = new ParamModel(); + paramModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + paramModel.setValue(attributes.getValue(VALUE_ATTRIBUTE)); + return paramModel; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/PreconditionValidator.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/PreconditionValidator.java new file mode 100755 index 0000000000..71267e3dfc --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/PreconditionValidator.java @@ -0,0 +1,114 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.OptionHelper; + +public class PreconditionValidator extends ContextAwareBase { + + boolean valid = true; + SaxEventInterpretationContext seic; + Attributes attributes; + String tag; + + public PreconditionValidator(ContextAware origin, SaxEventInterpretationContext seic, String name, + Attributes attributes) { + super(origin); + this.setContext(origin.getContext()); + this.seic = seic; + this.tag = name; + this.attributes = attributes; + } + + public PreconditionValidator validateZeroAttributes() { + if(attributes == null) + return this; + + if(attributes.getLength() != 0) { + addError("Element [" + tag + "] should have no attributes, near line " + + Action.getLineNumber(seic)); + this.valid = false; + } + return this; + } + + + public PreconditionValidator validateClassAttribute() { + return validateGivenAttribute(Action.CLASS_ATTRIBUTE); + } + + public PreconditionValidator validateNameAttribute() { + return validateGivenAttribute(Action.NAME_ATTRIBUTE); + } + + public PreconditionValidator validateValueAttribute() { + return validateGivenAttribute(JoranConstants.VALUE_ATTR); + } + + public PreconditionValidator validateRefAttribute() { + return validateGivenAttribute(JoranConstants.REF_ATTRIBUTE); + } + + public boolean isInvalidAttribute(String attributeName) { + String attributeValue = attributes.getValue(attributeName); + return OptionHelper.isNullOrEmptyOrAllSpaces(attributeValue); + } + + public PreconditionValidator validateGivenAttribute(String attributeName) { + boolean invalid = isInvalidAttribute(attributeName); + if (invalid) { + addMissingAttributeError(attributeName); + this.valid = false; + } + return this; + } + + + + /** + * + * @deprecated replaced by {@link #validateGivenAttribute(String)} + */ + @Deprecated + public PreconditionValidator generic(String attributeName) { + return validateGivenAttribute(attributeName); + } + + public void addMissingAttributeError(String attributeName) { + addError("Missing attribute [" + attributeName + "]. " + getLocationSuffix()); + } + + public String getLocationSuffix() { + return "See element [" + tag + "] near line " + Action.getLineNumber(seic); + } + +// public void addWarning(String msg) { +// super.addWarn(msg + getLocationSuffix()); +// } +// +// public void addError(String msg) { +// super.addError(msg + getLocationSuffix()); +// } + + public boolean isValid() { + return valid; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/PropertyAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/PropertyAction.java index 617feabbb4..774fe1f29e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/PropertyAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/PropertyAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,129 +13,41 @@ */ package ch.qos.logback.core.joran.action; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.util.Properties; - import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.ActionUtil.Scope; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.pattern.util.RegularEscapeUtil; -import ch.qos.logback.core.util.Loader; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.PropertyModel; /** - * This class serves as a base for other actions, which similar to the ANT + * This class serves to build a model for properties which are to the ANT * <property> task which add/set properties of a given object. * - * This action sets new substitution properties in the logging context by name, - * value pair, or adds all the properties passed in "file" or "resource" - * attribute. - * * @author Ceki Gülcü */ -public class PropertyAction extends Action { +public class PropertyAction extends BaseModelAction { static final String RESOURCE_ATTRIBUTE = "resource"; - static String INVALID_ATTRIBUTES = "In element, either the \"file\" attribute alone, or " - + "the \"resource\" element alone, or both the \"name\" and \"value\" attributes must be set."; - - /** - * Set a new property for the execution context by name, value pair, or adds - * all the properties found in the given file. - * - */ - public void begin(InterpretationContext ec, String localName, Attributes attributes) { - + @Override + protected boolean validPreconditions(SaxEventInterpretationContext interpretationContext, String localName, + Attributes attributes) { if ("substitutionProperty".equals(localName)) { - addWarn("[substitutionProperty] element has been deprecated. Please use the [property] element instead."); + addWarn("[substitutionProperty] element has been deprecated. Please use the [variable] element instead."); } - - String name = attributes.getValue(NAME_ATTRIBUTE); - String value = attributes.getValue(VALUE_ATTRIBUTE); - String scopeStr = attributes.getValue(SCOPE_ATTRIBUTE); - - Scope scope = ActionUtil.stringToScope(scopeStr); - - if (checkFileAttributeSanity(attributes)) { - String file = attributes.getValue(FILE_ATTRIBUTE); - file = ec.subst(file); - try { - FileInputStream istream = new FileInputStream(file); - loadAndSetProperties(ec, istream, scope); - } catch (FileNotFoundException e) { - addError("Could not find properties file [" + file + "]."); - } catch (IOException e1) { - addError("Could not read properties file [" + file + "].", e1); - } - } else if (checkResourceAttributeSanity(attributes)) { - String resource = attributes.getValue(RESOURCE_ATTRIBUTE); - resource = ec.subst(resource); - URL resourceURL = Loader.getResourceBySelfClassLoader(resource); - if (resourceURL == null) { - addError("Could not find resource [" + resource + "]."); - } else { - try { - InputStream istream = resourceURL.openStream(); - loadAndSetProperties(ec, istream, scope); - } catch (IOException e) { - addError("Could not read resource file [" + resource + "].", e); - } - } - } else if (checkValueNameAttributesSanity(attributes)) { - value = RegularEscapeUtil.basicEscape(value); - // now remove both leading and trailing spaces - value = value.trim(); - value = ec.subst(value); - ActionUtil.setProperty(ec, name, value, scope); - - } else { - addError(INVALID_ATTRIBUTES); - } - } - - void loadAndSetProperties(InterpretationContext ec, InputStream istream, Scope scope) throws IOException { - Properties props = new Properties(); - props.load(istream); - istream.close(); - ActionUtil.setProperties(ec, props, scope); - } - - boolean checkFileAttributeSanity(Attributes attributes) { - String file = attributes.getValue(FILE_ATTRIBUTE); - String name = attributes.getValue(NAME_ATTRIBUTE); - String value = attributes.getValue(VALUE_ATTRIBUTE); - String resource = attributes.getValue(RESOURCE_ATTRIBUTE); - - return !(OptionHelper.isEmpty(file)) && (OptionHelper.isEmpty(name) && OptionHelper.isEmpty(value) && OptionHelper.isEmpty(resource)); + return true; } - boolean checkResourceAttributeSanity(Attributes attributes) { - String file = attributes.getValue(FILE_ATTRIBUTE); - String name = attributes.getValue(NAME_ATTRIBUTE); - String value = attributes.getValue(VALUE_ATTRIBUTE); - String resource = attributes.getValue(RESOURCE_ATTRIBUTE); - - return !(OptionHelper.isEmpty(resource)) && (OptionHelper.isEmpty(name) && OptionHelper.isEmpty(value) && OptionHelper.isEmpty(file)); + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + PropertyModel propertyModel = new PropertyModel(); + propertyModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + propertyModel.setValue(attributes.getValue(VALUE_ATTRIBUTE)); + propertyModel.setFile(attributes.getValue(FILE_ATTRIBUTE)); + propertyModel.setResource(attributes.getValue(RESOURCE_ATTRIBUTE)); + propertyModel.setScopeStr(attributes.getValue(SCOPE_ATTRIBUTE)); + return propertyModel; } - boolean checkValueNameAttributesSanity(Attributes attributes) { - String file = attributes.getValue(FILE_ATTRIBUTE); - String name = attributes.getValue(NAME_ATTRIBUTE); - String value = attributes.getValue(VALUE_ATTRIBUTE); - String resource = attributes.getValue(RESOURCE_ATTRIBUTE); - - return (!(OptionHelper.isEmpty(name) || OptionHelper.isEmpty(value)) && (OptionHelper.isEmpty(file) && OptionHelper.isEmpty(resource))); - } - - public void end(InterpretationContext ec, String name) { - } - - public void finish(InterpretationContext ec) { - } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ResourceAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ResourceAction.java new file mode 100644 index 0000000000..079fe1a044 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ResourceAction.java @@ -0,0 +1,86 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ResourceModel; +import org.xml.sax.Attributes; + +/** + * An action which builds subclass instances of {@link ResourceModel}. + * + * @since 1.5.8 + */ +abstract public class ResourceAction extends Action { + + private static final String FILE_ATTR = "file"; + private static final String URL_ATTR = "url"; + private static final String RESOURCE_ATTR = "resource"; + private static final String OPTIONAL_ATTR = "optional"; + + Model parentModel; + ResourceModel resourceModel; + boolean inError = false; + + abstract protected ResourceModel makeNewResourceModel(); + + @Override + public void begin(SaxEventInterpretationContext seic, String tagName, Attributes attributes) throws ActionException { + String optionalStr = attributes.getValue(OPTIONAL_ATTR); + + this.resourceModel = makeNewResourceModel(); + this.resourceModel.setOptional(optionalStr); + fillInIncludeModelAttributes(resourceModel, tagName, attributes); + if (!seic.isModelStackEmpty()) { + parentModel = seic.peekModel(); + } + final int lineNumber = getLineNumber(seic); + this.resourceModel.setLineNumber(lineNumber); + seic.pushModel(this.resourceModel); + } + + private void fillInIncludeModelAttributes(ResourceModel resourceModel, String tagName, Attributes attributes) { + this.resourceModel.setTag(tagName); + String fileAttribute = attributes.getValue(FILE_ATTR); + String urlAttribute = attributes.getValue(URL_ATTR); + String resourceAttribute = attributes.getValue(RESOURCE_ATTR); + + this.resourceModel.setFile(fileAttribute); + this.resourceModel.setUrl(urlAttribute); + this.resourceModel.setResource(resourceAttribute); + } + + @Override + public void end(SaxEventInterpretationContext seic, String name) throws ActionException { + if(inError) + return; + + Model m = seic.peekModel(); + + if (m != resourceModel) { + addWarn("The object at the of the stack is not the model [" + resourceModel.idString() + + "] pushed earlier."); + addWarn("This is wholly unexpected."); + } + + // do not pop nor add to parent if there is no parent + if (parentModel != null) { + parentModel.addSubModel(resourceModel); + seic.popModel(); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/SequenceNumberGeneratorAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SequenceNumberGeneratorAction.java index bd5e890432..7018134b2b 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/SequenceNumberGeneratorAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SequenceNumberGeneratorAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,71 +15,30 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.spi.BasicSequenceNumberGenerator; -import ch.qos.logback.core.spi.SequenceNumberGenerator; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SequenceNumberGeneratorModel; /** - * Action which handles <sequenceNumberGenerator> elements in configuration files. + * Action which handles <sequenceNumberGenerator> elements in + * configuration files. * * @author Ceki Gülcü */ -public class SequenceNumberGeneratorAction extends Action { - - SequenceNumberGenerator sequenceNumberGenerator; - private boolean inError; +public class SequenceNumberGeneratorAction extends BaseModelAction { - /** - * Instantiates a shutdown hook of the given class and sets its name. - * - * The hook thus generated is placed in the {@link InterpretationContext}'s - * shutdown hook bag. - */ @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - sequenceNumberGenerator = null; - inError = false; - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - className = BasicSequenceNumberGenerator.class.getName(); - addInfo("Assuming className [" + className + "]"); - } - - try { - addInfo("About to instantiate SequenceNumberGenerator of type [" + className + "]"); - - sequenceNumberGenerator = (SequenceNumberGenerator) OptionHelper.instantiateByClassName(className, SequenceNumberGenerator.class, context); - sequenceNumberGenerator.setContext(context); - - ic.pushObject(sequenceNumberGenerator); - } catch (Exception e) { - inError = true; - addError("Could not create a SequenceNumberGenerator of type [" + className + "].", e); - throw new ActionException(e); - } + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + SequenceNumberGeneratorModel sngm = new SequenceNumberGeneratorModel(); + sngm.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + return sngm; } - /** - * Once the children elements are also parsed, now is the time to activate the - * shutdown hook options. - */ @Override - public void end(InterpretationContext ic, String name) throws ActionException { - if (inError) { - return; - } - - Object o = ic.peekObject(); - if (o != sequenceNumberGenerator) { - addWarn("The object at the of the stack is not the hook pushed earlier."); - } else { - ic.popObject(); - - addInfo("Registering sequenceNumberGenerator with context."); - context.setSequenceNumberGenerator(sequenceNumberGenerator); - } + protected boolean validPreconditions(SaxEventInterpretationContext seic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, seic, name, attributes); + validator.validateClassAttribute(); + return validator.isValid(); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/SerializeModelAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SerializeModelAction.java new file mode 100644 index 0000000000..1a85bb7de2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SerializeModelAction.java @@ -0,0 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.action; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SerializeModelModel; +import org.xml.sax.Attributes; + +public class SerializeModelAction extends BaseModelAction { + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + + SerializeModelModel serializeModelModel = new SerializeModelModel(); + serializeModelModel.setFile(attributes.getValue(FILE_ATTRIBUTE)); + + return serializeModelModel; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ShutdownHookAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ShutdownHookAction.java index 46c0cf0216..73ec730d0c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/ShutdownHookAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/ShutdownHookAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,74 +15,34 @@ import org.xml.sax.Attributes; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.hook.DefaultShutdownHook; -import ch.qos.logback.core.hook.ShutdownHookBase; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ShutdownHookModel; /** - * Action which handles <shutdownHook> elements in configuration files. + * Action which builds {@link ShutdownHookModel} based on <shutdownHook> + * elements found in configuration files. * * @author Mike Reinhold + * @author Ceki Gülcü */ -public class ShutdownHookAction extends Action { - - ShutdownHookBase hook; - private boolean inError; +public class ShutdownHookAction extends BaseModelAction { - /** - * Instantiates a shutdown hook of the given class and sets its name. - * - * The hook thus generated is placed in the {@link InterpretationContext}'s - * shutdown hook bag. - */ @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - hook = null; - inError = false; - - String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - className = DefaultShutdownHook.class.getName(); - addInfo("Assuming className [" + className + "]"); - } - - try { - addInfo("About to instantiate shutdown hook of type [" + className + "]"); - - hook = (ShutdownHookBase) OptionHelper.instantiateByClassName(className, ShutdownHookBase.class, context); - hook.setContext(context); - - ic.pushObject(hook); - } catch (Exception e) { - inError = true; - addError("Could not create a shutdown hook of type [" + className + "].", e); - throw new ActionException(e); - } + protected boolean validPreconditions(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + return true; } - /** - * Once the children elements are also parsed, now is the time to activate the - * shutdown hook options. - */ @Override - public void end(InterpretationContext ic, String name) throws ActionException { - if (inError) { - return; - } + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ShutdownHookModel shutdownHookModel = new ShutdownHookModel(); - Object o = ic.peekObject(); - if (o != hook) { - addWarn("The object at the of the stack is not the hook pushed earlier."); - } else { - ic.popObject(); + String className = attributes.getValue(CLASS_ATTRIBUTE); + shutdownHookModel.setClassName(className); - Thread hookThread = new Thread(hook, "Logback shutdown hook [" + context.getName() + "]"); - addInfo("Registeting shuthown hook with JVM runtime."); - context.putObject(CoreConstants.SHUTDOWN_HOOK_THREAD, hookThread); - Runtime.getRuntime().addShutdownHook(hookThread); - } + return shutdownHookModel; } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/SiftAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SiftAction.java new file mode 100644 index 0000000000..05909d0f58 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/SiftAction.java @@ -0,0 +1,38 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SiftModel; + +public class SiftAction extends BaseModelAction { + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext intercon, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, intercon, name, attributes); + pv.validateZeroAttributes(); + return pv.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String localName, + Attributes attributes) { + SiftModel siftModel = new SiftModel(); + return siftModel; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/StatusListenerAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/StatusListenerAction.java index d55c6ef5d2..9556eae84b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/StatusListenerAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/StatusListenerAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,68 +13,38 @@ */ package ch.qos.logback.core.joran.action; -import ch.qos.logback.core.spi.ContextAware; import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.StatusListenerModel; import ch.qos.logback.core.status.StatusListener; import ch.qos.logback.core.util.OptionHelper; -public class StatusListenerAction extends Action { +public class StatusListenerAction extends BaseModelAction { boolean inError = false; Boolean effectivelyAdded = null; StatusListener statusListener = null; - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - inError = false; - effectivelyAdded = null; + @Override + protected boolean validPreconditions(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { String className = attributes.getValue(CLASS_ATTRIBUTE); - if (OptionHelper.isEmpty(className)) { - addError("Missing class name for statusListener. Near [" + name + "] line " + getLineNumber(ec)); - inError = true; - return; - } - - try { - statusListener = (StatusListener) OptionHelper.instantiateByClassName(className, StatusListener.class, context); - effectivelyAdded = ec.getContext().getStatusManager().add(statusListener); - if (statusListener instanceof ContextAware) { - ((ContextAware) statusListener).setContext(context); - } - addInfo("Added status listener of type [" + className + "]"); - ec.pushObject(statusListener); - } catch (Exception e) { - inError = true; - addError("Could not create an StatusListener of type [" + className + "].", e); - throw new ActionException(e); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addError("Missing class name for statusListener. Near [" + name + "] line " + + getLineNumber(interpretationContext)); + return false; } - + return true; } - public void finish(InterpretationContext ec) { + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + StatusListenerModel statusListenerModel = new StatusListenerModel(); + statusListenerModel.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + return statusListenerModel; } - public void end(InterpretationContext ec, String e) { - if (inError) { - return; - } - if (isEffectivelyAdded() && statusListener instanceof LifeCycle) { - ((LifeCycle) statusListener).start(); - } - Object o = ec.peekObject(); - if (o != statusListener) { - addWarn("The object at the of the stack is not the statusListener pushed earlier."); - } else { - ec.popObject(); - } - } - - private boolean isEffectivelyAdded() { - if (effectivelyAdded == null) - return false; - return effectivelyAdded; - } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/action/TimestampAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/action/TimestampAction.java index b35aee1f70..ab6bfece37 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/action/TimestampAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/action/TimestampAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,11 @@ */ package ch.qos.logback.core.joran.action; -import ch.qos.logback.core.util.CachingDateFormatter; import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.ActionUtil.Scope; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.TimestampModel; import ch.qos.logback.core.util.OptionHelper; /** @@ -29,51 +28,40 @@ * @author Ceki Gülcü * */ -public class TimestampAction extends Action { - static String DATE_PATTERN_ATTRIBUTE = "datePattern"; - static String TIME_REFERENCE_ATTRIBUTE = "timeReference"; - static String CONTEXT_BIRTH = "contextBirth"; +public class TimestampAction extends BaseModelAction { - boolean inError = false; + public static final String DATE_PATTERN_ATTRIBUTE = "datePattern"; + public static final String TIME_REFERENCE_ATTRIBUTE = "timeReference"; @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { + protected boolean validPreconditions(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + boolean valid = true; String keyStr = attributes.getValue(KEY_ATTRIBUTE); - if (OptionHelper.isEmpty(keyStr)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(keyStr)) { addError("Attribute named [" + KEY_ATTRIBUTE + "] cannot be empty"); - inError = true; + valid = false; } String datePatternStr = attributes.getValue(DATE_PATTERN_ATTRIBUTE); - if (OptionHelper.isEmpty(datePatternStr)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(datePatternStr)) { addError("Attribute named [" + DATE_PATTERN_ATTRIBUTE + "] cannot be empty"); - inError = true; - } - - String timeReferenceStr = attributes.getValue(TIME_REFERENCE_ATTRIBUTE); - long timeReference; - if (CONTEXT_BIRTH.equalsIgnoreCase(timeReferenceStr)) { - addInfo("Using context birth as time reference."); - timeReference = context.getBirthTime(); - } else { - timeReference = System.currentTimeMillis(); - addInfo("Using current interpretation time, i.e. now, as time reference."); + valid = false; } + return valid; + } - if (inError) - return; - - String scopeStr = attributes.getValue(SCOPE_ATTRIBUTE); - Scope scope = ActionUtil.stringToScope(scopeStr); + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + TimestampModel timestampModel = new TimestampModel(); - CachingDateFormatter sdf = new CachingDateFormatter(datePatternStr); - String val = sdf.format(timeReference); + timestampModel.setKey(attributes.getValue(KEY_ATTRIBUTE)); + timestampModel.setDatePattern(attributes.getValue(DATE_PATTERN_ATTRIBUTE)); + timestampModel.setTimeReference(attributes.getValue(TIME_REFERENCE_ATTRIBUTE)); + timestampModel.setScopeStr(attributes.getValue(SCOPE_ATTRIBUTE)); - addInfo("Adding property to the context with key=\"" + keyStr + "\" and value=\"" + val + "\" to the " + scope + " scope"); - ActionUtil.setProperty(ec, keyStr, val, scope); - } + return timestampModel; - @Override - public void end(InterpretationContext ec, String name) throws ActionException { } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ByPropertiesConditionAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ByPropertiesConditionAction.java new file mode 100644 index 0000000000..c33e95d809 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ByPropertiesConditionAction.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.conditional; + +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.ByPropertiesConditionModel; +import org.xml.sax.Attributes; + +public class ByPropertiesConditionAction extends BaseModelAction { + + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ByPropertiesConditionModel sngm = new ByPropertiesConditionModel(); + sngm.setClassName(attributes.getValue(CLASS_ATTRIBUTE)); + return sngm; + } + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext seic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, seic, name, attributes); + validator.validateClassAttribute(); + return validator.isValid(); + } + +} + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/Condition.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/Condition.java index 250dbad672..bdeadc2192 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/Condition.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/Condition.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,26 @@ */ package ch.qos.logback.core.joran.conditional; +/** + *

A condition evaluated during Joran conditional processing.

+ * + *

Implementations of this interface encapsulate a boolean test that + * determines whether a conditional block in a Joran configuration should + * be processed.

+ * + *

Typical implementations evaluate configuration state, environment + * variables, or other runtime properties.

+ * + * @since 0.9.20 + * @author Ceki Gülcü + */ public interface Condition { + + /** + * Evaluate the condition. + * + * @return {@code true} if the condition is satisfied and the associated + * conditional block should be activated; {@code false} otherwise + */ boolean evaluate(); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ElseAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ElseAction.java index cb3b097359..cd0c54d3e4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ElseAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ElseAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,28 @@ */ package ch.qos.logback.core.joran.conditional; -import java.util.List; +import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.ElseModel; -public class ElseAction extends ThenOrElseActionBase { +public class ElseAction extends BaseModelAction { @Override - void registerEventList(IfAction ifAction, List eventList) { - ifAction.setElseSaxEventList(eventList); - + protected boolean validPreconditions(SaxEventInterpretationContext interpcont, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, interpcont, name, attributes); + pv.validateZeroAttributes(); + return pv.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + ElseModel elseModel = new ElseModel(); + return elseModel; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/IfAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/IfAction.java index 1b44099455..1d880fa148 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/IfAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/IfAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,140 +11,37 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.joran.conditional; -import java.util.List; -import java.util.Stack; +package ch.qos.logback.core.joran.conditional; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.util.EnvUtil; import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.Interpreter; -import ch.qos.logback.core.util.OptionHelper; - -public class IfAction extends Action { - private static final String CONDITION_ATTR = "condition"; - - public static final String MISSING_JANINO_MSG = "Could not find Janino library on the class path. Skipping conditional processing."; - public static final String MISSING_JANINO_SEE = "See also " + CoreConstants.CODES_URL + "#ifJanino"; - - Stack stack = new Stack(); +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.Model; +public class IfAction extends BaseModelAction { + + public static final String CONDITION_ATTRIBUTE = "condition"; + @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - - IfState state = new IfState(); - boolean emptyStack = stack.isEmpty(); - stack.push(state); - - if (!emptyStack) { - return; - } - - ic.pushObject(this); - if (!EnvUtil.isJaninoAvailable()) { - addError(MISSING_JANINO_MSG); - addError(MISSING_JANINO_SEE); - return; - } - - state.active = true; - Condition condition = null; - String conditionAttribute = attributes.getValue(CONDITION_ATTR); - - if (!OptionHelper.isEmpty(conditionAttribute)) { - conditionAttribute = OptionHelper.substVars(conditionAttribute, ic, context); - PropertyEvalScriptBuilder pesb = new PropertyEvalScriptBuilder(ic); - pesb.setContext(context); - try { - condition = pesb.build(conditionAttribute); - } catch (Exception e) { - addError("Failed to parse condition [" + conditionAttribute + "]", e); - } - - if (condition != null) { - state.boolResult = condition.evaluate(); - } - - } + protected boolean validPreconditions(SaxEventInterpretationContext interpcont, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, interpcont, name, attributes); + //pv.validateGivenAttribute(CONDITION_ATTRIBUTE); + return pv.isValid(); } - + @Override - public void end(InterpretationContext ic, String name) throws ActionException { - - IfState state = stack.pop(); - if (!state.active) { - return; - } - - Object o = ic.peekObject(); - if (o == null) { - throw new IllegalStateException("Unexpected null object on stack"); - } - if (!(o instanceof IfAction)) { - throw new IllegalStateException("Unexpected object of type [" + o.getClass() + "] on stack"); - } - - if (o != this) { - throw new IllegalStateException("IfAction different then current one on stack"); - } - ic.popObject(); - - if (state.boolResult == null) { - addError("Failed to determine \"if then else\" result"); - return; - } - - Interpreter interpreter = ic.getJoranInterpreter(); - List listToPlay = state.thenSaxEventList; - if (!state.boolResult) { - listToPlay = state.elseSaxEventList; - } - - // if boolResult==false & missing else, listToPlay may be null - if (listToPlay != null) { - // insert past this event - interpreter.getEventPlayer().addEventsDynamically(listToPlay, 1); - } - - } - - public void setThenSaxEventList(List thenSaxEventList) { - IfState state = stack.firstElement(); - if (state.active) { - state.thenSaxEventList = thenSaxEventList; - } else { - throw new IllegalStateException("setThenSaxEventList() invoked on inactive IfAction"); - } - } - - public void setElseSaxEventList(List elseSaxEventList) { - IfState state = stack.firstElement(); - if (state.active) { - state.elseSaxEventList = elseSaxEventList; - } else { - throw new IllegalStateException("setElseSaxEventList() invoked on inactive IfAction"); - } - + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + IfModel ifModel = new IfModel(); + + String condition = attributes.getValue(CONDITION_ATTRIBUTE); + ifModel.setCondition(condition); + + return ifModel; } - public boolean isActive() { - if (stack == null) - return false; - if (stack.isEmpty()) - return false; - return stack.peek().active; - } -} - -class IfState { - Boolean boolResult; - List thenSaxEventList; - List elseSaxEventList; - boolean active; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilder.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilder.java index 52da0e28fb..912926e90f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyEvalScriptBuilder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,14 +31,14 @@ public class PropertyEvalScriptBuilder extends ContextAwareBase { final PropertyContainer localPropContainer; - PropertyEvalScriptBuilder(PropertyContainer localPropContainer) { + public PropertyEvalScriptBuilder(PropertyContainer localPropContainer) { this.localPropContainer = localPropContainer; } Map map = new HashMap(); - public Condition build(String script) throws IllegalAccessException, CompileException, InstantiationException, SecurityException, NoSuchMethodException, - IllegalArgumentException, InvocationTargetException { + public Condition build(String script) throws IllegalAccessException, CompileException, InstantiationException, + SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { ClassBodyEvaluator cbe = new ClassBodyEvaluator(); cbe.setImplementedInterfaces(new Class[] { Condition.class }); @@ -48,7 +48,8 @@ public Condition build(String script) throws IllegalAccessException, CompileExce Class clazz = cbe.getClazz(); Condition instance = (Condition) clazz.getDeclaredConstructor().newInstance(); - Method setMapMethod = clazz.getMethod("setPropertyContainers", PropertyContainer.class, PropertyContainer.class); + Method setMapMethod = clazz.getMethod("setPropertyContainers", PropertyContainer.class, + PropertyContainer.class); setMapMethod.invoke(instance, localPropContainer, context); return instance; diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyWrapperForScripts.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyWrapperForScripts.java index 1409d40a92..1ba2bf2f22 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyWrapperForScripts.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/PropertyWrapperForScripts.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenAction.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenAction.java index 8217cbee12..bed28728bf 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenAction.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,30 @@ */ package ch.qos.logback.core.joran.conditional; -import java.util.List; +import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.ThenModel; -public class ThenAction extends ThenOrElseActionBase { +public class ThenAction extends BaseModelAction { @Override - void registerEventList(IfAction ifAction, List eventList) { - ifAction.setThenSaxEventList(eventList); + protected boolean validPreconditions(SaxEventInterpretationContext interpcont, String name, Attributes attributes) { + PreconditionValidator pv = new PreconditionValidator(this, interpcont, name, attributes); + pv.validateZeroAttributes(); + return pv.isValid(); + } + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + + ThenModel thenModel = new ThenModel(); + + return thenModel; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenOrElseActionBase.java b/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenOrElseActionBase.java deleted file mode 100644 index 5ce0b920c7..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/conditional/ThenOrElseActionBase.java +++ /dev/null @@ -1,90 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.conditional; - -import java.util.ArrayList; -import java.util.List; -import java.util.Stack; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.InPlayListener; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -abstract public class ThenOrElseActionBase extends Action { - - Stack stateStack = new Stack(); - - @Override - public void begin(InterpretationContext ic, String name, Attributes attributes) throws ActionException { - - if (!weAreActive(ic)) - return; - - ThenActionState state = new ThenActionState(); - if (ic.isListenerListEmpty()) { - ic.addInPlayListener(state); - state.isRegistered = true; - } - stateStack.push(state); - } - - boolean weAreActive(InterpretationContext ic) { - Object o = ic.peekObject(); - if (!(o instanceof IfAction)) - return false; - IfAction ifAction = (IfAction) o; - return ifAction.isActive(); - } - - @Override - public void end(InterpretationContext ic, String name) throws ActionException { - if (!weAreActive(ic)) - return; - - ThenActionState state = stateStack.pop(); - if (state.isRegistered) { - ic.removeInPlayListener(state); - Object o = ic.peekObject(); - if (o instanceof IfAction) { - IfAction ifAction = (IfAction) o; - removeFirstAndLastFromList(state.eventList); - registerEventList(ifAction, state.eventList); - } else { - throw new IllegalStateException("Missing IfAction on top of stack"); - } - } - } - - abstract void registerEventList(IfAction ifAction, List eventList); - - void removeFirstAndLastFromList(List eventList) { - eventList.remove(0); - eventList.remove(eventList.size() - 1); - } - -} - -class ThenActionState implements InPlayListener { - - List eventList = new ArrayList(); - boolean isRegistered = false; - - public void inPlay(SaxEvent event) { - eventList.add(event); - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/BodyEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/BodyEvent.java index 1dba8fadcc..815fad51ec 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/BodyEvent.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/event/BodyEvent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/EndEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/EndEvent.java index abafb8495e..0a7c2371f3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/EndEvent.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/event/EndEvent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/InPlayListener.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/InPlayListener.java deleted file mode 100644 index 7e22a74139..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/InPlayListener.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event; - -public interface InPlayListener { - void inPlay(SaxEvent event); -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEvent.java index 1dd4ed0c02..40cf89d7c7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEvent.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEvent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEventRecorder.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEventRecorder.java index 1a21a5a168..2a3bcab742 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEventRecorder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/event/SaxEventRecorder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,11 +15,13 @@ import static ch.qos.logback.core.CoreConstants.XML_PARSING; +import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; @@ -39,25 +41,57 @@ public class SaxEventRecorder extends DefaultHandler implements ContextAware { - final ContextAwareImpl cai; + // org.xml.sax.ext.LexicalHandler is an optional interface + final ContextAwareImpl contextAwareImpl; + final ElementPath elementPath; + List saxEventList = new ArrayList(); + Locator locator; + public SaxEventRecorder(Context context) { - cai = new ContextAwareImpl(context, this); + this(context, new ElementPath()); } - public List saxEventList = new ArrayList(); - Locator locator; - ElementPath globalElementPath = new ElementPath(); + + public SaxEventRecorder(Context context, ElementPath elementPath) { + contextAwareImpl = new ContextAwareImpl(context, this); + this.elementPath = elementPath; + } + + /** + * An implementation which disallows external DTDs + * + * @param publicId The public identifier, or null if none is + * available. + * @param systemId The system identifier provided in the XML + * document. + * @return + * @throws SAXException + * @throws IOException + * @since 1.5.13 + */ + @Override + public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { + addWarn("Document Type Declaration (DOCTYPE) with external file reference is"); + addWarn("disallowed to prevent Server-Side Request Forgery (SSRF) attacks."); + addWarn("returning contents of SYSTEM " +systemId+ " as a white space"); + return new InputSource(new ByteArrayInputStream(" ".getBytes())); + } final public void recordEvents(InputStream inputStream) throws JoranException { recordEvents(new InputSource(inputStream)); } - public List recordEvents(InputSource inputSource) throws JoranException { + public void recordEvents(InputSource inputSource) throws JoranException { SAXParser saxParser = buildSaxParser(); try { + // the following sax property can be set in order to add 'this' as LexicalHandler to the saxParser + // However, this is not needed as long as resolveEntity() method is implemented as above + // saxParser.setProperty("http://xml.org/sax/properties/lexical-handler", this); + saxParser.parse(inputSource, this); - return saxEventList; + + return; } catch (IOException ie) { handleError("I/O error occurred while parsing xml file", ie); } catch (SAXException se) { @@ -78,10 +112,18 @@ private SAXParser buildSaxParser() throws JoranException { try { SAXParserFactory spf = SAXParserFactory.newInstance(); spf.setValidating(false); + //spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + // See LOGBACK-1465 + spf.setFeature("http://xml.org/sax/features/external-general-entities", false); + spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false); spf.setNamespaceAware(true); return spf.newSAXParser(); - } catch (Exception pce) { - String errMsg = "Parser configuration error occurred"; + } catch (ParserConfigurationException pce) { + String errMsg = "Error during SAX paser configuration. See https://logback.qos.ch/codes.html#saxParserConfiguration"; + addError(errMsg, pce); + throw new JoranException(errMsg, pce); + } catch (SAXException pce) { + String errMsg = "Error during parser creation or parser configuration"; addError(errMsg, pce); throw new JoranException(errMsg, pce); } @@ -98,11 +140,16 @@ public void setDocumentLocator(Locator l) { locator = l; } - public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { + protected boolean shouldIgnoreForElementPath(String tagName) { + return false; + } + public void startElement(String namespaceURI, String localName, String qName, Attributes atts) { String tagName = getTagName(localName, qName); - globalElementPath.push(tagName); - ElementPath current = globalElementPath.duplicate(); + if (!shouldIgnoreForElementPath(tagName)) { + elementPath.push(tagName); + } + ElementPath current = elementPath.duplicate(); saxEventList.add(new StartEvent(current, namespaceURI, localName, qName, atts, getLocator())); } @@ -135,7 +182,10 @@ SaxEvent getLastEvent() { public void endElement(String namespaceURI, String localName, String qName) { saxEventList.add(new EndEvent(namespaceURI, localName, qName, getLocator())); - globalElementPath.pop(); + String tagName = getTagName(localName, qName); + if (!shouldIgnoreForElementPath(tagName)) { + elementPath.pop(); + } } String getTagName(String localName, String qName) { @@ -149,7 +199,6 @@ String getTagName(String localName, String qName) { public void error(SAXParseException spe) throws SAXException { addError(XML_PARSING + " - Parsing error on line " + spe.getLineNumber() + " and column " + spe.getColumnNumber()); addError(spe.toString()); - } public void fatalError(SAXParseException spe) throws SAXException { @@ -162,39 +211,39 @@ public void warning(SAXParseException spe) throws SAXException { } public void addError(String msg) { - cai.addError(msg); + contextAwareImpl.addError(msg); } public void addError(String msg, Throwable ex) { - cai.addError(msg, ex); + contextAwareImpl.addError(msg, ex); } public void addInfo(String msg) { - cai.addInfo(msg); + contextAwareImpl.addInfo(msg); } public void addInfo(String msg, Throwable ex) { - cai.addInfo(msg, ex); + contextAwareImpl.addInfo(msg, ex); } public void addStatus(Status status) { - cai.addStatus(status); + contextAwareImpl.addStatus(status); } public void addWarn(String msg) { - cai.addWarn(msg); + contextAwareImpl.addWarn(msg); } public void addWarn(String msg, Throwable ex) { - cai.addWarn(msg, ex); + contextAwareImpl.addWarn(msg, ex); } public Context getContext() { - return cai.getContext(); + return contextAwareImpl.getContext(); } public void setContext(Context context) { - cai.setContext(context); + contextAwareImpl.setContext(context); } public List getSaxEventList() { diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/StartEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/StartEvent.java index dc7777a41f..003b95f5ce 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/StartEvent.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/event/StartEvent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,7 +23,8 @@ public class StartEvent extends SaxEvent { final public Attributes attributes; final public ElementPath elementPath; - StartEvent(ElementPath elementPath, String namespaceURI, String localName, String qName, Attributes attributes, Locator locator) { + StartEvent(ElementPath elementPath, String namespaceURI, String localName, String qName, Attributes attributes, + Locator locator) { super(namespaceURI, localName, qName, locator); // locator impl is used to take a snapshot! this.attributes = new AttributesImpl(attributes); @@ -38,15 +39,14 @@ public Attributes getAttributes() { public String toString() { StringBuilder b = new StringBuilder("StartEvent("); b.append(getQName()); - if(attributes != null) { - for(int i = 0; i < attributes.getLength(); i++) { - if(i > 0) - b.append(' '); + if (attributes != null) { + for (int i = 0; i < attributes.getLength(); i++) { + b.append(' '); b.append(attributes.getLocalName(i)).append("=\"").append(attributes.getValue(i)).append("\""); } } b.append(") ["); - b.append( locator.getLineNumber()); + b.append(locator.getLineNumber()); b.append(","); b.append(locator.getColumnNumber()); b.append("]"); diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/BodyEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/BodyEvent.java deleted file mode 100755 index 39de16baf7..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/BodyEvent.java +++ /dev/null @@ -1,39 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import javax.xml.stream.Location; - -public class BodyEvent extends StaxEvent { - - private String text; - - BodyEvent(String text, Location location) { - super(null, location); - this.text = text; - } - - public String getText() { - return text; - } - - void append(String txt) { - text += txt; - } - - @Override - public String toString() { - return "BodyEvent(" + getText() + ")" + location.getLineNumber() + "," + location.getColumnNumber(); - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/EndEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/EndEvent.java deleted file mode 100755 index 94c89c688c..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/EndEvent.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import javax.xml.stream.Location; - -/** - * Created with IntelliJ IDEA. - * User: ceki - * Date: 7/2/13 - * Time: 4:07 PM - * To change this template use File | Settings | File Templates. - */ -public class EndEvent extends StaxEvent { - - public EndEvent(String name, Location location) { - super(name, location); - } - - @Override - public String toString() { - return "EndEvent(" + getName() + ") [" + location.getLineNumber() + "," + location.getColumnNumber() + "]"; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StartEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StartEvent.java deleted file mode 100755 index b848f0d91b..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StartEvent.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import ch.qos.logback.core.joran.spi.ElementPath; - -import javax.xml.stream.Location; -import javax.xml.stream.events.Attribute; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -public class StartEvent extends StaxEvent { - - List attributes; - public ElementPath elementPath; - - StartEvent(ElementPath elementPath, String name, Iterator attributeIterator, Location location) { - super(name, location); - populateAttributes(attributeIterator); - this.elementPath = elementPath; - } - - private void populateAttributes(Iterator attributeIterator) { - while (attributeIterator.hasNext()) { - if (attributes == null) { - attributes = new ArrayList(2); - } - attributes.add(attributeIterator.next()); - } - } - - public ElementPath getElementPath() { - return elementPath; - } - - public List getAttributeList() { - return attributes; - } - - Attribute getAttributeByName(String name) { - if (attributes == null) - return null; - - for (Attribute attr : attributes) { - if (name.equals(attr.getName().getLocalPart())) - return attr; - } - return null; - } - - @Override - public String toString() { - return "StartEvent(" + getName() + ") [" + location.getLineNumber() + "," + location.getColumnNumber() + "]"; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEvent.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEvent.java deleted file mode 100755 index 67f241368f..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEvent.java +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import javax.xml.stream.Location; - -public class StaxEvent { - - final String name; - final Location location; - - StaxEvent(String name, Location location) { - this.name = name; - this.location = location; - - } - - public String getName() { - return name; - } - - public Location getLocation() { - return location; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorder.java b/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorder.java deleted file mode 100755 index bef8c99c8f..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorder.java +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.joran.spi.ElementPath; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.spi.ContextAwareBase; - -import javax.xml.stream.XMLEventReader; -import javax.xml.stream.XMLInputFactory; -import javax.xml.stream.XMLStreamException; -import javax.xml.stream.events.Characters; -import javax.xml.stream.events.EndElement; -import javax.xml.stream.events.StartElement; -import javax.xml.stream.events.XMLEvent; -import java.io.InputStream; -import java.util.ArrayList; -import java.util.List; - -public class StaxEventRecorder extends ContextAwareBase { - - List eventList = new ArrayList(); - ElementPath globalElementPath = new ElementPath(); - - public StaxEventRecorder(Context context) { - setContext(context); - } - - public void recordEvents(InputStream inputStream) throws JoranException { - try { - XMLEventReader xmlEventReader = XMLInputFactory.newInstance().createXMLEventReader(inputStream); - read(xmlEventReader); - } catch (XMLStreamException e) { - throw new JoranException("Problem parsing XML document. See previously reported errors.", e); - } - } - - public List getEventList() { - return eventList; - } - - private void read(XMLEventReader xmlEventReader) throws XMLStreamException { - while (xmlEventReader.hasNext()) { - XMLEvent xmlEvent = xmlEventReader.nextEvent(); - switch (xmlEvent.getEventType()) { - case XMLEvent.START_ELEMENT: - addStartElement(xmlEvent); - break; - case XMLEvent.CHARACTERS: - addCharacters(xmlEvent); - break; - case XMLEvent.END_ELEMENT: - addEndEvent(xmlEvent); - break; - default: - break; - } - } - } - - private void addStartElement(XMLEvent xmlEvent) { - StartElement se = xmlEvent.asStartElement(); - String tagName = se.getName().getLocalPart(); - globalElementPath.push(tagName); - ElementPath current = globalElementPath.duplicate(); - StartEvent startEvent = new StartEvent(current, tagName, se.getAttributes(), se.getLocation()); - eventList.add(startEvent); - } - - private void addCharacters(XMLEvent xmlEvent) { - Characters characters = xmlEvent.asCharacters(); - StaxEvent lastEvent = getLastEvent(); - - if (lastEvent instanceof BodyEvent) { - BodyEvent be = (BodyEvent) lastEvent; - be.append(characters.getData()); - } else { - // ignore space only text if the previous event is not a BodyEvent - if (!characters.isWhiteSpace()) { - BodyEvent bodyEvent = new BodyEvent(characters.getData(), xmlEvent.getLocation()); - eventList.add(bodyEvent); - } - } - } - - private void addEndEvent(XMLEvent xmlEvent) { - EndElement ee = xmlEvent.asEndElement(); - String tagName = ee.getName().getLocalPart(); - EndEvent endEvent = new EndEvent(tagName, ee.getLocation()); - eventList.add(endEvent); - globalElementPath.pop(); - } - - StaxEvent getLastEvent() { - if (eventList.isEmpty()) { - return null; - } - int size = eventList.size(); - if (size == 0) - return null; - return eventList.get(size - 1); - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/node/ComponentNode.java b/logback-core/src/main/java/ch/qos/logback/core/joran/node/ComponentNode.java index 6cc9bf1f6d..7f67848b28 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/node/ComponentNode.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/node/ComponentNode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/package.html b/logback-core/src/main/java/ch/qos/logback/core/joran/package.html index 26491f9d79..6360a37c94 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityChecker.java b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityChecker.java new file mode 100644 index 0000000000..7fd866e96c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityChecker.java @@ -0,0 +1,60 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.sanity; + +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.ContextAwareBase; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class AppenderWithinAppenderSanityChecker extends ContextAwareBase implements SanityChecker { + + static public String NESTED_APPENDERS_WARNING = "As of logback version 1.3, nested appenders are not allowed."; + + @Override + public void check(Model model) { + if (model == null) + return; + + List appenderModels = new ArrayList<>(); + deepFindAllModelsOfType(AppenderModel.class, appenderModels, model); + + List> nestedPairs = deepFindNestedSubModelsOfType(AppenderModel.class, appenderModels); + + List> filteredNestedPairs = nestedPairs.stream().filter(pair -> !isSiftingAppender(pair.first)).collect(Collectors.toList()); + + if(filteredNestedPairs.isEmpty()) { + return; + } + addWarn(NESTED_APPENDERS_WARNING); + for(Pair pair: filteredNestedPairs) { + addWarn("Appender at line "+pair.first.getLineNumber() + " contains a nested appender at line "+pair.second.getLineNumber()); + } + } + + private boolean isSiftingAppender(Model first) { + if(first instanceof AppenderModel) { + AppenderModel appenderModel = (AppenderModel) first; + String classname = appenderModel.getClassName(); + if(classname == null) + return false; + return appenderModel.getClassName().contains("SiftingAppender"); + } + return false; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/Pair.java b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/Pair.java new file mode 100644 index 0000000000..55dbef58bb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/Pair.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.sanity; + +public class Pair { + + final public F first; + final public S second; + + Pair(F first, S second) { + this.first = first; + this.second = second; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/SanityChecker.java b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/SanityChecker.java new file mode 100644 index 0000000000..ec7f2daf24 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/sanity/SanityChecker.java @@ -0,0 +1,51 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.sanity; + +import ch.qos.logback.core.model.Model; + +import java.util.ArrayList; +import java.util.List; + +/** + * Interface for sanity checking Models. + * @since 1.3.2/1.4.2 + * @author ceki + */ +public interface SanityChecker { + + public void check(Model model); + + default void deepFindAllModelsOfType(Class modelClass, List modelList, Model model) { + if (modelClass.isInstance(model)) { + modelList.add(model); + } + + for (Model m : model.getSubModels()) { + deepFindAllModelsOfType(modelClass, modelList, m); + } + } + + default List> deepFindNestedSubModelsOfType(Class modelClass, List parentList) { + + List> nestingPairs = new ArrayList<>(); + + for (Model parent : parentList) { + List nestedElements = new ArrayList<>(); + parent.getSubModels().stream().forEach(m -> deepFindAllModelsOfType(modelClass, nestedElements, m)); + nestedElements.forEach(n -> nestingPairs.add(new Pair(parent, n))); + } + return nestingPairs; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ActionException.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ActionException.java index 33477c7ac3..9e057f3c29 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ActionException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ActionException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,6 +27,10 @@ public class ActionException extends Exception { public ActionException() { } + public ActionException(String msg) { + super(msg); + } + public ActionException(final Throwable rootCause) { super(rootCause); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConfigurationWatchList.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConfigurationWatchList.java index 1d817043da..32c6dbe4dc 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConfigurationWatchList.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConfigurationWatchList.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,45 +14,95 @@ package ch.qos.logback.core.joran.spi; import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.MD5Util; import java.io.File; +import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; +import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; +import java.util.stream.Collectors; + +import static ch.qos.logback.core.CoreConstants.PROPERTIES_FILE_EXTENSION; /** + * This class manages the list of files and/or urls that are watched for changes. + * * @author Ceki Gülcü */ public class ConfigurationWatchList extends ContextAwareBase { - URL mainURL; - List fileWatchList = new ArrayList(); - List lastModifiedList = new ArrayList(); + public static final String HTTPS_PROTOCOL_STR = "https"; + public static final String HTTP_PROTOCOL_STR = "http"; + public static final String FILE_PROTOCOL_STR = "file"; + + static final String[] WATCHABLE_PROTOCOLS = new String[] { FILE_PROTOCOL_STR, HTTPS_PROTOCOL_STR, HTTP_PROTOCOL_STR }; + + static final byte[] BUF_ZERO = new byte[] { 0 }; + + URL topURL; + List fileWatchList = new ArrayList<>(); + List urlWatchList = new ArrayList<>(); + List lastHashList = new ArrayList<>(); + + List lastModifiedList = new ArrayList<>(); public ConfigurationWatchList buildClone() { ConfigurationWatchList out = new ConfigurationWatchList(); - out.mainURL = this.mainURL; + out.topURL = this.topURL; out.fileWatchList = new ArrayList(this.fileWatchList); out.lastModifiedList = new ArrayList(this.lastModifiedList); + out.lastHashList = new ArrayList<>(this.lastHashList); return out; } - + public void clear() { - this.mainURL = null; - lastModifiedList.clear(); - fileWatchList.clear(); + this.topURL = null; + this.lastModifiedList.clear(); + this.fileWatchList.clear(); + this.urlWatchList.clear(); + this.lastHashList.clear(); } /** - * The mainURL for the configuration file. Null values are allowed. - * @param mainURL + * The topURL for the configuration file. Null values are allowed. + * + * @param topURL */ - public void setMainURL(URL mainURL) { - // main url can be null - this.mainURL = mainURL; - if (mainURL != null) - addAsFileToWatch(mainURL); + public void setTopURL(URL topURL) { + // topURL can be null + this.topURL = topURL; + if (topURL != null) + addAsFileToWatch(topURL); + } + + public boolean watchPredicateFulfilled() { + if (hasMainURLAndNonEmptyFileList()) { + return true; + } + + if(urlListContainsProperties()) { + return true; + } + + return fileWatchListContainsProperties(); + + } + + private boolean urlListContainsProperties() { + return urlWatchList.stream().anyMatch(url -> url.toString().endsWith(PROPERTIES_FILE_EXTENSION)); + } + + private boolean hasMainURLAndNonEmptyFileList() { + return topURL != null && !fileWatchList.isEmpty(); + } + + private boolean fileWatchListContainsProperties() { + return fileWatchList.stream().anyMatch(file -> file.getName().endsWith(PROPERTIES_FILE_EXTENSION)); + } private void addAsFileToWatch(URL url) { @@ -63,29 +113,127 @@ private void addAsFileToWatch(URL url) { } } + + private boolean isHTTP_Or_HTTPS(URL url) { + String protocolStr = url.getProtocol(); + return isHTTP_Or_HTTPS(protocolStr); + } + + private boolean isHTTP_Or_HTTPS(String protocolStr) { + return (protocolStr.equals(HTTP_PROTOCOL_STR) || protocolStr.equals(HTTPS_PROTOCOL_STR)); + } + + private void addAsHTTP_or_HTTPS_URLToWatch(URL url) { + if(isHTTP_Or_HTTPS(url)) { + urlWatchList.add(url); + lastHashList.add(BUF_ZERO); + } + } + + /** + * Add the url but only if it is file:// or http(s):// + * @param url should be a file or http(s) + */ public void addToWatchList(URL url) { - addAsFileToWatch(url); + // assume that the caller has checked that the protocol is one of {file, https, http}. + String protocolStr = url.getProtocol(); + if (protocolStr.equals(FILE_PROTOCOL_STR)) { + addAsFileToWatch(url); + } else if (isHTTP_Or_HTTPS(protocolStr)) { + addAsHTTP_or_HTTPS_URLToWatch(url); + } } - public URL getMainURL() { - return mainURL; + public URL getTopURL() { + return topURL; } public List getCopyOfFileWatchList() { return new ArrayList(fileWatchList); } - public boolean changeDetected() { + + public boolean emptyWatchLists() { + if(fileWatchList != null && !fileWatchList.isEmpty()) { + return false; + } + + if(urlWatchList != null && !urlWatchList.isEmpty()) { + return false; + } + return true; + } + + + /** + * + * @deprecated replaced by {@link #changeDetectedInFile()} + */ + public File changeDetected() { + return changeDetectedInFile(); + } + + /** + * Has a changed been detected in one of the files being watched? + * @return + */ + public File changeDetectedInFile() { int len = fileWatchList.size(); + for (int i = 0; i < len; i++) { long lastModified = lastModifiedList.get(i); File file = fileWatchList.get(i); - if (lastModified != file.lastModified()) { - return true; + long actualModificationDate = file.lastModified(); + + if (lastModified != actualModificationDate) { + // update modification date in case this instance is reused + lastModifiedList.set(i, actualModificationDate); + return file; + } + } + return null; + } + + public URL changeDetectedInURL() { + int len = urlWatchList.size(); + + for (int i = 0; i < len; i++) { + byte[] lastHash = this.lastHashList.get(i); + URL url = urlWatchList.get(i); + + HttpUtil httpGetUtil = new HttpUtil(HttpUtil.RequestMethod.GET, url); + HttpURLConnection getConnection = httpGetUtil.connectTextTxt(); + String response = httpGetUtil.readResponse(getConnection); + + byte[] hash = computeHash(response); + if (lastHash == BUF_ZERO) { + this.lastHashList.set(i, hash); + return null; + } + + if (Arrays.equals(lastHash, hash)) { + return null; + } else { + this.lastHashList.set(i, hash); + return url; } } - return false; - // return (lastModified != fileToScan.lastModified() && lastModified != SENTINEL); + return null; + } + + private byte[] computeHash(String response) { + if (response == null || response.trim().length() == 0) { + return null; + } + + try { + MD5Util md5Util = new MD5Util(); + byte[] hashBytes = md5Util.md5Hash(response); + return hashBytes; + } catch (NoSuchAlgorithmException e) { + addError("missing MD5 algorithm", e); + return null; + } } @SuppressWarnings("deprecation") @@ -99,4 +247,58 @@ File convertToFile(URL url) { } } + /** + * Returns true if there are watchable files, false otherwise. + * @return true if there are watchable files, false otherwise. + * @since 1.5.8 + */ + public boolean hasAtLeastOneWatchableFile() { + return !fileWatchList.isEmpty(); + } + + /** + * Is protocol for the given URL a protocol that we can watch for. + * + * @param url + * @return true if watchable, false otherwise + * @since 1.5.9 + */ + static public boolean isWatchableProtocol(URL url) { + if (url == null) { + return false; + } + String protocolStr = url.getProtocol(); + return isWatchableProtocol(protocolStr); + } + + /** + * Is the given protocol a protocol that we can watch for. + * + * @param protocolStr + * @return true if watchable, false otherwise + * @since 1.5.9 + */ + static public boolean isWatchableProtocol(String protocolStr) { + return Arrays.stream(WATCHABLE_PROTOCOLS).anyMatch(protocol -> protocol.equalsIgnoreCase(protocolStr)); + } + + /** + * Returns the urlWatchList field as a String + * @return the urlWatchList field as a String + * @since 1.5.19 + */ + public String getUrlWatchListAsStr() { + String urlWatchListStr = urlWatchList.stream().map(URL::toString).collect(Collectors.joining(", ")); + return urlWatchListStr; + } + + /** + * Returns the fileWatchList field as a String + * @return the fileWatchList field as a String + * @since 1.5.19 + */ + public String getFileWatchListAsStr() { + return fileWatchList.stream().map(File::getPath).collect(Collectors.joining(", ")); + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConsoleTarget.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConsoleTarget.java index 045f429aad..2d75e44d99 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConsoleTarget.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ConsoleTarget.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,7 +18,9 @@ /** * The set of console output targets. - + * + *

See LOGBACK-136

+ * * @author Ruediger Dohna * @author Ceki Gülcü * @author Tom SH Liu diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultClass.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultClass.java index 552462b96d..cf6984e632 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultClass.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultClass.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java index aa2f4716bf..bcd30ce33b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistry.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -25,10 +25,28 @@ public class DefaultNestedComponentRegistry { Map> defaultComponentMap = new HashMap>(); + Map> tagToClassMap = new HashMap<>(); + + + public void duplicate(DefaultNestedComponentRegistry other) { + this.defaultComponentMap.putAll(other.defaultComponentMap); + this.tagToClassMap.putAll(other.tagToClassMap); + } public void add(Class hostClass, String propertyName, Class componentClass) { HostClassAndPropertyDouble hpDouble = new HostClassAndPropertyDouble(hostClass, propertyName.toLowerCase()); defaultComponentMap.put(hpDouble, componentClass); + tagToClassMap.put(propertyName, componentClass); + } + + + + public String findDefaultComponentTypeByTag(String tagName) { + Class defaultClass = tagToClassMap.get(tagName); + if (defaultClass == null) + return null; + else + return defaultClass.getCanonicalName(); } public Class findDefaultComponentType(Class hostClass, String propertyName) { @@ -48,4 +66,6 @@ private Class oneShotFind(Class hostClass, String propertyName) { return defaultComponentMap.get(hpDouble); } + + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementPath.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementPath.java index 6644c17f3c..ca00d2f8d9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementPath.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementPath.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,13 +17,13 @@ import java.util.List; /** - * A element path characterizes a traversal path in an XML document. + * An element path characterizes a traversal path in an XML document. * * @author Ceki Gulcu * @since 1.1.0 */ public class ElementPath { - // contains String instances + ArrayList partList = new ArrayList(); public ElementPath() { diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementSelector.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementSelector.java index b80c58f593..c0a5ff681b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementSelector.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/ElementSelector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,10 +16,13 @@ import java.util.List; /** - * ElementSelector extends {@link ElementPath} with matching operations such as {@link #fullPathMatch(ElementPath)}, - * {@link #getPrefixMatchLength(ElementPath)} and {@link #getTailMatchLength(ElementPath)}. + * ElementSelector extends {@link ElementPath} with matching operations such as + * {@link #fullPathMatch(ElementPath)}, + * {@link #getPrefixMatchLength(ElementPath)} and + * {@link #getTailMatchLength(ElementPath)}. * - *

Parts of the path may contain '*' for wildcard matching. + *

+ * Parts of the path may contain '*' for wildcard matching. * * @author Ceki Gülcü * @since 1.1.0 @@ -60,8 +63,8 @@ public boolean fullPathMatch(ElementPath path) { } /** - * Returns the number of "tail" components that this pattern has in common - * with the pattern p passed as parameter. By "tail" components we mean the + * Returns the number of "tail" components that this pattern has in common with + * the pattern p passed as parameter. By "tail" components we mean the * components at the end of the pattern. */ public int getTailMatchLength(ElementPath p) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/EventPlayer.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/EventPlayer.java index 4167513781..ea2b5e2515 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/EventPlayer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/EventPlayer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,7 @@ */ package ch.qos.logback.core.joran.spi; + import java.util.ArrayList; import java.util.List; @@ -23,49 +24,47 @@ public class EventPlayer { - final Interpreter interpreter; - List eventList; + final SaxEventInterpreter interpreter; + final List saxEvents; int currentIndex; - public EventPlayer(Interpreter interpreter) { + public EventPlayer(SaxEventInterpreter interpreter, List saxEvents) { this.interpreter = interpreter; + this.saxEvents = saxEvents; } /** * Return a copy of the current event list in the player. + * * @return * @since 0.9.20 */ public List getCopyOfPlayerEventList() { - return new ArrayList(eventList); + return new ArrayList(saxEvents); } - public void play(List aSaxEventList) { - eventList = aSaxEventList; - SaxEvent se; - for (currentIndex = 0; currentIndex < eventList.size(); currentIndex++) { - se = eventList.get(currentIndex); + public void play() { + + for (currentIndex = 0; currentIndex < saxEvents.size(); currentIndex++) { + SaxEvent se = saxEvents.get(currentIndex); if (se instanceof StartEvent) { interpreter.startElement((StartEvent) se); - // invoke fireInPlay after startElement processing - interpreter.getInterpretationContext().fireInPlay(se); + continue; } if (se instanceof BodyEvent) { - // invoke fireInPlay before characters processing - interpreter.getInterpretationContext().fireInPlay(se); interpreter.characters((BodyEvent) se); + continue; } if (se instanceof EndEvent) { - // invoke fireInPlay before endElement processing - interpreter.getInterpretationContext().fireInPlay(se); interpreter.endElement((EndEvent) se); + continue; } } } public void addEventsDynamically(List eventList, int offset) { - this.eventList.addAll(currentIndex + offset, eventList); + this.saxEvents.addAll(currentIndex + offset, eventList); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java index f22ae0995b..7ddc03b361 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HostClassAndPropertyDouble.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HttpUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HttpUtil.java new file mode 100644 index 0000000000..cacafcdc2f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/HttpUtil.java @@ -0,0 +1,137 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.spi; + +import java.io.*; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.HashMap; +import java.util.Map; + +public class HttpUtil { + + URL url; + + public enum RequestMethod { + GET, + POST; + } + + + HttpURLConnection conn; + RequestMethod requestMethod; + + Map headerMap = new HashMap<>(2); + + public HttpUtil(RequestMethod requestMethod, URL url) { + this.requestMethod = requestMethod; + this.url =url; + } + + public HttpUtil(RequestMethod requestMethod, String urlStr) throws MalformedURLException { + this(requestMethod, new URL(urlStr)); + } + + Map getHeaderMap() { + return headerMap; + } + + public HttpURLConnection connectTextTxt() { + return connectType( "text/txt;charset=utf-8"); + } + + public HttpURLConnection connectTextPlain() { + return connectType("text/plain; charset=utf-8"); + } + + public HttpURLConnection connectType(String acceptType) { + try { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod(requestMethod.name()); + headerMap.forEach((h, v) -> conn.setRequestProperty(h, v)); + conn.setRequestProperty("Accept", acceptType); + + if(requestMethod == RequestMethod.POST) { + conn.setDoOutput(true); + } + + conn.connect(); + return conn; + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public String readResponse(HttpURLConnection conn) { + if(conn == null) + return null; + + try { + int responseCode = conn.getResponseCode(); + if(responseCode == HttpURLConnection.HTTP_OK) { + return innerReadResponse(conn); + } else { + System.out.println("status="+ responseCode+ " Failed response"); + return null; + } + } catch (IOException e) { + System.out.println("url="+ url.toString()+" failed to read status"); + e.printStackTrace(); + return null; + } + } + + private String innerReadResponse(HttpURLConnection conn) { + try (InputStream is = conn.getInputStream()) { + BufferedReader in = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); + String inputLine; + StringBuffer buffer = new StringBuffer(); + + while ((inputLine = in.readLine()) != null) { + buffer.append(inputLine); + } + return buffer.toString(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + public boolean post(HttpURLConnection conn, String str) { + if (conn == null) { + System.out.println("null HttpURLConnection object"); + return false; + } + + if(requestMethod != RequestMethod.POST) { + System.out.println("Incorrect request method "+requestMethod.name()); + return false; + } + + try (OutputStream os = conn.getOutputStream()) { + OutputStreamWriter wr = new OutputStreamWriter(os); + wr.write(str); + wr.flush(); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java deleted file mode 100644 index 72180851ce..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/InterpretationContext.java +++ /dev/null @@ -1,181 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.spi; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Properties; -import java.util.Stack; - -import org.xml.sax.Locator; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.InPlayListener; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.spi.PropertyContainer; -import ch.qos.logback.core.util.OptionHelper; - -/** - * - * An InterpretationContext contains the contextual state of a Joran parsing - * session. {@link Action} objects depend on this context to exchange and store - * information. - * - * @author Ceki Gülcü - */ -public class InterpretationContext extends ContextAwareBase implements PropertyContainer { - Stack objectStack; - Map objectMap; - Map propertiesMap; - - Interpreter joranInterpreter; - final List listenerList = new ArrayList(); - DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry(); - - public InterpretationContext(Context context, Interpreter joranInterpreter) { - this.context = context; - this.joranInterpreter = joranInterpreter; - objectStack = new Stack(); - objectMap = new HashMap(5); - propertiesMap = new HashMap(5); - } - - public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() { - return defaultNestedComponentRegistry; - } - - public Map getCopyOfPropertyMap() { - return new HashMap(propertiesMap); - } - - void setPropertiesMap(Map propertiesMap) { - this.propertiesMap = propertiesMap; - } - - String updateLocationInfo(String msg) { - Locator locator = joranInterpreter.getLocator(); - - if (locator != null) { - return msg + locator.getLineNumber() + ":" + locator.getColumnNumber(); - } else { - return msg; - } - } - - public Locator getLocator() { - return joranInterpreter.getLocator(); - } - - public Interpreter getJoranInterpreter() { - return joranInterpreter; - } - - public Stack getObjectStack() { - return objectStack; - } - - public boolean isEmpty() { - return objectStack.isEmpty(); - } - - public Object peekObject() { - return objectStack.peek(); - } - - public void pushObject(Object o) { - objectStack.push(o); - } - - public Object popObject() { - return objectStack.pop(); - } - - public Object getObject(int i) { - return objectStack.get(i); - } - - public Map getObjectMap() { - return objectMap; - } - - /** - * Add a property to the properties of this execution context. If the property - * exists already, it is overwritten. - */ - public void addSubstitutionProperty(String key, String value) { - if (key == null || value == null) { - return; - } - // values with leading or trailing spaces are bad. We remove them now. - value = value.trim(); - propertiesMap.put(key, value); - } - - public void addSubstitutionProperties(Properties props) { - if (props == null) { - return; - } - for (Object keyObject : props.keySet()) { - String key = (String) keyObject; - String val = props.getProperty(key); - addSubstitutionProperty(key, val); - } - } - - /** - * If a key is found in propertiesMap then return it. Otherwise, delegate to - * the context. - */ - public String getProperty(String key) { - String v = propertiesMap.get(key); - if (v != null) { - return v; - } else { - return context.getProperty(key); - } - } - - public String subst(String value) { - if (value == null) { - return null; - } - return OptionHelper.substVars(value, this, context); - } - - public boolean isListenerListEmpty() { - return listenerList.isEmpty(); - } - - public void addInPlayListener(InPlayListener ipl) { - if (listenerList.contains(ipl)) { - addWarn("InPlayListener " + ipl + " has been already registered"); - } else { - listenerList.add(ipl); - } - } - - public boolean removeInPlayListener(InPlayListener ipl) { - return listenerList.remove(ipl); - } - - void fireInPlay(SaxEvent event) { - for (InPlayListener ipl : listenerList) { - ipl.inPlay(event); - } - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/Interpreter.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/Interpreter.java deleted file mode 100644 index 1ba1437281..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/Interpreter.java +++ /dev/null @@ -1,349 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.spi; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Stack; -import java.util.Vector; - -import org.xml.sax.Attributes; -import org.xml.sax.Locator; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ImplicitAction; -import ch.qos.logback.core.joran.event.BodyEvent; -import ch.qos.logback.core.joran.event.EndEvent; -import ch.qos.logback.core.joran.event.StartEvent; -import ch.qos.logback.core.spi.ContextAwareImpl; - -/** - * {@code Interpreter} is Joran's main driving class. It extends SAX - * {@link org.xml.sax.helpers.DefaultHandler DefaultHandler} which invokes - * various {@link Action actions} according to predefined patterns. - * - *

- * Patterns are kept in a {@link RuleStore} which is programmed to store and - * then later produce the applicable actions for a given pattern. - * - *

- * The pattern corresponding to a top level <a> element is the string - * "a". - * - *

- * The pattern corresponding to an element <b> embedded within a top level - * <a> element is the string {@code "a/b"}. - * - *

- * The pattern corresponding to an <b> and any level of nesting is - * "*/b. Thus, the * character placed at the beginning of a pattern - * serves as a wildcard for the level of nesting. - * - * Conceptually, this is very similar to the API of commons-digester. Joran - * offers several small advantages. First and foremost, it offers support for - * implicit actions which result in a significant leap in flexibility. Second, - * in our opinion better error reporting capability. Third, it is self-reliant. - * It does not depend on other APIs, in particular commons-logging which is too - * unreliable. Last but not least, Joran is quite tiny and is expected to remain - * so. - * - * @author Ceki Gülcü - * - */ -public class Interpreter { - private static List EMPTY_LIST = new Vector(0); - - final private RuleStore ruleStore; - final private InterpretationContext interpretationContext; - final private ArrayList implicitActions; - final private CAI_WithLocatorSupport cai; - private ElementPath elementPath; - Locator locator; - EventPlayer eventPlayer; - - /** - * The actionListStack contains a list of actions that are executing - * for the given XML element. - * - * A list of actions is pushed by the {link #startElement} and popped by - * {@link #endElement}. - * - */ - Stack> actionListStack; - - /** - * If the skip nested is set, then we skip all its nested elements until it is - * set back to null at when the element's end is reached. - */ - ElementPath skip = null; - - public Interpreter(Context context, RuleStore rs, ElementPath initialElementPath) { - this.cai = new CAI_WithLocatorSupport(context, this); - ruleStore = rs; - interpretationContext = new InterpretationContext(context, this); - implicitActions = new ArrayList(3); - this.elementPath = initialElementPath; - actionListStack = new Stack>(); - eventPlayer = new EventPlayer(this); - } - - public EventPlayer getEventPlayer() { - return eventPlayer; - } - - public void setInterpretationContextPropertiesMap(Map propertiesMap) { - interpretationContext.setPropertiesMap(propertiesMap); - } - - /** - * @deprecated replaced by {@link #getInterpretationContext()} - */ - public InterpretationContext getExecutionContext() { - return getInterpretationContext(); - } - - public InterpretationContext getInterpretationContext() { - return interpretationContext; - } - - public void startDocument() { - } - - public void startElement(StartEvent se) { - setDocumentLocator(se.getLocator()); - startElement(se.namespaceURI, se.localName, se.qName, se.attributes); - } - - private void startElement(String namespaceURI, String localName, String qName, Attributes atts) { - - String tagName = getTagName(localName, qName); - elementPath.push(tagName); - - if (skip != null) { - // every startElement pushes an action list - pushEmptyActionList(); - return; - } - - List applicableActionList = getApplicableActionList(elementPath, atts); - if (applicableActionList != null) { - actionListStack.add(applicableActionList); - callBeginAction(applicableActionList, tagName, atts); - } else { - // every startElement pushes an action list - pushEmptyActionList(); - String errMsg = "no applicable action for [" + tagName + "], current ElementPath is [" + elementPath + "]"; - cai.addError(errMsg); - } - } - - /** - * This method is used to - */ - private void pushEmptyActionList() { - actionListStack.add(EMPTY_LIST); - } - - public void characters(BodyEvent be) { - - setDocumentLocator(be.locator); - - String body = be.getText(); - List applicableActionList = actionListStack.peek(); - - if (body != null) { - body = body.trim(); - if (body.length() > 0) { - // System.out.println("calling body method with ["+body+ "]"); - callBodyAction(applicableActionList, body); - } - } - } - - public void endElement(EndEvent endEvent) { - setDocumentLocator(endEvent.locator); - endElement(endEvent.namespaceURI, endEvent.localName, endEvent.qName); - } - - private void endElement(String namespaceURI, String localName, String qName) { - // given that an action list is always pushed for every startElement, we - // need - // to always pop for every endElement - List applicableActionList = (List) actionListStack.pop(); - - if (skip != null) { - if (skip.equals(elementPath)) { - skip = null; - } - } else if (applicableActionList != EMPTY_LIST) { - callEndAction(applicableActionList, getTagName(localName, qName)); - } - - // given that we always push, we must also pop the pattern - elementPath.pop(); - } - - public Locator getLocator() { - return locator; - } - - public void setDocumentLocator(Locator l) { - locator = l; - } - - String getTagName(String localName, String qName) { - String tagName = localName; - - if ((tagName == null) || (tagName.length() < 1)) { - tagName = qName; - } - - return tagName; - } - - public void addImplicitAction(ImplicitAction ia) { - implicitActions.add(ia); - } - - /** - * Check if any implicit actions are applicable. As soon as an applicable - * action is found, it is returned. Thus, the returned list will have at most - * one element. - */ - List lookupImplicitAction(ElementPath elementPath, Attributes attributes, InterpretationContext ec) { - int len = implicitActions.size(); - - for (int i = 0; i < len; i++) { - ImplicitAction ia = (ImplicitAction) implicitActions.get(i); - - if (ia.isApplicable(elementPath, attributes, ec)) { - List actionList = new ArrayList(1); - actionList.add(ia); - - return actionList; - } - } - - return null; - } - - /** - * Return the list of applicable patterns for this - */ - List getApplicableActionList(ElementPath elementPath, Attributes attributes) { - List applicableActionList = ruleStore.matchActions(elementPath); - - // logger.debug("set of applicable patterns: " + applicableActionList); - if (applicableActionList == null) { - applicableActionList = lookupImplicitAction(elementPath, attributes, interpretationContext); - } - - return applicableActionList; - } - - void callBeginAction(List applicableActionList, String tagName, Attributes atts) { - if (applicableActionList == null) { - return; - } - - Iterator i = applicableActionList.iterator(); - while (i.hasNext()) { - Action action = (Action) i.next(); - // now let us invoke the action. We catch and report any eventual - // exceptions - try { - action.begin(interpretationContext, tagName, atts); - } catch (ActionException e) { - skip = elementPath.duplicate(); - cai.addError("ActionException in Action for tag [" + tagName + "]", e); - } catch (RuntimeException e) { - skip = elementPath.duplicate(); - cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); - } - } - } - - private void callBodyAction(List applicableActionList, String body) { - if (applicableActionList == null) { - return; - } - Iterator i = applicableActionList.iterator(); - - while (i.hasNext()) { - Action action = i.next(); - try { - action.body(interpretationContext, body); - } catch (ActionException ae) { - cai.addError("Exception in end() methd for action [" + action + "]", ae); - } - } - } - - private void callEndAction(List applicableActionList, String tagName) { - if (applicableActionList == null) { - return; - } - - // logger.debug("About to call end actions on node: [" + localName + "]"); - Iterator i = applicableActionList.iterator(); - - while (i.hasNext()) { - Action action = i.next(); - // now let us invoke the end method of the action. We catch and report - // any eventual exceptions - try { - action.end(interpretationContext, tagName); - } catch (ActionException ae) { - // at this point endAction, there is no point in skipping children as - // they have been already processed - cai.addError("ActionException in Action for tag [" + tagName + "]", ae); - } catch (RuntimeException e) { - // no point in setting skip - cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); - } - } - } - - public RuleStore getRuleStore() { - return ruleStore; - } -} - -/** - * When {@link Interpreter} class is used as the origin of an - * {@link ContextAwareImpl} instance, then XML locator information is lost. This - * class preserves locator information (as a string). - * - * @author ceki - */ -class CAI_WithLocatorSupport extends ContextAwareImpl { - - CAI_WithLocatorSupport(Context context, Interpreter interpreter) { - super(context, interpreter); - } - - @Override - protected Object getOrigin() { - Interpreter i = (Interpreter) super.getOrigin(); - Locator locator = i.locator; - if (locator != null) { - return Interpreter.class.getName() + "@" + locator.getLineNumber() + ":" + locator.getColumnNumber(); - } else { - return Interpreter.class.getName() + "@NA:NA"; - } - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/JoranException.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/JoranException.java index f8b308eb73..e9622a289c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/JoranException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/JoranException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NewRuleProvider.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NewRuleProvider.java new file mode 100644 index 0000000000..5cdcca1433 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NewRuleProvider.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.spi; + +import ch.qos.logback.core.model.processor.DefaultProcessor; + +public interface NewRuleProvider { + + void addPathActionAssociations(RuleStore rs); + void addModelHandlerAssociations(DefaultProcessor defaultProcessor); + void addModelAnalyserAssociations(DefaultProcessor defaultProcessor); +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStart.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStart.java index 7e075bf2ce..901f476df7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStart.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStart.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStartUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStartUtil.java index 5bf622575e..6f9f9ef92c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStartUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/NoAutoStartUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,22 +13,98 @@ */ package ch.qos.logback.core.joran.spi; + +import java.lang.annotation.Annotation; +import java.util.HashSet; +import java.util.Set; +import ch.qos.logback.core.spi.LifeCycle; + public class NoAutoStartUtil { /** * Returns true if the class of the object 'o' passed as parameter is *not* * marked with the NoAutoStart annotation. Return true otherwise. - * + * * @param o * @return true for classes not marked with the NoAutoStart annotation */ - static public boolean notMarkedWithNoAutoStart(Object o) { + public static boolean notMarkedWithNoAutoStart(Object o) { if (o == null) { return false; } Class clazz = o.getClass(); - NoAutoStart a = clazz.getAnnotation(NoAutoStart.class); + NoAutoStart a = findAnnotation(clazz, NoAutoStart.class); return a == null; } + /** + * Find a single {@link Annotation} of {@code annotationType} on the + * supplied {@link Class}, traversing its interfaces, annotations, and + * superclasses if the annotation is not directly present on + * the given class itself. + *

This method explicitly handles class-level annotations which are not + * declared as {@link java.lang.annotation.Inherited inherited} as well + * as meta-annotations and annotations on interfaces. + *

The algorithm operates as follows: + *

    + *
  1. Search for the annotation on the given class and return it if found. + *
  2. Recursively search through all interfaces that the given class declares. + *
  3. Recursively search through the superclass hierarchy of the given class. + *
+ *

Note: in this context, the term recursively means that the search + * process continues by returning to step #1 with the current interface, + * annotation, or superclass as the class to look for annotations on. + * @param clazz the class to look for annotations on + * @param annotationType the type of annotation to look for + * @return the first matching annotation, or {@code null} if not found + */ + private static A findAnnotation(Class clazz, Class annotationType) { + return findAnnotation(clazz, annotationType, new HashSet<>()); + } + + /** + * Perform the search algorithm for {@link #findAnnotation(Class, Class)}, + * avoiding endless recursion by tracking which annotations have already + * been visited. + * @param clazz the class to look for annotations on + * @param annotationType the type of annotation to look for + * @param visited the set of annotations that have already been visited + * @return the first matching annotation, or {@code null} if not found + */ + @SuppressWarnings("unchecked") + private static A findAnnotation(Class clazz, Class annotationType, Set visited) { + + Annotation foundAnnotation = clazz.getAnnotation(annotationType); + if(foundAnnotation != null) { + return (A) foundAnnotation; + } + + + for (Class ifc : clazz.getInterfaces()) { + A annotation = findAnnotation(ifc, annotationType, visited); + if (annotation != null) { + return annotation; + } + } + + Class superclass = clazz.getSuperclass(); + if (superclass == null || Object.class == superclass) { + return null; + } + return findAnnotation(superclass, annotationType, visited); + } + + /** + * Is the object a {@link LifeCycle} and is it marked not marked with + * the NoAutoStart annotation. + * @param o + * @return + * @ since 1.5.2 + */ + static public boolean shouldBeStarted(Object o) { + if(o instanceof LifeCycle) { + return notMarkedWithNoAutoStart(o); + } else + return false; + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/RuleStore.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/RuleStore.java index 4c3ea2f523..f2c8aced65 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/RuleStore.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/RuleStore.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,19 @@ */ package ch.qos.logback.core.joran.spi; -import java.util.List; +import java.util.function.Supplier; import ch.qos.logback.core.joran.action.Action; /** * - * As its name indicates, a RuleStore contains 2-tuples consists of a ElementSelector - * and an Action. + * As its name indicates, a RuleStore contains 2-tuples consists of a + * ElementSelector and an Action. * - *

As a joran configurator goes through the elements in a document, it asks - * the rule store whether there are rules matching the current pattern by - * invoking the {@link #matchActions(ElementPath)} method. + *

+ * As a joran configurator goes through the elements in a document, it asks the + * rule store whether there are rules matching the current pattern by invoking + * the {@link #matchActions(ElementPath)} method. * * @author Ceki Gülcü * @@ -32,7 +33,7 @@ public interface RuleStore { /** - * Add a new rule, given by a pattern and a action class (String). + * Add a new rule, given by a pattern and an action class (String). * * @param elementSelector * @param actionClassStr @@ -44,9 +45,9 @@ public interface RuleStore { * Add a new rule, given by a pattern and an action instance. * * @param elementSelector - * @param action + * @param actionSupplier */ - void addRule(ElementSelector elementSelector, Action action); + void addRule(ElementSelector elementSelector, Supplier actionSupplier); /** * Return a list of actions matching a pattern. @@ -54,5 +55,10 @@ public interface RuleStore { * @param elementPath the path to match for * @return list of matching actions */ - List matchActions(ElementPath elementPath); -} + Supplier matchActions(ElementPath elementPath); + + void addTransparentPathPart(String pathPart); + + public void addPathPathMapping(String originalName, String modifiedName); + + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpretationContext.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpretationContext.java new file mode 100644 index 0000000000..cf283f90bc --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpretationContext.java @@ -0,0 +1,109 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.spi; + +import java.util.Map; +import java.util.Stack; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.PropertyContainer; +import ch.qos.logback.core.spi.ScanException; +import ch.qos.logback.core.util.OptionHelper; + +/** + * + * An InterpretationContext contains the contextual state of a Joran parsing + * session. {@link Action} objects depend on this context to exchange and store + * information. + * + * @author Ceki Gülcü + */ +public class SaxEventInterpretationContext extends ContextAwareBase implements PropertyContainer { + Stack modelStack; + + SaxEventInterpreter saxEventInterpreter; + + public SaxEventInterpretationContext(Context context, SaxEventInterpreter saxEventInterpreter) { + this.context = context; + this.saxEventInterpreter = saxEventInterpreter; + this.modelStack = new Stack<>(); + } + + public SaxEventInterpreter getSaxEventInterpreter() { + return saxEventInterpreter; + } + + /** + * Return the Model at the top of the model stack, may return null. + * + * @return + */ + public Model peekModel() { + if(modelStack.isEmpty()) { + return null; + } + return modelStack.peek(); + } + + public void pushModel(Model m) { + modelStack.push(m); + } + + public boolean isModelStackEmpty() { + return modelStack.isEmpty(); + } + + public Model popModel() { + return modelStack.pop(); + } + + public Stack getCopyOfModelStack() { + Stack copy = new Stack<>(); + copy.addAll(modelStack); + return copy; + } + + public void addSubstitutionProperty(String key, String value) { + throw new UnsupportedOperationException(); + } + + /** + * If a key is found in propertiesMap then return it. Otherwise, delegate to the + * context. + */ + public String getProperty(String key) { + return context.getProperty(key); + } + + public Map getCopyOfPropertyMap() { + return null; + } + + public String subst(String value) { + if (value == null) { + return null; + } + + try { + return OptionHelper.substVars(value, this, context); + } catch (ScanException | IllegalArgumentException e) { + addError("Problem while parsing [" + value + "]", e); + return value; + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpreter.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpreter.java new file mode 100644 index 0000000000..f71e6d9545 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SaxEventInterpreter.java @@ -0,0 +1,309 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.spi; + +import java.util.List; +import java.util.Stack; +import java.util.function.Supplier; + +import org.xml.sax.Attributes; +import org.xml.sax.Locator; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.NOPAction; +import ch.qos.logback.core.joran.event.BodyEvent; +import ch.qos.logback.core.joran.event.EndEvent; +import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.event.StartEvent; +import ch.qos.logback.core.spi.ContextAwareImpl; + +/** + * {@code SaxEventInterpreter} is Joran's driving class for handling "low-level" + * SAX events. It extends SAX {@link org.xml.sax.helpers.DefaultHandler + * DefaultHandler} which invokes various {@link Action actions} according to + * predefined patterns. + * + *

+ * Patterns are kept in a {@link RuleStore} which is programmed to store and + * then later produce the applicable actions for a given pattern. + * + *

+ * The pattern corresponding to a top level <a> element is the string "a". + * + *

+ * The pattern corresponding to an element <b> embedded within a top level + * <a> element is the string {@code "a/b"}. + * + *

+ * The pattern corresponding to an <b> and any level of nesting is + * "*/b. Thus, the * character placed at the beginning of a pattern + * serves as a wildcard for the level of nesting. + * + * Conceptually, this is very similar to the API of commons-digester. Joran + * offers several small advantages. First and foremost, it offers support for + * implicit actions which result in a significant leap in flexibility. Second, + * in our opinion better error reporting capability. Third, it is self-reliant. + * It does not depend on other APIs, in particular commons-logging which is too + * unreliable. Last but not least, Joran is quite tiny and is expected to remain + * so. + * + * @author Ceki Gülcü + * + */ +public class SaxEventInterpreter { + private static Action NOP_ACTION_SINGLETON = new NOPAction(); + + final private RuleStore ruleStore; + final private SaxEventInterpretationContext interpretationContext; + private Supplier implicitActionSupplier; + final private CAI_WithLocatorSupport cai; + private ElementPath elementPath; + Locator locator; + EventPlayer eventPlayer; + Context context; + + /** + * The actionStack contain the action that is executing + * for the given XML element. + * + * An action is pushed by the {link #startElement} and popped by + * {@link #endElement}. + * + */ + Stack actionStack; + + /** + * If the skip nested is set, then we skip all its nested elements until it is + * set back to null at when the element's end is reached. + */ + ElementPath skip = null; + + public SaxEventInterpreter(Context context, RuleStore rs, ElementPath initialElementPath, List saxEvents) { + this.context = context; + this.cai = new CAI_WithLocatorSupport(context, this); + ruleStore = rs; + interpretationContext = new SaxEventInterpretationContext(context, this); + this.elementPath = initialElementPath; + actionStack = new Stack<>(); + eventPlayer = new EventPlayer(this, saxEvents); + } + + public EventPlayer getEventPlayer() { + return eventPlayer; + } + + public ElementPath getCopyOfElementPath() { + return elementPath.duplicate(); + } + + public SaxEventInterpretationContext getSaxEventInterpretationContext() { + return interpretationContext; + } + + public void startDocument() { + } + + public void startElement(StartEvent se) { + setDocumentLocator(se.getLocator()); + startElement(se.namespaceURI, se.localName, se.qName, se.attributes); + } + + private void startElement(String namespaceURI, String localName, String qName, Attributes atts) { + + String tagName = getTagName(localName, qName); + + + elementPath.push(tagName); + + if (skip != null) { + // every startElement pushes an action list + pushEmptyActionOntoActionStack(); + return; + } + + Action applicableAction = getApplicableAction(elementPath, atts); + if (applicableAction != null) { + actionStack.add(applicableAction); + callBeginAction(applicableAction, tagName, atts); + } else { + // every startElement pushes an action list + pushEmptyActionOntoActionStack(); + String errMsg = "no applicable action for [" + tagName + "], current ElementPath is [" + elementPath + "]"; + cai.addError(errMsg); + } + } + + /** + * This method is used to + */ + private void pushEmptyActionOntoActionStack() { + actionStack.push(NOP_ACTION_SINGLETON); + } + + public void characters(BodyEvent be) { + + setDocumentLocator(be.locator); + + String body = be.getText(); + Action applicableAction = actionStack.peek(); + + if (body != null) { + body = body.trim(); + if (body.length() > 0) { + callBodyAction(applicableAction, body); + } + } + } + + public void endElement(EndEvent endEvent) { + setDocumentLocator(endEvent.locator); + endElement(endEvent.namespaceURI, endEvent.localName, endEvent.qName); + } + + private void endElement(String namespaceURI, String localName, String qName) { + // given that an action is always pushed for every startElement, we + // need to always pop for every endElement + Action applicableAction = actionStack.pop(); + + if (skip != null) { + if (skip.equals(elementPath)) { + skip = null; + } + } else if (applicableAction != NOP_ACTION_SINGLETON) { + callEndAction(applicableAction, getTagName(localName, qName)); + } + + // given that we always push, we must also pop the pattern + elementPath.pop(); + } + + public Locator getLocator() { + return locator; + } + + // having the locator set as parsing progresses is quite ugly + public void setDocumentLocator(Locator l) { + locator = l; + } + + String getTagName(String localName, String qName) { + String tagName = localName; + + if ((tagName == null) || (tagName.length() < 1)) { + tagName = qName; + } + + return tagName; + } + + public void setImplicitActionSupplier(Supplier actionSupplier) { + this.implicitActionSupplier = actionSupplier; + } + + /** + * Return the list of applicable patterns for this + */ + Action getApplicableAction(ElementPath elementPath, Attributes attributes) { + Supplier applicableActionSupplier = ruleStore.matchActions(elementPath); + + if (applicableActionSupplier != null) { + Action applicableAction = applicableActionSupplier.get(); + applicableAction.setContext(context); + return applicableAction; + } else { + Action implicitAction = implicitActionSupplier.get(); + implicitAction.setContext(context); + return implicitAction; + } + } + + void callBeginAction(Action applicableAction, String tagName, Attributes atts) { + if (applicableAction == null) { + return; + } + + // now let us invoke the action. We catch and report any eventual + // exceptions + try { + applicableAction.begin(interpretationContext, tagName, atts); + } catch (ActionException e) { + skip = elementPath.duplicate(); + cai.addError("ActionException in Action for tag [" + tagName + "]", e); + } catch (RuntimeException e) { + skip = elementPath.duplicate(); + cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); + } + + } + + private void callBodyAction(Action applicableAction, String body) { + if (applicableAction == null) { + return; + } + + try { + applicableAction.body(interpretationContext, body); + } catch (ActionException ae) { + cai.addError("Exception in body() method for action [" + applicableAction + "]", ae); + } + } + + private void callEndAction(Action applicableAction, String tagName) { + if (applicableAction == null) { + return; + } + + try { + applicableAction.end(interpretationContext, tagName); + } catch (ActionException ae) { + // at this point endAction, there is no point in skipping children as + // they have been already processed + cai.addError("ActionException in Action for tag [" + tagName + "]", ae); + } catch (RuntimeException e) { + // no point in setting skip + cai.addError("RuntimeException in Action for tag [" + tagName + "]", e); + } + } + + public RuleStore getRuleStore() { + return ruleStore; + } +} + +/** + * When {@link SaxEventInterpreter} class is used as the origin of an + * {@link ContextAwareImpl} instance, then XML locator information is lost. This + * class preserves locator information (as a string). + * + * @author ceki + */ +class CAI_WithLocatorSupport extends ContextAwareImpl { + + CAI_WithLocatorSupport(Context context, SaxEventInterpreter interpreter) { + super(context, interpreter); + } + + @Override + protected Object getOrigin() { + SaxEventInterpreter i = (SaxEventInterpreter) super.getOrigin(); + Locator locator = i.locator; + if (locator != null) { + return SaxEventInterpreter.class.getName() + "@" + locator.getLineNumber() + ":" + + locator.getColumnNumber(); + } else { + return SaxEventInterpreter.class.getName() + "@NA:NA"; + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SimpleRuleStore.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SimpleRuleStore.java index 6c840a4487..37e3648229 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SimpleRuleStore.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/SimpleRuleStore.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,6 +16,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.Context; import ch.qos.logback.core.joran.action.Action; @@ -34,30 +36,58 @@ public class SimpleRuleStore extends ContextAwareBase implements RuleStore { static String KLEENE_STAR = "*"; // key: Pattern instance, value: ArrayList containing actions - HashMap> rules = new HashMap>(); + HashMap> rules = new HashMap<>(); - // public SimpleRuleStore() { - // } + List transparentPathParts = new ArrayList<>(2); + Map pathPartsMapForRenaming = new HashMap<>(2); public SimpleRuleStore(Context context) { setContext(context); } + + public void addTransparentPathPart(String pathPart) { + if (pathPart == null) + throw new IllegalArgumentException("pathPart cannot be null"); + + pathPart = pathPart.trim(); + + if (pathPart.isEmpty()) + throw new IllegalArgumentException("pathPart cannot be empty or to consist of only spaces"); + + if (pathPart.contains("/")) + throw new IllegalArgumentException("pathPart cannot contain '/', i.e. the forward slash character"); + + transparentPathParts.add(pathPart); + + } + /** - * Add a new rule, i.e. a pattern, action pair to the rule store.

Note - * that the added action's LoggerRepository will be set in the process. + * Rename path parts. + * + * @param originalName the name before renaming + * @param modifiedName the after renaming + * @since 1.5.5 */ - public void addRule(ElementSelector elementSelector, Action action) { - action.setContext(context); + @Override + public void addPathPathMapping(String originalName, String modifiedName) { + pathPartsMapForRenaming.put(originalName, modifiedName); + } - List a4p = rules.get(elementSelector); + /** + * Add a new rule, i.e. a pattern, action pair to the rule store. + *

+ * Note that the added action's LoggerRepository will be set in the process. + */ + public void addRule(ElementSelector elementSelector, Supplier actionSupplier) { - if (a4p == null) { - a4p = new ArrayList(); - rules.put(elementSelector, a4p); - } + Supplier existing = rules.get(elementSelector); - a4p.add(action); + if (existing == null) { + rules.put(elementSelector, actionSupplier); + } else { + throw new IllegalStateException(elementSelector.toString() + " already has an associated action supplier"); + } } public void addRule(ElementSelector elementSelector, String actionClassName) { @@ -69,34 +99,89 @@ public void addRule(ElementSelector elementSelector, String actionClassName) { addError("Could not instantiate class [" + actionClassName + "]", e); } if (action != null) { - addRule(elementSelector, action); + // addRule(elementSelector, action); } } - // exact match has highest priority + // exact match has the highest priority // if no exact match, check for suffix (tail) match, i.e matches // of type */x/y. Suffix match for */x/y has higher priority than match for // */x // if no suffix match, check for prefix match, i.e. matches for x/* // match for x/y/* has higher priority than matches for x/* - public List matchActions(ElementPath elementPath) { - List actionList; - - if ((actionList = fullPathMatch(elementPath)) != null) { - return actionList; - } else if ((actionList = suffixMatch(elementPath)) != null) { - return actionList; - } else if ((actionList = prefixMatch(elementPath)) != null) { - return actionList; - } else if ((actionList = middleMatch(elementPath)) != null) { - return actionList; + public Supplier matchActions(ElementPath elementPath) { + + Supplier actionSupplier = internalMatchAction(elementPath); + if(actionSupplier != null) { + return actionSupplier; + } + + return matchActionsWithoutTransparentPartsAndRenamedParts(elementPath); + } + + private Supplier matchActionsWithoutTransparentPartsAndRenamedParts(ElementPath elementPath) { + ElementPath cleanedElementPath = removeTransparentPathParts(elementPath); + ElementPath renamePathParts = renamePathParts(cleanedElementPath); + + return internalMatchAction(renamePathParts); + } + +// private Supplier matchActionsWithoutTransparentParts(ElementPath elementPath) { +// ElementPath cleanedElementPath = removeTransparentPathParts(elementPath); +// return internalMatchAction(cleanedElementPath); +// } +// +// private Supplier matchActionsWithRenamedParts(ElementPath elementPath) { +// ElementPath renamedElementPath = renamePathParts(elementPath); +// return internalMatchAction(renamedElementPath); +// } + + private Supplier internalMatchAction(ElementPath elementPath) { + Supplier actionSupplier; + + if ((actionSupplier = fullPathMatch(elementPath)) != null) { + return actionSupplier; + } else if ((actionSupplier = suffixMatch(elementPath)) != null) { + return actionSupplier; + } else if ((actionSupplier = prefixMatch(elementPath)) != null) { + return actionSupplier; + } else if ((actionSupplier = middleMatch(elementPath)) != null) { + return actionSupplier; } else { return null; } } - List fullPathMatch(ElementPath elementPath) { + ElementPath removeTransparentPathParts(ElementPath originalElementPath) { + + List preservedElementList = new ArrayList<>(originalElementPath.partList.size()); + + for (String part : originalElementPath.partList) { + boolean shouldKeep = transparentPathParts.stream().noneMatch(p -> p.equalsIgnoreCase(part)); + if (shouldKeep) + preservedElementList.add(part); + } + + return new ElementPath(preservedElementList); + + } + + + ElementPath renamePathParts(ElementPath originalElementPath) { + + List result = new ArrayList<>(originalElementPath.partList.size()); + + for (String part : originalElementPath.partList) { + String modifiedName = pathPartsMapForRenaming.getOrDefault(part, part); + result.add(modifiedName); + } + + return new ElementPath(result); + } + + + Supplier fullPathMatch(ElementPath elementPath) { for (ElementSelector selector : rules.keySet()) { if (selector.fullPathMatch(elementPath)) return rules.get(selector); @@ -105,7 +190,7 @@ List fullPathMatch(ElementPath elementPath) { } // Suffix matches are matches of type */x/y - List suffixMatch(ElementPath elementPath) { + Supplier suffixMatch(ElementPath elementPath) { int max = 0; ElementSelector longestMatchingElementSelector = null; @@ -130,7 +215,7 @@ private boolean isSuffixPattern(ElementSelector p) { return (p.size() > 1) && p.get(0).equals(KLEENE_STAR); } - List prefixMatch(ElementPath elementPath) { + Supplier prefixMatch(ElementPath elementPath) { int max = 0; ElementSelector longestMatchingElementSelector = null; @@ -157,7 +242,7 @@ private boolean isKleeneStar(String last) { return KLEENE_STAR.equals(last); } - List middleMatch(ElementPath path) { + Supplier middleMatch(ElementPath path) { int max = 0; ElementSelector longestMatchingElementSelector = null; diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/XMLUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/XMLUtil.java index 07308e31a0..3c60552f30 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/spi/XMLUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/spi/XMLUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/AggregationAssessor.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/AggregationAssessor.java new file mode 100644 index 0000000000..b34729bca4 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/AggregationAssessor.java @@ -0,0 +1,215 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.util; + +import ch.qos.logback.core.joran.spi.DefaultClass; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.util.beans.BeanDescription; +import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.joran.util.beans.BeanUtil; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.AggregationType; +import ch.qos.logback.core.util.StringUtil; + +import java.lang.annotation.Annotation; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * + * Various utility methods for computing the {@link AggregationType} of a given property or + * the class name of a property given implicit rules. + * + *

This class was extracted from {@link PropertySetter}.

+ * + * @since 1.5.1 + */ +public class AggregationAssessor extends ContextAwareBase { + + protected final Class objClass; + protected final BeanDescription beanDescription; + + public AggregationAssessor(BeanDescriptionCache beanDescriptionCache, Class objClass) { + this.objClass = objClass; + this.beanDescription = beanDescriptionCache.getBeanDescription(objClass); + } + + /** + * Given a property name, this method computes/assesses {@link AggregationType} + * for the property for the class passed to the constructor. + * + * @param name + * @return the computed {@link AggregationType} + */ + public AggregationType computeAggregationType(String name) { + String cName = StringUtil.capitalizeFirstLetter(name); + + Method addMethod = findAdderMethod(cName); + + if (addMethod != null) { + AggregationType type = computeRawAggregationType(addMethod); + switch (type) { + case NOT_FOUND: + return AggregationType.NOT_FOUND; + case AS_BASIC_PROPERTY: + return AggregationType.AS_BASIC_PROPERTY_COLLECTION; + + case AS_COMPLEX_PROPERTY: + return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION; + + // computeRawAggregationType cannot return these values + case AS_BASIC_PROPERTY_COLLECTION: + case AS_COMPLEX_PROPERTY_COLLECTION: + addError("Unexpected AggregationType " + type); + } + } + + Method setter = findSetterMethod(name); + if (setter != null) { + return computeRawAggregationType(setter); + } else { + // we have failed + return AggregationType.NOT_FOUND; + } + } + + +// String capitalizeFirstLetter(String name) { +// return StringUtil.capitalizeFirstLetter(name); +// } + + public Method findAdderMethod(String name) { + String propertyName = BeanUtil.toLowerCamelCase(name); + return beanDescription.getAdder(propertyName); + } + + public Method findSetterMethod(String name) { + String propertyName = BeanUtil.toLowerCamelCase(name); + return beanDescription.getSetter(propertyName); + } + + private AggregationType computeRawAggregationType(Method method) { + Class parameterClass = getParameterClassForMethod(method); + if (parameterClass == null) { + return AggregationType.NOT_FOUND; + } + if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) { + return AggregationType.AS_BASIC_PROPERTY; + } else { + return AggregationType.AS_COMPLEX_PROPERTY; + } + } + + private Class getParameterClassForMethod(Method method) { + if (method == null) { + return null; + } + Class[] classArray = method.getParameterTypes(); + if (classArray.length != 1) { + return null; + } else { + return classArray[0]; + } + } + + public Class getClassNameViaImplicitRules(String name, AggregationType aggregationType, + DefaultNestedComponentRegistry registry) { + + Class registryResult = registry.findDefaultComponentType(objClass, name); + if (registryResult != null) { + return registryResult; + } + // find the relevant method for the given property name and aggregationType + Method relevantMethod = getRelevantMethod(name, aggregationType); + if (relevantMethod == null) { + return null; + } + Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod); + if (byAnnotation != null) { + return byAnnotation; + } + return getByConcreteType(name, relevantMethod); + } + + T getAnnotation(String name, Class annonationClass, Method relevantMethod) { + + if (relevantMethod != null) { + return relevantMethod.getAnnotation(annonationClass); + } else { + return null; + } + } + + Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) { + DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod); + if (defaultClassAnnon != null) { + return defaultClassAnnon.value(); + } + return null; + } + Method getRelevantMethod(String name, AggregationType aggregationType) { + Method relevantMethod; + if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) { + relevantMethod = findAdderMethod(name); + } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) { + relevantMethod = findSetterMethod(name); + } else { + throw new IllegalStateException(aggregationType + " not allowed here"); + } + return relevantMethod; + } + + Class getByConcreteType(String name, Method relevantMethod) { + + Class paramType = getParameterClassForMethod(relevantMethod); + if (paramType == null) { + return null; + } + + boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType); + if (isUnequivocallyInstantiable) { + return paramType; + } else { + return null; + } + } + + /** + * Can the given clazz instantiable with certainty? + * + * @param clazz The class to test for instantiability + * @return true if clazz can be instantiated, and false otherwise. + */ + private boolean isUnequivocallyInstantiable(Class clazz) { + if (clazz.isInterface()) { + return false; + } + // checking for constructors would be more elegant, but in + // classes without any declared constructors, Class.getConstructor() + // returns null. + Object o; + try { + o = clazz.getDeclaredConstructor().newInstance(); + if (o != null) { + return true; + } else { + return false; + } + } catch (InstantiationException | IllegalAccessException | InvocationTargetException + | NoSuchMethodException e) { + return false; + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/ConfigurationWatchListUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/ConfigurationWatchListUtil.java index 6718bb25f2..80c5b56fdb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/ConfigurationWatchListUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/ConfigurationWatchListUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,6 +16,7 @@ import ch.qos.logback.core.Context; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.joran.spi.ConfigurationWatchList; +import ch.qos.logback.core.status.ErrorStatus; import ch.qos.logback.core.status.InfoStatus; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.StatusManager; @@ -24,30 +25,55 @@ import java.net.URL; /** + * A thin layer on top of {@link ConfigurationWatchList}. + * * @author Ceki Gülcü */ public class ConfigurationWatchListUtil { - final static ConfigurationWatchListUtil origin = new ConfigurationWatchListUtil(); + final static ConfigurationWatchListUtil ORIGIN = new ConfigurationWatchListUtil(); private ConfigurationWatchListUtil() { } - public static void registerConfigurationWatchList(Context context, ConfigurationWatchList cwl) { context.putObject(CoreConstants.CONFIGURATION_WATCH_LIST, cwl); } + + /** + * Sets the main configuration watch URL in the given context's configuration watch list. + * If the provided URL is null, the method exits without making any changes. + * If the configuration watch list is not initialized, an error is added to the context's status. + * Otherwise, the configuration watch list is cleared and the given URL is set as the main URL. + * + * @param context the context in which the configuration watch list is managed + * @param url the main configuration watch URL to be set; if null, no action is taken + */ public static void setMainWatchURL(Context context, URL url) { + if(url == null) { + return; + } ConfigurationWatchList cwl = getConfigurationWatchList(context); if (cwl == null) { - cwl = new ConfigurationWatchList(); - cwl.setContext(context); - context.putObject(CoreConstants.CONFIGURATION_WATCH_LIST, cwl); + addError(context, "ConfigurationWatchList should have been initialized at this stage."); + return; } else { cwl.clear(); } - //setConfigurationWatchListResetFlag(context, true); - cwl.setMainURL(url); + cwl.setTopURL(url); + } + + /** + * Returns true if there are watchable files, false otherwise. + * @return true if there are watchable files, false otherwise. + * @since 1.5.8 + */ + public static boolean watchPredicateFulfilled(Context context) { + ConfigurationWatchList cwl = getConfigurationWatchList(context); + if (cwl == null) { + return false; + } + return cwl.watchPredicateFulfilled(); } public static URL getMainWatchURL(Context context) { @@ -55,32 +81,41 @@ public static URL getMainWatchURL(Context context) { if (cwl == null) { return null; } else { - return cwl.getMainURL(); + return cwl.getTopURL(); } } public static void addToWatchList(Context context, URL url) { + addToWatchList(context, url, false); + } + + public static void addToWatchList(Context context, URL url, boolean createCWL) { ConfigurationWatchList cwl = getConfigurationWatchList(context); - if (cwl == null) { - addWarn(context, "Null ConfigurationWatchList. Cannot add " + url); - } else { - addInfo(context, "Adding [" + url + "] to configuration watch list."); + if(cwl == null) { + if(createCWL && ConfigurationWatchList.isWatchableProtocol(url)) { + cwl = registerNewConfigurationWatchListWithContext(context); + } else { + addInfo(context, "ConfigurationWatchList not initialized due to absence of scan directive. Will not add " + url); + return; + } + } + + String protocol = url.getProtocol(); + if(cwl.isWatchableProtocol(protocol)) { + addInfo(context, "Will add [" + url + "] to configuration watch list."); cwl.addToWatchList(url); + } else { + addInfo(context, "Will not add configuration file ["+url + "] to watch list, because '"+protocol+"' protocol is not watchable."); + addInfo(context, "Only the protocols 'file', 'http' and 'https' are watchable."); } } -// public static boolean wasConfigurationWatchListReset(Context context) { -// Object o = context.getObject(CoreConstants.CONFIGURATION_WATCH_LIST_RESET); -// if (o == null) -// return false; -// else { -// return ((Boolean) o).booleanValue(); -// } -// } - -// public static void setConfigurationWatchListResetFlag(Context context, boolean val) { -// context.putObject(CoreConstants.CONFIGURATION_WATCH_LIST_RESET, new Boolean(val)); -// } + public static ConfigurationWatchList registerNewConfigurationWatchListWithContext(Context context) { + ConfigurationWatchList cwl = new ConfigurationWatchList(); + cwl.setContext(context); + context.putObject(CoreConstants.CONFIGURATION_WATCH_LIST, cwl); + return cwl; + } public static ConfigurationWatchList getConfigurationWatchList(Context context) { return (ConfigurationWatchList) context.getObject(CoreConstants.CONFIGURATION_WATCH_LIST); @@ -98,10 +133,13 @@ static void addStatus(Context context, Status s) { } static void addInfo(Context context, String msg) { - addStatus(context, new InfoStatus(msg, origin)); + addStatus(context, new InfoStatus(msg, ORIGIN)); } static void addWarn(Context context, String msg) { - addStatus(context, new WarnStatus(msg, origin)); + addStatus(context, new WarnStatus(msg, ORIGIN)); + } + static void addError(Context context, String msg) { + addStatus(context, new ErrorStatus(msg, ORIGIN)); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/ParentTag_Tag_Class_Tuple.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/ParentTag_Tag_Class_Tuple.java new file mode 100644 index 0000000000..bf6cc99d99 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/ParentTag_Tag_Class_Tuple.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.util; + + +/** + * + * Each tuple represents the association of a parentTag, tag, and a className + * which corresponds to default class name rules. + * + * @author Ceki Gülcü + * @since 1.3.0-alpha15 + * + */ +public class ParentTag_Tag_Class_Tuple { + public final String parentTag; + public final String tag; + public final String className; + + public ParentTag_Tag_Class_Tuple(String parentTag, String tag, String className) { + super(); + this.parentTag = parentTag; + this.tag = tag; + this.className = className; + } + + @Override + public String toString() { + return "ParentTag_Tag_Class_Tuple [parentTag=" + parentTag + ", tag=" + tag + ", className=" + className + "]"; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/PropertySetter.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/PropertySetter.java index b9bc3188a0..566ef42e51 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/PropertySetter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/PropertySetter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,24 +14,22 @@ // Contributors: Georg Lundesgaard package ch.qos.logback.core.joran.util; -import java.lang.annotation.Annotation; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -import ch.qos.logback.core.joran.spi.DefaultClass; +import ch.qos.logback.core.Context; import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; import ch.qos.logback.core.joran.util.beans.BeanDescription; import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; -import ch.qos.logback.core.joran.util.beans.BeanUtil; import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.util.AggregationType; import ch.qos.logback.core.util.PropertySetterException; +import ch.qos.logback.core.util.StringUtil; + +import java.lang.reflect.Method; /** * General purpose Object property setter. Clients repeatedly invokes * {@link #setProperty setProperty(name,value)} in order to invoke setters on - * the Object specified in the constructor. This class relies on reflection - * to analyze the given Object Class. + * the Object specified in the constructor. This class relies on reflection to + * analyze the given Object Class. * *

* Usage: @@ -42,7 +40,7 @@ * ps.set("age", "32"); * ps.set("isMale", "true"); * - * + *

* will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and * setMale(true) if such methods exist with those signatures. Otherwise an * {@link PropertySetterException} is thrown. @@ -55,26 +53,34 @@ public class PropertySetter extends ContextAwareBase { protected final Object obj; protected final Class objClass; protected final BeanDescription beanDescription; + protected final AggregationAssessor aggregationAssessor; /** * Create a new PropertySetter for the specified Object. This is done in * preparation for invoking {@link #setProperty} one or more times. * - * @param obj - * the object for which to set properties + * @param obj the object for which to set properties */ public PropertySetter(BeanDescriptionCache beanDescriptionCache, Object obj) { this.obj = obj; this.objClass = obj.getClass(); this.beanDescription = beanDescriptionCache.getBeanDescription(objClass); + this.aggregationAssessor = new AggregationAssessor(beanDescriptionCache, this.objClass); + } + + @Override + public void setContext(Context context) { + super.setContext(context); + aggregationAssessor.setContext(context); } + /** * Set a property on this PropertySetter's Object. If successful, this method * will invoke a setter method on the underlying Object. The setter is the one * for the specified property name and the value is determined partly from the - * setter argument type and partly from the value specified in the call to - * this method. + * setter argument type and partly from the value specified in the call to this + * method. * *

* If the setter expects a String no conversion is necessary. If it expects an @@ -82,21 +88,19 @@ public PropertySetter(BeanDescriptionCache beanDescriptionCache, Object obj) { * Integer(value). If the setter expects a boolean, the conversion is by new * Boolean(value). * - * @param name - * name of the property - * @param value - * String value of the property + * @param name name of the property + * @param value String value of the property */ public void setProperty(String name, String value) { if (value == null) { return; } - Method setter = findSetterMethod(name); + Method setter = aggregationAssessor.findSetterMethod(name); if (setter == null) { addWarn("No setter for property [" + name + "] in " + objClass.getName() + "."); } else { try { - setProperty(setter, name, value); + setProperty(setter, value); } catch (PropertySetterException ex) { addWarn("Failed to set property [" + name + "] to value \"" + value + "\". ", ex); } @@ -104,17 +108,13 @@ public void setProperty(String name, String value) { } /** - * Set the named property given a {@link PropertyDescriptor}. + * Set the named property using a {@link Method setter}. * - * @param prop - * A PropertyDescriptor describing the characteristics of the - * property to set. - * @param name - * The named of the property to set. - * @param value - * The value of the property. + * @param setter A Method describing the characteristics of the + * property to set. + * @param value The value of the property. */ - private void setProperty(Method setter, String name, String value) throws PropertySetterException { + private void setProperty(Method setter, String value) throws PropertySetterException { Class[] paramTypes = setter.getParameterTypes(); Object arg; @@ -136,102 +136,16 @@ private void setProperty(Method setter, String name, String value) throws Proper } public AggregationType computeAggregationType(String name) { - String cName = capitalizeFirstLetter(name); - - Method addMethod = findAdderMethod(cName); - - if (addMethod != null) { - AggregationType type = computeRawAggregationType(addMethod); - switch (type) { - case NOT_FOUND: - return AggregationType.NOT_FOUND; - case AS_BASIC_PROPERTY: - return AggregationType.AS_BASIC_PROPERTY_COLLECTION; - - case AS_COMPLEX_PROPERTY: - return AggregationType.AS_COMPLEX_PROPERTY_COLLECTION; - case AS_BASIC_PROPERTY_COLLECTION: - case AS_COMPLEX_PROPERTY_COLLECTION: - addError("Unexpected AggregationType " + type); - } - } - - Method setter = findSetterMethod(name); - if (setter != null) { - return computeRawAggregationType(setter); - } else { - // we have failed - return AggregationType.NOT_FOUND; - } + return this.aggregationAssessor.computeAggregationType(name); } - private Method findAdderMethod(String name) { - String propertyName = BeanUtil.toLowerCamelCase(name); - return beanDescription.getAdder(propertyName); - } - - private Method findSetterMethod(String name) { - String propertyName = BeanUtil.toLowerCamelCase(name); - return beanDescription.getSetter(propertyName); - } - - private Class getParameterClassForMethod(Method method) { - if (method == null) { - return null; - } - Class[] classArray = method.getParameterTypes(); - if (classArray.length != 1) { - return null; - } else { - return classArray[0]; - } - } - - private AggregationType computeRawAggregationType(Method method) { - Class parameterClass = getParameterClassForMethod(method); - if (parameterClass == null) { - return AggregationType.NOT_FOUND; - } - if (StringToObjectConverter.canBeBuiltFromSimpleString(parameterClass)) { - return AggregationType.AS_BASIC_PROPERTY; - } else { - return AggregationType.AS_COMPLEX_PROPERTY; - } - } - - /** - * Can the given clazz instantiable with certainty? - * - * @param clazz - * The class to test for instantiability - * @return true if clazz can be instantiated, and false otherwise. - */ - private boolean isUnequivocallyInstantiable(Class clazz) { - if (clazz.isInterface()) { - return false; - } - // checking for constructors would be more elegant, but in - // classes without any declared constructors, Class.getConstructor() - // returns null. - Object o; - try { - o = clazz.getDeclaredConstructor().newInstance(); - if (o != null) { - return true; - } else { - return false; - } - } catch (InstantiationException|IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { - return false; - } - } public Class getObjClass() { return objClass; } public void addComplexProperty(String name, Object complexProperty) { - Method adderMethod = findAdderMethod(name); + Method adderMethod = aggregationAssessor.findAdderMethod(name); // first let us use the addXXX method if (adderMethod != null) { Class[] paramTypes = adderMethod.getParameterTypes(); @@ -249,7 +163,8 @@ void invokeMethodWithSingleParameterOnThisObject(Method method, Object parameter try { method.invoke(this.obj, parameter); } catch (Exception e) { - addError("Could not invoke method " + method.getName() + " in class " + obj.getClass().getName() + " with parameter of type " + ccc.getName(), e); + addError("Could not invoke method " + method.getName() + " in class " + obj.getClass().getName() + + " with parameter of type " + ccc.getName(), e); } } @@ -259,8 +174,8 @@ public void addBasicProperty(String name, String strValue) { return; } - name = capitalizeFirstLetter(name); - Method adderMethod = findAdderMethod(name); + name = StringUtil.capitalizeFirstLetter(name); + Method adderMethod = aggregationAssessor.findAdderMethod(name); if (adderMethod == null) { addError("No adder for property [" + name + "]."); @@ -278,12 +193,12 @@ public void addBasicProperty(String name, String strValue) { return; } if (arg != null) { - invokeMethodWithSingleParameterOnThisObject(adderMethod, strValue); + invokeMethodWithSingleParameterOnThisObject(adderMethod, arg); } } public void setComplexProperty(String name, Object complexProperty) { - Method setter = findSetterMethod(name); + Method setter = aggregationAssessor.findSetterMethod(name); if (setter == null) { addWarn("Not setter method for property [" + name + "] in " + obj.getClass().getName()); @@ -307,13 +222,15 @@ public void setComplexProperty(String name, Object complexProperty) { private boolean isSanityCheckSuccessful(String name, Method method, Class[] params, Object complexProperty) { Class ccc = complexProperty.getClass(); if (params.length != 1) { - addError("Wrong number of parameters in setter method for property [" + name + "] in " + obj.getClass().getName()); + addError("Wrong number of parameters in setter method for property [" + name + "] in " + + obj.getClass().getName()); return false; } if (!params[0].isAssignableFrom(complexProperty.getClass())) { - addError("A \"" + ccc.getName() + "\" object is not assignable to a \"" + params[0].getName() + "\" variable."); + addError("A \"" + ccc.getName() + "\" object is not assignable to a \"" + params[0].getName() + + "\" variable."); addError("The class \"" + params[0].getName() + "\" was loaded by "); addError("[" + params[0].getClassLoader() + "] whereas object of type "); addError("\"" + ccc.getName() + "\" was loaded by [" + ccc.getClassLoader() + "]."); @@ -323,75 +240,46 @@ private boolean isSanityCheckSuccessful(String name, Method method, Class[] p return true; } - private String capitalizeFirstLetter(String name) { - return name.substring(0, 1).toUpperCase() + name.substring(1); - } - public Object getObj() { return obj; } - Method getRelevantMethod(String name, AggregationType aggregationType) { - Method relevantMethod; - if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY_COLLECTION) { - relevantMethod = findAdderMethod(name); - } else if (aggregationType == AggregationType.AS_COMPLEX_PROPERTY) { - relevantMethod = findSetterMethod(name); - } else { - throw new IllegalStateException(aggregationType + " not allowed here"); - } - return relevantMethod; - } - - T getAnnotation(String name, Class annonationClass, Method relevantMethod) { - if (relevantMethod != null) { - return relevantMethod.getAnnotation(annonationClass); - } else { - return null; - } + public Class getClassNameViaImplicitRules(String name, AggregationType aggregationType, + DefaultNestedComponentRegistry registry) { + return aggregationAssessor.getClassNameViaImplicitRules(name, aggregationType, registry); } - Class getDefaultClassNameByAnnonation(String name, Method relevantMethod) { - DefaultClass defaultClassAnnon = getAnnotation(name, DefaultClass.class, relevantMethod); - if (defaultClassAnnon != null) { - return defaultClassAnnon.value(); + public Class getTypeForComplexProperty(String nestedElementTagName, AggregationType aggregationType) { + + Method aMethod = null; + switch (aggregationType) { + case AS_COMPLEX_PROPERTY: + aMethod = aggregationAssessor.findSetterMethod(nestedElementTagName); + break; + case AS_COMPLEX_PROPERTY_COLLECTION: + aMethod = aggregationAssessor.findAdderMethod(nestedElementTagName); } - return null; - } - Class getByConcreteType(String name, Method relevantMethod) { - Class paramType = getParameterClassForMethod(relevantMethod); - if (paramType == null) { - return null; - } + checkParameterCount(aMethod, nestedElementTagName); - boolean isUnequivocallyInstantiable = isUnequivocallyInstantiable(paramType); - if (isUnequivocallyInstantiable) { - return paramType; - } else { - return null; - } + Class[] paramTypes = aMethod.getParameterTypes(); + return paramTypes[0]; } - public Class getClassNameViaImplicitRules(String name, AggregationType aggregationType, DefaultNestedComponentRegistry registry) { - - Class registryResult = registry.findDefaultComponentType(obj.getClass(), name); - if (registryResult != null) { - return registryResult; - } - // find the relevant method for the given property name and aggregationType - Method relevantMethod = getRelevantMethod(name, aggregationType); - if (relevantMethod == null) { - return null; + private void checkParameterCount(Method aMethod, String nestedElementTagName) { + if(aMethod == null) { + String msg = "Could not find method for property [" + nestedElementTagName + "]."; + addError(msg); + throw new IllegalStateException(msg); } - Class byAnnotation = getDefaultClassNameByAnnonation(name, relevantMethod); - if (byAnnotation != null) { - return byAnnotation; + int parameterCount = aMethod.getParameterCount(); + if (parameterCount != 1) { + String msg = "Expected ["+aMethod.getName()+"] for property [" + nestedElementTagName + "] to have exactly one parameter."; + addError(msg); + throw new IllegalStateException(msg); } - return getByConcreteType(name, relevantMethod); } - } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/StringToObjectConverter.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/StringToObjectConverter.java index 12a4e56965..ab983bf894 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/StringToObjectConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/StringToObjectConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,22 +13,25 @@ */ package ch.qos.logback.core.joran.util; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.spi.ContextAware; + import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.spi.ContextAware; +import static ch.qos.logback.core.CoreConstants.NULL_STR; /** * Utility class which can convert string into objects. + * * @author Ceki Gülcü * */ -public class StringToObjectConverter { +public class StringToObjectConverter { - private static final Class[] STING_CLASS_PARAMETER = new Class[] { String.class }; + private static final Class[] STRING_CLASS_PARAMETER = new Class[] { String.class }; static public boolean canBeBuiltFromSimpleString(Class parameterClass) { Package p = parameterClass.getPackage(); @@ -50,7 +53,7 @@ static public boolean canBeBuiltFromSimpleString(Class parameterClass) { * Convert val a String parameter to an object of a given type. */ @SuppressWarnings("unchecked") - public static Object convertArg(ContextAware ca, String val, Class type) { + static public Object convertArg(ContextAware ca, String val, Class type) { if (val == null) { return null; } @@ -87,6 +90,12 @@ static private boolean isOfTypeCharset(Class type) { } static private Charset convertToCharset(ContextAware ca, String val) { + + if (NULL_STR.equalsIgnoreCase(val)) { + ca.addInfo("Converting the string \"null\" as Charset.defaultCharset()"); + return Charset.defaultCharset(); + } + try { return Charset.forName(val); } catch (UnsupportedCharsetException e) { @@ -96,9 +105,9 @@ static private Charset convertToCharset(ContextAware ca, String val) { } // returned value may be null and in most cases it is null. - public static Method getValueOfMethod(Class type) { + static public Method getValueOfMethod(Class type) { try { - return type.getMethod(CoreConstants.VALUE_OF, STING_CLASS_PARAMETER); + return type.getMethod(CoreConstants.VALUE_OF, STRING_CLASS_PARAMETER); } catch (NoSuchMethodException e) { return null; } catch (SecurityException e) { @@ -106,7 +115,7 @@ public static Method getValueOfMethod(Class type) { } } - static private boolean followsTheValueOfConvention(Class parameterClass) { + static public boolean followsTheValueOfConvention(Class parameterClass) { Method valueOfMethod = getValueOfMethod(parameterClass); if (valueOfMethod == null) return false; @@ -117,10 +126,11 @@ static private boolean followsTheValueOfConvention(Class parameterClass) { private static Object convertByValueOfMethod(ContextAware ca, Class type, String val) { try { - Method valueOfMethod = type.getMethod(CoreConstants.VALUE_OF, STING_CLASS_PARAMETER); + Method valueOfMethod = type.getMethod(CoreConstants.VALUE_OF, STRING_CLASS_PARAMETER); return valueOfMethod.invoke(null, val); } catch (Exception e) { - ca.addError("Failed to invoke " + CoreConstants.VALUE_OF + "{} method in class [" + type.getName() + "] with value [" + val + "]"); + ca.addError("Failed to invoke " + CoreConstants.VALUE_OF + "{} method in class [" + type.getName() + + "] with value [" + val + "]"); return null; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescription.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescription.java index 2ceb875bbf..22d7ab9664 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescription.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescription.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util.beans; import java.lang.reflect.Method; @@ -15,57 +29,58 @@ */ public class BeanDescription { - private final Class clazz; - - private final Map propertyNameToGetter; - - private final Map propertyNameToSetter; - - private final Map propertyNameToAdder; - - /** - * Scope protected since only the {@link BeanDescriptionFactory} must create - * BeanDescriptions in order to guarantee consistency between the given - * parameters. - * - * @param clazz of the bean. - * @param propertyNameToGetter map of property names to the associated getter. - * @param propertyNameToSetter map of property names to the associated setter. - * @param propertyNameToAdder map of property names to the associated adder. - */ - protected BeanDescription(Class clazz,Map propertyNameToGetter,Map propertyNameToSetter,Map propertyNameToAdder) { - this.clazz = clazz; - this.propertyNameToGetter = Collections.unmodifiableMap(propertyNameToGetter); - this.propertyNameToSetter = Collections.unmodifiableMap(propertyNameToSetter); - this.propertyNameToAdder = Collections.unmodifiableMap(propertyNameToAdder); - } - - public Class getClazz() { - return clazz; - } - - public Map getPropertyNameToGetter() { - return propertyNameToGetter; - } - - public Map getPropertyNameToSetter() { - return propertyNameToSetter; - } - - public Method getGetter(String propertyName) { - return propertyNameToGetter.get(propertyName); - } - - public Method getSetter(String propertyName) { - return propertyNameToSetter.get(propertyName); - } - - public Map getPropertyNameToAdder() { - return propertyNameToAdder; - } - - public Method getAdder(String propertyName) { - return propertyNameToAdder.get(propertyName); - } + private final Class clazz; + + private final Map propertyNameToGetter; + + private final Map propertyNameToSetter; + + private final Map propertyNameToAdder; + + /** + * Scope protected since only the {@link BeanDescriptionFactory} must create + * BeanDescriptions in order to guarantee consistency between the given + * parameters. + * + * @param clazz of the bean. + * @param propertyNameToGetter map of property names to the associated getter. + * @param propertyNameToSetter map of property names to the associated setter. + * @param propertyNameToAdder map of property names to the associated adder. + */ + protected BeanDescription(Class clazz, Map propertyNameToGetter, + Map propertyNameToSetter, Map propertyNameToAdder) { + this.clazz = clazz; + this.propertyNameToGetter = Collections.unmodifiableMap(propertyNameToGetter); + this.propertyNameToSetter = Collections.unmodifiableMap(propertyNameToSetter); + this.propertyNameToAdder = Collections.unmodifiableMap(propertyNameToAdder); + } + + public Class getClazz() { + return clazz; + } + + public Map getPropertyNameToGetter() { + return propertyNameToGetter; + } + + public Map getPropertyNameToSetter() { + return propertyNameToSetter; + } + + public Method getGetter(String propertyName) { + return propertyNameToGetter.get(propertyName); + } + + public Method getSetter(String propertyName) { + return propertyNameToSetter.get(propertyName); + } + + public Map getPropertyNameToAdder() { + return propertyNameToAdder; + } + + public Method getAdder(String propertyName) { + return propertyNameToAdder.get(propertyName); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionCache.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionCache.java index 706ac4a8cd..947b68e17a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionCache.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionCache.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util.beans; import java.util.HashMap; @@ -10,9 +24,11 @@ * * Cache for {@link BeanDescription} instances. All the cache users which use * the same instance of BeanDescriptionCache can profit from each others cached - * bean descriptions. + * bean descriptions. * - *

The cache is not thread-safe and should not be shared across configurator instances. + *

+ * The cache is not thread-safe and should not be shared across configurator + * instances. * * @author urechm * @@ -34,12 +50,11 @@ private BeanDescriptionFactory getBeanDescriptionFactory() { } /** - * Returned bean descriptions are hold in a cache. If the cache does not - * contain a description for a given class, a new bean description is - * created and put in the cache, before it is returned. + * Returned bean descriptions are hold in a cache. If the cache does not contain + * a description for a given class, a new bean description is created and put in + * the cache, before it is returned. * - * @param clazz - * to get a bean description for. + * @param clazz to get a bean description for. * @return a bean description for the given class. */ public BeanDescription getBeanDescription(Class clazz) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionFactory.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionFactory.java index a6c730cce7..593e00477d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanDescriptionFactory.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util.beans; import java.lang.reflect.Method; @@ -8,11 +22,12 @@ import ch.qos.logback.core.spi.ContextAwareBase; /** - * Encapsulates creation of {@link BeanDescription} instances. - * This factory is kind of a lightweight Introspector as described in the Java Beans API specification. - * The given class is only analyzed for its public getters, setters and adders methods. - * Implementations of the BeanInfo interface are not taken into account for analysis. - * Therefore this class is only partially compatible with the Java Beans API specification. + * Encapsulates creation of {@link BeanDescription} instances. This factory is + * kind of a lightweight Introspector as described in the Java Beans API + * specification. The given class is only analyzed for its public getters, + * setters and adders methods. Implementations of the BeanInfo interface are not + * taken into account for analysis. Therefore this class is only partially + * compatible with the Java Beans API specification. * * * @author urechm @@ -34,7 +49,7 @@ public BeanDescription create(Class clazz) { Map propertyNameToAdder = new HashMap(); Method[] methods = clazz.getMethods(); for (Method method : methods) { - if(method.isBridge()) { + if (method.isBridge()) { // we can safely ignore bridge methods continue; } @@ -45,21 +60,24 @@ public BeanDescription create(Class clazz) { if (oldGetter.getName().startsWith(BeanUtil.PREFIX_GETTER_IS)) { propertyNameToGetter.put(propertyName, oldGetter); } - String message = String.format("Class '%s' contains multiple getters for the same property '%s'.", clazz.getCanonicalName(), propertyName); + String message = String.format("Class '%s' contains multiple getters for the same property '%s'.", + clazz.getCanonicalName(), propertyName); addWarn(message); } } else if (BeanUtil.isSetter(method)) { String propertyName = BeanUtil.getPropertyName(method); Method oldSetter = propertyNameToSetter.put(propertyName, method); if (oldSetter != null) { - String message = String.format("Class '%s' contains multiple setters for the same property '%s'.", clazz.getCanonicalName(), propertyName); + String message = String.format("Class '%s' contains multiple setters for the same property '%s'.", + clazz.getCanonicalName(), propertyName); addWarn(message); } } else if (BeanUtil.isAdder(method)) { String propertyName = BeanUtil.getPropertyName(method); Method oldAdder = propertyNameToAdder.put(propertyName, method); if (oldAdder != null) { - String message = String.format("Class '%s' contains multiple adders for the same property '%s'.", clazz.getCanonicalName(), propertyName); + String message = String.format("Class '%s' contains multiple adders for the same property '%s'.", + clazz.getCanonicalName(), propertyName); addWarn(message); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanUtil.java b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanUtil.java index 9c981b6564..7adc578d32 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/joran/util/beans/BeanUtil.java @@ -1,14 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util.beans; import java.lang.reflect.Method; /** * Encapsulates utility methods associated with standard java beans. + * * @author urechm */ public class BeanUtil { - //public static final BeanUtil SINGLETON = new BeanUtil(); + // public static final BeanUtil SINGLETON = new BeanUtil(); public static final String PREFIX_GETTER_IS = "is"; public static final String PREFIX_GETTER_GET = "get"; @@ -86,7 +101,8 @@ static public boolean isSetter(Method method) { /** * @param method to get the associated property name for. - * @return The property name of the associated property if the given method matches a standard java beans getter or setter. + * @return The property name of the associated property if the given method + * matches a standard java beans getter or setter. */ static public String getPropertyName(Method method) { String methodName = method.getName(); @@ -105,11 +121,12 @@ static public String getPropertyName(Method method) { /** * Converts the given String into lower camel case form. + * * @param string to decapitalize. - * @return null if the given String is null. - * Emtpy string if the given string is empty. - * The given string if the first two consecutive letters are in upper case. - * The given string with the first letter in lower case otherwise, which might be the given string. + * @return null if the given String is null. Empty string if the given string is + * empty. The given string if the first two consecutive letters are in + * upper case. The given string with the first letter in lower case + * otherwise, which might be the given string. */ static public String toLowerCamelCase(String string) { if (string == null) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/layout/EchoLayout.java b/logback-core/src/main/java/ch/qos/logback/core/layout/EchoLayout.java index c9ecdea7db..9974910ef7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/layout/EchoLayout.java +++ b/logback-core/src/main/java/ch/qos/logback/core/layout/EchoLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/logback-core-version.properties b/logback-core/src/main/java/ch/qos/logback/core/logback-core-version.properties new file mode 100644 index 0000000000..5e502fd1cb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/logback-core-version.properties @@ -0,0 +1,15 @@ +# +# Logback: the reliable, generic, fast and flexible logging framework. +# Copyright (C) 1999-2026, QOS.ch. All rights reserved. +# +# This program and the accompanying materials are dual-licensed under +# either the terms of the Eclipse Public License v2.0 as published by +# the Eclipse Foundation +# +# or (per the licensee's choosing) +# +# under the terms of the GNU Lesser General Public License version 2.1 +# as published by the Free Software Foundation. +# + +logback-core-version=${project.version} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/AppenderModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/AppenderModel.java new file mode 100755 index 0000000000..6f3d5f294e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/AppenderModel.java @@ -0,0 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import ch.qos.logback.core.model.processor.PhaseIndicator; +import ch.qos.logback.core.model.processor.ProcessingPhase; + +@PhaseIndicator(phase = ProcessingPhase.SECOND) +public class AppenderModel extends NamedComponentModel { + + private static final long serialVersionUID = 1096234203123945432L; + + @Override + protected AppenderModel makeNewInstance() { + return new AppenderModel(); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/AppenderRefModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/AppenderRefModel.java new file mode 100755 index 0000000000..b34df7fca0 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/AppenderRefModel.java @@ -0,0 +1,69 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +import ch.qos.logback.core.model.processor.PhaseIndicator; +import ch.qos.logback.core.model.processor.ProcessingPhase; + +@PhaseIndicator(phase = ProcessingPhase.SECOND) +public class AppenderRefModel extends Model { + + private static final long serialVersionUID = 5238705468395447547L; + + String ref; + + protected AppenderRefModel makeNewInstance() { + return new AppenderRefModel(); + } + + @Override + protected void mirror(Model that) { + AppenderRefModel actual = (AppenderRefModel) that; + super.mirror(actual); + this.ref = actual.ref; + } + + public String getRef() { + return ref; + } + + public void setRef(String ref) { + this.ref = ref; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(ref); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + AppenderRefModel other = (AppenderRefModel) obj; + return Objects.equals(ref, other.ref); + } + + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ComponentModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ComponentModel.java new file mode 100755 index 0000000000..acb7eea574 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ComponentModel.java @@ -0,0 +1,77 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +/** + * Abstract representation of configuration elements which have class names and are instantiated. + * + * @author Ceki Gülcü + * @since 1.3.0 + */ +public class ComponentModel extends Model { + + private static final long serialVersionUID = -7117814935763453139L; + + String className; + + @Override + protected ComponentModel makeNewInstance() { + return new ComponentModel(); + } + + @Override + protected void mirror(Model that) { + ComponentModel actual = (ComponentModel) that; + super.mirror(actual); + this.className = actual.className; + } + + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " [tag=" + tag + ", className=" + className + ", bodyText=" + bodyText + + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(className); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ComponentModel other = (ComponentModel) obj; + return Objects.equals(className, other.className); + } + +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ConversionRuleModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ConversionRuleModel.java new file mode 100644 index 0000000000..0927bb2912 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ConversionRuleModel.java @@ -0,0 +1,42 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +public class ConversionRuleModel extends ComponentModel { + + String conversionWord; + + @Override + protected ConversionRuleModel makeNewInstance() { + return new ConversionRuleModel(); + } + + public String getConversionWord() { + return conversionWord; + } + + public void setConversionWord(String conversionWord) { + this.conversionWord = conversionWord; + } + + public String getConverterClass() { + return getClassName(); + } + + public void setConverterClass(String converterClass) { + setClassName(converterClass); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/DefineModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/DefineModel.java new file mode 100755 index 0000000000..69a12c15a5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/DefineModel.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class DefineModel extends NamedComponentModel { + + private static final long serialVersionUID = 6209642548924431065L; + String scopeStr; + + @Override + protected DefineModel makeNewInstance() { + return new DefineModel(); + } + + @Override + protected void mirror(Model that) { + DefineModel actual = (DefineModel) that; + super.mirror(actual); + this.scopeStr = actual.scopeStr; + } + + public String getScopeStr() { + return scopeStr; + } + + public void setScopeStr(String scopeStr) { + this.scopeStr = scopeStr; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(scopeStr); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + DefineModel other = (DefineModel) obj; + return Objects.equals(scopeStr, other.scopeStr); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/EventEvaluatorModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/EventEvaluatorModel.java new file mode 100755 index 0000000000..f35963488c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/EventEvaluatorModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class EventEvaluatorModel extends NamedComponentModel { + private static final long serialVersionUID = 4600344286104093766L; + + + @Override + protected EventEvaluatorModel makeNewInstance() { + return new EventEvaluatorModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/INamedModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/INamedModel.java new file mode 100755 index 0000000000..0eaee9b7a2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/INamedModel.java @@ -0,0 +1,23 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +public interface INamedModel { + + public String getName(); + + public void setName(String name); + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ImplicitModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ImplicitModel.java new file mode 100755 index 0000000000..262684f32f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ImplicitModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class ImplicitModel extends ComponentModel { + + private static final long serialVersionUID = 5507123447813692833L; + + @Override + protected ImplicitModel makeNewInstance() { + return new ImplicitModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ImportModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ImportModel.java new file mode 100644 index 0000000000..cdb9a91c89 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ImportModel.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class ImportModel extends Model { + + private static final long serialVersionUID = 1L; + + // Class/getClass() are part of java.lang.Object. We use 'className' instead. + String className; + + @Override + protected ImportModel makeNewInstance() { + return new ImportModel(); + } + + @Override + protected void mirror(Model that) { + ImportModel actual = (ImportModel) that; + super.mirror(actual); + this.className = actual.className; + } + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/IncludeModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/IncludeModel.java new file mode 100755 index 0000000000..2fd569a8bc --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/IncludeModel.java @@ -0,0 +1,19 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +public class IncludeModel extends ResourceModel { + private static final long serialVersionUID = -9114108510322703902L; +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/InsertFromJNDIModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/InsertFromJNDIModel.java new file mode 100644 index 0000000000..bd51574d52 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/InsertFromJNDIModel.java @@ -0,0 +1,89 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class InsertFromJNDIModel extends Model { + + private static final long serialVersionUID = -7803377963650426197L; + + public static final String ENV_ENTRY_NAME_ATTR = "env-entry-name"; + public static final String AS_ATTR = "as"; + + String as; + String envEntryName; + String scopeStr; + + @Override + protected InsertFromJNDIModel makeNewInstance() { + return new InsertFromJNDIModel(); + } + + @Override + protected void mirror(Model that) { + InsertFromJNDIModel actual = (InsertFromJNDIModel) that; + super.mirror(actual); + this.as = actual.as; + this.envEntryName = actual.envEntryName; + this.scopeStr = actual.scopeStr; + } + + + public String getScopeStr() { + return scopeStr; + } + + public void setScopeStr(String scopeStr) { + this.scopeStr = scopeStr; + } + + public String getAs() { + return as; + } + + public void setAs(String as) { + this.as = as; + } + + public String getEnvEntryName() { + return envEntryName; + } + + public void setEnvEntryName(String envEntryName) { + this.envEntryName = envEntryName; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(as, envEntryName, scopeStr); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + InsertFromJNDIModel other = (InsertFromJNDIModel) obj; + return Objects.equals(as, other.as) && Objects.equals(envEntryName, other.envEntryName) + && Objects.equals(scopeStr, other.scopeStr); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/Model.java b/logback-core/src/main/java/ch/qos/logback/core/model/Model.java new file mode 100755 index 0000000000..b1f76fc3a0 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/Model.java @@ -0,0 +1,165 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +/** + * Abstract representation of configuration elements + * + * @author Ceki Gülcü + * @since 1.3.0 + */ +public class Model implements Serializable { + + private static final long serialVersionUID = -797372668713068159L; + + // this state should not be here but should be treated via listeners + // between processors and ModelHandlers + boolean handled = false; + boolean skipped = false; + + String tag; + String bodyText; + int lineNumber; + + List subModels = new ArrayList<>(); + + static public Model duplicate(Model that) { + Model copy = that.makeNewInstance(); + copy.mirror(that); + for(Model m: that.subModels) { + Model duplicate = duplicate(m); + copy.subModels.add(duplicate); + } + return copy; + } + + protected Model makeNewInstance() { + return new Model(); + } + + protected void mirror(Model that) { + this.tag = that.tag; + this.bodyText = that.bodyText; + this.lineNumber = that.lineNumber; + } + + + public void markAsSkipped() { + skipped = true; + } + public void deepMarkAsSkipped() { + markAsSkipped(); + for(Model m: this.getSubModels()) { + m.deepMarkAsSkipped(); + } + } + /** + * The model can re-used at reconfiguration time. + * + * @since 1.3.0-alpha14 + */ + void resetForReuse() { + this.handled = false; + this.skipped = false; + for(Model sub: subModels) { + sub.resetForReuse(); + } + } + + public boolean isSkipped() { + return skipped; + } + + public boolean isUnhandled() { + return !handled; + } + + public boolean isHandled() { + return handled; + } + + public void markAsHandled() { + handled = true; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } + + public int getLineNumber() { + return lineNumber; + } + + public void setLineNumber(int lineNumber) { + this.lineNumber = lineNumber; + } + + public List getSubModels() { + return subModels; + } + + public void addSubModel(Model m) { + subModels.add(m); + } + + public String getBodyText() { + return bodyText; + } + + public void addText(String bodytext) { + if (bodyText == null) + this.bodyText = bodytext; + else + this.bodyText += bodytext; + } + + public String idString() { + return "<" + tag + "> at line " + lineNumber; + } + + + @Override + public int hashCode() { + return Objects.hash(bodyText, lineNumber, subModels, tag); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Model other = (Model) obj; + return Objects.equals(bodyText, other.bodyText) && lineNumber == other.lineNumber + && Objects.equals(subModels, other.subModels) && Objects.equals(tag, other.tag); + } + + + @Override + public String toString() { + return this.getClass().getSimpleName() + " [tag=" + tag + ", bodyText=" + bodyText + ", id="+hashCode()+"]"; + } + +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ModelConstants.java b/logback-core/src/main/java/ch/qos/logback/core/model/ModelConstants.java new file mode 100644 index 0000000000..2b5c3d49e5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ModelConstants.java @@ -0,0 +1,28 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import ch.qos.logback.core.CoreConstants; + +public class ModelConstants { + + + public static final String DEBUG_SYSTEM_PROPERTY_KEY = "logback.debug"; + public static final String NULL_STR = CoreConstants.NULL_STR; + + public static final String INVALID_ATTRIBUTES = "In element, either the \"file\" attribute alone, or " + + "the \"resource\" element alone, or both the \"name\" and \"value\" attributes must be set."; + + public static final String PARENT_PROPPERTY_KEY = "parent"; +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ModelHandlerFactoryMethod.java b/logback-core/src/main/java/ch/qos/logback/core/model/ModelHandlerFactoryMethod.java new file mode 100644 index 0000000000..2712b23d20 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ModelHandlerFactoryMethod.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +@FunctionalInterface +public interface ModelHandlerFactoryMethod { + + public ModelHandlerBase make(Context context, ModelInterpretationContext ic); +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ModelUtil.java b/logback-core/src/main/java/ch/qos/logback/core/model/ModelUtil.java new file mode 100644 index 0000000000..e30436d03c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ModelUtil.java @@ -0,0 +1,55 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.model.util.PropertyModelHandlerHelper; + +import java.util.Properties; + +public class ModelUtil { + + + static public void resetForReuse(Model model) { + if(model == null) + return; + model.resetForReuse(); + } + + + + /** + * Add all the properties found in the argument named 'props' to an + * ModelInterpretationContext. + * + * @deprecated moved to {@link PropertyModelHandlerHelper#setProperty} + */ + @Deprecated + static public void setProperty(ModelInterpretationContext mic, String key, String value, Scope scope) { + PropertyModelHandlerHelper.setProperty(mic, key, value, scope); + } + + /** + * Add all the properties found in the argument named 'props' to an + * ModelInterpretationContext. + * + * @deprecated moved to {@link PropertyModelHandlerHelper#setProperties} + */ + @Deprecated + static public void setProperties(ModelInterpretationContext mic, Properties props, Scope scope) { + PropertyModelHandlerHelper.setProperties(mic, props, scope); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/NamedComponentModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/NamedComponentModel.java new file mode 100755 index 0000000000..aa9b0c748f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/NamedComponentModel.java @@ -0,0 +1,70 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class NamedComponentModel extends ComponentModel implements INamedModel { + + private static final long serialVersionUID = -6388316680413871442L; + String name; + + @Override + protected NamedComponentModel makeNewInstance() { + return new NamedComponentModel(); + } + + @Override + protected void mirror(Model that) { + NamedComponentModel actual = (NamedComponentModel) that; + super.mirror(actual); + this.name = actual.name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String toString() { + return "NamedComponentModel [name=" + name + ", className=" + className + ", tag=" + tag + ", bodyText=" + + bodyText + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(name); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + NamedComponentModel other = (NamedComponentModel) obj; + return Objects.equals(name, other.name); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/NamedModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/NamedModel.java new file mode 100755 index 0000000000..9179a3a335 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/NamedModel.java @@ -0,0 +1,65 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class NamedModel extends Model implements INamedModel { + + private static final long serialVersionUID = 3549881638769570183L; + + String name; + + @Override + protected NamedModel makeNewInstance() { + return new NamedModel(); + } + + @Override + protected void mirror(Model that) { + NamedModel actual = (NamedModel) that; + super.mirror(actual); + this.name = actual.name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(name); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + NamedModel other = (NamedModel) obj; + return Objects.equals(name, other.name); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ParamModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ParamModel.java new file mode 100755 index 0000000000..aae22261e0 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ParamModel.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class ParamModel extends NamedModel { + + private static final long serialVersionUID = -3697627721759508667L; + String value; + + @Override + protected ParamModel makeNewInstance() { + return new ParamModel(); + } + + @Override + protected void mirror(Model that) { + ParamModel actual = (ParamModel) that; + super.mirror(actual); + this.value = actual.value; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(value); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + ParamModel other = (ParamModel) obj; + return Objects.equals(value, other.value); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/PropertyModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/PropertyModel.java new file mode 100755 index 0000000000..53b0ad3716 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/PropertyModel.java @@ -0,0 +1,99 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class PropertyModel extends NamedModel { + + private static final long serialVersionUID = 1494176979175092052L; + + String value; + String scopeStr; + + String file; + String resource; + + @Override + protected PropertyModel makeNewInstance() { + return new PropertyModel(); + } + + @Override + protected void mirror(Model that) { + PropertyModel actual = (PropertyModel) that; + super.mirror(actual); + this.value = actual.value; + this.scopeStr = actual.scopeStr; + this.file = actual.file; + this.resource = actual.resource; + + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getScopeStr() { + return scopeStr; + } + + public void setScopeStr(String scopeStr) { + this.scopeStr = scopeStr; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getResource() { + return resource; + } + + public void setResource(String resource) { + this.resource = resource; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(file, resource, scopeStr, value); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + PropertyModel other = (PropertyModel) obj; + return Objects.equals(file, other.file) && Objects.equals(resource, other.resource) + && Objects.equals(scopeStr, other.scopeStr) && Objects.equals(value, other.value); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ResourceModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ResourceModel.java new file mode 100644 index 0000000000..c945b49a45 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ResourceModel.java @@ -0,0 +1,59 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + + +public class ResourceModel extends Model { + + private static final long serialVersionUID = 2185210901733958800L; + + String file; + String url; + String resource; + String optional; + + public String getOptional() { + return optional; + } + + public void setOptional(String optional) { + this.optional = optional; + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getResource() { + return resource; + } + + public void setResource(String resource) { + this.resource = resource; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/SequenceNumberGeneratorModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/SequenceNumberGeneratorModel.java new file mode 100644 index 0000000000..4719e6d3c2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/SequenceNumberGeneratorModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class SequenceNumberGeneratorModel extends ComponentModel { + + private static final long serialVersionUID = 4109015583434648277L; + + @Override + protected SequenceNumberGeneratorModel makeNewInstance() { + return new SequenceNumberGeneratorModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/SerializeModelModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/SerializeModelModel.java new file mode 100644 index 0000000000..c5576c3e1f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/SerializeModelModel.java @@ -0,0 +1,54 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class SerializeModelModel extends Model { + + private static final long serialVersionUID = 16385651235687L; + + String file; + + @Override + protected SerializeModelModel makeNewInstance() { + return new SerializeModelModel(); + } + + public String getFile() { + return file; + } + + public void setFile(String file) { + this.file = file; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + if (!super.equals(o)) + return false; + SerializeModelModel that = (SerializeModelModel) o; + return Objects.equals(file, that.file); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), file); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/ShutdownHookModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/ShutdownHookModel.java new file mode 100755 index 0000000000..9f916e8f4e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/ShutdownHookModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class ShutdownHookModel extends ComponentModel { + + private static final long serialVersionUID = 8886561840058239494L; + + @Override + protected ShutdownHookModel makeNewInstance() { + return new ShutdownHookModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/SiftModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/SiftModel.java new file mode 100644 index 0000000000..037853e0f3 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/SiftModel.java @@ -0,0 +1,25 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class SiftModel extends Model { + + private static final long serialVersionUID = 6456117795231083546L; + + @Override + protected SiftModel makeNewInstance() { + return new SiftModel(); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/StatusListenerModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/StatusListenerModel.java new file mode 100755 index 0000000000..fa1cce5829 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/StatusListenerModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class StatusListenerModel extends ComponentModel { + + private static final long serialVersionUID = -236751010143929616L; + + @Override + protected StatusListenerModel makeNewInstance() { + return new StatusListenerModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/TimestampModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/TimestampModel.java new file mode 100755 index 0000000000..4a83a9ff0e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/TimestampModel.java @@ -0,0 +1,96 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class TimestampModel extends NamedModel { + + private static final long serialVersionUID = 2096655273673863306L; + + public static final String CONTEXT_BIRTH = "contextBirth"; + + String datePattern; + String timeReference; + String scopeStr; + + @Override + protected TimestampModel makeNewInstance() { + return new TimestampModel(); + } + + @Override + protected void mirror(Model that) { + TimestampModel actual = (TimestampModel) that; + super.mirror(actual); + this.datePattern = actual.datePattern; + this.timeReference = actual.timeReference; + this.scopeStr = actual.scopeStr; + } + + public String getKey() { + return getName(); + } + + public void setKey(String key) { + this.setName(key); + } + + public String getDatePattern() { + return datePattern; + } + + public void setDatePattern(String datePattern) { + this.datePattern = datePattern; + } + + public String getTimeReference() { + return timeReference; + } + + public void setTimeReference(String timeReference) { + this.timeReference = timeReference; + } + + public String getScopeStr() { + return scopeStr; + } + + public void setScopeStr(String scopeStr) { + this.scopeStr = scopeStr; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(datePattern, scopeStr, timeReference); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + TimestampModel other = (TimestampModel) obj; + return Objects.equals(datePattern, other.datePattern) && Objects.equals(scopeStr, other.scopeStr) + && Objects.equals(timeReference, other.timeReference); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ByPropertiesConditionModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ByPropertiesConditionModel.java new file mode 100644 index 0000000000..928c37534b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ByPropertiesConditionModel.java @@ -0,0 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.conditional; + +import ch.qos.logback.core.model.ComponentModel; + +public class ByPropertiesConditionModel extends ComponentModel { + + private static final long serialVersionUID = -1788292310734560420L; + + @Override + protected ByPropertiesConditionModel makeNewInstance() { + return new ByPropertiesConditionModel(); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ElseModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ElseModel.java new file mode 100644 index 0000000000..eb676b4ce8 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ElseModel.java @@ -0,0 +1,28 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.conditional; + +import ch.qos.logback.core.model.Model; + +public class ElseModel extends Model { + + private static final long serialVersionUID = -8409916706993952710L; + + @Override + protected ElseModel makeNewInstance() { + return new ElseModel(); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/conditional/IfModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/IfModel.java new file mode 100644 index 0000000000..486b448b47 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/IfModel.java @@ -0,0 +1,96 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.conditional; + +import java.util.Objects; + +import ch.qos.logback.core.model.Model; + +public class IfModel extends Model { + + private static final long serialVersionUID = 1516046821762377019L; + + public enum BranchState {IN_ERROR, IF_BRANCH, ELSE_BRANCH; } + + String condition; + BranchState branchState = null; + + @Override + protected IfModel makeNewInstance() { + return new IfModel(); + } + + @Override + protected void mirror(Model that) { + IfModel actual = (IfModel) that; + super.mirror(actual); + this.condition = actual.condition; + this.branchState = actual.branchState; + } + + public String getCondition() { + return condition; + } + + public void setCondition(String condition) { + this.condition = condition; + } + + public BranchState getBranchState() { + return branchState; + } + + public void setBranchState(BranchState state) { + this.branchState = state; + } + + + public void setBranchState(boolean booleanProxy) { + if(booleanProxy) + setBranchState(BranchState.IF_BRANCH); + else + setBranchState(BranchState.ELSE_BRANCH); + } + + public void resetBranchState() { + setBranchState(null); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " [condition=\""+condition+"\"]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(branchState, condition); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + IfModel other = (IfModel) obj; + return branchState == other.branchState && Objects.equals(condition, other.condition); + } + + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ThenModel.java b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ThenModel.java new file mode 100644 index 0000000000..99fae5b25e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/conditional/ThenModel.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.conditional; + +import ch.qos.logback.core.model.Model; + +public class ThenModel extends Model { + + private static final long serialVersionUID = -3264631638136701741L; + + @Override + protected ThenModel makeNewInstance() { + return new ThenModel(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowAllModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowAllModelFilter.java new file mode 100755 index 0000000000..c645016cca --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowAllModelFilter.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public class AllowAllModelFilter implements ModelFilter { + + @Override + public FilterReply decide(Model model) { + return FilterReply.ACCEPT; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowModelFilter.java new file mode 100755 index 0000000000..6b245b7441 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AllowModelFilter.java @@ -0,0 +1,38 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public class AllowModelFilter implements ModelFilter { + + final Class allowedModelType; + + AllowModelFilter(Class allowedType) { + this.allowedModelType = allowedType; + } + + @Override + public FilterReply decide(Model model) { + + if (model.getClass() == allowedModelType) { + return FilterReply.ACCEPT; + } + + return FilterReply.NEUTRAL; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderDeclarationAnalyser.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderDeclarationAnalyser.java new file mode 100644 index 0000000000..33d62abd61 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderDeclarationAnalyser.java @@ -0,0 +1,76 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; + +import java.util.HashSet; +import java.util.Set; + +/** + * The AppenderAvailabilityAnalyser class is responsible for analyzing the availability + * of appenders. By available, we mean whether an appender with a given name is declared + * somewhere in the configuration. This availability information is later used by + * AppenderRefModelHandler to attempt to attach only those appenders that were previously + * declared. + */ +@PhaseIndicator(phase = ProcessingPhase.DEPENDENCY_ANALYSIS) +public class AppenderDeclarationAnalyser extends ModelHandlerBase { + + static final String DECLARED_APPENDER_NAME_SET_KEY = "DECLARED_APPENDER_NAME_SET"; + + + public AppenderDeclarationAnalyser(Context context) { + super(context); + } + + @Override + protected Class getSupportedModelClass() { + return AppenderModel.class; + } + + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + AppenderModel appenderModel = (AppenderModel) model; + String appenderName = mic.subst(appenderModel.getName()); + + addAppenderDeclaration(mic, appenderName); + } + + + static public Set getAppenderNameSet(ModelInterpretationContext mic) { + Set set = (Set) mic.getObjectMap().get(DECLARED_APPENDER_NAME_SET_KEY); + if(set == null) { + set = new HashSet<>(); + mic.getObjectMap().put(DECLARED_APPENDER_NAME_SET_KEY, set); + } + return set; + } + + static public void addAppenderDeclaration(ModelInterpretationContext mic, String appenderName) { + Set set = getAppenderNameSet(mic); + set.add(appenderName); + } + + + static public boolean isAppenderDeclared(ModelInterpretationContext mic, String appenderName) { + Set set = getAppenderNameSet(mic); + return set.contains(appenderName); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderModelHandler.java new file mode 100755 index 0000000000..8d315ce3a2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderModelHandler.java @@ -0,0 +1,104 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import java.util.Map; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.AppenderAttachable; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.util.OptionHelper; + +public class AppenderModelHandler extends ModelHandlerBase { + Appender appender; + private boolean inError = false; + private boolean skipped = false; + AppenderAttachable appenderAttachable; + + public AppenderModelHandler(Context context) { + super(context); + } + + @SuppressWarnings("rawtypes") + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new AppenderModelHandler(context); + } + + @Override + @SuppressWarnings("unchecked") + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + this.appender = null; + this.inError = false; + + AppenderModel appenderModel = (AppenderModel) model; + + String appenderName = mic.subst(appenderModel.getName()); + + if (!mic.hasDependers(appenderName)) { + addWarn("Appender named [" + appenderName + "] not referenced. Skipping further processing."); + skipped = true; + appenderModel.markAsSkipped(); + return; + } + + addInfo("Processing appender named [" + appenderName + "]"); + + String originalClassName = appenderModel.getClassName(); + String className = mic.getImport(originalClassName); + + try { + addInfo("About to instantiate appender of type [" + className + "]"); + + appender = (Appender) OptionHelper.instantiateByClassName(className, ch.qos.logback.core.Appender.class, + context); + appender.setContext(context); + appender.setName(appenderName); + mic.pushObject(appender); + } catch (Exception oops) { + inError = true; + addError("Could not create an Appender of type [" + className + "].", oops); + throw new ModelHandlerException(oops); + } + } + + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + if (inError || skipped) { + return; + } + if (appender instanceof LifeCycle) { + ((LifeCycle) appender).start(); + } + mic.markStartOfNamedDependee(appender.getName()); + + Object o = mic.peekObject(); + + @SuppressWarnings("unchecked") + Map> appenderBag = (Map>) mic.getObjectMap() + .get(JoranConstants.APPENDER_BAG); + appenderBag.put(appender.getName(), appender); + + if (o != appender) { + addWarn("The object at the of the stack is not the appender named [" + appender.getName() + + "] pushed earlier."); + } else { + mic.popObject(); + } + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefDependencyAnalyser.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefDependencyAnalyser.java new file mode 100755 index 0000000000..f3bd2b53df --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefDependencyAnalyser.java @@ -0,0 +1,51 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.AppenderRefModel; +import ch.qos.logback.core.model.Model; + +import java.util.List; +import java.util.stream.Collectors; + +@PhaseIndicator(phase = ProcessingPhase.DEPENDENCY_ANALYSIS) +public class AppenderRefDependencyAnalyser extends ModelHandlerBase { + + public AppenderRefDependencyAnalyser(Context context) { + super(context); + } + + @Override + protected Class getSupportedModelClass() { + return Model.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model parentModel) throws ModelHandlerException { + + List appenderRefModels = + parentModel.getSubModels().stream().filter(m -> m instanceof AppenderRefModel).map(m -> (AppenderRefModel) m).collect(Collectors.toList()); + + + for (AppenderRefModel appenderRefModel : appenderRefModels) { + String ref = mic.subst(appenderRefModel.getRef()); + DependencyDefinition dd = new DependencyDefinition(parentModel, ref); + mic.addDependencyDefinition(dd); + } + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefModelHandler.java new file mode 100755 index 0000000000..d2c12e9846 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/AppenderRefModelHandler.java @@ -0,0 +1,85 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.model.AppenderRefModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.AppenderAttachable; + +import java.util.Map; + +import static ch.qos.logback.core.model.processor.AppenderDeclarationAnalyser.isAppenderDeclared; + +public class AppenderRefModelHandler extends ModelHandlerBase { + boolean inError = false; + + public AppenderRefModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new AppenderRefModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return AppenderRefModel.class; + } + + @Override + public void handle(ModelInterpretationContext interpContext, Model model) throws ModelHandlerException { + + Object o = interpContext.peekObject(); + + if (!(o instanceof AppenderAttachable)) { + inError = true; + String errMsg = "Could not find an AppenderAttachable at the top of execution stack. Near " + + model.idString(); + addError(errMsg); + return; + } + + AppenderRefModel appenderRefModel = (AppenderRefModel) model; + AppenderAttachable appenderAttachable = (AppenderAttachable) o; + + attachReferencedAppenders(interpContext, appenderRefModel, appenderAttachable); + + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + void attachReferencedAppenders(ModelInterpretationContext mic, AppenderRefModel appenderRefModel, + AppenderAttachable appenderAttachable) { + String appenderName = mic.subst(appenderRefModel.getRef()); + + if(!isAppenderDeclared(mic, appenderName)) { + addWarn("Appender named [" + appenderName + "] could not be found. Skipping attachment to "+appenderAttachable+"."); + return; + } + + Map appenderBag = (Map) mic.getObjectMap().get(JoranConstants.APPENDER_BAG); + + Appender appender = appenderBag.get(appenderName); + if (appender == null) { + addError("Failed to find appender named [" + appenderName + "]"); + } else { + addInfo("Attaching appender named [" + appenderName + "] to " + appenderAttachable); + appenderAttachable.addAppender(appender); + } + + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ChainedModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ChainedModelFilter.java new file mode 100755 index 0000000000..389a83b9fe --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ChainedModelFilter.java @@ -0,0 +1,71 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import java.util.ArrayList; +import java.util.List; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public class ChainedModelFilter implements ModelFilter { + + List modelFilters = new ArrayList<>(); + + public ChainedModelFilter() { + } + + static public ChainedModelFilter newInstance() { + return new ChainedModelFilter(); + } + + public ChainedModelFilter allow(Class allowedType) { + modelFilters.add(new AllowModelFilter(allowedType)); + return this; + } + + public ChainedModelFilter deny(Class allowedType) { + modelFilters.add(new DenyModelFilter(allowedType)); + return this; + } + + public ChainedModelFilter denyAll() { + modelFilters.add(new DenyAllModelFilter()); + return this; + } + + public ChainedModelFilter allowAll() { + modelFilters.add(new AllowAllModelFilter()); + return this; + } + + @Override + public FilterReply decide(Model model) { + + for (ModelFilter modelFilter : modelFilters) { + FilterReply reply = modelFilter.decide(model); + + switch (reply) { + case ACCEPT: + case DENY: + return reply; + case NEUTRAL: + // next + } + } + return FilterReply.NEUTRAL; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ConversionRuleModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ConversionRuleModelHandler.java new file mode 100644 index 0000000000..aa0aabd657 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ConversionRuleModelHandler.java @@ -0,0 +1,79 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.model.ConversionRuleModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.pattern.DynamicConverter; +import ch.qos.logback.core.pattern.color.ConverterSupplierByClassName; +import ch.qos.logback.core.util.OptionHelper; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class ConversionRuleModelHandler extends ModelHandlerBase { + + private boolean inError; + + public ConversionRuleModelHandler(Context context) { + super(context); + } + + static public ConversionRuleModelHandler makeInstance(Context context, ModelInterpretationContext mic) { + return new ConversionRuleModelHandler(context); + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ConversionRuleModel conversionRuleModel = (ConversionRuleModel) model; + String converterClass = conversionRuleModel.getClassName(); + + if (OptionHelper.isNullOrEmptyOrAllSpaces(converterClass)) { + addWarn("Missing className. This should have been caught earlier."); + inError = true; + return; + } else { + converterClass = mic.getImport(converterClass); + } + + String conversionWord = conversionRuleModel.getConversionWord(); + + + try { + Map> ruleRegistry = (Map>) context + .getObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS); + if (ruleRegistry == null) { + ruleRegistry = new HashMap<>(); + context.putObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS, ruleRegistry); + } + // put the new rule into the rule registry + addInfo("registering conversion word " + conversionWord + " with class [" + converterClass + "]"); + ConverterSupplierByClassName converterSupplierByClassName = new ConverterSupplierByClassName(conversionWord, converterClass); + converterSupplierByClassName.setContext(getContext()); + ruleRegistry.put(conversionWord, converterSupplierByClassName); + } catch (Exception oops) { + inError = true; + String errorMsg = "Could not add conversion rule to PatternLayout."; + addError(errorMsg); + } + + + + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefaultProcessor.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefaultProcessor.java new file mode 100755 index 0000000000..e606a4bc9b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefaultProcessor.java @@ -0,0 +1,308 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.function.Supplier; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelHandlerFactoryMethod; +import ch.qos.logback.core.model.NamedComponentModel; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.FilterReply; + +/** + * DefaultProcessor traverses the Model produced at an earlier step and performs actual + * configuration of logback according to the handlers it was given. + * + * @author Ceki Gülcü + * @since 1.3.0 + */ +public class DefaultProcessor extends ContextAwareBase { + + interface TraverseMethod { + int traverse(Model model, ModelFilter modelFiler); + } + + final protected ModelInterpretationContext mic; + final HashMap, ModelHandlerFactoryMethod> modelClassToHandlerMap = new HashMap<>(); + final HashMap, List>> modelClassToDependencyAnalyserMap = new HashMap<>(); + + ChainedModelFilter phaseOneFilter = new ChainedModelFilter(); + ChainedModelFilter phaseTwoFilter = new ChainedModelFilter(); + + public DefaultProcessor(Context context, ModelInterpretationContext mic) { + this.setContext(context); + this.mic = mic; + } + + public void addHandler(Class modelClass, ModelHandlerFactoryMethod modelFactoryMethod) { + + modelClassToHandlerMap.put(modelClass, modelFactoryMethod); + + ProcessingPhase phase = determineProcessingPhase(modelClass); + switch (phase) { + case FIRST: + getPhaseOneFilter().allow(modelClass); + break; + case SECOND: + getPhaseTwoFilter().allow(modelClass); + break; + default: + throw new IllegalArgumentException("unexpected value " + phase + " for model class " + modelClass.getName()); + } + } + + private ProcessingPhase determineProcessingPhase(Class modelClass) { + + PhaseIndicator phaseIndicator = modelClass.getAnnotation(PhaseIndicator.class); + if (phaseIndicator == null) { + return ProcessingPhase.FIRST; + } + + ProcessingPhase phase = phaseIndicator.phase(); + return phase; + } + + public void addAnalyser(Class modelClass, Supplier analyserSupplier) { + modelClassToDependencyAnalyserMap.computeIfAbsent(modelClass, x -> new ArrayList<>()).add(analyserSupplier); + } + + private void traversalLoop(TraverseMethod traverseMethod, Model model, ModelFilter modelfFilter, String phaseName) { + int LIMIT = 3; + for (int i = 0; i < LIMIT; i++) { + int handledModelCount = traverseMethod.traverse(model, modelfFilter); + if (handledModelCount == 0) + break; + } + } + + public void process(Model model) { + + if (model == null) { + addError("Expecting non null model to process"); + return; + } + initialObjectPush(); + + mainTraverse(model, getPhaseOneFilter()); + analyseDependencies(model); + traversalLoop(this::secondPhaseTraverse, model, getPhaseTwoFilter(), "phase 2"); + + addInfo("End of configuration."); + finalObjectPop(); + } + + private void finalObjectPop() { + mic.popObject(); + } + + private void initialObjectPush() { + mic.pushObject(context); + } + + public ChainedModelFilter getPhaseOneFilter() { + return phaseOneFilter; + } + + public ChainedModelFilter getPhaseTwoFilter() { + return phaseTwoFilter; + } + + + protected void analyseDependencies(Model model) { + + List> analyserSupplierList = modelClassToDependencyAnalyserMap.get(model.getClass()); + + if (analyserSupplierList != null) { + for (Supplier analyserSupplier : analyserSupplierList) { + ModelHandlerBase analyser = null; + + if (analyserSupplier != null) { + analyser = analyserSupplier.get(); + } + + if (analyser != null && !model.isSkipped()) { + callAnalyserHandleOnModel(model, analyser); + } + + if (analyser != null && !model.isSkipped()) { + callAnalyserPostHandleOnModel(model, analyser); + } + } + } + + for (Model m : model.getSubModels()) { + analyseDependencies(m); + } + + } + + private void callAnalyserPostHandleOnModel(Model model, ModelHandlerBase analyser) { + try { + analyser.postHandle(mic, model); + } catch (ModelHandlerException e) { + addError("Failed to invoke postHandle on model " + model.getTag(), e); + } + } + + private void callAnalyserHandleOnModel(Model model, ModelHandlerBase analyser) { + try { + analyser.handle(mic, model); + } catch (ModelHandlerException e) { + addError("Failed to traverse model " + model.getTag(), e); + } + } + + static final int DENIED = -1; + + private ModelHandlerBase createHandler(Model model) { + ModelHandlerFactoryMethod modelFactoryMethod = modelClassToHandlerMap.get(model.getClass()); + + if (modelFactoryMethod == null) { + addError("Can't handle model of type " + model.getClass() + " with tag: " + model.getTag() + " at line " + + model.getLineNumber()); + return null; + } + + ModelHandlerBase handler = modelFactoryMethod.make(context, mic); + if (handler == null) + return null; + if (!handler.isSupportedModelType(model)) { + addWarn("Handler [" + handler.getClass() + "] does not support " + model.idString()); + return null; + } + return handler; + } + + protected int mainTraverse(Model model, ModelFilter modelFiler) { + + FilterReply filterReply = modelFiler.decide(model); + if (filterReply == FilterReply.DENY) + return DENIED; + + int count = 0; + + try { + ModelHandlerBase handler = null; + boolean unhandled = model.isUnhandled(); + + if (unhandled) { + handler = createHandler(model); + if (handler != null) { + handler.handle(mic, model); + model.markAsHandled(); + count++; + } + } + // recurse into submodels handled or not + if (!model.isSkipped()) { + for (Model m : model.getSubModels()) { + count += mainTraverse(m, modelFiler); + } + } + + if (unhandled && handler != null) { + handler.postHandle(mic, model); + } + } catch (ModelHandlerException e) { + addError("Failed to traverse model " + model.getTag(), e); + } + return count; + } + + protected int secondPhaseTraverse(Model model, ModelFilter modelFilter) { + + FilterReply filterReply = modelFilter.decide(model); + if (filterReply == FilterReply.DENY) { + return 0; + } + + int count = 0; + + try { + + boolean allDependenciesStarted = allDependenciesStarted(model); + + ModelHandlerBase handler = null; + if (model.isUnhandled() && allDependenciesStarted) { + handler = createHandler(model); + if (handler != null) { + handler.handle(mic, model); + model.markAsHandled(); + count++; + } + } + + if (!allDependenciesStarted && !dependencyIsADirectSubmodel(model)) { + return count; + } + + if (!model.isSkipped()) { + for (Model m : model.getSubModels()) { + count += secondPhaseTraverse(m, modelFilter); + } + } + if (handler != null) { + handler.postHandle(mic, model); + } + } catch (ModelHandlerException e) { + addError("Failed to traverse model " + model.getTag(), e); + } + return count; + } + + private boolean dependencyIsADirectSubmodel(Model model) { + List dependecyNames = this.mic.getDependencyNamesForModel(model); + if (dependecyNames == null || dependecyNames.isEmpty()) { + return false; + } + for (Model submodel : model.getSubModels()) { + if (submodel instanceof NamedComponentModel) { + NamedComponentModel namedComponentModel = (NamedComponentModel) submodel; + String subModelName = namedComponentModel.getName(); + if (dependecyNames.contains(subModelName)) { + return true; + } + } + } + + return false; + } + + private boolean allDependenciesStarted(Model model) { + // assumes that DependencyDefinitions have been registered + List dependencyNames = mic.getDependencyNamesForModel(model); + + if (dependencyNames == null || dependencyNames.isEmpty()) { + return true; + } + for (String name : dependencyNames) { + boolean isRegistered = AppenderDeclarationAnalyser.isAppenderDeclared(mic, name); + if (!isRegistered) { + // non registered dependencies are not taken into account + continue; + } + boolean isStarted = mic.isNamedDependemcyStarted(name); + if (!isStarted) { + return false; + } + } + return true; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefineModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefineModelHandler.java new file mode 100755 index 0000000000..397d05821b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DefineModelHandler.java @@ -0,0 +1,128 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.model.DefineModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.spi.PropertyDefiner; +import ch.qos.logback.core.util.OptionHelper; + +/** + * Instantiate class for define property value. Get future property name and + * property definer class from attributes. Some property definer properties + * could be used. After defining put new property to context. + * + * @author Aleksey Didik + */ +public class DefineModelHandler extends ModelHandlerBase { + + boolean inError; + PropertyDefiner definer; + String propertyName; + Scope scope; + + public DefineModelHandler(Context context) { + super(context); + } + + static public DefineModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new DefineModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return DefineModel.class; + } + + @Override + public void handle(ModelInterpretationContext interpretationContext, Model model) throws ModelHandlerException { + definer = null; + inError = false; + propertyName = null; + + DefineModel defineModel = (DefineModel) model; + + propertyName = defineModel.getName(); + String scopeStr = defineModel.getScopeStr(); + + scope = ActionUtil.stringToScope(scopeStr); + + if (OptionHelper.isNullOrEmptyOrAllSpaces(propertyName)) { + addError("Missing property name for property definer. Near [" + model.getTag() + "] line " + + model.getLineNumber()); + inError = true; + } + + // read property definer class name + String className = defineModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addError("Missing class name for property definer. Near [" + model.getTag() + "] line " + + model.getLineNumber()); + inError = true; + } else { + className = interpretationContext.getImport(className); + } + + if (inError) + return; + + // try to instantiate property definer + try { + addInfo("About to instantiate property definer of type [" + className + "]"); + definer = (PropertyDefiner) OptionHelper.instantiateByClassName(className, PropertyDefiner.class, context); + definer.setContext(context); + interpretationContext.pushObject(definer); + } catch (Exception oops) { + inError = true; + addError("Could not create an PropertyDefiner of type [" + className + "].", oops); + throw new ModelHandlerException(oops); + } + + } + + /** + * Now property definer is initialized by all properties and we can put property + * value to context + */ + @Override + public void postHandle(ModelInterpretationContext interpretationContext, Model model) throws ModelHandlerException { + if (inError) { + return; + } + + Object o = interpretationContext.peekObject(); + + if (o != definer) { + addWarn("The object at the of the stack is not the property definer for property named [" + propertyName + + "] pushed earlier."); + } else { + interpretationContext.popObject(); + if (definer instanceof LifeCycle) + ((LifeCycle) definer).start(); + + // let's put defined property and value to context but only if it is + // not null + String propertyValue = definer.getPropertyValue(); + if (propertyValue != null) { + addInfo("Setting property " + propertyName + "=" + propertyValue + " in scope " + scope); + ActionUtil.setProperty(interpretationContext, propertyName, propertyValue, scope); + } + } + + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyAllModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyAllModelFilter.java new file mode 100755 index 0000000000..aa4ceaf14f --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyAllModelFilter.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public class DenyAllModelFilter implements ModelFilter { + + @Override + public FilterReply decide(Model model) { + return FilterReply.DENY; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyModelFilter.java new file mode 100755 index 0000000000..804e85be47 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DenyModelFilter.java @@ -0,0 +1,38 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public class DenyModelFilter implements ModelFilter { + + final Class deniedModelType; + + DenyModelFilter(Class deniedModelType) { + this.deniedModelType = deniedModelType; + } + + @Override + public FilterReply decide(Model model) { + + if (model.getClass() == deniedModelType) { + return FilterReply.DENY; + } + + return FilterReply.NEUTRAL; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/DependencyDefinition.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DependencyDefinition.java new file mode 100644 index 0000000000..47f599dabb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/DependencyDefinition.java @@ -0,0 +1,58 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; + +/** + * Defines the relation between a depender (of type Model) and a dependency name (String). + * + * Note that a depender may have multiple dependencies but + * {@link DependencyDefinition} applies to just one dependency relation. + * + * @author ceki + * + */ +public class DependencyDefinition { + + // OLD terminology: depender: a component of type Model which depends on a dependee + // NEW terminology: dependent: a component of type Model which depends on a dependency + Model depender; + // dependee or dependency: the string name of a component depended upon by the depender of type Model + String dependency; + + public DependencyDefinition(Model depender, String dependency) { + this.depender = depender; + this.dependency = dependency; + + + } + + public String getDependency() { + return dependency; + } + + public Model getDepender() { + return depender; + } + + + @Override + public String toString() { + return "DependencyDefinition{" + + "depender=" + depender + + ", dependency='" + dependency + '\'' + + '}'; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/EventEvaluatorModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/EventEvaluatorModelHandler.java new file mode 100755 index 0000000000..b640baf112 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/EventEvaluatorModelHandler.java @@ -0,0 +1,121 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import static ch.qos.logback.core.joran.action.Action.CLASS_ATTRIBUTE; + +import java.util.Map; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.boolex.EventEvaluator; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.model.EventEvaluatorModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.util.OptionHelper; + +public class EventEvaluatorModelHandler extends ModelHandlerBase { + + EventEvaluator evaluator; + boolean inError = false; + + public EventEvaluatorModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new EventEvaluatorModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return EventEvaluatorModel.class; + } + + @Override + public void handle(ModelInterpretationContext intercon, Model model) throws ModelHandlerException { + EventEvaluatorModel eem = (EventEvaluatorModel) model; + + String className = eem.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + String defaultClassName = defaultClassName(intercon, eem); + if (OptionHelper.isNullOrEmptyOrAllSpaces(defaultClassName)) { + inError = true; + addError("Mandatory \"" + CLASS_ATTRIBUTE + "\" attribute missing for "); + addError("No default classname could be found."); + return; + } else { + addInfo("Assuming default evaluator class [" + defaultClassName + "]"); + className = defaultClassName; + } + } else { + className = intercon.getImport(className); + } + + String evaluatorName = intercon.subst(eem.getName()); + try { + evaluator = (EventEvaluator) OptionHelper.instantiateByClassName(className, + ch.qos.logback.core.boolex.EventEvaluator.class, context); + evaluator.setContext(this.context); + evaluator.setName(evaluatorName); + intercon.pushObject(evaluator); + + } catch (Exception oops) { + inError = true; + addError("Could not create evaluator of type " + className + "].", oops); + } + + } + + private String defaultClassName(ModelInterpretationContext mic, EventEvaluatorModel model) { + DefaultNestedComponentRegistry registry = mic.getDefaultNestedComponentRegistry(); + return registry.findDefaultComponentTypeByTag(model.getTag()); + } + + @Override + public void postHandle(ModelInterpretationContext intercon, Model model) throws ModelHandlerException { + if (inError) { + return; + } + + if (evaluator instanceof LifeCycle) { + ((LifeCycle) evaluator).start(); + addInfo("Starting evaluator named [" + evaluator.getName() + "]"); + } + + Object o = intercon.peekObject(); + + if (o != evaluator) { + addWarn("The object on the top the of the stack is not the evaluator pushed earlier."); + } else { + intercon.popObject(); + + try { + @SuppressWarnings("unchecked") + Map> evaluatorMap = (Map>) context + .getObject(CoreConstants.EVALUATOR_MAP); + if (evaluatorMap == null) { + addError("Could not find EvaluatorMap"); + } else { + evaluatorMap.put(evaluator.getName(), evaluator); + } + } catch (Exception ex) { + addError("Could not set evaluator named [" + evaluator + "].", ex); + } + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/FileCollisionAnalyser.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/FileCollisionAnalyser.java new file mode 100644 index 0000000000..3ae79e25c5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/FileCollisionAnalyser.java @@ -0,0 +1,129 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.FileAppender; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.rolling.helper.FileNamePattern; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +@PhaseIndicator(phase = ProcessingPhase.DEPENDENCY_ANALYSIS) +public class FileCollisionAnalyser extends ModelHandlerBase { + + public FileCollisionAnalyser(Context context) { + super(context); + } + + @Override + protected Class getSupportedModelClass() { + return AppenderModel.class; + } + + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + AppenderModel appenderModel = (AppenderModel) model; + + String originalClassName = appenderModel.getClassName(); + String className = mic.getImport(originalClassName); + + String appenderName = appenderModel.getName(); + + if (!fileAppenderOrRollingFileAppender(className)) { + return; + } + + String tagName0 = "file"; + checkForCollisions(mic, MapKey.FILE_COLLISION_MAP_KEY, appenderModel, appenderName, tagName0); + + String tagName1 = "fileNamePattern"; + checkForCollisions(mic, MapKey.RFA_FILENAME_COLLISION_MAP, appenderModel, appenderName, tagName1); + } + + private static boolean fileAppenderOrRollingFileAppender(String className) { + return FileAppender.class.getName().equals(className) || RollingFileAppender.class.getName().equals(className); + } + + + boolean tagPredicate(Model model, String tagName) { + return (model instanceof ImplicitModel) && tagName.equals(model.getTag()); + } + + enum MapKey { + FILE_COLLISION_MAP_KEY, RFA_FILENAME_COLLISION_MAP + } + + private void checkForCollisions(ModelInterpretationContext mic, MapKey mapKey, AppenderModel appenderModel, String appenderName, final String tagName) { + + + Stream streamLevel1 = appenderModel.getSubModels().stream(); + Stream streamLevel2 = appenderModel.getSubModels().stream().flatMap(child -> child.getSubModels().stream()); + + List matchingModels = Stream.concat(streamLevel1, streamLevel2).filter(m -> tagPredicate(m, tagName)).collect(Collectors.toList()); + + //List matchingModels = appenderModel.getSubModels().stream().filter(m -> tagPredicate(m, tagName)).collect(Collectors.toList()); + + if(!matchingModels.isEmpty()) { + ImplicitModel implicitModel = (ImplicitModel) matchingModels.get(0); + String bodyValue = mic.subst(implicitModel.getBodyText()); + + + Map faileCollisionMap = getCollisionMapByKey(mic, mapKey); + + Optional> collision = faileCollisionMap.entrySet() + .stream() + .filter(entry -> bodyValue.equals(entry.getValue())) + .findFirst(); + + if (collision.isPresent()) { + addErrorForCollision(tagName, appenderName, collision.get().getKey(), bodyValue); + appenderModel.markAsHandled(); + appenderModel.deepMarkAsSkipped(); + } else { + // add to collision map if and only if no collision detected + // reasoning: single entry is as effective as multiple entries for collision detection + faileCollisionMap.put(appenderName, bodyValue); + } + } + } + + private Map getCollisionMapByKey(ModelInterpretationContext mic, MapKey mapKey) { + Map map = (Map) mic.getObjectMap().get(mapKey.name()); + if(map == null) { + map = new HashMap<>(); + mic.getObjectMap().put(mapKey.name(), map); + } + return map; + } + + + static public final String COLLISION_DETECTED = "Collision detected. Skipping initialization of appender named [%s]"; + static public final String COLLISION_MESSAGE = "In appender [%s] option '%s' has the same value '%s' as that set for appender [%s] defined earlier"; + private void addErrorForCollision(String optionName, String appenderName, String previousAppenderName, String optionValue) { + addError(String.format(COLLISION_DETECTED, appenderName)); + addError(String.format(COLLISION_MESSAGE, appenderName, optionName, optionValue, previousAppenderName)); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImplicitModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImplicitModelHandler.java new file mode 100755 index 0000000000..c3447fabe1 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImplicitModelHandler.java @@ -0,0 +1,239 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.ImcplicitActionDataForBasicProperty; +import ch.qos.logback.core.joran.action.ImplicitModelData; +import ch.qos.logback.core.joran.action.ImplicitModelDataForComplexProperty; +import ch.qos.logback.core.joran.spi.NoAutoStartUtil; +import ch.qos.logback.core.joran.util.PropertySetter; +import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.model.ComponentModel; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelConstants; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.util.AggregationType; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +public class ImplicitModelHandler extends ModelHandlerBase { + + private final BeanDescriptionCache beanDescriptionCache; + private ImplicitModelData implicitModelData; + + static public final String IGNORING_UNKNOWN_PROP = "Ignoring unknown property"; + + boolean inError = false; + + public ImplicitModelHandler(Context context, BeanDescriptionCache beanDescriptionCache) { + super(context); + this.beanDescriptionCache = beanDescriptionCache; + } + + protected Class getSupportedModelClass() { + return ImplicitModel.class; + } + + static public ImplicitModelHandler makeInstance(Context context, ModelInterpretationContext mic) { + BeanDescriptionCache beanDescriptionCache = mic.getBeanDescriptionCache(); + return new ImplicitModelHandler(context, beanDescriptionCache); + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) { + + ImplicitModel implicitModel = (ImplicitModel) model; + + // calling intercon.peekObject with an empty stack will throw an exception + if (mic.isObjectStackEmpty()) { + inError = true; + return; + } + String nestedElementTagName = implicitModel.getTag(); + + Object o = mic.peekObject(); + PropertySetter parentBean = new PropertySetter(beanDescriptionCache, o); + parentBean.setContext(context); + + AggregationType aggregationType = parentBean.computeAggregationType(nestedElementTagName); + + switch (aggregationType) { + case NOT_FOUND: + addWarn(IGNORING_UNKNOWN_PROP+" [" + nestedElementTagName + "] in [" + o.getClass().getName() + "]"); + this.inError = true; + // no point in processing submodels + implicitModel.markAsSkipped(); + return; + case AS_BASIC_PROPERTY: + case AS_BASIC_PROPERTY_COLLECTION: + ImcplicitActionDataForBasicProperty adBasicProperty = new ImcplicitActionDataForBasicProperty(parentBean, + aggregationType, nestedElementTagName); + this.implicitModelData = adBasicProperty; + doBasicProperty(mic, implicitModel, adBasicProperty); + return; + // we only push action data if NestComponentIA is applicable + case AS_COMPLEX_PROPERTY_COLLECTION: + case AS_COMPLEX_PROPERTY: + Class propertyType = parentBean.getTypeForComplexProperty(nestedElementTagName, aggregationType); + ImplicitModelDataForComplexProperty imdForComplexProperty = new ImplicitModelDataForComplexProperty(parentBean, + aggregationType, nestedElementTagName); + imdForComplexProperty.setExpectedPropertyType(propertyType); + this.implicitModelData = imdForComplexProperty; + doComplex(mic, implicitModel, imdForComplexProperty); + return; + default: + addError("PropertySetter.computeAggregationType returned " + aggregationType); + return; + } + + } + + void doBasicProperty(ModelInterpretationContext interpretationContext, Model model, + ImcplicitActionDataForBasicProperty actionData) { + String finalBody = interpretationContext.subst(model.getBodyText()); + // get the action data object pushed in isApplicable() method call + // IADataForBasicProperty actionData = (IADataForBasicProperty) + // actionDataStack.peek(); + switch (actionData.aggregationType) { + case AS_BASIC_PROPERTY: + actionData.parentBean.setProperty(actionData.propertyName, finalBody); + break; + case AS_BASIC_PROPERTY_COLLECTION: + actionData.parentBean.addBasicProperty(actionData.propertyName, finalBody); + break; + default: + addError("Unexpected aggregationType " + actionData.aggregationType); + } + } + + public void doComplex(ModelInterpretationContext interpretationContext, ComponentModel componentModel, + ImplicitModelDataForComplexProperty imdForComplexProperty) { + + String propertyClassName = componentModel.getClassName(); + // perform variable name substitution + String substPropertyClassName = interpretationContext.subst(propertyClassName); + + String fqcn = interpretationContext.getImport(substPropertyClassName); + + Class propertyClass = null; + try { + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(fqcn)) { + propertyClass = Loader.loadClass(fqcn, context); + } else { + // guess class name via implicit rules + PropertySetter parentBean = imdForComplexProperty.parentBean; + propertyClass = parentBean.getClassNameViaImplicitRules(imdForComplexProperty.propertyName, + imdForComplexProperty.getAggregationType(), interpretationContext.getDefaultNestedComponentRegistry()); + } + + if (propertyClass == null) { + imdForComplexProperty.inError = true; + String errMsg = "Could not find an appropriate class for property [" + componentModel.getTag() + "]"; + addError(errMsg); + return; + } + + if (OptionHelper.isNullOrEmptyOrAllSpaces(fqcn)) { + addInfo("Assuming default type [" + propertyClass.getName() + "] for [" + componentModel.getTag() + + "] property"); + } + + + + Class expectedPropertyType = imdForComplexProperty.getExpectedPropertyType(); + + Object object = OptionHelper.instantiateClassWithSuperclassRestriction(propertyClass, expectedPropertyType); + + imdForComplexProperty.setNestedComplexProperty(object); + + // pass along the context + if (imdForComplexProperty.getNestedComplexProperty() instanceof ContextAware) { + ((ContextAware) imdForComplexProperty.getNestedComplexProperty()).setContext(this.context); + } + // addInfo("Pushing component [" + localName + // + "] on top of the object stack."); + interpretationContext.pushObject(imdForComplexProperty.getNestedComplexProperty()); + + } catch (Exception oops) { + imdForComplexProperty.inError = true; + String msg = "Could not create component [" + componentModel.getTag() + "] of type [" + fqcn + "]"; + addError(msg, oops); + } + } + + @Override + public void postHandle(ModelInterpretationContext intercon, Model model) { + if (inError) { + return; + } + + if(implicitModelData == null) + return; + + // the action data can in an incorrect state, in which case we need to + // disengage + if(implicitModelData.inError) { + return; + } + if (implicitModelData instanceof ImplicitModelDataForComplexProperty) { + postHandleComplex(intercon, model, (ImplicitModelDataForComplexProperty) implicitModelData); + } + + } + + private void postHandleComplex(ModelInterpretationContext mic, Model model, + ImplicitModelDataForComplexProperty imdComplex) { + + PropertySetter nestedBean = new PropertySetter(beanDescriptionCache, + imdComplex.getNestedComplexProperty()); + nestedBean.setContext(context); + + // have the nested element point to its parent if possible + if (nestedBean.computeAggregationType(ModelConstants.PARENT_PROPPERTY_KEY) == AggregationType.AS_COMPLEX_PROPERTY) { + nestedBean.setComplexProperty(ModelConstants.PARENT_PROPPERTY_KEY, imdComplex.parentBean.getObj()); + } + + // start the nested complex property if it implements LifeCycle and is not + // marked with a @NoAutoStart annotation + Object nestedComplexProperty = imdComplex.getNestedComplexProperty(); + if (NoAutoStartUtil.shouldBeStarted(nestedComplexProperty)) { + ((LifeCycle) nestedComplexProperty).start(); + } + + Object o = mic.peekObject(); + + if (o != imdComplex.getNestedComplexProperty()) { + addError("The object on the top the of the stack is not the component pushed earlier."); + } else { + mic.popObject(); + // Now let us attach the component + switch (imdComplex.aggregationType) { + case AS_COMPLEX_PROPERTY: + imdComplex.parentBean.setComplexProperty(model.getTag(), imdComplex.getNestedComplexProperty()); + + break; + case AS_COMPLEX_PROPERTY_COLLECTION: + imdComplex.parentBean.addComplexProperty(model.getTag(), imdComplex.getNestedComplexProperty()); + break; + default: + addError("Unexpected aggregationType " + imdComplex.aggregationType); + } + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImportModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImportModelHandler.java new file mode 100644 index 0000000000..c8ec4a2a4a --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ImportModelHandler.java @@ -0,0 +1,70 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.model.ImportModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.util.OptionHelper; + +public class ImportModelHandler extends ModelHandlerBase { + + public ImportModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new ImportModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return ImportModel.class; + } + + @Override + public void handle(ModelInterpretationContext intercon, Model model) throws ModelHandlerException { + ImportModel importModel = (ImportModel) model; + + String className = importModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addWarn("Empty className not allowed"); + return; + } + + String stem = extractStem(className); + if (stem == null) { + addWarn("[" + className + "] could not be imported due to incorrect format"); + return; + } + + intercon.addImport(stem, className); + + } + + String extractStem(String className) { + if (className == null) + return null; + + int lastDotIndex = className.lastIndexOf(CoreConstants.DOT); + if (lastDotIndex == -1) + return null; + if ((lastDotIndex + 1) == className.length()) + return null; + return className.substring(lastDotIndex + 1); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/IncludeModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/IncludeModelHandler.java new file mode 100644 index 0000000000..1862984851 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/IncludeModelHandler.java @@ -0,0 +1,160 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.GenericXMLConfigurator; +import ch.qos.logback.core.joran.event.SaxEvent; +import ch.qos.logback.core.joran.event.SaxEventRecorder; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.joran.util.ConfigurationWatchListUtil; +import ch.qos.logback.core.model.IncludeModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.spi.ErrorCodes; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; +import java.util.List; +import java.util.function.Supplier; + +import static ch.qos.logback.core.joran.JoranConstants.CONFIGURATION_TAG; +import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG; + +/** + * @since 1.5.5 + */ +public class IncludeModelHandler extends ResourceHandlerBase { + boolean inError = false; + + public IncludeModelHandler(Context context) { + super(context); + } + + static public IncludeModelHandler makeInstance(Context context, ModelInterpretationContext mic) { + return new IncludeModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return IncludeModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + IncludeModel includeModel = (IncludeModel) model; + + URL topURL = mic.getTopURL(); + Boolean topScan = mic.getTopScanBoolean(); + Model modelFromIncludedFile = buildModelFromIncludedFile(mic, topURL, topScan, includeModel); + if (modelFromIncludedFile == null) { + warnIfRequired("Failed to build include model from included file"); + return; + } + processModelFromIncludedFile(includeModel, modelFromIncludedFile); + } + + /** + * This method is called by logback-tyler at TylerConfigurator run-time. + * + * @param capc + * @param includeModel + * @throws ModelHandlerException + * @since 1.5.11 + */ + public Model buildModelFromIncludedFile(ContextAwarePropertyContainer capc, URL topURL, Boolean topScan, IncludeModel includeModel) throws ModelHandlerException { + + this.optional = OptionHelper.toBoolean(includeModel.getOptional(), false); + + if (!checkAttributes(includeModel)) { + inError = true; + return null; + } + + + URL inputURL = getInputURL(capc, includeModel); + if (inputURL == null) { + inError = true; + return null; + } + + InputStream in = openURL(inputURL); + if (in == null) { + inError = true; + return null; + } + + updateConfigurationWatchList(inputURL, topURL, topScan); + + + SaxEventRecorder recorder = null; + + try { + recorder = populateSaxEventRecorder(in); + + List saxEvents = recorder.getSaxEventList(); + if (saxEvents.isEmpty()) { + addWarn("Empty sax event list"); + return null; + } + + Supplier jcSupplier = capc.getConfiguratorSupplier(); + if (jcSupplier == null) { + addError("null configurator supplier. Abandoning inclusion of [" + attributeInUse + "]"); + inError = true; + return null; + } + + GenericXMLConfigurator genericXMLConfigurator = jcSupplier.get(); + genericXMLConfigurator.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG); + + Model modelFromIncludedFile = genericXMLConfigurator.buildModelFromSaxEventList(recorder.getSaxEventList()); + return modelFromIncludedFile; + } catch (JoranException e) { + inError = true; + addError("Error processing XML data in [" + attributeInUse + "]", e); + return null; + } + } + + private void processModelFromIncludedFile(IncludeModel includeModel, Model modelFromIncludedFile) { + includeModel.getSubModels().addAll(modelFromIncludedFile.getSubModels()); + } + + public SaxEventRecorder populateSaxEventRecorder(final InputStream inputStream) throws JoranException { + SaxEventRecorder recorder = new SaxEventRecorder(context); + recorder.recordEvents(inputStream); + return recorder; + } + + private void updateConfigurationWatchList(URL inputURL, URL topURL, Boolean topScan) throws ModelHandlerException { + + if(topScan == Boolean.TRUE) { + if(topURL != null) { + ConfigurationWatchListUtil.addToWatchList(context, inputURL); + } else { + addWarn("No top URL for XML configuration file. Will not add [" + inputURL + "] to watch list."); + } + } + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/InsertFromJNDIModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/InsertFromJNDIModelHandler.java new file mode 100644 index 0000000000..a5555fb6fb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/InsertFromJNDIModelHandler.java @@ -0,0 +1,93 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.model.InsertFromJNDIModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.util.PropertyModelHandlerHelper; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.util.JNDIUtil; +import ch.qos.logback.core.util.OptionHelper; + +public class InsertFromJNDIModelHandler extends ModelHandlerBase { + + public InsertFromJNDIModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new InsertFromJNDIModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return InsertFromJNDIModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + InsertFromJNDIModel ifjm = (InsertFromJNDIModel) model; + detachedHandle(mic, ifjm); + } + + /** + * + * @param capc + * @param ifjm + * @since 1.5.11 + */ + public void detachedHandle(ContextAwarePropertyContainer capc, InsertFromJNDIModel ifjm) { + int errorCount = 0; + String envEntryName = capc.subst(ifjm.getEnvEntryName()); + String asKey = capc.subst(ifjm.getAs()); + + String scopeStr = capc.subst(ifjm.getScopeStr()); + Scope scope = ActionUtil.stringToScope(scopeStr); + + String envEntryValue; + + if (OptionHelper.isNullOrEmptyOrAllSpaces(envEntryName)) { + addError("[" + InsertFromJNDIModel.ENV_ENTRY_NAME_ATTR + "] missing"); + errorCount++; + } + + if (OptionHelper.isNullOrEmptyOrAllSpaces(asKey)) { + addError("[" + InsertFromJNDIModel.AS_ATTR + "] missing"); + errorCount++; + } + + if (errorCount != 0) { + return; + } + + try { + javax.naming.Context ctx = JNDIUtil.getInitialContext(); + envEntryValue = JNDIUtil.lookupString(ctx, envEntryName); + if (OptionHelper.isNullOrEmptyOrAllSpaces(envEntryValue)) { + addError("[" + envEntryName + "] has null or empty value"); + } else { + addInfo("Setting variable [" + asKey + "] to [" + envEntryValue + "] in [" + scope + "] scope"); + PropertyModelHandlerHelper.setProperty(capc, asKey, envEntryValue, scope); + } + } catch (Exception e) { + addError("Failed to lookup JNDI env-entry [" + envEntryName + "]"); + } + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelFilter.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelFilter.java new file mode 100755 index 0000000000..8b304a44e5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelFilter.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.FilterReply; + +public interface ModelFilter { + + FilterReply decide(Model model); + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerBase.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerBase.java new file mode 100755 index 0000000000..98b7cb68e9 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerBase.java @@ -0,0 +1,60 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.spi.ContextAwareBase; + +abstract public class ModelHandlerBase extends ContextAwareBase { + + public ModelHandlerBase(Context context) { + setContext(context); + } + + /** + * Subclasses should return the subclass of Model that they expect to handle. + * + * The default implementation assumes that all Model classes are supported. This + * a very lax assumption which is usually not true. + * + * @return supported model class + * @see ModelHandlerBase#isSupportedModelType(Model) + */ + protected Class getSupportedModelClass() { + // Assume lax default where all model objects are supported + return Model.class; + } + + protected boolean isSupportedModelType(Model model) { + Class modelClass = getSupportedModelClass(); + if (modelClass.isInstance(model)) { + return true; + } else { + addError("This handler can only handle models of type [" + modelClass + "]"); + return false; + } + } + + abstract public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException; + + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + // let specialized handlers override + } + + public String toString() { + return this.getClass().getName(); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerException.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerException.java new file mode 100755 index 0000000000..afbdea3983 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelHandlerException.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +public class ModelHandlerException extends Exception { + + private static final long serialVersionUID = -6486247349285796564L; + + public ModelHandlerException() { + } + + public ModelHandlerException(final Throwable rootCause) { + super(rootCause); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelInterpretationContext.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelInterpretationContext.java new file mode 100644 index 0000000000..9ca7f90d74 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ModelInterpretationContext.java @@ -0,0 +1,339 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Stack; +import java.util.function.Supplier; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.GenericXMLConfigurator; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.util.VariableSubstitutionsHelper; +import ch.qos.logback.core.spi.AppenderAttachable; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; + +public class ModelInterpretationContext extends ContextAwareBase implements ContextAwarePropertyContainer { + + Stack objectStack; + Stack modelStack; + + + URL topURL; + Boolean topScanBoolean = null; + + /** + * A supplier of JoranConfigurator instances. + * + * May be null. + * + * @since 1.5.5 + */ + Supplier configuratorSupplier; + + + Map objectMap; + protected VariableSubstitutionsHelper variableSubstitutionsHelper; + protected Map importMap; + + final private BeanDescriptionCache beanDescriptionCache; + final DefaultNestedComponentRegistry defaultNestedComponentRegistry = new DefaultNestedComponentRegistry(); + List dependencyDefinitionList = new ArrayList<>(); + final List startedDependees = new ArrayList<>(); + + Object configuratorHint; + + Model topModel; + + public ModelInterpretationContext(Context context) { + this(context, null); + } + + public ModelInterpretationContext(Context context, Object configuratorHint) { + this.context = context; + this.configuratorHint = configuratorHint; + this.objectStack = new Stack<>(); + this.modelStack = new Stack<>(); + this.beanDescriptionCache = new BeanDescriptionCache(context); + objectMap = new HashMap<>(5); + variableSubstitutionsHelper = new VariableSubstitutionsHelper(context); + importMap = new HashMap<>(5); + } + + public ModelInterpretationContext(ModelInterpretationContext otherMic) { + this(otherMic.context, otherMic.configuratorHint); + importMap = new HashMap<>(otherMic.importMap); + variableSubstitutionsHelper = new VariableSubstitutionsHelper(context, otherMic.getCopyOfPropertyMap()); + defaultNestedComponentRegistry.duplicate(otherMic.getDefaultNestedComponentRegistry()); + createAppenderBags(); + } + + public Map getObjectMap() { + return objectMap; + } + + public void createAppenderBags() { + objectMap.put(JoranConstants.APPENDER_BAG, new HashMap>()); + objectMap.put(JoranConstants.APPENDER_REF_BAG, new HashMap>()); + } + + public Model getTopModel() { + return topModel; + } + + public void setTopModel(Model topModel) { + this.topModel = topModel; + } + + // modelStack ================================= + + public void pushModel(Model m) { + modelStack.push(m); + } + + public Model peekModel() { + return modelStack.peek(); + } + + public boolean isModelStackEmpty() { + return modelStack.isEmpty(); + } + + public Model popModel() { + return modelStack.pop(); + } + + // =================== object stack + + public Stack getObjectStack() { + return objectStack; + } + + public boolean isObjectStackEmpty() { + return objectStack.isEmpty(); + } + + public Object peekObject() { + return objectStack.peek(); + } + + public void pushObject(Object o) { + objectStack.push(o); + } + + public Object popObject() { + return objectStack.pop(); + } + + public Object getObject(int i) { + return objectStack.get(i); + } + + // ===================== END object stack + + public Object getConfiguratorHint() { + return configuratorHint; + } + + public void setConfiguratorHint(Object configuratorHint) { + this.configuratorHint = configuratorHint; + } + + public BeanDescriptionCache getBeanDescriptionCache() { + return beanDescriptionCache; + } + + /** + * Performs variable substitution on the provided {@code ref} string. + * + *

Value substitution will follow the order

+ *
    + *
  1. properties defined in this {@link ModelInterpretationContext}
  2. + *
  3. properties defined in the {@link Context context} of this {@link ModelInterpretationContext}
  4. + *
  5. System properties
  6. + *
  7. Environment variables
  8. + *
+ * + *

If value substitution occurs it will be output as a status message, unless marked confidential, that is, + * if {@code ref} contains the case-insensitive strings PASSWORD, SECRET or CONFIDENTIAL.

+ * + * @param ref the string that may contain variables to be substituted; can be {@code null} + * @return the string with substitutions applied if applicable; may return {@code null} if {@code ref} is {@code null} + */ + public String subst(String ref) { + + String substituted = variableSubstitutionsHelper.subst(ref); + if(ref != null && !ref.equals(substituted) ) { + String sanitized = variableSubstitutionsHelper.sanitizeIfConfidential(ref, substituted); + addInfo("value \""+sanitized+"\" substituted for \""+ref+"\""); + } + return substituted; + } + + public DefaultNestedComponentRegistry getDefaultNestedComponentRegistry() { + return defaultNestedComponentRegistry; + } + + // ================================== dependencies + + public void addDependencyDefinition(DependencyDefinition dd) { + dependencyDefinitionList.add(dd); + } + + public List getDependencyDefinitions() { + return Collections.unmodifiableList(dependencyDefinitionList); + } + + public List getDependencyNamesForModel(Model model) { + List dependencyList = new ArrayList<>(); + for (DependencyDefinition dd : dependencyDefinitionList) { + if (dd.getDepender() == model) { + dependencyList.add(dd.getDependency()); + } + } + return dependencyList; + } + + public boolean hasDependers(String dependencyName) { + + if (dependencyName == null || dependencyName.trim().length() == 0) { + new IllegalArgumentException("Empty dependeeName name not allowed here"); + } + + for (DependencyDefinition dd : dependencyDefinitionList) { + if (dd.dependency.equals(dependencyName)) + return true; + } + + return false; + } + + + public void markStartOfNamedDependee(String name) { + startedDependees.add(name); + } + + public boolean isNamedDependemcyStarted(String name) { + return startedDependees.contains(name); + } + + // ========================================== object map + + /** + * Add a property to the properties of this execution context. If the property + * exists already, it is overwritten. + */ + @Override + public void addSubstitutionProperty(String key, String value) { + variableSubstitutionsHelper.addSubstitutionProperty(key, value); + } + + /** + * If a key is found in propertiesMap then return it. Otherwise, delegate to the + * context. + */ + public String getProperty(String key) { + return variableSubstitutionsHelper.getProperty(key); + } + + @Override + public Map getCopyOfPropertyMap() { + return variableSubstitutionsHelper.getCopyOfPropertyMap(); + } + + // imports =================================================================== + + /** + * Add an import to the importMao + * + * @param stem the class to import + * @param fqcn the fully qualified name of the class + * + * @since 1.3 + */ + public void addImport(String stem, String fqcn) { + importMap.put(stem, fqcn); + } + + public Map getImportMapCopy() { + return new HashMap<>(importMap); + } + + + /** + * Given a stem, get the fully qualified name of the class corresponding to the + * stem. For unknown stems, returns the stem as is. If stem is null, null is + * returned. + * + * @param stem may be null + * @return fully qualified name of the class corresponding to the stem. For + * unknown stems, returns the stem as is. If stem is null, null is + * returned. + * @since 1.3 + */ + public String getImport(String stem) { + if (stem == null) + return null; + + String result = importMap.get(stem); + if (result == null) + return stem; + else + return result; + } + + /** + * Returns a supplier of {@link GenericXMLConfigurator} instance. The returned value may be null. + * + * @return a supplier of {@link GenericXMLConfigurator} instance, may be null + */ + @Override + public Supplier getConfiguratorSupplier() { + return this.configuratorSupplier; + } + + /** + * + * @param configuratorSupplier + */ + public void setConfiguratorSupplier(Supplier configuratorSupplier) { + this.configuratorSupplier = configuratorSupplier; + } + + + public URL getTopURL() { + return topURL; + } + + public void setTopURL(URL topURL) { + this.topURL = topURL; + } + + public Boolean getTopScanBoolean() { + return topScanBoolean; + } + public void setTopScanBoolean(Boolean topScanBoolean) { + this.topScanBoolean = topScanBoolean; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/NOPModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/NOPModelHandler.java new file mode 100755 index 0000000000..0daed8ef5c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/NOPModelHandler.java @@ -0,0 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; + +public class NOPModelHandler extends ModelHandlerBase { + + public NOPModelHandler(Context context) { + super(context); + } + + static public NOPModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new NOPModelHandler(context); + } + + @Override + public void handle(ModelInterpretationContext interpretationContext, Model model) { + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/PhaseIndicator.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/PhaseIndicator.java new file mode 100644 index 0000000000..e169bffbab --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/PhaseIndicator.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface PhaseIndicator { + ProcessingPhase phase() default ProcessingPhase.FIRST; +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessingPhase.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessingPhase.java new file mode 100644 index 0000000000..38901a20a8 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessingPhase.java @@ -0,0 +1,27 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +/** + * An enumeration of processing phases. + * + * @author Ceki Gülcü + * @since 1.3.0 + * + */ +public enum ProcessingPhase { + FIRST, + DEPENDENCY_ANALYSIS, + SECOND; +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessorException.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessorException.java new file mode 100755 index 0000000000..8645fb39d0 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ProcessorException.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +public class ProcessorException extends Exception { + private static final long serialVersionUID = 2245242609539650480L; + + public ProcessorException() { + } + + public ProcessorException(final Throwable rootCause) { + super(rootCause); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/PropertyModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/PropertyModelHandler.java new file mode 100755 index 0000000000..73ec5365eb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/PropertyModelHandler.java @@ -0,0 +1,56 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ModelConstants; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.util.PropertyModelHandlerHelper; +import ch.qos.logback.core.util.Loader; + +public class PropertyModelHandler extends ModelHandlerBase { + + public PropertyModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new PropertyModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return PropertyModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) { + + PropertyModel propertyModel = (PropertyModel) model; + PropertyModelHandlerHelper propertyModelHandlerHelper = new PropertyModelHandlerHelper(this); + propertyModelHandlerHelper.setContext(context); + propertyModelHandlerHelper.handlePropertyModel(mic, propertyModel); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/RefContainerDependencyAnalyser.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/RefContainerDependencyAnalyser.java new file mode 100644 index 0000000000..2b3ad5fbf6 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/RefContainerDependencyAnalyser.java @@ -0,0 +1,67 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; + +/** + *

RefContainerDependencyAnalyser pushes relevant models into the modelStack + * of ModelInterpretationContext.

+ * + *

Relevant models are LoggerModel, RootLoggerModel and AppenderModel as defined + * in {@link ch.qos.logback.core.joran.ModelClassToModelHandlerLinkerBase#link} + * method.

+ * + *

This class could have been called RefContainerDependencyAnalysisHelper.

+ * + * @author Ceki Gülcü + * + */ +@PhaseIndicator(phase = ProcessingPhase.DEPENDENCY_ANALYSIS) +public class RefContainerDependencyAnalyser extends ModelHandlerBase { + + final Class modelClass; + + public RefContainerDependencyAnalyser(Context context, Class modelClass) { + super(context); + this.modelClass = modelClass; + } + + @Override + protected boolean isSupportedModelType(Model model) { + + if (modelClass.isInstance(model)) { + return true; + } + + StringBuilder buf = new StringBuilder("This handler can only handle models of type "); + buf.append(modelClass.getName()); + addError(buf.toString()); + return false; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + mic.pushModel(model); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + Model poppedModel = mic.popModel(); + if (model != poppedModel) { + addError("Popped model [" + poppedModel + "] different than expected [" + model + "]"); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ResourceHandlerBase.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ResourceHandlerBase.java new file mode 100644 index 0000000000..dc3beb9d2d --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ResourceHandlerBase.java @@ -0,0 +1,141 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.ResourceModel; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URL; + +abstract public class ResourceHandlerBase extends ModelHandlerBase { + + protected String attributeInUse; + protected boolean optional; + + protected ResourceHandlerBase(Context context) { + super(context); + } + + protected InputStream openURL(URL url) { + try { + return url.openStream(); + } catch (IOException e) { + warnIfRequired("Failed to open [" + url.toString() + "]"); + return null; + } + } + + protected boolean checkAttributes(ResourceModel resourceModel) { + String fileAttribute = resourceModel.getFile(); + String urlAttribute = resourceModel.getUrl(); + String resourceAttribute = resourceModel.getResource(); + + int count = 0; + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(fileAttribute)) { + count++; + } + if (!OptionHelper.isNullOrEmptyOrAllSpaces(urlAttribute)) { + count++; + } + if (!OptionHelper.isNullOrEmptyOrAllSpaces(resourceAttribute)) { + count++; + } + + if (count == 0) { + addError("One of \"path\", \"resource\" or \"url\" attributes must be set."); + return false; + } else if (count > 1) { + addError("Only one of \"file\", \"url\" or \"resource\" attributes should be set."); + return false; + } else if (count == 1) { + return true; + } + throw new IllegalStateException("Count value [" + count + "] is not expected"); + } + + + protected String getAttribureInUse() { + return this.attributeInUse; + } + + protected URL getInputURL(ContextAwarePropertyContainer contextAwarePropertyContainer, ResourceModel resourceModel) { + String fileAttribute = resourceModel.getFile(); + String urlAttribute = resourceModel.getUrl(); + String resourceAttribute = resourceModel.getResource(); + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(fileAttribute)) { + this.attributeInUse = contextAwarePropertyContainer.subst(fileAttribute); + return filePathAsURL(attributeInUse); + } + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(urlAttribute)) { + this.attributeInUse = contextAwarePropertyContainer.subst(urlAttribute); + return attributeToURL(attributeInUse); + } + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(resourceAttribute)) { + this.attributeInUse = contextAwarePropertyContainer.subst(resourceAttribute); + return resourceAsURL(attributeInUse); + } + // given preceding checkAttributes() check we cannot reach this line + throw new IllegalStateException("A URL stream should have been returned at this stage"); + + } + + protected URL filePathAsURL(String path) { + URI uri = new File(path).toURI(); + try { + return uri.toURL(); + } catch (MalformedURLException e) { + // impossible to get here + e.printStackTrace(); + return null; + } + } + + protected URL attributeToURL(String urlAttribute) { + try { + return new URL(urlAttribute); + } catch (MalformedURLException mue) { + String errMsg = "URL [" + urlAttribute + "] is not well formed."; + addError(errMsg, mue); + return null; + } + } + + protected URL resourceAsURL(String resourceAttribute) { + URL url = Loader.getResourceBySelfClassLoader(resourceAttribute); + if (url == null) { + warnIfRequired("Could not find resource corresponding to [" + resourceAttribute + "]"); + return null; + } else + return url; + } + + protected void warnIfRequired(String msg) { + if (!optional) { + addWarn(msg); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/SequenceNumberGeneratorModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/SequenceNumberGeneratorModelHandler.java new file mode 100644 index 0000000000..bbd512998a --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/SequenceNumberGeneratorModelHandler.java @@ -0,0 +1,85 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SequenceNumberGeneratorModel; +import ch.qos.logback.core.spi.BasicSequenceNumberGenerator; +import ch.qos.logback.core.spi.SequenceNumberGenerator; +import ch.qos.logback.core.util.OptionHelper; + +public class SequenceNumberGeneratorModelHandler extends ModelHandlerBase { + + SequenceNumberGenerator sequenceNumberGenerator; + private boolean inError; + + public SequenceNumberGeneratorModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new SequenceNumberGeneratorModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return SequenceNumberGeneratorModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + SequenceNumberGeneratorModel sequenceNumberGeneratorModel = (SequenceNumberGeneratorModel) model; + String className = sequenceNumberGeneratorModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addWarn("Missing className. This should have been caught earlier."); + inError = true; + return; + } else { + className = mic.getImport(className); + } + + try { + addInfo("About to instantiate SequenceNumberGenerator of type [" + className + "]"); + + sequenceNumberGenerator = (SequenceNumberGenerator) OptionHelper.instantiateByClassName(className, + SequenceNumberGenerator.class, context); + sequenceNumberGenerator.setContext(context); + + mic.pushObject(sequenceNumberGenerator); + } catch (Exception e) { + inError = true; + addError("Could not create a SequenceNumberGenerator of type [" + className + "].", e); + throw new ModelHandlerException(e); + } + } + + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + if (inError) { + return; + } + + Object o = mic.peekObject(); + if (o != sequenceNumberGenerator) { + addWarn("The object at the of the stack is not the hook pushed earlier."); + } else { + mic.popObject(); + + addInfo("Registering "+o+" with context."); + context.setSequenceNumberGenerator(sequenceNumberGenerator); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/SerializeModelModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/SerializeModelModelHandler.java new file mode 100644 index 0000000000..00923fdbd4 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/SerializeModelModelHandler.java @@ -0,0 +1,93 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SerializeModelModel; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.time.Instant; +import java.time.format.DateTimeFormatter; + +import static ch.qos.logback.core.CoreConstants.FILE_TIMESTAMP_PATTERN; +import static ch.qos.logback.core.CoreConstants.MODEL_CONFIG_FILE_EXTENSION; + +public class SerializeModelModelHandler extends ModelHandlerBase { + + public SerializeModelModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new SerializeModelModelHandler(context); + } + + @Override + public void handle(ModelInterpretationContext modelInterpretationContext, Model model) + throws ModelHandlerException { + + + Object configuratorHint = modelInterpretationContext.getConfiguratorHint(); + + if(configuratorHint != null && configuratorHint.getClass().getName().equals("ch.qos.logback.classic.joran.SerializedModelConfigurator")) { + addInfo("Skipping model serialization as calling configurator is already model based."); + return; + } + + if (!(model instanceof SerializeModelModel)) { + addWarn("Model parameter is not of type SerializeModelModel. Skipping serialization of model structure"); + return; + } + + SerializeModelModel serializeModelModel = (SerializeModelModel) model; + + Model topModel = modelInterpretationContext.getTopModel(); + + if (topModel == null) { + addWarn("Could not find top most model. Skipping serialization of model structure."); + return; + } + + String fileStr = serializeModelModel.getFile(); + if (fileStr == null) { + DateTimeFormatter dft = DateTimeFormatter.ofPattern(FILE_TIMESTAMP_PATTERN); + Instant now = Instant.now(); + String timestamp = dft.format(now); + fileStr = "logback-" + timestamp + MODEL_CONFIG_FILE_EXTENSION; + addInfo("For model serialization, using default file destination [" + fileStr + "]"); + } else { + fileStr = modelInterpretationContext.subst(fileStr); + } + + writeModel(fileStr, topModel); + } + + private void writeModel(String fileStr, Model firstModel) { + + addInfo("Serializing model to file ["+fileStr+"]"); + + try (FileOutputStream fos = new FileOutputStream(fileStr)) { + ObjectOutputStream oos = new ObjectOutputStream(fos); + oos.writeObject(firstModel); + oos.flush(); + oos.close(); + } catch (IOException e) { + addError("IO failure while serializing Model ["+fileStr+"]"); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/ShutdownHookModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ShutdownHookModelHandler.java new file mode 100755 index 0000000000..7cc44be0ef --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/ShutdownHookModelHandler.java @@ -0,0 +1,96 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.hook.DefaultShutdownHook; +import ch.qos.logback.core.hook.ShutdownHook; +import ch.qos.logback.core.hook.ShutdownHookBase; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ShutdownHookModel; +import ch.qos.logback.core.util.ContextUtil; +import ch.qos.logback.core.util.DynamicClassLoadingException; +import ch.qos.logback.core.util.IncompatibleClassException; +import ch.qos.logback.core.util.OptionHelper; + +public class ShutdownHookModelHandler extends ModelHandlerBase { + + static final String OLD_SHUTDOWN_HOOK_CLASSNAME = "ch.qos.logback.core.hook.DelayingShutdownHook"; + static final String DEFAULT_SHUTDOWN_HOOK_CLASSNAME = DefaultShutdownHook.class.getName(); + static public final String RENAME_WARNING = OLD_SHUTDOWN_HOOK_CLASSNAME + " was renamed as "+ DEFAULT_SHUTDOWN_HOOK_CLASSNAME; + + public ShutdownHookModelHandler(Context context) { + super(context); + } + boolean inError = false; + ShutdownHook hook = null; + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new ShutdownHookModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return ShutdownHookModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) { + + ShutdownHookModel shutdownHookModel = (ShutdownHookModel) model; + + String className = shutdownHookModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; + addInfo("Assuming className [" + className + "]"); + } else { + className = mic.getImport(className); + if(className.equals(OLD_SHUTDOWN_HOOK_CLASSNAME)) { + className = DEFAULT_SHUTDOWN_HOOK_CLASSNAME; + addWarn(RENAME_WARNING); + addWarn("Please use the new class name"); + } + } + + addInfo("About to instantiate shutdown hook of type [" + className + "]"); + + try { + hook = (ShutdownHookBase) OptionHelper.instantiateByClassName(className, ShutdownHookBase.class, context); + hook.setContext(context); + } catch (IncompatibleClassException | DynamicClassLoadingException e) { + addError("Could not create a shutdown hook of type [" + className + "].", e); + inError = true; + return; + } + + mic.pushObject(hook); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + if (inError) { + return; + } + Object o = mic.peekObject(); + + if (o != hook) { + addWarn("The object on the top the of the stack is not the hook object pushed earlier."); + } else { + ContextUtil contextUtil = new ContextUtil(context); + contextUtil.addOrReplaceShutdownHook(hook); + mic.popObject(); + } + } +} \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/StatusListenerModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/StatusListenerModelHandler.java new file mode 100755 index 0000000000..b875edabd5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/StatusListenerModelHandler.java @@ -0,0 +1,97 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.StatusListenerModel; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.status.StatusListener; +import ch.qos.logback.core.util.OptionHelper; + +public class StatusListenerModelHandler extends ModelHandlerBase { + + boolean inError = false; + Boolean effectivelyAdded = null; + StatusListener statusListener = null; + + public StatusListenerModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new StatusListenerModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return StatusListenerModel.class; + } + + @Override + public void handle(ModelInterpretationContext ic, Model model) throws ModelHandlerException { + + StatusListenerModel slModel = (StatusListenerModel) model; + + String className = slModel.getClassName(); + + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addError("Empty class name for StatusListener"); + inError = true; + return; + } else { + className = ic.getImport(className); + } + + try { + statusListener = (StatusListener) OptionHelper.instantiateByClassName(className, StatusListener.class, + context); + effectivelyAdded = ic.getContext().getStatusManager().add(statusListener); + if (statusListener instanceof ContextAware) { + ((ContextAware) statusListener).setContext(context); + } + addInfo("Added status listener of type [" + slModel.getClassName() + "]"); + ic.pushObject(statusListener); + } catch (Exception e) { + inError = true; + addError("Could not create an StatusListener of type [" + slModel.getClassName() + "].", e); + throw new ModelHandlerException(e); + } + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model m) { + if (inError) { + return; + } + + if (isEffectivelyAdded() && statusListener instanceof LifeCycle) { + ((LifeCycle) statusListener).start(); + } + Object o = mic.peekObject(); + if (o != statusListener) { + addWarn("The object at the of the stack is not the statusListener pushed earlier."); + } else { + mic.popObject(); + } + } + + private boolean isEffectivelyAdded() { + if (effectivelyAdded == null) + return false; + return effectivelyAdded; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/TimestampModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/TimestampModelHandler.java new file mode 100755 index 0000000000..e22fbde5e3 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/TimestampModelHandler.java @@ -0,0 +1,82 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.joran.action.ActionUtil.Scope; +import ch.qos.logback.core.joran.action.TimestampAction; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.TimestampModel; +import ch.qos.logback.core.util.CachingDateFormatter; +import ch.qos.logback.core.util.OptionHelper; + +public class TimestampModelHandler extends ModelHandlerBase { + + boolean inError = false; + + public TimestampModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new TimestampModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return TimestampModel.class; + } + + @Override + public void handle(ModelInterpretationContext interpretationContext, Model model) { + TimestampModel timestampModel = (TimestampModel) model; + String keyStr = timestampModel.getKey(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(keyStr)) { + addError("Attribute named [" + Action.KEY_ATTRIBUTE + "] cannot be empty"); + inError = true; + } + String datePatternStr = timestampModel.getDatePattern(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(datePatternStr)) { + addError("Attribute named [" + TimestampAction.DATE_PATTERN_ATTRIBUTE + "] cannot be empty"); + inError = true; + } + + String timeReferenceStr = timestampModel.getTimeReference(); + long timeReference; + if (TimestampModel.CONTEXT_BIRTH.equalsIgnoreCase(timeReferenceStr)) { + addInfo("Using context birth as time reference."); + timeReference = context.getBirthTime(); + } else { + timeReference = System.currentTimeMillis(); + addInfo("Using current interpretation time, i.e. now, as time reference."); + } + + if (inError) + return; + + String scopeStr = timestampModel.getScopeStr(); + Scope scope = ActionUtil.stringToScope(scopeStr); + + CachingDateFormatter sdf = new CachingDateFormatter(datePatternStr); + String val = sdf.format(timeReference); + + addInfo("Adding property to the context with key=\"" + keyStr + "\" and value=\"" + val + "\" to the " + scope + + " scope"); + ActionUtil.setProperty(interpretationContext, keyStr, val, scope); + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ByPropertiesConditionModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ByPropertiesConditionModelHandler.java new file mode 100644 index 0000000000..1c3826dfcb --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ByPropertiesConditionModelHandler.java @@ -0,0 +1,99 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor.conditional; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.boolex.PropertyCondition; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.ByPropertiesConditionModel; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.util.OptionHelper; + +import static ch.qos.logback.core.model.conditional.IfModel.BranchState.ELSE_BRANCH; +import static ch.qos.logback.core.model.conditional.IfModel.BranchState.IF_BRANCH; + +public class ByPropertiesConditionModelHandler extends ModelHandlerBase { + + private boolean inError = false; + PropertyCondition propertyEvaluator; + + public ByPropertiesConditionModelHandler(Context context) { + super(context); + } + + @Override + protected Class getSupportedModelClass() { + return ByPropertiesConditionModel.class; + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext mic) { + return new ByPropertiesConditionModelHandler(context); + } + + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ByPropertiesConditionModel byPropertiesConditionModel = (ByPropertiesConditionModel) model; + String className = byPropertiesConditionModel.getClassName(); + if (OptionHelper.isNullOrEmptyOrAllSpaces(className)) { + addWarn("Missing className. This should have been caught earlier."); + inError = true; + return; + } else { + className = mic.getImport(className); + } + try { + addInfo("About to instantiate PropertyEvaluator of type [" + className + "]"); + + propertyEvaluator = (PropertyCondition) OptionHelper.instantiateByClassName(className, + PropertyCondition.class, context); + propertyEvaluator.setContext(context); + propertyEvaluator.setLocalPropertyContainer(mic); + mic.pushObject(propertyEvaluator); + } catch (Exception e) { + inError = true; + mic.pushObject(IfModel.BranchState.IN_ERROR); + addError("Could not create a SequenceNumberGenerator of type [" + className + "].", e); + throw new ModelHandlerException(e); + } + } + + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + if (inError) { + return; + } + Object o = mic.peekObject(); + if (o != propertyEvaluator) { + addWarn("The object at the of the stack is not the propertyEvaluator instance pushed earlier."); + } else { + mic.popObject(); + } + + propertyEvaluator.start(); + if(!propertyEvaluator.isStarted()) { + addError("PropertyEvaluator of type ["+propertyEvaluator.getClass().getName()+"] did not start successfully."); + mic.pushObject(IfModel.BranchState.IN_ERROR); + return; + } + boolean evaluationResult = propertyEvaluator.evaluate(); + IfModel.BranchState branchState = evaluationResult ? IF_BRANCH : ELSE_BRANCH; + mic.pushObject(branchState); + + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ElseModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ElseModelHandler.java new file mode 100644 index 0000000000..5b87bb66d3 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ElseModelHandler.java @@ -0,0 +1,59 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor.conditional; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.ElseModel; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.IfModel.BranchState; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class ElseModelHandler extends ModelHandlerBase { + + public ElseModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new ElseModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return ElseModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ElseModel elseModel = (ElseModel) model; + + Model parent = mic.peekModel(); + + if (!(parent instanceof IfModel)) { + addError("Unexpected type for parent model [" + parent + "]"); + elseModel.markAsSkipped(); + return; + } + + IfModel ifModel = (IfModel) parent; + if(ifModel.getBranchState() != BranchState.ELSE_BRANCH) { + elseModel.deepMarkAsSkipped(); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/IfModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/IfModelHandler.java new file mode 100644 index 0000000000..e7638e5c25 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/IfModelHandler.java @@ -0,0 +1,155 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor.conditional; + +import ch.qos.logback.core.util.EnvUtil; +import ch.qos.logback.core.util.OptionHelper; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.conditional.Condition; +import ch.qos.logback.core.joran.conditional.PropertyEvalScriptBuilder; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.IfModel.BranchState; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.spi.ScanException; + +public class IfModelHandler extends ModelHandlerBase { + + + public static final String MISSING_JANINO_MSG = "Could not find Janino library on the class path. Skipping conditional processing."; + public static final String MISSING_JANINO_SEE = "See also " + CoreConstants.CODES_URL + "#ifJanino"; + + public static final String NEW_OPERATOR_DISALLOWED_MSG = "The 'condition' attribute may not contain the 'new' operator."; + public static final String NEW_OPERATOR_DISALLOWED_SEE = "See also " + CoreConstants.CODES_URL + "#conditionNew"; + + public static final String CONDITION_ATTR_DEPRECATED_MSG = "The 'condition' attribute in element is deprecated and slated for removal. Use element instead."; + public static final String CONDITION_ATTR_DEPRECATED_SEE = "See also " + CoreConstants.CODES_URL + "#conditionAttributeDeprecation"; + + enum Branch {IF_BRANCH, ELSE_BRANCH; } + + IfModel ifModel = null; + + public IfModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new IfModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return IfModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ifModel = (IfModel) model; + mic.pushModel(ifModel); + Object micTopObject = mic.peekObject(); + String conditionStr = ifModel.getCondition(); + emitDeprecationWarningIfNecessary(conditionStr); + + + if(micTopObject instanceof BranchState) { + BranchState branchState = (BranchState) micTopObject; + ifModel.setBranchState(branchState); + // consume the BranchState at top of the object stack + mic.popObject(); + } else { + janinoFallback(mic, model, conditionStr); + } + } + + private void janinoFallback(ModelInterpretationContext mic, Model model, String conditionStr) { + if (!EnvUtil.isJaninoAvailable()) { + addError(MISSING_JANINO_MSG); + addError(MISSING_JANINO_SEE); + return; + } + + Condition condition = null; + int lineNum = model.getLineNumber(); + + if (!OptionHelper.isNullOrEmptyOrAllSpaces(conditionStr)) { + try { + conditionStr = OptionHelper.substVars(conditionStr, mic, context); + } catch (ScanException e) { + addError("Failed to parse input [" + conditionStr + "] on line "+lineNum, e); + ifModel.setBranchState(BranchState.IN_ERROR); + return; + } + + // do not allow 'new' operator + if(hasNew(conditionStr)) { + addError(NEW_OPERATOR_DISALLOWED_MSG); + addError(NEW_OPERATOR_DISALLOWED_SEE); + return; + } + + try { + PropertyEvalScriptBuilder pesb = new PropertyEvalScriptBuilder(mic); + pesb.setContext(context); + condition = pesb.build(conditionStr); + } catch (Exception|NoClassDefFoundError e) { + ifModel.setBranchState(BranchState.IN_ERROR); + addError("Failed to parse condition [" + conditionStr + "] on line "+lineNum, e); + return; + } + + if (condition != null) { + boolean boolResult = condition.evaluate(); + addInfo("Condition ["+conditionStr+"] evaluated to "+boolResult+ " on line "+lineNum); + ifModel.setBranchState(boolResult); + } else { + addError("The condition variable is null. This should not occur."); + ifModel.setBranchState(BranchState.IN_ERROR); + return; + } + } + } + + private void emitDeprecationWarningIfNecessary(String conditionStr) { + if(!OptionHelper.isNullOrEmptyOrAllSpaces(conditionStr)) { + addWarn(CONDITION_ATTR_DEPRECATED_MSG); + addWarn(CONDITION_ATTR_DEPRECATED_SEE); + } + } + + + private boolean hasNew(String conditionStr) { + return conditionStr.contains("new "); + } + + @Override + public void postHandle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + if(mic.isModelStackEmpty()) { + addError("Unexpected unexpected empty model stack."); + return; + } + + Object o = mic.peekModel(); + if (o != ifModel) { + addWarn("The object [" + o + "] on the top the of the stack is not the expected [" + ifModel); + } else { + mic.popModel(); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ThenModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ThenModelHandler.java new file mode 100644 index 0000000000..f31773f6aa --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/processor/conditional/ThenModelHandler.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor.conditional; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.conditional.IfModel; +import ch.qos.logback.core.model.conditional.IfModel.BranchState; +import ch.qos.logback.core.model.conditional.ThenModel; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +import static ch.qos.logback.core.spi.ErrorCodes.MISSING_IF_EMPTY_MODEL_STACK; + +public class ThenModelHandler extends ModelHandlerBase { + + public ThenModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new ThenModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return ThenModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + ThenModel thenModel = (ThenModel) model; + + if(mic.isModelStackEmpty()) { + addError(MISSING_IF_EMPTY_MODEL_STACK); + thenModel.markAsSkipped(); + return; + } + Model parent = mic.peekModel(); + + if (!(parent instanceof IfModel)) { + addError("Unexpected type for parent model [" + parent + "]"); + thenModel.markAsSkipped(); + return; + } + + IfModel ifModel = (IfModel) parent; + if(ifModel.getBranchState() != BranchState.IF_BRANCH) { + thenModel.deepMarkAsSkipped(); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/util/PropertyModelHandlerHelper.java b/logback-core/src/main/java/ch/qos/logback/core/model/util/PropertyModelHandlerHelper.java new file mode 100644 index 0000000000..2de5525f70 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/util/PropertyModelHandlerHelper.java @@ -0,0 +1,191 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.util; + +import ch.qos.logback.core.joran.action.ActionUtil; +import ch.qos.logback.core.model.ModelConstants; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.spi.PropertyContainer; +import ch.qos.logback.core.util.ContextUtil; +import ch.qos.logback.core.util.Loader; +import ch.qos.logback.core.util.OptionHelper; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.Properties; + +/** + * Given a {@link PropertyModel} offers methods to inject properties into a {@link PropertyContainer}. + * + * @since 1.5.1 + */ +public class PropertyModelHandlerHelper extends ContextAwareBase { + + public static final String HANDLE_PROPERTY_MODEL_METHOD_NAME = "handlePropertyModel"; + + public PropertyModelHandlerHelper(ContextAware declaredOrigin) { + super(declaredOrigin); + } + + /** + * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer} + * parameter. + * + * @param capcm + * @param nameStr + * @param valueStr + * @param fileStr + * @param resourceStr + * @param scopeStr + * + */ + public void handlePropertyModel(ContextAwarePropertyContainer capcm, String nameStr, String valueStr, + String fileStr, String resourceStr, String scopeStr) { + PropertyModel propertyModel = new PropertyModel(); + propertyModel.setName(nameStr); + propertyModel.setValue(valueStr); + propertyModel.setFile(fileStr); + propertyModel.setResource(resourceStr); + + propertyModel.setScopeStr(scopeStr); + + handlePropertyModel(capcm, propertyModel); + } + + /** + * Given a {@link PropertyModel} inject relevant properties into the given {@link ContextAwarePropertyContainer} + * parameter. + * + * @param capc + * @param propertyModel + */ + public void handlePropertyModel(ContextAwarePropertyContainer capc, PropertyModel propertyModel) { + ActionUtil.Scope scope = ActionUtil.stringToScope(propertyModel.getScopeStr()); + + if (checkFileAttributeSanity(propertyModel)) { + String file = propertyModel.getFile(); + file = capc.subst(file); + try (FileInputStream istream = new FileInputStream(file)) { + PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope); + } catch (FileNotFoundException e) { + addError("Could not find properties file [" + file + "]."); + } catch (IOException | IllegalArgumentException e1) { // IllegalArgumentException is thrown in case the file + // is badly malformed, i.e a binary. + addError("Could not read properties file [" + file + "].", e1); + } + } else if (checkResourceAttributeSanity(propertyModel)) { + String resource = propertyModel.getResource(); + resource = capc.subst(resource); + URL resourceURL = Loader.getResourceBySelfClassLoader(resource); + if (resourceURL == null) { + addError("Could not find resource [" + resource + "]."); + } else { + try (InputStream istream = resourceURL.openStream();) { + PropertyModelHandlerHelper.loadAndSetProperties(capc, istream, scope); + } catch (IOException e) { + addError("Could not read resource file [" + resource + "].", e); + } + } + } else if (checkValueNameAttributesSanity(propertyModel)) { + // earlier versions performed Java '\' escapes for '\\' '\t' etc. Howevver, there is no + // need to do this. See RegularEscapeUtil.__UNUSED__basicEscape + String value = propertyModel.getValue(); + + // now remove both leading and trailing spaces + value = value.trim(); + value = capc.subst(value); + ActionUtil.setProperty(capc, propertyModel.getName(), value, scope); + + } else { + addError(ModelConstants.INVALID_ATTRIBUTES); + } + } + + public static boolean checkFileAttributeSanity(PropertyModel propertyModel) { + String file = propertyModel.getFile(); + String name = propertyModel.getName(); + String value = propertyModel.getValue(); + String resource = propertyModel.getResource(); + + return !(OptionHelper.isNullOrEmptyOrAllSpaces(file)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name) + && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(resource)); + } + + public static boolean checkResourceAttributeSanity(PropertyModel propertyModel) { + String file = propertyModel.getFile(); + String name = propertyModel.getName(); + String value = propertyModel.getValue(); + String resource = propertyModel.getResource(); + + return !(OptionHelper.isNullOrEmptyOrAllSpaces(resource)) && (OptionHelper.isNullOrEmptyOrAllSpaces(name) + && OptionHelper.isNullOrEmptyOrAllSpaces(value) && OptionHelper.isNullOrEmptyOrAllSpaces(file)); + } + + public static boolean checkValueNameAttributesSanity(PropertyModel propertyModel) { + String file = propertyModel.getFile(); + String name = propertyModel.getName(); + String value = propertyModel.getValue(); + String resource = propertyModel.getResource(); + return (!(OptionHelper.isNullOrEmptyOrAllSpaces(name) || OptionHelper.isNullOrEmptyOrAllSpaces(value)) && ( + OptionHelper.isNullOrEmptyOrAllSpaces(file) && OptionHelper.isNullOrEmptyOrAllSpaces(resource))); + } + + /** + * Add all the properties found in the argument named 'props' to an InterpretationContext. + */ + static public void setProperty(ContextAwarePropertyContainer capc, String key, String value, + ActionUtil.Scope scope) { + switch (scope) { + case LOCAL: + capc.addSubstitutionProperty(key, value); + break; + case CONTEXT: + capc.getContext().putProperty(key, value); + break; + case SYSTEM: + OptionHelper.setSystemProperty(capc, key, value); + } + } + + /** + * Add all the properties found in the argument named 'props' to an InterpretationContext. + */ + static public void setProperties(ContextAwarePropertyContainer capc, Properties props, ActionUtil.Scope scope) { + switch (scope) { + case LOCAL: + capc.addSubstitutionProperties(props); + break; + case CONTEXT: + ContextUtil cu = new ContextUtil(capc.getContext()); + cu.addProperties(props); + break; + case SYSTEM: + OptionHelper.setSystemProperties(capc, props); + } + } + + static public void loadAndSetProperties(ContextAwarePropertyContainer capc, InputStream istream, + ActionUtil.Scope scope) throws IOException { + Properties props = new Properties(); + props.load(istream); + PropertyModelHandlerHelper.setProperties(capc, props, scope); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/util/TagUtil.java b/logback-core/src/main/java/ch/qos/logback/core/model/util/TagUtil.java new file mode 100644 index 0000000000..61e005026c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/util/TagUtil.java @@ -0,0 +1,30 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.util; + +import ch.qos.logback.core.model.Model; + +public class TagUtil { + + public static String unifiedTag(Model aModel) { + String tag = aModel.getTag(); + + char first = tag.charAt(0); + if (Character.isUpperCase(first)) { + char lower = Character.toLowerCase(first); + return lower + tag.substring(1); + } else + return tag; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/model/util/VariableSubstitutionsHelper.java b/logback-core/src/main/java/ch/qos/logback/core/model/util/VariableSubstitutionsHelper.java new file mode 100644 index 0000000000..105478f172 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/model/util/VariableSubstitutionsHelper.java @@ -0,0 +1,160 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.util; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.spi.ContextAwarePropertyContainer; +import ch.qos.logback.core.spi.ScanException; +import ch.qos.logback.core.util.OptionHelper; + +import java.util.HashMap; +import java.util.Map; + +/** + * Helper methods to deal with properties. + * + *

This class acts as a small container for substitution properties and + * delegates actual variable substitution to {@link OptionHelper#substVars}. + * It also offers a convenience method to mask confidential property values + * (for example passwords) by returning a blurred placeholder.

+ * + * @since 1.5.1 + */ +public class VariableSubstitutionsHelper extends ContextAwareBase implements ContextAwarePropertyContainer { + + static final String PASSWORD = "password"; + static final String SECRET = "secret"; + static final String CONFIDENTIAL = "confidential"; + + static final String BLURRED_STR = "******"; + + protected Map propertiesMap; + + /** + * Create a helper backed by an empty property map. + * + * @param context the logback context to associate with this helper; may be null + */ + public VariableSubstitutionsHelper(Context context) { + this.setContext(context); + this.propertiesMap = new HashMap<>(); + } + + /** + * Create a helper pre-populated with the contents of {@code otherMap}. + * The provided map is copied and further modifications do not affect the + * original map. + * + * @param context the logback context to associate with this helper; may be null + * @param otherMap initial properties to copy; if null an empty map is created + */ + public VariableSubstitutionsHelper(Context context, Map otherMap) { + this.setContext(context); + this.propertiesMap = new HashMap<>(otherMap); + } + + /** + * Perform variable substitution on the provided reference string. + * + *

Returns {@code null} if {@code ref} is {@code null}. On parse errors + * the original input string is returned and an error is logged.

+ * + * @param ref the string possibly containing variables to substitute + * @return the string with substitutions applied, or {@code null} if {@code ref} was {@code null} + */ + @Override + public String subst(String ref) { + if (ref == null) { + return null; + } + + try { + return OptionHelper.substVars(ref, this, context); + } catch (ScanException | IllegalArgumentException e) { + addError("Problem while parsing [" + ref + "]", e); + return ref; + } + } + + /** + * Return a blurred placeholder for confidential properties. + * + *

If the property name {@code ref} contains any of the case-insensitive + * substrings {@code "password"}, {@code "secret"} or {@code "confidential"} + * this method returns a fixed blurred string ("******"). Otherwise, the + * supplied {@code substituted} value is returned unchanged.

+ * + * @param ref the property name to inspect; must not be {@code null} + * @param substituted the substituted value to return when the property is not confidential + * @return a blurred placeholder when the property appears confidential, otherwise {@code substituted} + * @throws IllegalArgumentException when {@code ref} is {@code null} + */ + public String sanitizeIfConfidential(String ref, String substituted) { + if(ref == null) { + throw new IllegalArgumentException("ref cannot be null"); + } + + String lowerCaseRef = ref.toLowerCase(); + + if(lowerCaseRef.contains(PASSWORD) || lowerCaseRef.contains(SECRET) || lowerCaseRef.contains(CONFIDENTIAL)) { + return BLURRED_STR; + } else + return substituted; + } + + /** + * Add or overwrite a substitution property. + * + *

Null keys or values are ignored. Values are trimmed before storing + * to avoid surprises caused by leading or trailing whitespace.

+ * + * @param key the property name; ignored if {@code null} + * @param value the property value; ignored if {@code null} + */ + @Override + public void addSubstitutionProperty(String key, String value) { + if (key == null || value == null) { + return; + } + // values with leading or trailing spaces are bad. We remove them now. + value = value.trim(); + propertiesMap.put(key, value); + } + + /** + * Retrieve a property value by name. + * + * @param key the property name + * @return the property value or {@code null} if not present + */ + @Override + public String getProperty(String key) { + return propertiesMap.get(key); + } + + /** + * Return a shallow copy of the internal property map. + * + *

The returned map is a copy and modifications to it do not affect the + * internal state of this helper.

+ * + * @return a copy of the property map + */ + @Override + public Map getCopyOfPropertyMap() { + return new HashMap<>(propertiesMap); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSSLSocketAppender.java b/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSSLSocketAppender.java index 251b20ed9d..467773b5ea 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSSLSocketAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSSLSocketAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -41,6 +41,7 @@ protected AbstractSSLSocketAppender() { /** * Gets an {@link SocketFactory} that produces SSL sockets using an * {@link SSLContext} that is derived from the appender's configuration. + * * @return socket factory */ @Override @@ -66,8 +67,9 @@ public void start() { /** * Gets the SSL configuration. - * @return SSL configuration; if no configuration has been set, a - * default configuration is returned + * + * @return SSL configuration; if no configuration has been set, a default + * configuration is returned */ public SSLConfiguration getSsl() { if (ssl == null) { @@ -78,6 +80,7 @@ public SSLConfiguration getSsl() { /** * Sets the SSL configuration. + * * @param ssl the SSL configuration to set */ public void setSsl(SSLConfiguration ssl) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSocketAppender.java b/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSocketAppender.java index e28e26c603..66d1441063 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSocketAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/AbstractSocketAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,13 +27,14 @@ import javax.net.SocketFactory; import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.spi.DeferredProcessingAware; import ch.qos.logback.core.spi.PreSerializationTransformer; import ch.qos.logback.core.util.CloseUtil; import ch.qos.logback.core.util.Duration; /** - * An abstract base for module specific {@code SocketAppender} - * implementations in other logback modules. + * An abstract base for module specific {@code SocketAppender} implementations + * in other logback modules. * * @author Ceki Gülcü * @author Sébastien Pennec @@ -54,20 +55,19 @@ public abstract class AbstractSocketAppender extends AppenderBase implemen public static final int DEFAULT_RECONNECTION_DELAY = 30000; /** - * Default size of the deque used to hold logging events that are destined - * for the remote peer. + * Default size of the deque used to hold logging events that are destined for + * the remote peer. */ public static final int DEFAULT_QUEUE_SIZE = 128; /** - * Default timeout when waiting for the remote server to accept our - * connection. + * Default timeout when waiting for the remote server to accept our connection. */ private static final int DEFAULT_ACCEPT_CONNECTION_DELAY = 5000; /** - * Default timeout for how long to wait when inserting an event into - * the BlockingQueue. + * Default timeout for how long to wait when inserting an event into the + * BlockingQueue. */ private static final int DEFAULT_EVENT_DELAY_TIMEOUT = 100; @@ -97,7 +97,8 @@ protected AbstractSocketAppender() { } /** - * Constructs a new appender using the given {@link QueueFactory} and {@link ObjectWriterFactory}. + * Constructs a new appender using the given {@link QueueFactory} and + * {@link ObjectWriterFactory}. */ AbstractSocketAppender(QueueFactory queueFactory, ObjectWriterFactory objectWriterFactory) { this.objectWriterFactory = objectWriterFactory; @@ -113,13 +114,14 @@ public void start() { int errorCount = 0; if (port <= 0) { errorCount++; - addError("No port was configured for appender" + name + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_port"); + addError("No port was configured for appender" + name + + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_port"); } if (remoteHost == null) { errorCount++; addError("No remote host was configured for appender" + name - + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); + + " For more information, please visit http://logback.qos.ch/codes.html#socket_no_host"); } if (queueSize == 0) { @@ -144,7 +146,7 @@ public void start() { deque = queueFactory.newLinkedBlockingDeque(queueSize); peerId = "remote peer " + remoteHost + ":" + port + ": "; connector = createConnector(address, port, 0, reconnectionDelay.getMilliseconds()); - task = getContext().getScheduledExecutorService().submit(new Runnable() { + task = getContext().getExecutorService().submit(new Runnable() { @Override public void run() { connectSocketAndDispatchEvents(); @@ -175,6 +177,12 @@ protected void append(E event) { return; try { + + // otherwise MDC information is not transferred. See also logback/issues/1010 + if(event instanceof DeferredProcessingAware) { + ((DeferredProcessingAware) event).prepareForDeferredProcessing(); + } + final boolean inserted = deque.offer(event, eventDelayLimit.getMilliseconds(), TimeUnit.MILLISECONDS); if (!inserted) { addInfo("Dropping event due to timeout limit of [" + eventDelayLimit + "] being exceeded"); @@ -191,8 +199,11 @@ private void connectSocketAndDispatchEvents() { ObjectWriter objectWriter = createObjectWriterForSocket(); addInfo(peerId + "connection established"); dispatchEvents(objectWriter); + } catch (javax.net.ssl.SSLHandshakeException she) { + // FIXME + Thread.sleep(DEFAULT_RECONNECTION_DELAY); } catch (IOException ex) { - addInfo(peerId + "connection failed: " + ex); + addInfo(peerId + "connection failed: ", ex); } finally { CloseUtil.closeQuietly(socket); socket = null; @@ -260,14 +271,14 @@ public void connectionFailed(SocketConnector connector, Exception ex) { /** * Creates a new {@link SocketConnector}. *

- * The default implementation creates an instance of {@link DefaultSocketConnector}. - * A subclass may override to provide a different {@link SocketConnector} - * implementation. + * The default implementation creates an instance of + * {@link DefaultSocketConnector}. A subclass may override to provide a + * different {@link SocketConnector} implementation. * - * @param address target remote address - * @param port target remote port + * @param address target remote address + * @param port target remote port * @param initialDelay delay before the first connection attempt - * @param retryDelay delay before a reconnection attempt + * @param retryDelay delay before a reconnection attempt * @return socket connector */ protected SocketConnector newConnector(InetAddress address, int port, long initialDelay, long retryDelay) { @@ -284,22 +295,24 @@ protected SocketFactory getSocketFactory() { } /** - * Post-processes an event before it is serialized for delivery to the - * remote receiver. + * Post-processes an event before it is serialized for delivery to the remote + * receiver. + * * @param event the event to post-process */ protected abstract void postProcessEvent(E event); /** - * Get the pre-serialization transformer that will be used to transform - * each event into a Serializable object before delivery to the remote - * receiver. + * Get the pre-serialization transformer that will be used to transform each + * event into a Serializable object before delivery to the remote receiver. + * * @return transformer object */ protected abstract PreSerializationTransformer getPST(); /** - * The RemoteHost property takes the name of of the host where a corresponding server is running. + * The RemoteHost property takes the name of the host where a + * corresponding server is running. */ public void setRemoteHost(String host) { remoteHost = host; @@ -313,8 +326,8 @@ public String getRemoteHost() { } /** - * The Port property takes a positive integer representing the port - * where the server is waiting for connections. + * The Port property takes a positive integer representing the port where + * the server is waiting for connections. */ public void setPort(int port) { this.port = port; @@ -329,8 +342,8 @@ public int getPort() { /** * The reconnectionDelay property takes a positive {@link Duration} value - * representing the time to wait between each failed connection attempt - * to the server. The default value of this option is to 30 seconds. + * representing the time to wait between each failed connection attempt to the + * server. The default value of this option is to 30 seconds. * *

* Setting this option to zero turns off reconnection capability. @@ -347,14 +360,13 @@ public Duration getReconnectionDelay() { } /** - * The queueSize property takes a non-negative integer representing - * the number of logging events to retain for delivery to the remote receiver. - * When the deque size is zero, event delivery to the remote receiver is - * synchronous. When the deque size is greater than zero, the - * {@link #append(Object)} method returns immediately after enqueing the - * event, assuming that there is space available in the deque. Using a - * non-zero deque length can improve performance by eliminating delays - * caused by transient network delays. + * The queueSize property takes a non-negative integer representing the + * number of logging events to retain for delivery to the remote receiver. When + * the deque size is zero, event delivery to the remote receiver is synchronous. + * When the deque size is greater than zero, the {@link #append(Object)} method + * returns immediately after enqueing the event, assuming that there is space + * available in the deque. Using a non-zero deque length can improve performance + * by eliminating delays caused by transient network delays. * * @param queueSize the deque size to set. */ @@ -388,11 +400,11 @@ public Duration getEventDelayLimit() { } /** - * Sets the timeout that controls how long we'll wait for the remote - * peer to accept our connection attempt. + * Sets the timeout that controls how long we'll wait for the remote peer to + * accept our connection attempt. *

- * This property is configurable primarily to support instrumentation - * for unit testing. + * This property is configurable primarily to support instrumentation for unit + * testing. * * @param acceptConnectionTimeout timeout value in milliseconds */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/AutoFlushingObjectWriter.java b/logback-core/src/main/java/ch/qos/logback/core/net/AutoFlushingObjectWriter.java index 6a781b75b0..2354b9b8e4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/AutoFlushingObjectWriter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/AutoFlushingObjectWriter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,9 @@ import java.io.ObjectOutputStream; /** - * Automatically flushes the underlying {@link java.io.ObjectOutputStream} immediately after calling - * it's {@link java.io.ObjectOutputStream#writeObject(Object)} method. + * Automatically flushes the underlying {@link java.io.ObjectOutputStream} + * immediately after calling it's + * {@link java.io.ObjectOutputStream#writeObject(Object)} method. * * @author Sebastian Gröbler */ @@ -32,8 +33,8 @@ public class AutoFlushingObjectWriter implements ObjectWriter { * Creates a new instance for the given {@link java.io.ObjectOutputStream}. * * @param objectOutputStream the stream to write to - * @param resetFrequency the frequency with which the given stream will be - * automatically reset to prevent a memory leak + * @param resetFrequency the frequency with which the given stream will be + * automatically reset to prevent a memory leak */ public AutoFlushingObjectWriter(ObjectOutputStream objectOutputStream, int resetFrequency) { this.objectOutputStream = objectOutputStream; @@ -48,8 +49,9 @@ public void write(Object object) throws IOException { } /** - * Failing to reset the object output stream every now and then creates a serious memory leak which - * is why the underlying stream will be reset according to the {@code resetFrequency}. + * Failing to reset the object output stream every now and then creates a + * serious memory leak which is why the underlying stream will be reset + * according to the {@code resetFrequency}. */ private void preventMemoryLeak() throws IOException { if (++writeCounter >= resetFrequency) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/DefaultSocketConnector.java b/logback-core/src/main/java/ch/qos/logback/core/net/DefaultSocketConnector.java index 426777b890..d219830086 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/net/DefaultSocketConnector.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/DefaultSocketConnector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -54,8 +54,8 @@ public DefaultSocketConnector(InetAddress address, int port, long initialDelay, * * @param address address of remote listener * @param port port of remote listener - * @param delayStrategy strategy for choosing the delay to impose before - * each connection attempt + * @param delayStrategy strategy for choosing the delay to impose before each + * connection attempt */ public DefaultSocketConnector(InetAddress address, int port, DelayStrategy delayStrategy) { this.address = address; @@ -64,7 +64,8 @@ public DefaultSocketConnector(InetAddress address, int port, DelayStrategy delay } /** - * Loops until the desired connection is established and returns the resulting connector. + * Loops until the desired connection is established and returns the resulting + * connector. */ public Socket call() throws InterruptedException { useDefaultsForMissingFields(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java b/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java index d1b7301ea4..295216d12f 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/HardenedObjectInputStream.java @@ -1,51 +1,75 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.core.net; import java.io.IOException; import java.io.InputStream; import java.io.InvalidClassException; +import java.io.ObjectInputFilter; import java.io.ObjectInputStream; import java.io.ObjectStreamClass; import java.util.ArrayList; import java.util.List; /** - * HardenedObjectInputStream restricts the set of classes that can be deserialized to a set of - * explicitly whitelisted classes. This prevents certain type of attacks from being successful. + * HardenedObjectInputStream restricts the set of classes that can be + * deserialized to a set of explicitly whitelisted classes. This prevents + * certain type of attacks from being successful. * - *

It is assumed that classes in the "java.lang" and "java.util" packages are - * always authorized.

+ *

+ * It is assumed that classes in the "java.lang" and "java.util" packages are + * always authorized. + *

* * @author Ceki Gülcü * @since 1.2.0 */ public class HardenedObjectInputStream extends ObjectInputStream { - final List whitelistedClassNames; - final static String[] JAVA_PACKAGES = new String[] { "java.lang", "java.util" }; + final private List whitelistedClassNames; + final private static String[] JAVA_PACKAGES = new String[] { "java.lang", "java.util" }; + final private static int DEPTH_LIMIT = 16; + final private static int ARRAY_LIMIT = 10000; - public HardenedObjectInputStream(InputStream in, String[] whilelist) throws IOException { + public HardenedObjectInputStream(InputStream in, String[] whitelist) throws IOException { super(in); - + this.initObjectFilter(); this.whitelistedClassNames = new ArrayList(); - if (whilelist != null) { - for (int i = 0; i < whilelist.length; i++) { - this.whitelistedClassNames.add(whilelist[i]); + if (whitelist != null) { + for (int i = 0; i < whitelist.length; i++) { + this.whitelistedClassNames.add(whitelist[i]); } } } + private void initObjectFilter() { + this.setObjectInputFilter(ObjectInputFilter.Config.createFilter( + "maxarray=" + ARRAY_LIMIT + ";maxdepth=" + DEPTH_LIMIT + ";" + )); + } public HardenedObjectInputStream(InputStream in, List whitelist) throws IOException { super(in); - + this.initObjectFilter(); this.whitelistedClassNames = new ArrayList(); this.whitelistedClassNames.addAll(whitelist); } @Override protected Class resolveClass(ObjectStreamClass anObjectStreamClass) throws IOException, ClassNotFoundException { - + String incomingClassName = anObjectStreamClass.getName(); - + if (!isWhitelisted(incomingClassName)) { throw new InvalidClassException("Unauthorized deserialization attempt", anObjectStreamClass.getName()); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/LoginAuthenticator.java b/logback-core/src/main/java/ch/qos/logback/core/net/LoginAuthenticator.java index 018545337d..fd99655c12 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/LoginAuthenticator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/LoginAuthenticator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,8 @@ */ package ch.qos.logback.core.net; -import javax.mail.Authenticator; -import javax.mail.PasswordAuthentication; +import jakarta.mail.Authenticator; +import jakarta.mail.PasswordAuthentication; /** * Used by SMTPAppender for authentication purposes. diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriter.java b/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriter.java index 9ac2e7c6a6..cd61c08efa 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -26,7 +26,8 @@ public interface ObjectWriter { * Writes an object to an output. * * @param object the {@link Object} to write - * @throws IOException in case input/output fails, details are defined by the implementation + * @throws IOException in case input/output fails, details are defined by the + * implementation */ void write(Object object) throws IOException; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriterFactory.java b/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriterFactory.java index e79cc4f175..52e0295c4e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriterFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ObjectWriterFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,10 +27,12 @@ public class ObjectWriterFactory { /** - * Creates a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} instance. + * Creates a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} + * instance. * * @param outputStream the underlying {@link java.io.OutputStream} to write to - * @return a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} instance + * @return a new {@link ch.qos.logback.core.net.AutoFlushingObjectWriter} + * instance * @throws IOException if an I/O error occurs while writing stream header */ public AutoFlushingObjectWriter newAutoFlushingObjectWriter(OutputStream outputStream) throws IOException { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/QueueFactory.java b/logback-core/src/main/java/ch/qos/logback/core/net/QueueFactory.java index 4493739af3..64ad99b4b2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/QueueFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/QueueFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -24,12 +24,12 @@ public class QueueFactory { /** - * Creates a new {@link LinkedBlockingDeque} with the given {@code capacity}. - * In case the given capacity is smaller than one it will automatically be + * Creates a new {@link LinkedBlockingDeque} with the given {@code capacity}. In + * case the given capacity is smaller than one it will automatically be * converted to one. * * @param capacity the capacity to use for the queue - * @param the type of elements held in the queue + * @param the type of elements held in the queue * @return a new instance of {@link ArrayBlockingQueue} */ public LinkedBlockingDeque newLinkedBlockingDeque(int capacity) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java index 99eadf70e0..3681655eb6 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,18 +18,18 @@ import java.util.Date; import java.util.List; import java.util.Properties; - -import javax.mail.Message; -import javax.mail.Multipart; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.AddressException; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeBodyPart; -import javax.mail.internet.MimeMessage; -import javax.mail.internet.MimeMultipart; +import java.util.concurrent.Future; + +import jakarta.mail.Message; +import jakarta.mail.Multipart; +import jakarta.mail.Session; +import jakarta.mail.Transport; +import jakarta.mail.internet.AddressException; +import jakarta.mail.internet.InternetAddress; +import jakarta.mail.internet.MimeBodyPart; +import jakarta.mail.internet.MimeMessage; +import jakarta.mail.internet.MimeMultipart; import javax.naming.Context; -import javax.naming.InitialContext; import ch.qos.logback.core.AppenderBase; import ch.qos.logback.core.CoreConstants; @@ -42,6 +42,7 @@ import ch.qos.logback.core.sift.Discriminator; import ch.qos.logback.core.spi.CyclicBufferTracker; import ch.qos.logback.core.util.ContentTypeUtil; +import ch.qos.logback.core.util.JNDIUtil; import ch.qos.logback.core.util.OptionHelper; // Contributors: @@ -59,6 +60,13 @@ */ public abstract class SMTPAppenderBase extends AppenderBase { + public static final String MAIL_SMTP_HOST_PK = "mail.smtp.host"; + public static final String MAIL_SMTP_PORT_PK = "mail.smtp.port"; + public static final String MAIL_SMTP_LOCALHOST_PK = "mail.smtp.localhost"; + public static final String MAIL_SMTP_AUTH_PK = "mail.smtp.auth"; + public static final String MAIL_SMTP_STARTTLS_ENABLE_PK = "mail.smtp.starttls.enable"; + public static final String MAIL_TRANSPORT_PROTOCOL_PK = "mail.transport.protocol"; + public static final String MAIL_SMTP_SSL_ENABLE_PK = "mail.smtp.ssl.enable"; static InternetAddress[] EMPTY_IA_ARRAY = new InternetAddress[0]; // ~ 14 days static final long MAX_DELAY_BETWEEN_STATUS_MESSAGES = 1228800 * CoreConstants.MILLIS_IN_ONE_SECOND; @@ -84,7 +92,7 @@ public abstract class SMTPAppenderBase extends AppenderBase { String localhost; boolean asynchronousSending = true; - + protected Future asynchronousSendingFuture = null; private String charsetEncoding = "UTF-8"; protected Session session; @@ -133,11 +141,11 @@ public void start() { private Session lookupSessionInJNDI() { addInfo("Looking up javax.mail.Session at JNDI location [" + jndiLocation + "]"); try { - Context initialContext = new InitialContext(); - Object obj = initialContext.lookup(jndiLocation); + Context initialContext = JNDIUtil.getInitialContext(); + Object obj = JNDIUtil.lookupObject(initialContext, jndiLocation); return (Session) obj; } catch (Exception e) { - addError("Failed to obtain javax.mail.Session from JNDI location [" + jndiLocation + "]"); + addError("Failed to obtain javax.mail.Session from JNDI location [" + jndiLocation + "]", e); return null; } } @@ -145,19 +153,20 @@ private Session lookupSessionInJNDI() { private Session buildSessionFromProperties() { Properties props = new Properties(OptionHelper.getSystemProperties()); if (smtpHost != null) { - props.put("mail.smtp.host", smtpHost); + props.put(MAIL_SMTP_HOST_PK, smtpHost); } - props.put("mail.smtp.port", Integer.toString(smtpPort)); + + props.put(MAIL_SMTP_PORT_PK, Integer.toString(smtpPort)); if (localhost != null) { - props.put("mail.smtp.localhost", localhost); + props.put(MAIL_SMTP_LOCALHOST_PK, localhost); } LoginAuthenticator loginAuthenticator = null; - if (username != null) { + if (!OptionHelper.isNullOrEmptyOrAllSpaces(username)) { loginAuthenticator = new LoginAuthenticator(username, password); - props.put("mail.smtp.auth", "true"); + props.put(MAIL_SMTP_AUTH_PK, "true"); } if (isSTARTTLS() && isSSL()) { @@ -165,11 +174,11 @@ private Session buildSessionFromProperties() { } else { if (isSTARTTLS()) { // see also http://jira.qos.ch/browse/LOGBACK-193 - props.put("mail.smtp.starttls.enable", "true"); - props.put("mail.transport.protocol", "true"); + props.put(MAIL_SMTP_STARTTLS_ENABLE_PK, "true"); + props.put(MAIL_TRANSPORT_PROTOCOL_PK, "true"); } if (isSSL()) { - props.put("mail.smtp.ssl.enable", "true"); + props.put(MAIL_SMTP_SSL_ENABLE_PK, "true"); } } @@ -179,8 +188,8 @@ private Session buildSessionFromProperties() { } /** - * Perform SMTPAppender specific appending actions, delegating some of them to - * a subclass and checking if the event triggers an e-mail to be sent. + * Perform SMTPAppender specific appending actions, delegating some of them to a + * subclass and checking if the event triggers an e-mail to be sent. */ protected void append(E eventObject) { @@ -203,7 +212,7 @@ protected void append(E eventObject) { if (asynchronousSending) { // perform actual sending asynchronously SenderRunnable senderRunnable = new SenderRunnable(cbClone, eventObject); - context.getScheduledExecutorService().execute(senderRunnable); + this.asynchronousSendingFuture = context.getExecutorService().submit(senderRunnable); } else { // synchronous sending sendBuffer(cbClone, eventObject); @@ -256,7 +265,8 @@ public boolean checkEntryConditions() { } if (this.layout == null) { - addError("No layout set for appender named [" + name + "]. For more information, please visit http://logback.qos.ch/codes.html#smtp_no_layout"); + addError("No layout set for appender named [" + name + + "]. For more information, please visit http://logback.qos.ch/codes.html#smtp_no_layout"); return false; } return true; @@ -290,7 +300,8 @@ private List parseAddress(E event) { InternetAddress[] tmp = InternetAddress.parse(emailAdrr, true); iaList.addAll(Arrays.asList(tmp)); } catch (AddressException e) { - addError("Could not parse email address for [" + toPatternLayoutList.get(i) + "] for event [" + event + "]", e); + addError("Could not parse email address for [" + toPatternLayoutList.get(i) + "] for event [" + event + + "]", e); return iaList; } } @@ -306,7 +317,7 @@ public List> getToList() { } /** - * Allows extend classes to update mime message (e.g.: Add headers) + * Allows to extend classes to update mime message (e.g.: Add headers) */ protected void updateMimeMsg(MimeMessage mimeMsg, CyclicBuffer cb, E lastEventObject) { } @@ -314,6 +325,7 @@ protected void updateMimeMsg(MimeMessage mimeMsg, CyclicBuffer cb, E lastEven /** * Send the contents of the cyclic buffer as an e-mail message. */ + @SuppressWarnings("null") protected void sendBuffer(CyclicBuffer cb, E lastEventObject) { // Note: this code already owns the monitor for this @@ -413,16 +425,16 @@ public String getSubject() { } /** - * The From option takes a string value which should be a e-mail - * address of the sender. + * The From option takes a string value which should be an e-mail address + * of the sender. */ public void setFrom(String from) { this.from = from; } /** - * The Subject option takes a string value which should be a the - * subject of the e-mail message. + * The Subject option takes a string value which should be the subject + * of the e-mail message. */ public void setSubject(String subject) { this.subjectStr = subject; @@ -438,7 +450,7 @@ public void setSMTPHost(String smtpHost) { } /** - * The smtpHost option takes a string value which should be a the host + * The smtpHost option takes a string value which should be the host * name of the SMTP server that will send the e-mail message. */ public void setSmtpHost(String smtpHost) { @@ -503,8 +515,9 @@ public String getLocalhost() { * Set the "mail.smtp.localhost" property to the value passed as parameter to * this method. * - *

Useful in case the hostname for the client host is not fully qualified - * and as a consequence the SMTP server rejects the clients HELO/EHLO command. + *

+ * Useful in case the hostname for the client host is not fully qualified and as + * a consequence the SMTP server rejects the clients HELO/EHLO command. *

* * @param localhost @@ -534,10 +547,11 @@ public boolean isAsynchronousSending() { } /** - * By default, SMTAppender transmits emails asynchronously. For synchronous email transmission set - * asynchronousSending to 'false'. + * By default, SMTAppender transmits emails asynchronously. For synchronous + * email transmission set asynchronousSending to 'false'. * - * @param asynchronousSending determines whether sending is done asynchronously or not + * @param asynchronousSending determines whether sending is done asynchronously + * or not * @since 1.0.4 */ public void setAsynchronousSending(boolean asynchronousSending) { @@ -583,8 +597,8 @@ public void setSSL(boolean ssl) { /** * The EventEvaluator option takes a string value representing the name * of the class implementing the {@link EventEvaluator} interface. A - * corresponding object will be instantiated and assigned as the event - * evaluator for the SMTPAppender. + * corresponding object will be instantiated and assigned as the event evaluator + * for the SMTPAppender. */ public void setEvaluator(EventEvaluator eventEvaluator) { this.eventEvaluator = eventEvaluator; @@ -619,8 +633,8 @@ public String getJndiLocation() { } /** - * Set the location where a {@link javax.mail.Session} resource is located in JNDI. Default value is - * "java:comp/env/mail/Session". + * Set the location where a {@link jakarta.mail.Session} resource is located in + * JNDI. Default value is "java:comp/env/mail/Session". * * @param jndiLocation * @since 1.0.6 @@ -634,7 +648,8 @@ public boolean isSessionViaJNDI() { } /** - * If set to true, a {@link javax.mail.Session} resource will be retrieved from JNDI. Default is false. + * If set to true, a {@link jakarta.mail.Session} resource will be retrieved from + * JNDI. Default is false. * * @param sessionViaJNDI whether to obtain a javax.mail.Session by JNDI * @since 1.0.6 diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SocketConnector.java b/logback-core/src/main/java/ch/qos/logback/core/net/SocketConnector.java index b6e5cc0ef8..46fd94a791 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/SocketConnector.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/SocketConnector.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,24 +21,24 @@ /** * A {@link Runnable} that (re)connects a socket. *

- * An implementation of this interface is responsible for repeatedly - * attempting to create a socket connection to a remote host. + * An implementation of this interface is responsible for repeatedly attempting + * to create a socket connection to a remote host. * * @author Carl Harris */ public interface SocketConnector extends Callable { /** - * An exception handler that is notified of all exceptions that occur - * during the (re)connection process. + * An exception handler that is notified of all exceptions that occur during the + * (re)connection process. */ public interface ExceptionHandler { void connectionFailed(SocketConnector connector, Exception ex); } /** - * Blocks the calling thread until a connection is successfully - * established. + * Blocks the calling thread until a connection is successfully established. + * * @return the connected socket * @throws InterruptedException */ @@ -48,6 +48,7 @@ public interface ExceptionHandler { * Sets the connector's exception handler. *

* The handler must be set before the {@link #call()} method is invoked. + * * @param exceptionHandler the handler to set */ void setExceptionHandler(ExceptionHandler exceptionHandler); @@ -55,8 +56,8 @@ public interface ExceptionHandler { /** * Sets the connector's socket factory. *

- * If no factory is configured that connector will use the platform's - * default factory. + * If no factory is configured that connector will use the platform's default + * factory. * * @param socketFactory the factory to set */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogAppenderBase.java index c6477928f9..fcb5bc6aa1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -52,7 +52,8 @@ public void start() { } if (charset == null) { - // Using defaultCharset() preserves the previous behavior when String.getBytes() was + // Using defaultCharset() preserves the previous behavior when String.getBytes() + // was // called without arguments charset = Charset.defaultCharset(); } @@ -65,7 +66,8 @@ public void start() { maxMessageSize = Math.min(systemDatagramSize, MAX_MESSAGE_SIZE_LIMIT); addInfo("Defaulting maxMessageSize to [" + maxMessageSize + "]"); } else if (maxMessageSize > systemDatagramSize) { - addWarn("maxMessageSize of [" + maxMessageSize + "] is larger than the system defined datagram size of [" + systemDatagramSize + "]."); + addWarn("maxMessageSize of [" + maxMessageSize + + "] is larger than the system defined datagram size of [" + systemDatagramSize + "]."); addWarn("This may result in dropped logs."); } } catch (UnknownHostException e) { @@ -119,8 +121,7 @@ protected void postProcess(Object event, OutputStream sw) { /** * Returns the integer value corresponding to the named syslog facility. * - * @throws IllegalArgumentException - * if the facility string is not recognized + * @throws IllegalArgumentException if the facility string is not recognized */ static public int facilityStringToint(String facilityStr) { if ("KERN".equalsIgnoreCase(facilityStr)) { @@ -184,7 +185,7 @@ public String getSyslogHost() { } /** - * The SyslogHost option is the name of the the syslog host where log + * The SyslogHost option is the name of the syslog host where log * output should go. * * WARNING If the SyslogHost is not set, then this appender will fail. @@ -205,12 +206,12 @@ public String getFacility() { /** * The Facility option must be set one of the strings KERN, USER, MAIL, * DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT, - * ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, - * LOCAL7. Case is not important. + * ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. + * Case is not important. * *

- * See {@link ch.qos.logback.core.net.SyslogConstants SyslogConstants} and RFC 3164 for more information about the - * Facility option. + * See {@link ch.qos.logback.core.net.SyslogConstants SyslogConstants} and RFC + * 3164 for more information about the Facility option. */ public void setFacility(String facilityStr) { if (facilityStr != null) { @@ -240,11 +241,10 @@ public int getMaxMessageSize() { } /** - * Maximum size for the syslog message (in characters); messages - * longer than this are truncated. The default value is 65400 (which - * is near the maximum for syslog-over-UDP). Note that the value is - * characters; the number of bytes may vary if non-ASCII characters - * are present. + * Maximum size for the syslog message (in characters); messages longer than + * this are truncated. The default value is 65400 (which is near the maximum for + * syslog-over-UDP). Note that the value is characters; the number of bytes may + * vary if non-ASCII characters are present. */ public void setMaxMessageSize(int maxMessageSize) { this.maxMessageSize = maxMessageSize; @@ -267,17 +267,17 @@ public void stop() { } /** - * See {@link #setSuffixPattern #setSuffixPattern(String)}. - * - * @return - */ + * See {@link #setSuffixPattern #setSuffixPattern(String)}. + * + * @return + */ public String getSuffixPattern() { return suffixPattern; } /** - * The suffixPattern option specifies the format of the - * non-standardized part of the message sent to the syslog server. + * The suffixPattern option specifies the format of the non-standardized + * part of the message sent to the syslog server. * * @param suffixPattern */ @@ -286,8 +286,8 @@ public void setSuffixPattern(String suffixPattern) { } /** - * Returns the Charset used to encode String messages into byte sequences when writing to - * syslog. + * Returns the Charset used to encode String messages into byte sequences when + * writing to syslog. */ public Charset getCharset() { return charset; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogConstants.java b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogConstants.java index 7d92197883..9ecb60c36a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogConstants.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,7 +14,8 @@ package ch.qos.logback.core.net; /** - * Constants used by syslog daemon and transitively by {@link SyslogAppenderBase}. + * Constants used by syslog daemon and transitively by + * {@link SyslogAppenderBase}. * * @author Ceki Gülcü **/ @@ -30,7 +31,7 @@ public class SyslogConstants { public static final int EMERGENCY_SEVERITY = 0; /** Alert: action must be taken immediately */ public static final int ALERT_SEVERITY = 1; - /** Critical: critical conditions */ + /** Critical: critical conditions */ public static final int CRITICAL_SEVERITY = 2; /** Error: error conditions */ public static final int ERROR_SEVERITY = 3; @@ -38,7 +39,7 @@ public class SyslogConstants { public static final int WARNING_SEVERITY = 4; /** Notice: normal but significant condition */ public static final int NOTICE_SEVERITY = 5; - /** Informational: informational messages */ + /** Informational: informational messages */ public static final int INFO_SEVERITY = 6; /** Debug: debug-level messages */ public static final int DEBUG_SEVERITY = 7; @@ -63,7 +64,7 @@ public class SyslogConstants { public static final int LOG_UUCP = 8 << 3; /** clock daemon, numerical code 9. */ public static final int LOG_CRON = 9 << 3; - /** security/authorization messages, numerical code 10. */ + /** security/authorization messages, numerical code 10. */ public static final int LOG_AUTHPRIV = 10 << 3; /** ftp daemon, numerical code 11. */ public static final int LOG_FTP = 11 << 3; @@ -89,6 +90,6 @@ public class SyslogConstants { public static final int LOG_LOCAL5 = 21 << 3; /** reserved for local use, numerical code 22. */ public static final int LOG_LOCAL6 = 22 << 3; - /** reserved for local use, numerical code 23.*/ + /** reserved for local use, numerical code 23. */ public static final int LOG_LOCAL7 = 23 << 3; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogOutputStream.java b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogOutputStream.java index f56da878cd..796e42641d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/SyslogOutputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/SyslogOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,8 +23,8 @@ import java.net.UnknownHostException; /** - * SyslogOutputStream is a wrapper around the {@link DatagramSocket} class so that it - * behaves like an {@link OutputStream}. + * SyslogOutputStream is a wrapper around the {@link DatagramSocket} class so + * that it behaves like an {@link OutputStream}. */ public class SyslogOutputStream extends OutputStream { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/package.html b/logback-core/src/main/java/ch/qos/logback/core/net/package.html index 9e93088c69..248014bfaf 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/net/package.html @@ -1,3 +1,17 @@ + + @@ -11,7 +25,7 @@ destinations.

SMTPAppender logs events and sends an email when appropriate. -SyslogAppender logs to a Syslog deamon.

+SyslogAppender logs to a Syslog daemon.

diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/AbstractServerSocketAppender.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/AbstractServerSocketAppender.java index 748d94e7d5..1172d66de1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/AbstractServerSocketAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/AbstractServerSocketAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -40,7 +40,7 @@ public abstract class AbstractServerSocketAppender extends AppenderBase { */ public static final int DEFAULT_BACKLOG = 50; - /** + /** * Default queue size used for each client */ public static final int DEFAULT_CLIENT_QUEUE_SIZE = 100; @@ -58,12 +58,13 @@ public void start() { if (isStarted()) return; try { - ServerSocket socket = getServerSocketFactory().createServerSocket(getPort(), getBacklog(), getInetAddress()); + ServerSocket socket = getServerSocketFactory().createServerSocket(getPort(), getBacklog(), + getInetAddress()); ServerListener listener = createServerListener(socket); - runner = createServerRunner(listener, getContext().getScheduledExecutorService()); + runner = createServerRunner(listener, getContext().getExecutorService()); runner.setContext(getContext()); - getContext().getScheduledExecutorService().execute(runner); + getContext().getExecutorService().execute(runner); super.start(); } catch (Exception ex) { addError("server startup error: " + ex, ex); @@ -74,7 +75,8 @@ protected ServerListener createServerListener(ServerSocket return new RemoteReceiverServerListener(socket); } - protected ServerRunner createServerRunner(ServerListener listener, Executor executor) { + protected ServerRunner createServerRunner(ServerListener listener, + Executor executor) { return new RemoteReceiverServerRunner(listener, executor, getClientQueueSize()); } @@ -82,6 +84,7 @@ protected ServerRunner createServerRunner(ServerListener getPST(); @@ -119,9 +124,9 @@ public void visit(RemoteReceiverClient client) { /** * Gets the factory used to create {@link ServerSocket} objects. *

- * The default implementation delegates to - * {@link ServerSocketFactory#getDefault()}. Subclasses may override to - * private a different socket factory implementation. + * The default implementation delegates to + * {@link ServerSocketFactory#getDefault()}. Subclasses may override to private + * a different socket factory implementation. * * @return socket factory. */ @@ -131,6 +136,7 @@ protected ServerSocketFactory getServerSocketFactory() throws Exception { /** * Gets the local address for the listener. + * * @return an {@link InetAddress} representation of the local address. * @throws UnknownHostException */ @@ -142,6 +148,7 @@ protected InetAddress getInetAddress() throws UnknownHostException { /** * Gets the local port for the listener. + * * @return local port */ public int getPort() { @@ -150,6 +157,7 @@ public int getPort() { /** * Sets the local port for the listener. + * * @param port the local port to set */ public void setPort(int port) { @@ -159,8 +167,9 @@ public void setPort(int port) { /** * Gets the listener queue depth. *

- * This represents the number of connected clients whose connections - * have not yet been accepted. + * This represents the number of connected clients whose connections have not + * yet been accepted. + * * @return queue depth * @see java.net.ServerSocket */ @@ -171,8 +180,9 @@ public int getBacklog() { /** * Sets the listener queue depth. *

- * This represents the number of connected clients whose connections - * have not yet been accepted. + * This represents the number of connected clients whose connections have not + * yet been accepted. + * * @param backlog the queue depth to set * @see java.net.ServerSocket */ @@ -182,6 +192,7 @@ public void setBacklog(int backlog) { /** * Gets the local address for the listener. + * * @return a string representation of the local address */ public String getAddress() { @@ -190,6 +201,7 @@ public String getAddress() { /** * Sets the local address for the listener. + * * @param address a host name or a string representation of an IP address */ public void setAddress(String address) { @@ -197,7 +209,8 @@ public void setAddress(String address) { } /** - * Gets the event queue size used for each client connection. + * Gets the event queue size used for each client connection. + * * @return queue size */ public int getClientQueueSize() { @@ -206,6 +219,7 @@ public int getClientQueueSize() { /** * Sets the event queue size used for each client connection. + * * @param clientQueueSize the queue size to set */ public void setClientQueueSize(int clientQueueSize) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/Client.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/Client.java index 72f217ccd1..3f1f6bf66b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/Client.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/Client.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,18 +19,17 @@ /** * A client of a {@link ServerRunner}. *

- * This interface exists primarily to abstract away the details of the - * client's underlying {@code Socket} and the concurrency associated with - * handling multiple clients. Such realities make it difficult to create - * effective unit tests for the {@link ServerRunner} that are easy to - * understand and maintain. + * This interface exists primarily to abstract away the details of the client's + * underlying {@code Socket} and the concurrency associated with handling + * multiple clients. Such realities make it difficult to create effective unit + * tests for the {@link ServerRunner} that are easy to understand and maintain. *

- * This interface captures the only those details about a client that - * the {@code ServerRunner} cares about; namely, that it is something that + * This interface captures the only those details about a client that the + * {@code ServerRunner} cares about; namely, that it is something that *

    - *
  1. is Runnable — i.e. it can be executed concurrently
  2. - *
  3. holds resources that need to be closed before the client is - * discarded
  4. + *
  5. is Runnable — i.e. it can be executed concurrently
  6. + *
  7. holds resources that need to be closed before the client is + * discarded
  8. *
* * @author Carl Harris @@ -41,16 +40,15 @@ public interface Client extends Runnable, Closeable { * Closes any resources that are held by the client. *

* Note that (as described in Doug Lea's discussion about interrupting I/O - * operations in "Concurrent Programming in Java" (Addison-Wesley - * Professional, 2nd edition, 1999) this method is used to interrupt - * any blocked I/O operation in the client when the server is shutting - * down. The client implementation must anticipate this potential, - * and gracefully exit when the blocked I/O operation throws the - * relevant {@link IOException} subclass. + * operations in "Concurrent Programming in Java" - Addison-Wesley Professional, + * 2nd edition, 1999) this method is used to interrupt any blocked I/O operation + * in the client when the server is shutting down. The client implementation + * must anticipate this potential, and gracefully exit when the blocked I/O + * operation throws the relevant {@link IOException} subclass. *

- * Note also, that unlike {@link Closeable#close()} this method is not - * permitted to propagate any {@link IOException} that occurs when closing - * the underlying resource(s). + * Note also, that unlike {@link Closeable#close()} this method is not permitted + * to propagate any {@link IOException} that occurs when closing the underlying + * resource(s). */ void close(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/ClientVisitor.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/ClientVisitor.java index 0563ee935c..c2318c90e1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/ClientVisitor.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/ClientVisitor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/ConcurrentServerRunner.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/ConcurrentServerRunner.java index e96f1a3b1f..7b40ac3a2f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/ConcurrentServerRunner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/ConcurrentServerRunner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -26,25 +26,26 @@ /** * A concurrent {@link ServerRunner}. *

- * An instance of this object is created with a {@link ServerListener} and - * an {@link java.util.concurrent.Executor Executor}. On invocation of the {@code start()} method, it - * passes itself to the given {@code Executor} and returns immediately. On - * invocation of its {@link #run()} method by the {@link Executor} it begins - * accepting client connections via its {@code ServerListener}. As each - * new {@link Client} is accepted, the client is configured with the - * runner's LoggingContext and is then passed to the {@code - * Executor} for concurrent execution of the client's service loop. + * An instance of this object is created with a {@link ServerListener} and an + * {@link java.util.concurrent.Executor Executor}. On invocation of the + * {@code start()} method, it passes itself to the given {@code Executor} and + * returns immediately. On invocation of its {@link #run()} method by the + * {@link Executor} it begins accepting client connections via its + * {@code ServerListener}. As each new {@link Client} is accepted, the client is + * configured with the runner's LoggingContext and is then passed to the {@code + * Executor} for concurrent execution of the client's service loop. *

* On invocation of the {@link #stop()} method, the runner closes the listener - * and each of the connected clients (by invoking {@link Client#close()} + * and each of the connected clients (by invoking {@link Client#close()} * effectively interrupting any blocked I/O calls and causing these concurrent - * subtasks to exit gracefully). This ensures that before the {@link #stop()} - * method returns (1) all I/O resources have been released and (2) all - * of the threads of the {@code Executor} are idle. + * subtasks to exit gracefully). This ensures that before the {@link #stop()} + * method returns (1) all I/O resources have been released and (2) all of the + * threads of the {@code Executor} are idle. * * @author Carl Harris */ -public abstract class ConcurrentServerRunner extends ContextAwareBase implements Runnable, ServerRunner { +public abstract class ConcurrentServerRunner extends ContextAwareBase + implements Runnable, ServerRunner { private final Lock clientsLock = new ReentrantLock(); @@ -57,12 +58,12 @@ public abstract class ConcurrentServerRunner extends ContextAw /** * Constructs a new server runner. - * @param listener the listener from which the server will accept new - * clients - * @param executor a executor that will facilitate execution of the - * listening and client-handling tasks; while any {@link Executor} - * is allowed here, outside of unit testing the only reasonable choice - * is a bounded thread pool of some kind. + * + * @param listener the listener from which the server will accept new clients + * @param executor an executor that will facilitate execution of the listening + * and client-handling tasks; while any {@link Executor} is + * allowed here, outside of unit testing the only reasonable + * choice is a bounded thread pool of some kind. */ public ConcurrentServerRunner(ServerListener listener, Executor executor) { this.listener = listener; @@ -107,8 +108,9 @@ public void accept(ClientVisitor visitor) { } /** - * Creates a copy of the collection of all clients that are presently - * being tracked by the server. + * Creates a copy of the collection of all clients that are presently being + * tracked by the server. + * * @return collection of client objects */ private Collection copyClients() { @@ -156,17 +158,18 @@ public void run() { /** * Configures a connected client. *

- * A subclass implements this method to perform any necessary configuration - * of the client object before its {@link Client#run()} method is invoked. + * A subclass implements this method to perform any necessary configuration of + * the client object before its {@link Client#run()} method is invoked. * * @param client the subject client - * @return {@code true} if configuration was successful; if the return - * value is {@code false} the client connection will be dropped + * @return {@code true} if configuration was successful; if the return value is + * {@code false} the client connection will be dropped */ protected abstract boolean configureClient(T client); /** * Adds a client to the collection of those being tracked by the server. + * * @param client the client to add */ private void addClient(T client) { @@ -180,6 +183,7 @@ private void addClient(T client) { /** * Removes a client from the collection of those being tracked by the server. + * * @param client the client to remote */ private void removeClient(T client) { @@ -192,8 +196,8 @@ private void removeClient(T client) { } /** - * A wrapper for a {@link Client} responsible for ensuring that client - * tracking is performed properly. + * A wrapper for a {@link Client} responsible for ensuring that client tracking + * is performed properly. */ private class ClientWrapper implements Client { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverClient.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverClient.java index 9af5cee224..6482f133ea 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverClient.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverClient.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,8 @@ import ch.qos.logback.core.spi.ContextAware; /** - * A client of a {@link ServerRunner} that receives events from a local - * appender and logs them according to local policy. + * A client of a {@link ServerRunner} that receives events from a local appender + * and logs them according to local policy. * * @author Carl Harris */ @@ -30,15 +30,17 @@ interface RemoteReceiverClient extends Client, ContextAware { * Sets the client's event queue. *

* This method must be invoked before the {@link #run()} method is invoked. + * * @param queue the queue to set */ void setQueue(BlockingQueue queue); /** * Offers an event to the client. + * * @param event the subject event - * @return {@code true} if the client's queue accepted the event, - * {@code false} if the client's queue is full + * @return {@code true} if the client's queue accepted the event, {@code false} + * if the client's queue is full */ boolean offer(Serializable event); diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverServerListener.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverServerListener.java index 3bac12447b..c670efcada 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverServerListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverServerListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,8 +27,9 @@ class RemoteReceiverServerListener extends ServerSocketListener listener, Executor executor, int clientQueueSize) { + public RemoteReceiverServerRunner(ServerListener listener, Executor executor, + int clientQueueSize) { super(listener, executor); this.clientQueueSize = clientQueueSize; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClient.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClient.java index 5a27af2894..6c9be5ca40 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClient.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClient.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -41,7 +41,8 @@ class RemoteReceiverStreamClient extends ContextAwareBase implements RemoteRecei /** * Constructs a new client. - * @param id identifier string for the client + * + * @param id identifier string for the client * @param socket socket to which logging events will be written */ public RemoteReceiverStreamClient(String id, Socket socket) { @@ -52,11 +53,11 @@ public RemoteReceiverStreamClient(String id, Socket socket) { /** * Constructs a new client. - *

- * This constructor exists primarily to support unit tests where it - * is inconvenient to have to create a socket for the test. + *

+ * This constructor exists primarily to support unit tests where it is + * inconvenient to have to create a socket for the test. * - * @param id identifier string for the client + * @param id identifier string for the client * @param outputStream output stream to which logging Events will be written */ RemoteReceiverStreamClient(String id, OutputStream outputStream) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBase.java index f93deacf59..e01c7ab8dd 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,7 +23,7 @@ /** * - * This is the base class for module specific ServerSocketAppender + * This is the base class for module specific ServerSocketAppender * implementations. * * @author Carl Harris @@ -56,8 +56,9 @@ public void start() { /** * Gets the SSL configuration. - * @return SSL configuration; if no configuration has been set, a - * default configuration is returned + * + * @return SSL configuration; if no configuration has been set, a default + * configuration is returned */ public SSLConfiguration getSsl() { if (ssl == null) { @@ -68,6 +69,7 @@ public SSLConfiguration getSsl() { /** * Sets the SSL configuration. + * * @param ssl the SSL configuration to set */ public void setSsl(SSLConfiguration ssl) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerListener.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerListener.java index 0c056acad1..b31ec410c1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -23,13 +23,13 @@ * This interface exists primarily to abstract away the details of the * listener's underlying {@code ServerSocket} and the concurrency associated * with handling multiple clients. Such realities make it difficult to create - * effective unit tests for the {@link ServerRunner} that are easy to - * understand and maintain. + * effective unit tests for the {@link ServerRunner} that are easy to understand + * and maintain. *

* This interface captures the only those details about the listener that the - * {@code ServerRunner} cares about; namely, that it is something that has - * an underlying resource (or resources) that need to be closed before the - * listener is discarded. + * {@code ServerRunner} cares about; namely, that it is something that has an + * underlying resource (or resources) that need to be closed before the listener + * is discarded. * */ public interface ServerListener extends Closeable { @@ -37,8 +37,8 @@ public interface ServerListener extends Closeable { /** * Accepts the next client that appears on this listener. *

- * An implementation of this method is expected to block the calling thread - * and not return until either a client appears or an exception occurs. + * An implementation of this method is expected to block the calling thread and + * not return until either a client appears or an exception occurs. * * @return client object * @throws IOException @@ -51,16 +51,15 @@ public interface ServerListener extends Closeable { * listener. *

* Note that (as described in Doug Lea's discussion about interrupting I/O - * operations in "Concurrent Programming in Java" (Addison-Wesley - * Professional, 2nd edition, 1999) this method is used to interrupt - * any blocked I/O operation in the client when the server is shutting - * down. The client implementation must anticipate this potential, - * and gracefully exit when the blocked I/O operation throws the - * relevant {@link IOException} subclass. + * operations in "Concurrent Programming in Java" - Addison-Wesley Professional, + * 2nd edition, 1999) this method is used to interrupt any blocked I/O operation + * in the client when the server is shutting down. The client implementation + * must anticipate this potential, and gracefully exit when the blocked I/O + * operation throws the relevant {@link IOException} subclass. *

- * Note also, that unlike {@link Closeable#close()} this method is not - * permitted to propagate any {@link IOException} that occurs when closing - * the underlying resource(s). + * Note also, that unlike {@link Closeable#close()} this method is not permitted + * to propagate any {@link IOException} that occurs when closing the underlying + * resource(s). */ void close(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerRunner.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerRunner.java index 07993ab998..2f82d95d60 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerRunner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerRunner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,11 +18,11 @@ import ch.qos.logback.core.spi.ContextAware; /** - * An object that is responsible for the asynchronous execution of a - * socket server. + * An object that is responsible for the asynchronous execution of a socket + * server. *

- * This interface exists primarily to allow the runner to be mocked for - * the purpose of unit testing the socket server implementation. + * This interface exists primarily to allow the runner to be mocked for the + * purpose of unit testing the socket server implementation. * * @author Carl Harris */ @@ -30,6 +30,7 @@ public interface ServerRunner extends ContextAware, Runnable { /** * Gets a flag indicating whether the server is currently running. + * * @return flag state */ boolean isRunning(); @@ -37,15 +38,17 @@ public interface ServerRunner extends ContextAware, Runnable { /** * Stops execution of the runner. *

- * This method must cause all I/O and thread resources associated with - * the runner to be released. If the receiver has not been started, this - * method must have no effect. + * This method must cause all I/O and thread resources associated with the + * runner to be released. If the receiver has not been started, this method must + * have no effect. + * * @throws IOException */ void stop() throws IOException; /** - * Presents each connected client to the given visitor. + * Presents each connected client to the given visitor. + * * @param visitor the subject visitor */ void accept(ClientVisitor visitor); diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerSocketListener.java b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerSocketListener.java index 8f260aac71..6fe8339892 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerSocketListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/ServerSocketListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,6 +31,7 @@ public abstract class ServerSocketListener implements ServerLi /** * Constructs a new listener. + * * @param serverSocket server socket delegate */ public ServerSocketListener(ServerSocket serverSocket) { @@ -47,7 +48,8 @@ public T acceptClient() throws IOException { /** * Creates the client object for a new socket connection - * @param id identifier string for the client + * + * @param id identifier string for the client * @param socket client's socket connection * @return client object * @throws IOException @@ -71,6 +73,7 @@ public String toString() { /** * Converts a socket address to a reasonable display string. + * * @param address the subject socket address * @return display string */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/server/package.html b/logback-core/src/main/java/ch/qos/logback/core/net/server/package.html index 323f31f41f..5968dadadb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/server/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/net/server/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLServerSocketFactory.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLServerSocketFactory.java index de93092e3b..9748fa5462 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLServerSocketFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLServerSocketFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,14 +22,13 @@ import javax.net.ssl.SSLServerSocketFactory; /** - * An {@link SSLServerSocketFactory} that configures SSL parameters - * (those specified in {@link SSLParametersConfiguration} on each newly - * created socket. + * An {@link SSLServerSocketFactory} that configures SSL parameters (those + * specified in {@link SSLParametersConfiguration}) on each newly created socket. *

- * When any of this factory's {@code createServerSocket} methods are invoked, - * it calls on a delegate {@link SSLServerSocketFactory} to create the socket, - * and then sets the SSL parameters of the socket (using the provided - * configuration) before returning the socket to the caller. + * When any of this factory's {@code createServerSocket} methods are invoked, it + * calls on a delegate {@link SSLServerSocketFactory} to create the socket, and + * then sets the SSL parameters of the socket (using the provided configuration) + * before returning the socket to the caller. * * @author Carl Harris */ @@ -40,10 +39,11 @@ public class ConfigurableSSLServerSocketFactory extends ServerSocketFactory { /** * Creates a new factory. - * @param parameters parameters that will be configured on each - * socket created by the factory - * @param delegate socket factory that will be called upon to create - * server sockets before configuration + * + * @param parameters parameters that will be configured on each socket created + * by the factory + * @param delegate socket factory that will be called upon to create server + * sockets before configuration */ public ConfigurableSSLServerSocketFactory(SSLParametersConfiguration parameters, SSLServerSocketFactory delegate) { this.parameters = parameters; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLSocketFactory.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLSocketFactory.java index 289f37719a..601287e641 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLSocketFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/ConfigurableSSLSocketFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -24,13 +24,13 @@ import javax.net.ssl.SSLSocketFactory; /** - * An {@link SSLSocketFactory} that configures SSL parameters - * (those covered by {@link SSLParameters}) on each newly created socket. + * An {@link SSLSocketFactory} that configures SSL parameters (those covered by + * {@link SSLParameters}) on each newly created socket. *

- * When any of this factory's {@code createSocket} methods are invoked, it - * calls on a {@link SSLSocketFactory} delegate to create the socket, and - * then sets the SSL parameters of the socket (using the provided - * configuration) before returning the socket to the caller. + * When any of this factory's {@code createSocket} methods are invoked, it calls + * on a {@link SSLSocketFactory} delegate to create the socket, and then sets + * the SSL parameters of the socket (using the provided configuration) before + * returning the socket to the caller. * * @author Carl Harris */ @@ -41,10 +41,11 @@ public class ConfigurableSSLSocketFactory extends SocketFactory { /** * Creates a new factory. - * @param parameters parameters that will be configured on each - * socket created by the factory - * @param delegate socket factory that will be called upon to create - * sockets before configuration + * + * @param parameters parameters that will be configured on each socket created + * by the factory + * @param delegate socket factory that will be called upon to create sockets + * before configuration */ public ConfigurableSSLSocketFactory(SSLParametersConfiguration parameters, SSLSocketFactory delegate) { this.parameters = parameters; @@ -55,7 +56,8 @@ public ConfigurableSSLSocketFactory(SSLParametersConfiguration parameters, SSLSo * {@inheritDoc} */ @Override - public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException { + public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) + throws IOException { SSLSocket socket = (SSLSocket) delegate.createSocket(address, port, localAddress, localPort); parameters.configure(new SSLConfigurableSocket(socket)); return socket; @@ -75,7 +77,8 @@ public Socket createSocket(InetAddress host, int port) throws IOException { * {@inheritDoc} */ @Override - public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException { + public Socket createSocket(String host, int port, InetAddress localHost, int localPort) + throws IOException, UnknownHostException { SSLSocket socket = (SSLSocket) delegate.createSocket(host, port, localHost, localPort); parameters.configure(new SSLConfigurableSocket(socket)); return socket; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.java index ee6923f74e..792d2e8d6c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,8 +21,8 @@ /** * A factory bean for a JSSE {@link KeyManagerFactory}. *

- * This object holds the configurable properties of a key manager factory - * and uses them to create and load a {@link KeyManagerFactory} instance. + * This object holds the configurable properties of a key manager factory and + * uses them to create and load a {@link KeyManagerFactory} instance. * * @author Carl Harris */ @@ -33,23 +33,29 @@ public class KeyManagerFactoryFactoryBean { /** * Creates a {@link KeyManagerFactory} using the receiver's configuration. + * * @return factory object - * @throws NoSuchProviderException if the provider specified by - * {@link #setProvider(String)} is not known to the platform + * @throws NoSuchProviderException if the provider specified by + * {@link #setProvider(String)} is not known to + * the platform * @throws NoSuchAlgorithmException if the algorithm specified by - * {@link #setAlgorithm(String)} is not known to the specified provider - * (or to the default platform provider if no provider is specified) + * {@link #setAlgorithm(String)} is not known + * to the specified provider (or to the default + * platform provider if no provider is + * specified) */ public KeyManagerFactory createKeyManagerFactory() throws NoSuchProviderException, NoSuchAlgorithmException { - return getProvider() != null ? KeyManagerFactory.getInstance(getAlgorithm(), getProvider()) : KeyManagerFactory.getInstance(getAlgorithm()); + return getProvider() != null ? KeyManagerFactory.getInstance(getAlgorithm(), getProvider()) + : KeyManagerFactory.getInstance(getAlgorithm()); } /** * Gets the algorithm name for the key manager factory. + * * @return algorithm name (e.g. {@code SunX509}); the default algorithm - * (obtained from {@link KeyManagerFactory#getDefaultAlgorithm()}) - * is returned if no algorithm has been configured + * (obtained from {@link KeyManagerFactory#getDefaultAlgorithm()}) is + * returned if no algorithm has been configured */ public String getAlgorithm() { if (algorithm == null) { @@ -60,9 +66,10 @@ public String getAlgorithm() { /** * Sets the algorithm name for the key manager factory. - * @param algorithm an algorithm name, which must be recognized by the - * provider specified by {@link #setProvider(String)} or by the - * platform's default provider if no provider is specified. + * + * @param algorithm an algorithm name, which must be recognized by the provider + * specified by {@link #setProvider(String)} or by the + * platform's default provider if no provider is specified. */ public void setAlgorithm(String algorithm) { this.algorithm = algorithm; @@ -70,6 +77,7 @@ public void setAlgorithm(String algorithm) { /** * Gets the JSSE provider name for the key manager factory. + * * @return provider name */ public String getProvider() { @@ -78,8 +86,9 @@ public String getProvider() { /** * Sets the JSSE provider name for the key manager factory. - * @param provider name of the JSSE provider to utilize in creating the - * key manager factory + * + * @param provider name of the JSSE provider to utilize in creating the key + * manager factory */ public void setProvider(String provider) { this.provider = provider; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.java index 0f0b72a5fd..aeaf4995a9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,8 +27,8 @@ /** * A factory bean for a JCA {@link KeyStore}. *

- * This object holds the configurable properties of a key store and uses - * them to create and load a {@link KeyStore} instance. + * This object holds the configurable properties of a key store and uses them to + * create and load a {@link KeyStore} instance. * * @author Carl Harris */ @@ -41,15 +41,19 @@ public class KeyStoreFactoryBean { /** * Creates a new {@link KeyStore} using the receiver's configuration. + * * @return key store - * @throws NoSuchProviderException if the provider specified by - * {@link #setProvider(String)} is not known to the platform + * @throws NoSuchProviderException if the provider specified by + * {@link #setProvider(String)} is not known to + * the platform * @throws NoSuchAlgorithmException if the key store type specified by - * {@link #setType(String)} is not known to the specified provider - * (or the platform's default provider if the provider isn't specified) - * @throws KeyStoreException if some other error occurs in loading - * the key store from the resource specified by - * {@link #setLocation(String)} + * {@link #setType(String)} is not known to the + * specified provider (or the platform's + * default provider if the provider isn't + * specified) + * @throws KeyStoreException if some other error occurs in loading the + * key store from the resource specified by + * {@link #setLocation(String)} */ public KeyStore createKeyStore() throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException { @@ -84,8 +88,8 @@ public KeyStore createKeyStore() throws NoSuchProviderException, NoSuchAlgorithm } /** - * Invokes the appropriate JCE factory method to obtain a new - * {@link KeyStore} object. + * Invokes the appropriate JCE factory method to obtain a new {@link KeyStore} + * object. */ private KeyStore newKeyStore() throws NoSuchAlgorithmException, NoSuchProviderException, KeyStoreException { @@ -94,6 +98,7 @@ private KeyStore newKeyStore() throws NoSuchAlgorithmException, NoSuchProviderEx /** * Gets the location of the key store resource. + * * @return a String containing a URL for the resource */ public String getLocation() { @@ -102,9 +107,10 @@ public String getLocation() { /** * Sets the location of the key store resource. - * @param location a String containing a URL for the resource; if the - * URL string isn't prefixed by a scheme, the path is assumed to be - * relative to the root of the classpath. + * + * @param location a String containing a URL for the resource; if the URL string + * isn't prefixed by a scheme, the path is assumed to be + * relative to the root of the classpath. */ public void setLocation(String location) { this.location = location; @@ -112,8 +118,10 @@ public void setLocation(String location) { /** * Gets the type of key store to load. - * @return a key store type name (e.g. {@code JKS}); the - * {@link SSL#DEFAULT_KEYSTORE_TYPE} is returned if no type has been configured + * + * @return a key store type name (e.g. {@code JKS}); the + * {@link SSL#DEFAULT_KEYSTORE_TYPE} is returned if no type has been + * configured */ public String getType() { if (type == null) { @@ -124,10 +132,11 @@ public String getType() { /** * Sets the type of key store to load. - * @param type a key store type name (e.g. {@code JKS}, {@code PKCS12}); - * the type specified must be supported by the provider specified by - * {@link #setProvider(String)} or by the platform's default provider - * if no provider is specified + * + * @param type a key store type name (e.g. {@code JKS}, {@code PKCS12}); the + * type specified must be supported by the provider specified by + * {@link #setProvider(String)} or by the platform's default + * provider if no provider is specified */ public void setType(String type) { this.type = type; @@ -135,6 +144,7 @@ public void setType(String type) { /** * Gets the JCA key store provider name. + * * @return provider name or {@code null} if no provider has been configured */ public String getProvider() { @@ -143,8 +153,8 @@ public String getProvider() { /** * Sets the JCA key store provider name. - * @param provider name of the JCA provider to utilize in creating the - * key store + * + * @param provider name of the JCA provider to utilize in creating the key store */ public void setProvider(String provider) { this.provider = provider; @@ -152,8 +162,9 @@ public void setProvider(String provider) { /** * Gets the password to use to access the key store. - * @return password string; the {@link SSL#DEFAULT_KEYSTORE_PASSWORD} is returned - * if no password has been configured + * + * @return password string; the {@link SSL#DEFAULT_KEYSTORE_PASSWORD} is + * returned if no password has been configured */ public String getPassword() { if (password == null) { @@ -164,6 +175,7 @@ public String getPassword() { /** * Sets the password to use to access the keystore. + * * @param password the password to set */ public void setPassword(String password) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSL.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSL.java index c2df583fb3..47e4dd4742 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSL.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSL.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLComponent.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLComponent.java index 05f9475342..5266d5bab0 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLComponent.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLComponent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,7 +14,7 @@ package ch.qos.logback.core.net.ssl; /** - * A interface used to identify components that have an SSL configuration. + * An interface used to identify components that have an SSL configuration. * * @author Carl Harris */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurable.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurable.java index cce9eab438..d58f1c89e8 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurable.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,11 +16,14 @@ /** * An object that has configurable SSL parameters. *

- * This interface allows us o decouple the {@link ch.qos.logback.core.net.ssl.SSLParametersConfiguration SSLParametersConfiguration} - * from {@link javax.net.ssl.SSLSocket SSLSocket} and {@link javax.net.ssl.SSLServerSocket SSLServerSocket} to facilitate unit + * This interface allows us o decouple the + * {@link ch.qos.logback.core.net.ssl.SSLParametersConfiguration + * SSLParametersConfiguration} from {@link javax.net.ssl.SSLSocket SSLSocket} + * and {@link javax.net.ssl.SSLServerSocket SSLServerSocket} to facilitate unit * testing. * * @author Carl Harris + * @author Bruno Harbulot */ public interface SSLConfigurable { @@ -28,15 +31,16 @@ public interface SSLConfigurable { * Gets the set of protocols that the SSL component enables by default. * * @return protocols (generally a subset of the set returned by - * {@link #getSupportedProtocols()}); the return value may be - * an empty array but must never be {@code null}. + * {@link #getSupportedProtocols()}); the return value may be an empty + * array but must never be {@code null}. */ String[] getDefaultProtocols(); /** * Gets the set of protocols that the SSL component supports. - * @return protocols supported protocols; the return value may be - * an empty array but must never be {@code null}. + * + * @return protocols supported protocols; the return value may be an empty array + * but must never be {@code null}. */ String[] getSupportedProtocols(); @@ -51,36 +55,42 @@ public interface SSLConfigurable { * Gets the set of cipher suites that the SSL component enables by default. * * @return cipher suites (generally a subset of the set returned by - * {@link #getSupportedCipherSuites()}); the return value may be - * an empty array but must never be {@code null} + * {@link #getSupportedCipherSuites()}); the return value may be an + * empty array but must never be {@code null} */ String[] getDefaultCipherSuites(); /** * Gets the set of cipher suites that the SSL component supports. - * @return supported cipher suites; the return value may be - * an empty array but must never be {@code null} + * + * @return supported cipher suites; the return value may be an empty array but + * must never be {@code null} */ String[] getSupportedCipherSuites(); /** * Sets the enabled cipher suites on the SSL component. + * * @param cipherSuites the cipher suites to enable */ void setEnabledCipherSuites(String[] cipherSuites); /** - * Sets a flag indicating whether the SSL component should require - * client authentication. + * Sets a flag indicating whether the SSL component should require client + * authentication. + * * @param state the flag state to set */ void setNeedClientAuth(boolean state); /** - * Sets a flag indicating whether the SSL component should request - * client authentication. + * Sets a flag indicating whether the SSL component should request client + * authentication. + * * @param state the flag state to set */ void setWantClientAuth(boolean state); + void setHostnameVerification(boolean verifyHostname); + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableServerSocket.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableServerSocket.java index fa5af3f676..911c08feab 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableServerSocket.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableServerSocket.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,6 +19,7 @@ * An {@link SSLConfigurable} wrapper for an {@link SSLServerSocket}. * * @author Carl Harris + * @author Bruno Harbulot */ public class SSLConfigurableServerSocket implements SSLConfigurable { @@ -60,4 +61,9 @@ public void setWantClientAuth(boolean state) { delegate.setWantClientAuth(state); } + @Override + public void setHostnameVerification(boolean verifyHostname) { + // This is not relevant for a server socket + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableSocket.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableSocket.java index 2628718926..0e4be0cafa 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableSocket.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfigurableSocket.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,14 @@ */ package ch.qos.logback.core.net.ssl; +import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; /** * An {@link SSLConfigurable} wrapper for an {@link SSLSocket}. * * @author Carl Harris + * @author Bruno Harbulot */ public class SSLConfigurableSocket implements SSLConfigurable { @@ -60,4 +62,14 @@ public void setWantClientAuth(boolean state) { delegate.setWantClientAuth(state); } + @Override + public void setHostnameVerification(boolean hostnameVerification) { + if (!hostnameVerification) { + return; + } + SSLParameters sslParameters = delegate.getSSLParameters(); + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + delegate.setSSLParameters(sslParameters); + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfiguration.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfiguration.java index d196b8d968..be1f296f6a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfiguration.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLConfiguration.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ /** * A configuration for an {@link SSLContext}. - *

+ *

* * @author Carl Harris */ @@ -27,8 +27,9 @@ public class SSLConfiguration extends SSLContextFactoryBean { /** * Gets the SSL parameters configuration. - * @return parameters configuration; if no parameters object was - * configured, a default parameters object is returned + * + * @return parameters configuration; if no parameters object was configured, a + * default parameters object is returned */ public SSLParametersConfiguration getParameters() { if (parameters == null) { @@ -39,6 +40,7 @@ public SSLParametersConfiguration getParameters() { /** * Sets the SSL parameters configuration. + * * @param parameters the parameters configuration to set */ public void setParameters(SSLParametersConfiguration parameters) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBean.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBean.java index 9f7f078604..ae4a69e288 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBean.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -53,28 +53,32 @@ public class SSLContextFactoryBean { /** * Creates a new {@link SSLContext} using the receiver's configuration. + * * @param context context for status messages * @return {@link SSLContext} object - * @throws NoSuchProviderException if a provider specified for one of the - * JCA or JSSE components utilized in creating the context is not - * known to the platform - * @throws NoSuchAlgorithmException if a JCA or JSSE algorithm, protocol, - * or type name specified for one of the context's components is not - * known to a given provider (or platform default provider for the - * component) - * @throws KeyManagementException if an error occurs in creating a - * {@link KeyManager} for the context - * @throws UnrecoverableKeyException if a private key needed by a - * {@link KeyManager} cannot be obtained from a key store - * @throws KeyStoreException if an error occurs in reading the - * contents of a key store - * @throws CertificateException if an error occurs in reading the - * contents of a certificate + * @throws NoSuchProviderException if a provider specified for one of the JCA + * or JSSE components utilized in creating the + * context is not known to the platform + * @throws NoSuchAlgorithmException if a JCA or JSSE algorithm, protocol, or + * type name specified for one of the + * context's components is not known to a + * given provider (or platform default + * provider for the component) + * @throws KeyManagementException if an error occurs in creating a + * {@link KeyManager} for the context + * @throws UnrecoverableKeyException if a private key needed by a + * {@link KeyManager} cannot be obtained from + * a key store + * @throws KeyStoreException if an error occurs in reading the contents + * of a key store + * @throws CertificateException if an error occurs in reading the contents + * of a certificate */ - public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, KeyManagementException, - UnrecoverableKeyException, KeyStoreException, CertificateException { + public SSLContext createContext(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, + KeyManagementException, UnrecoverableKeyException, KeyStoreException, CertificateException { - SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider()) : SSLContext.getInstance(getProtocol()); + SSLContext sslContext = getProvider() != null ? SSLContext.getInstance(getProtocol(), getProvider()) + : SSLContext.getInstance(getProtocol()); context.addInfo("SSL protocol '" + sslContext.getProtocol() + "' provider '" + sslContext.getProvider() + "'"); @@ -87,24 +91,27 @@ public SSLContext createContext(ContextAware context) throws NoSuchProviderExcep /** * Creates key managers using the receiver's key store configuration. + * * @param context context for status messages * @return an array of key managers or {@code null} if no key store - * configuration was provided - * @throws NoSuchProviderException if a provider specified for one - * of the key manager components is not known to the platform - * @throws NoSuchAlgorithmException if an algorithm specified for - * one of the key manager components is not known to the relevant - * provider - * @throws KeyStoreException if an error occurs in reading a key store + * configuration was provided + * @throws NoSuchProviderException if a provider specified for one of the key + * manager components is not known to the + * platform + * @throws NoSuchAlgorithmException if an algorithm specified for one of the key + * manager components is not known to the + * relevant provider + * @throws KeyStoreException if an error occurs in reading a key store */ - private KeyManager[] createKeyManagers(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, UnrecoverableKeyException, - KeyStoreException { + private KeyManager[] createKeyManagers(ContextAware context) + throws NoSuchProviderException, NoSuchAlgorithmException, UnrecoverableKeyException, KeyStoreException { if (getKeyStore() == null) return null; KeyStore keyStore = getKeyStore().createKeyStore(); - context.addInfo("key store of type '" + keyStore.getType() + "' provider '" + keyStore.getProvider() + "': " + getKeyStore().getLocation()); + context.addInfo("key store of type '" + keyStore.getType() + "' provider '" + keyStore.getProvider() + "': " + + getKeyStore().getLocation()); KeyManagerFactory kmf = getKeyManagerFactory().createKeyManagerFactory(); context.addInfo("key manager algorithm '" + kmf.getAlgorithm() + "' provider '" + kmf.getProvider() + "'"); @@ -116,24 +123,28 @@ private KeyManager[] createKeyManagers(ContextAware context) throws NoSuchProvid /** * Creates trust managers using the receiver's trust store configuration. + * * @param context context for status messages * @return an array of trust managers or {@code null} if no trust store - * configuration was provided - * @throws NoSuchProviderException if a provider specified for one - * of the trust manager components is not known to the platform - * @throws NoSuchAlgorithmException if an algorithm specified for - * one of the trust manager components is not known to the relevant - * provider - * @throws KeyStoreException if an error occurs in reading a key - * store containing trust anchors + * configuration was provided + * @throws NoSuchProviderException if a provider specified for one of the trust + * manager components is not known to the + * platform + * @throws NoSuchAlgorithmException if an algorithm specified for one of the + * trust manager components is not known to the + * relevant provider + * @throws KeyStoreException if an error occurs in reading a key store + * containing trust anchors */ - private TrustManager[] createTrustManagers(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException { + private TrustManager[] createTrustManagers(ContextAware context) + throws NoSuchProviderException, NoSuchAlgorithmException, KeyStoreException { if (getTrustStore() == null) return null; KeyStore trustStore = getTrustStore().createKeyStore(); - context.addInfo("trust store of type '" + trustStore.getType() + "' provider '" + trustStore.getProvider() + "': " + getTrustStore().getLocation()); + context.addInfo("trust store of type '" + trustStore.getType() + "' provider '" + trustStore.getProvider() + + "': " + getTrustStore().getLocation()); TrustManagerFactory tmf = getTrustManagerFactory().createTrustManagerFactory(); context.addInfo("trust manager algorithm '" + tmf.getAlgorithm() + "' provider '" + tmf.getProvider() + "'"); @@ -142,18 +153,21 @@ private TrustManager[] createTrustManagers(ContextAware context) throws NoSuchPr return tmf.getTrustManagers(); } - private SecureRandom createSecureRandom(ContextAware context) throws NoSuchProviderException, NoSuchAlgorithmException { + private SecureRandom createSecureRandom(ContextAware context) + throws NoSuchProviderException, NoSuchAlgorithmException { SecureRandom secureRandom = getSecureRandom().createSecureRandom(); - context.addInfo("secure random algorithm '" + secureRandom.getAlgorithm() + "' provider '" + secureRandom.getProvider() + "'"); + context.addInfo("secure random algorithm '" + secureRandom.getAlgorithm() + "' provider '" + + secureRandom.getProvider() + "'"); return secureRandom; } /** * Gets the key store configuration. - * @return key store factory bean or {@code null} if no key store - * configuration was provided + * + * @return key store factory bean or {@code null} if no key store configuration + * was provided */ public KeyStoreFactoryBean getKeyStore() { if (keyStore == null) { @@ -164,6 +178,7 @@ public KeyStoreFactoryBean getKeyStore() { /** * Sets the key store configuration. + * * @param keyStore the key store factory bean to set */ public void setKeyStore(KeyStoreFactoryBean keyStore) { @@ -172,8 +187,9 @@ public void setKeyStore(KeyStoreFactoryBean keyStore) { /** * Gets the trust store configuration. - * @return trust store factory bean or {@code null} if no trust store - * configuration was provided + * + * @return trust store factory bean or {@code null} if no trust store + * configuration was provided */ public KeyStoreFactoryBean getTrustStore() { if (trustStore == null) { @@ -184,6 +200,7 @@ public KeyStoreFactoryBean getTrustStore() { /** * Sets the trust store configuration. + * * @param trustStore the trust store factory bean to set */ public void setTrustStore(KeyStoreFactoryBean trustStore) { @@ -192,9 +209,10 @@ public void setTrustStore(KeyStoreFactoryBean trustStore) { /** * Constructs a key store factory bean using JSSE system properties. + * * @param property base property name (e.g. {@code javax.net.ssl.keyStore}) - * @return key store or {@code null} if no value is defined for the - * base system property name + * @return key store or {@code null} if no value is defined for the base system + * property name */ private KeyStoreFactoryBean keyStoreFromSystemProperties(String property) { if (System.getProperty(property) == null) @@ -209,9 +227,10 @@ private KeyStoreFactoryBean keyStoreFromSystemProperties(String property) { /** * Constructs a resource location from a JSSE system property. + * * @param name property name (e.g. {@code javax.net.ssl.keyStore}) - * @return URL for the location specified in the property or {@code null} - * if no value is defined for the property + * @return URL for the location specified in the property or {@code null} if no + * value is defined for the property */ private String locationFromSystemProperty(String name) { String location = System.getProperty(name); @@ -223,8 +242,9 @@ private String locationFromSystemProperty(String name) { /** * Gets the secure random generator configuration. - * @return secure random factory bean; if no secure random generator - * configuration has been set, a default factory bean is returned + * + * @return secure random factory bean; if no secure random generator + * configuration has been set, a default factory bean is returned */ public SecureRandomFactoryBean getSecureRandom() { if (secureRandom == null) { @@ -235,6 +255,7 @@ public SecureRandomFactoryBean getSecureRandom() { /** * Sets the secure random generator configuration. + * * @param secureRandom the secure random factory bean to set */ public void setSecureRandom(SecureRandomFactoryBean secureRandom) { @@ -243,8 +264,9 @@ public void setSecureRandom(SecureRandomFactoryBean secureRandom) { /** * Gets the key manager factory configuration. - * @return factory bean; if no key manager factory - * configuration has been set, a default factory bean is returned + * + * @return factory bean; if no key manager factory configuration has been set, a + * default factory bean is returned */ public KeyManagerFactoryFactoryBean getKeyManagerFactory() { if (keyManagerFactory == null) { @@ -255,7 +277,8 @@ public KeyManagerFactoryFactoryBean getKeyManagerFactory() { /** * Sets the key manager factory configuration. - * @param keyManagerFactory the key manager factory factory bean to set + * + * @param keyManagerFactory the key manager factory bean to set */ public void setKeyManagerFactory(KeyManagerFactoryFactoryBean keyManagerFactory) { this.keyManagerFactory = keyManagerFactory; @@ -263,8 +286,9 @@ public void setKeyManagerFactory(KeyManagerFactoryFactoryBean keyManagerFactory) /** * Gets the trust manager factory configuration. - * @return factory bean; if no trust manager factory - * configuration has been set, a default factory bean is returned + * + * @return factory bean; if no trust manager factory configuration has been set, + * a default factory bean is returned */ public TrustManagerFactoryFactoryBean getTrustManagerFactory() { if (trustManagerFactory == null) { @@ -275,6 +299,7 @@ public TrustManagerFactoryFactoryBean getTrustManagerFactory() { /** * Sets the trust manager factory configuration. + * * @param trustManagerFactory the factory bean to set */ public void setTrustManagerFactory(TrustManagerFactoryFactoryBean trustManagerFactory) { @@ -283,9 +308,10 @@ public void setTrustManagerFactory(TrustManagerFactoryFactoryBean trustManagerFa /** * Gets the secure transport protocol name. + * * @return protocol name (e.g. {@code SSL}, {@code TLS}); the - * {@link SSL#DEFAULT_PROTOCOL} is returned if no protocol has been - * configured + * {@link SSL#DEFAULT_PROTOCOL} is returned if no protocol has been + * configured */ public String getProtocol() { if (protocol == null) { @@ -296,9 +322,10 @@ public String getProtocol() { /** * Sets the secure transport protocol name. + * * @param protocol a protocol name, which must be recognized by the provider - * specified by {@link #setProvider(String)} or by the platform's - * default provider if no platform was specified. + * specified by {@link #setProvider(String)} or by the + * platform's default provider if no platform was specified. */ public void setProtocol(String protocol) { this.protocol = protocol; @@ -306,6 +333,7 @@ public void setProtocol(String protocol) { /** * Gets the JSSE provider name for the SSL context. + * * @return JSSE provider name */ public String getProvider() { @@ -314,8 +342,8 @@ public String getProvider() { /** * Sets the JSSE provider name for the SSL context. - * @param provider name of the JSSE provider to use in creating the - * SSL context + * + * @param provider name of the JSSE provider to use in creating the SSL context */ public void setProvider(String provider) { this.provider = provider; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLNestedComponentRegistryRules.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLNestedComponentRegistryRules.java index abd86101bf..b580db0118 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLNestedComponentRegistryRules.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLNestedComponentRegistryRules.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.java index 1f874e9da5..ab9aab5134 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,6 @@ import javax.net.ssl.SSLEngine; -import org.codehaus.janino.Java; - import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.util.OptionHelper; import ch.qos.logback.core.util.StringCollectionUtil; @@ -29,6 +27,7 @@ * A configuration of SSL parameters for an {@link SSLEngine}. * * @author Carl Harris + * @author Bruno Harbulot */ public class SSLParametersConfiguration extends ContextAwareBase { @@ -40,33 +39,52 @@ public class SSLParametersConfiguration extends ContextAwareBase { private Boolean wantClientAuth; private String[] enabledProtocols; private String[] enabledCipherSuites; + private Boolean hostnameVerification; /** * Configures SSL parameters on an {@link SSLConfigurable}. + * * @param socket the subject configurable */ public void configure(SSLConfigurable socket) { socket.setEnabledProtocols(enabledProtocols(socket.getSupportedProtocols(), socket.getDefaultProtocols())); - socket.setEnabledCipherSuites(enabledCipherSuites(socket.getSupportedCipherSuites(), socket.getDefaultCipherSuites())); + socket.setEnabledCipherSuites( + enabledCipherSuites(socket.getSupportedCipherSuites(), socket.getDefaultCipherSuites())); if (isNeedClientAuth() != null) { socket.setNeedClientAuth(isNeedClientAuth()); } if (isWantClientAuth() != null) { socket.setWantClientAuth(isWantClientAuth()); } + if (hostnameVerification != null) { + addInfo("hostnameVerification=" + hostnameVerification); + socket.setHostnameVerification(hostnameVerification); + } + } + + public boolean getHostnameVerification() { + if (hostnameVerification == null) + return false; + return hostnameVerification; + } + + public void setHostnameVerification(boolean hostnameVerification) { + this.hostnameVerification = hostnameVerification; } /** * Gets the set of enabled protocols based on the configuration. - * @param supportedProtocols protocols supported by the SSL engine - * @param defaultProtocols default protocols enabled by the SSL engine + * + * @param supportedProtocols protocols supported by the SSL engine + * @param defaultProtocols default protocols enabled by the SSL engine * @return enabled protocols */ private String[] enabledProtocols(String[] supportedProtocols, String[] defaultProtocols) { if (enabledProtocols == null) { // we're assuming that the same engine is used for all configurables // so once we determine the enabled set, we won't do it again - if (OptionHelper.isEmpty(getIncludedProtocols()) && OptionHelper.isEmpty(getExcludedProtocols())) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedProtocols()) + && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedProtocols())) { enabledProtocols = Arrays.copyOf(defaultProtocols, defaultProtocols.length); } else { enabledProtocols = includedStrings(supportedProtocols, getIncludedProtocols(), getExcludedProtocols()); @@ -80,18 +98,21 @@ private String[] enabledProtocols(String[] supportedProtocols, String[] defaultP /** * Gets the set of enabled cipher suites based on the configuration. - * @param supportedCipherSuites cipher suites supported by the SSL engine - * @param defaultCipherSuites default cipher suites enabled by the SSL engine + * + * @param supportedCipherSuites cipher suites supported by the SSL engine + * @param defaultCipherSuites default cipher suites enabled by the SSL engine * @return enabled cipher suites */ private String[] enabledCipherSuites(String[] supportedCipherSuites, String[] defaultCipherSuites) { if (enabledCipherSuites == null) { // we're assuming that the same engine is used for all configurables // so once we determine the enabled set, we won't do it again - if (OptionHelper.isEmpty(getIncludedCipherSuites()) && OptionHelper.isEmpty(getExcludedCipherSuites())) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(getIncludedCipherSuites()) + && OptionHelper.isNullOrEmptyOrAllSpaces(getExcludedCipherSuites())) { enabledCipherSuites = Arrays.copyOf(defaultCipherSuites, defaultCipherSuites.length); } else { - enabledCipherSuites = includedStrings(supportedCipherSuites, getIncludedCipherSuites(), getExcludedCipherSuites()); + enabledCipherSuites = includedStrings(supportedCipherSuites, getIncludedCipherSuites(), + getExcludedCipherSuites()); } for (String cipherSuite : enabledCipherSuites) { addInfo("enabled cipher suite: " + cipherSuite); @@ -101,14 +122,15 @@ private String[] enabledCipherSuites(String[] supportedCipherSuites, String[] de } /** - * Applies include and exclude patterns to an array of default string values - * to produce an array of strings included by the patterns. + * Applies include and exclude patterns to an array of default string values to + * produce an array of strings included by the patterns. + * * @param defaults default list of string values * @param included comma-separated patterns that identity values to include * @param excluded comma-separated patterns that identity string to exclude * @return an array of strings containing those strings from {@code defaults} - * that match at least one pattern in {@code included} that are not - * matched by any pattern in {@code excluded} + * that match at least one pattern in {@code included} that are not + * matched by any pattern in {@code excluded} */ private String[] includedStrings(String[] defaults, String included, String excluded) { List values = new ArrayList(defaults.length); @@ -124,6 +146,7 @@ private String[] includedStrings(String[] defaults, String included, String excl /** * Splits a string containing comma-separated values into an array. + * * @param s the subject string * @return array of values contained in {@code s} */ @@ -133,8 +156,9 @@ private String[] stringToArray(String s) { /** * Gets the JSSE secure transport protocols to include. - * @return a string containing comma-separated JSSE secure transport - * protocol names (e.g. {@code TLSv1}) + * + * @return a string containing comma-separated JSSE secure transport protocol + * names (e.g. {@code TLSv1}) */ public String getIncludedProtocols() { return includedProtocols; @@ -142,9 +166,11 @@ public String getIncludedProtocols() { /** * Sets the JSSE secure transport protocols to include. - * @param protocols a string containing comma-separated JSSE secure - * transport protocol names - * @see Java Cryptography Architecture Standard Algorithm Name Documentation + * + *

See Java Cryptography Architecture Standard Algorithm Name Documentation

+ * + * @param protocols a string containing comma-separated JSSE secure transport + * protocol names */ public void setIncludedProtocols(String protocols) { this.includedProtocols = protocols; @@ -152,8 +178,9 @@ public void setIncludedProtocols(String protocols) { /** * Gets the JSSE secure transport protocols to exclude. - * @return a string containing comma-separated JSSE secure transport - * protocol names (e.g. {@code TLSv1}) + * + * @return a string containing comma-separated JSSE secure transport protocol + * names (e.g. {@code TLSv1}) */ public String getExcludedProtocols() { return excludedProtocols; @@ -161,9 +188,11 @@ public String getExcludedProtocols() { /** * Sets the JSSE secure transport protocols to exclude. - * @param protocols a string containing comma-separated JSSE secure - * transport protocol names - * @see Java Cryptography Architecture Standard Algorithm Name Documentation + * + *

See Java Cryptography Architecture Standard Algorithm Name Documentation

+ * + * @param protocols a string containing comma-separated JSSE secure transport + * protocol names */ public void setExcludedProtocols(String protocols) { this.excludedProtocols = protocols; @@ -171,8 +200,9 @@ public void setExcludedProtocols(String protocols) { /** * Gets the JSSE cipher suite names to include. - * @return a string containing comma-separated JSSE cipher suite names - * (e.g. {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA}) + * + * @return a string containing comma-separated JSSE cipher suite names (e.g. + * {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA}) */ public String getIncludedCipherSuites() { return includedCipherSuites; @@ -180,9 +210,11 @@ public String getIncludedCipherSuites() { /** * Sets the JSSE cipher suite names to include. - * @param cipherSuites a string containing comma-separated JSSE cipher - * suite names - * @see Java Cryptography Architecture Standard Algorithm Name Documentation + * + *

See Java Cryptography Architecture Standard Algorithm Name Documentation

+ * + * @param cipherSuites a string containing comma-separated JSSE cipher suite + * names */ public void setIncludedCipherSuites(String cipherSuites) { this.includedCipherSuites = cipherSuites; @@ -190,8 +222,9 @@ public void setIncludedCipherSuites(String cipherSuites) { /** * Gets the JSSE cipher suite names to exclude. - * @return a string containing comma-separated JSSE cipher suite names - * (e.g. {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA}) + * + * @return a string containing comma-separated JSSE cipher suite names (e.g. + * {@code TLS_DHE_RSA_WITH_AES_256_CBC_SHA}) */ public String getExcludedCipherSuites() { return excludedCipherSuites; @@ -199,9 +232,12 @@ public String getExcludedCipherSuites() { /** * Sets the JSSE cipher suite names to exclude. - * @param cipherSuites a string containing comma-separated JSSE cipher - * suite names - * @see Java Cryptography Architecture Standard Algorithm Name Documentation + * + *

See Java Cryptography Architecture Standard Algorithm Name Documentation

+ * + * @param cipherSuites a string containing comma-separated JSSE cipher suite + * names + * */ public void setExcludedCipherSuites(String cipherSuites) { this.excludedCipherSuites = cipherSuites; @@ -209,6 +245,7 @@ public void setExcludedCipherSuites(String cipherSuites) { /** * Gets a flag indicating whether client authentication is required. + * * @return flag state */ public Boolean isNeedClientAuth() { @@ -217,6 +254,7 @@ public Boolean isNeedClientAuth() { /** * Sets a flag indicating whether client authentication is required. + * * @param needClientAuth the flag state to set */ public void setNeedClientAuth(Boolean needClientAuth) { @@ -225,6 +263,7 @@ public void setNeedClientAuth(Boolean needClientAuth) { /** * Gets a flag indicating whether client authentication is desired. + * * @return flag state */ public Boolean isWantClientAuth() { @@ -233,6 +272,7 @@ public Boolean isWantClientAuth() { /** * Sets a flag indicating whether client authentication is desired. + * * @param wantClientAuth the flag state to set */ public void setWantClientAuth(Boolean wantClientAuth) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.java index 65a26d11e6..cacf32ec17 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,19 +31,23 @@ public class SecureRandomFactoryBean { private String provider; /** - * Creates a new {@link SecureRandom} generator using the receiver's + * Creates a new {@link SecureRandom} generator using the receiver's * configuration. + * * @return secure random generator instance - * @throws NoSuchProviderException if the provider name specified by - * {@link #setProvider(String)} is not known to the platform + * @throws NoSuchProviderException if the provider name specified by + * {@link #setProvider(String)} is not known to + * the platform * @throws NoSuchAlgorithmException if the algorithm name specified by - * {@link #setAlgorithm(String)} is not recognized by the specified - * provider (or the platform's default provider if the provider isn't - * specified) + * {@link #setAlgorithm(String)} is not + * recognized by the specified provider (or the + * platform's default provider if the provider + * isn't specified) */ public SecureRandom createSecureRandom() throws NoSuchProviderException, NoSuchAlgorithmException { try { - return getProvider() != null ? SecureRandom.getInstance(getAlgorithm(), getProvider()) : SecureRandom.getInstance(getAlgorithm()); + return getProvider() != null ? SecureRandom.getInstance(getAlgorithm(), getProvider()) + : SecureRandom.getInstance(getAlgorithm()); } catch (NoSuchProviderException ex) { throw new NoSuchProviderException("no such secure random provider: " + getProvider()); } catch (NoSuchAlgorithmException ex) { @@ -52,10 +56,11 @@ public SecureRandom createSecureRandom() throws NoSuchProviderException, NoSuchA } /** - * Gets the secure random generator algorithm name. - * @return an algorithm name (e.g. {@code SHA1PRNG}); the - * {@link SSL#DEFAULT_SECURE_RANDOM_ALGORITHM} is returned if no algorithm has been - * specified + * Gets the secure random generator algorithm name. + * + * @return an algorithm name (e.g. {@code SHA1PRNG}); the + * {@link SSL#DEFAULT_SECURE_RANDOM_ALGORITHM} is returned if no + * algorithm has been specified */ public String getAlgorithm() { if (algorithm == null) { @@ -66,9 +71,10 @@ public String getAlgorithm() { /** * Sets the secure random generator algorithm name. - * @param algorithm an algorithm name, which must be recognized by the - * provider specified via {@link #setProvider(String)} or by the - * platform's default provider if no provider is specified. + * + * @param algorithm an algorithm name, which must be recognized by the provider + * specified via {@link #setProvider(String)} or by the + * platform's default provider if no provider is specified. */ public void setAlgorithm(String algorithm) { this.algorithm = algorithm; @@ -76,6 +82,7 @@ public void setAlgorithm(String algorithm) { /** * Gets the JCA provider name for the secure random generator. + * * @return provider name */ public String getProvider() { @@ -84,8 +91,9 @@ public String getProvider() { /** * Sets the JCA provider name for the secure random generator. - * @param provider name of the JCA provider to utilize in creating the - * secure random generator + * + * @param provider name of the JCA provider to utilize in creating the secure + * random generator */ public void setProvider(String provider) { this.provider = provider; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.java b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.java index 3e9f143fdf..8c84080d5e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.java +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,8 +21,8 @@ /** * A factory bean for a JSSE {@link TrustManagerFactory}. *

- * This object holds the configurable properties of a trust manager factory - * and uses them to create and load a {@link TrustManagerFactory} instance. + * This object holds the configurable properties of a trust manager factory and + * uses them to create and load a {@link TrustManagerFactory} instance. * * @author Carl Harris */ @@ -33,23 +33,29 @@ public class TrustManagerFactoryFactoryBean { /** * Creates a {@link TrustManagerFactory} using the receiver's configuration. + * * @return factory object - * @throws NoSuchProviderException if the provider specified by - * {@link #setProvider(String)} is not known to the platform + * @throws NoSuchProviderException if the provider specified by + * {@link #setProvider(String)} is not known to + * the platform * @throws NoSuchAlgorithmException if the algorithm specified by - * {@link #setAlgorithm(String)} is not known to the specified provider - * (or to the default platform provider if no provider is specified) + * {@link #setAlgorithm(String)} is not known + * to the specified provider (or to the default + * platform provider if no provider is + * specified) */ public TrustManagerFactory createTrustManagerFactory() throws NoSuchProviderException, NoSuchAlgorithmException { - return getProvider() != null ? TrustManagerFactory.getInstance(getAlgorithm(), getProvider()) : TrustManagerFactory.getInstance(getAlgorithm()); + return getProvider() != null ? TrustManagerFactory.getInstance(getAlgorithm(), getProvider()) + : TrustManagerFactory.getInstance(getAlgorithm()); } /** * Gets the algorithm name for the trust manager factory. - * @return algorithm name (e.g. {@code PKIX}); the default algorithm - * (obtained from {@link TrustManagerFactory#getDefaultAlgorithm()}) - * is returned if no algorithm has been configured + * + * @return algorithm name (e.g. {@code PKIX}); the default algorithm (obtained + * from {@link TrustManagerFactory#getDefaultAlgorithm()}) is returned + * if no algorithm has been configured */ public String getAlgorithm() { if (algorithm == null) { @@ -60,9 +66,10 @@ public String getAlgorithm() { /** * Sets the algorithm name for the trust manager factory. - * @param algorithm an algorithm name, which must be recognized by the - * provider specified by {@link #setProvider(String)} or by the - * platform's default provider if no provider is specified. + * + * @param algorithm an algorithm name, which must be recognized by the provider + * specified by {@link #setProvider(String)} or by the + * platform's default provider if no provider is specified. */ public void setAlgorithm(String algorithm) { this.algorithm = algorithm; @@ -70,6 +77,7 @@ public void setAlgorithm(String algorithm) { /** * Gets the JSSE provider name for the trust manager factory. + * * @return provider name */ public String getProvider() { @@ -78,8 +86,9 @@ public String getProvider() { /** * Sets the JSSE provider name for the trust manager factory. - * @param provider name of the JSSE provider to utilize in creating the - * trust manager factory + * + * @param provider name of the JSSE provider to utilize in creating the trust + * manager factory */ public void setProvider(String provider) { this.provider = provider; diff --git a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/package.html b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/package.html index 7a6b3e0c30..c7e2ba2bea 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/net/ssl/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/net/ssl/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/package.html b/logback-core/src/main/java/ch/qos/logback/core/package.html index 608e010fdb..1beeadab23 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/CompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/CompositeConverter.java index 61faf52b5a..52d81e12f2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/CompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/CompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/Converter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/Converter.java index b8cace7f9d..85ad588bdc 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/Converter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/Converter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,10 @@ package ch.qos.logback.core.pattern; /** - * A minimal converter which sets up the general interface for derived classes. + * A minimal converter which sets up the general interface for derived classes. * It also implements the functionality to chain converters in a linked list. - * + * + * @param The type of the event object * @author ceki */ abstract public class Converter { @@ -27,7 +28,7 @@ abstract public class Converter { * The convert method is responsible for extracting data from the event and * storing it for later use by the write method. * - * @param event + * @param event the event to convert */ public abstract String convert(E event); @@ -35,7 +36,7 @@ abstract public class Converter { * In its simplest incarnation, a convert simply appends the data extracted from * the event to the buffer passed as parameter. * - * @param buf The input buffer where data is appended + * @param buf The input buffer where data is appended * @param event The event from where data is extracted */ public void write(StringBuilder buf, E event) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/ConverterUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/ConverterUtil.java index e0c4b982d4..683b919b79 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/ConverterUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/ConverterUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -59,6 +59,11 @@ public static void setContextForConverters(Context context, Converter hea if (c instanceof ContextAware) { ((ContextAware) c).setContext(context); } + if (c instanceof CompositeConverter) { + CompositeConverter cc = (CompositeConverter) c; + Converter childConverter = cc.childConverter; + setContextForConverters(context, childConverter); + } c = c.getNext(); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/DynamicConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/DynamicConverter.java index 80f65c6388..8cf2485536 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/DynamicConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/DynamicConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,6 +21,13 @@ import ch.qos.logback.core.spi.LifeCycle; import ch.qos.logback.core.status.Status; +/** + * As the name suggests, a DynamicConverter performs a conversion based on the parameter E + * given to the {@link #convert} method. Almost all converters are derived from the + * DynamicConverter class. + * + * @param + */ abstract public class DynamicConverter extends FormattingConverter implements LifeCycle, ContextAware { ContextAwareBase cab = new ContextAwareBase(this); @@ -39,14 +46,17 @@ abstract public class DynamicConverter extends FormattingConverter impleme * components, the trivial implementation found in this abstract class will be * sufficient. */ + @Override public void start() { started = true; } + @Override public void stop() { started = false; } + @Override public boolean isStarted() { return started; } @@ -73,38 +83,47 @@ protected List getOptionList() { return optionList; } + @Override public void setContext(Context context) { cab.setContext(context); } + @Override public Context getContext() { return cab.getContext(); } + @Override public void addStatus(Status status) { cab.addStatus(status); } + @Override public void addInfo(String msg) { cab.addInfo(msg); } + @Override public void addInfo(String msg, Throwable ex) { cab.addInfo(msg, ex); } + @Override public void addWarn(String msg) { cab.addWarn(msg); } + @Override public void addWarn(String msg, Throwable ex) { cab.addWarn(msg, ex); } + @Override public void addError(String msg) { cab.addError(msg); } + @Override public void addError(String msg, Throwable ex) { cab.addError(msg, ex); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/FormatInfo.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/FormatInfo.java index 91b015bd0f..9b3cae9f89 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/FormatInfo.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/FormatInfo.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/FormattingConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/FormattingConverter.java index 668cc8938b..08a5cc79cb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/FormattingConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/FormattingConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/IdentityCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/IdentityCompositeConverter.java index e9d67aa54d..1098dde477 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/IdentityCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/IdentityCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/LiteralConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/LiteralConverter.java index 4369fe5a58..8cc39733bf 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/LiteralConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/LiteralConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutBase.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutBase.java index 7402838a13..4942d3feb4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,10 +15,12 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.Context; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.LayoutBase; +import ch.qos.logback.core.pattern.color.ConverterSupplierByClassName; import ch.qos.logback.core.pattern.parser.Node; import ch.qos.logback.core.pattern.parser.Parser; import ch.qos.logback.core.spi.ScanException; @@ -31,36 +33,62 @@ abstract public class PatternLayoutBase extends LayoutBase { Converter head; String pattern; protected PostCompileProcessor postCompileProcessor; - - Map instanceConverterMap = new HashMap(); + + /** + *

It should be noted that the default converter map is a static variable. Thus, changes made + * through {@link #getDefaultConverterSupplierMap()} apply to all instances of this class. + *

+ * + *

The {@link #getInstanceConverterMap} variable allows for very specific extensions + * without impacting other instances

+ */ + Map> instanceConverterMap = new HashMap<>(); protected boolean outputPatternAsHeader = false; - + /** * Concrete implementations of this class are responsible for elaborating the - * mapping between pattern words and converters. + * mapping between pattern words and supplying converter instances. * - * @return A map associating pattern words to the names of converter classes + * @return A map associating pattern words to the names of converter suppliers + * @since 1.5.13 */ + protected abstract Map> getDefaultConverterSupplierMap(); + + /** + *

BEWARE: The map of type String,String for mapping conversion words is deprecated. + * Use {@link #getDefaultConverterSupplierMap()} instead.

+ * + *

Existing code such as getDefaultMap().put("k", X.class.getName()) should be replaced by + * getDefaultConverterSupplierMap().put("k", X::new)

+ * + *

Note that values in the map will still be taken into account and processed correctly.

+ * + * @return a map of keys and class names + */ + @Deprecated abstract public Map getDefaultConverterMap(); /** * Returns a map where the default converter map is merged with the map * contained in the context. */ - public Map getEffectiveConverterMap() { - Map effectiveMap = new HashMap(); + public Map> getEffectiveConverterMap() { + Map> effectiveMap = new HashMap<>(); // add the least specific map fist - Map defaultMap = getDefaultConverterMap(); - if (defaultMap != null) { - effectiveMap.putAll(defaultMap); + Map> defaultConverterSupplierMap = getDefaultConverterSupplierMap(); + if (defaultConverterSupplierMap != null) { + effectiveMap.putAll(defaultConverterSupplierMap); } + caterForLegacyConverterMaps(effectiveMap); + // contextMap is more specific than the default map Context context = getContext(); if (context != null) { @SuppressWarnings("unchecked") - Map contextMap = (Map) context.getObject(CoreConstants.PATTERN_RULE_REGISTRY); + Map> contextMap = (Map>) context + .getObject(CoreConstants.PATTERN_RULE_REGISTRY_FOR_SUPPLIERS); if (contextMap != null) { effectiveMap.putAll(contextMap); } @@ -70,6 +98,37 @@ public Map getEffectiveConverterMap() { return effectiveMap; } + /** + * Add class name values into the effective map to support external extensions + * and subclasses. + * + * @param effectiveMap + */ + private void caterForLegacyConverterMaps(Map> effectiveMap) { + Map mapFromContext = (Map) this.context + .getObject(CoreConstants.PATTERN_RULE_REGISTRY); + + migrateFromStringMapToSupplierMap(mapFromContext, effectiveMap); + + Map defaultConverterMap = getDefaultConverterMap(); + migrateFromStringMapToSupplierMap(defaultConverterMap, effectiveMap); + } + + private void migrateFromStringMapToSupplierMap(Map legacyMap, Map> targetSupplierMap) { + if(legacyMap == null) + return; + + // this transformation is for backward compatibility of existing code + for(Map.Entry entry: legacyMap.entrySet()) { + String key = entry.getKey(); + String converterClassName = entry.getValue(); + ConverterSupplierByClassName converterSupplierByClassName = new ConverterSupplierByClassName(key, converterClassName); + converterSupplierByClassName.setContext(getContext()); + targetSupplierMap.put(key, converterSupplierByClassName); + } + + } + public void start() { if (pattern == null || pattern.length() == 0) { addError("Empty or null pattern."); @@ -101,8 +160,8 @@ public void setPostCompileProcessor(PostCompileProcessor postCompileProcessor /** * * @param head - * @deprecated Use {@link ConverterUtil#setContextForConverters} instead. This method will - * be removed in future releases. + * @deprecated Use {@link ConverterUtil#setContextForConverters} instead. This + * method will be removed in future releases. */ protected void setContextForConverters(Converter head) { ConverterUtil.setContextForConverters(getContext(), head); @@ -130,7 +189,7 @@ public String toString() { return this.getClass().getName() + "(\"" + getPattern() + "\")"; } - public Map getInstanceConverterMap() { + public Map> getInstanceConverterMap() { return instanceConverterMap; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutEncoderBase.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutEncoderBase.java index 062d085d02..5aa9cbe3ac 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutEncoderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/PatternLayoutEncoderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/PostCompileProcessor.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/PostCompileProcessor.java index 5966d49c5b..795710475e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/PostCompileProcessor.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/PostCompileProcessor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -28,8 +28,7 @@ public interface PostCompileProcessor { /** * Post compile processing of the converter chain. * - * @param head - * The first converter in the chain + * @param head The first converter in the chain */ void process(Context context, Converter head); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/ReplacingCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/ReplacingCompositeConverter.java index 6c79044e17..43c9a8779d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/ReplacingCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/ReplacingCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -32,7 +32,8 @@ public void start() { int numOpts = optionList.size(); if (numOpts < 2) { - addError("at least two options are expected whereas you have declared only " + numOpts + "as [" + optionList + "]"); + addError("at least two options are expected whereas you have declared only " + numOpts + "as [" + optionList + + "]"); return; } regex = optionList.get(0); diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/SpacePadder.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/SpacePadder.java index 1d83e0b396..69ac583549 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/SpacePadder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/SpacePadder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ANSIConstants.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ANSIConstants.java index c67168feb0..16cca249bd 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ANSIConstants.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ANSIConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,6 +18,7 @@ public class ANSIConstants { public final static String ESC_START = "\033["; public final static String ESC_END = "m"; public final static String BOLD = "1;"; + public final static String RESET = "0;"; public final static String BLACK_FG = "30"; public final static String RED_FG = "31"; diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlackCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlackCompositeConverter.java index 16069544eb..7a3355ab1c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlackCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlackCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in black using the appropriate ANSI escape codes. + * Encloses a given set of converter output in black using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class BlackCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlueCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlueCompositeConverter.java index 8f227b75de..a79be1c5d7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlueCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BlueCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in blue using the appropriate ANSI escape codes. + * Encloses a given set of converter output in blue using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class BlueCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldBlueCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldBlueCompositeConverter.java index e9100e7c37..37c9941316 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldBlueCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldBlueCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.BOLD; /** - * Encloses a given set of converter output in bold blue using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold blue using the appropriate + * ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldCyanCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldCyanCompositeConverter.java index 327b4f720e..5996337d4a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldCyanCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldCyanCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.CYAN_FG; /** - * Encloses a given set of converter output in bold cyan using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold cyan using the appropriate + * ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldGreenCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldGreenCompositeConverter.java index 23e2677f1b..b1428537b8 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldGreenCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldGreenCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.GREEN_FG; /** - * Encloses a given set of converter output in bold green using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold green using the appropriate + * ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldMagentaCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldMagentaCompositeConverter.java index 2d0751d21f..c78a62faf6 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldMagentaCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldMagentaCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.MAGENTA_FG; /** - * Encloses a given set of converter output in bold magenta using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold magenta using the + * appropriate ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldRedCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldRedCompositeConverter.java index 3edc98e2ad..71f62fe779 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldRedCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldRedCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,9 +16,11 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.*; /** - * Encloses a given set of converter output in bold red using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold red using the appropriate + * ANSI escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class BoldRedCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldWhiteCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldWhiteCompositeConverter.java index 75bb915b22..f00cabd56c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldWhiteCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldWhiteCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.WHITE_FG; /** - * Encloses a given set of converter output in bold white using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold white using the appropriate + * ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldYellowCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldYellowCompositeConverter.java index c11603c942..0aed495021 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldYellowCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/BoldYellowCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import static ch.qos.logback.core.pattern.color.ANSIConstants.YELLOW_FG; /** - * Encloses a given set of converter output in bold yellow using the appropriate ANSI escape codes. + * Encloses a given set of converter output in bold yellow using the appropriate + * ANSI escape codes. * * @param * @author Ceki Gülcü diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ConverterSupplierByClassName.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ConverterSupplierByClassName.java new file mode 100644 index 0000000000..c483dd6e83 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ConverterSupplierByClassName.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.pattern.color; + +import ch.qos.logback.core.pattern.DynamicConverter; +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.OptionHelper; + +import java.util.function.Supplier; + +/** + * + *

Implements the {@link Supplier} interface in order to cater for legacy code using the class name + * of a converter. + *

+ *

Should not be used in non-legacy code.

+ */ +public class ConverterSupplierByClassName extends ContextAwareBase implements Supplier { + + String conversionWord; + String converterClassStr; + + public ConverterSupplierByClassName(String conversionWord, String converterClassStr) { + this.conversionWord = conversionWord; + this.converterClassStr = converterClassStr; + } + + @Override + public DynamicConverter get() { + try { + return (DynamicConverter) OptionHelper.instantiateByClassName(converterClassStr, DynamicConverter.class, context); + } catch (Exception e) { + addError("Failed to instantiate converter class [" + converterClassStr + "] for conversion word ["+conversionWord+"]", e); + return null; + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/CyanCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/CyanCompositeConverter.java index 43f138b904..6b681d31a3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/CyanCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/CyanCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in cyan using the appropriate ANSI escape codes. + * Encloses a given set of converter output in cyan using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class CyanCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ForegroundCompositeConverterBase.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ForegroundCompositeConverterBase.java index cb1dc7facf..39ee65776a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ForegroundCompositeConverterBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/ForegroundCompositeConverterBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -24,7 +24,7 @@ */ abstract public class ForegroundCompositeConverterBase extends CompositeConverter { - final private static String SET_DEFAULT_COLOR = ESC_START + "0;" + DEFAULT_FG + ESC_END; + final private static String SET_DEFAULT_COLOR = ESC_START + RESET + DEFAULT_FG + ESC_END; @Override protected String transform(E event, String in) { @@ -38,8 +38,10 @@ protected String transform(E event, String in) { } /** - * Derived classes return the foreground color specific to the derived class instance. - * @return the foreground color for this instance + * Derived classes return the foreground color specific to the derived class + * instance. + * + * @return the foreground color for this instance */ abstract protected String getForegroundColorCode(E event); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GrayCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GrayCompositeConverter.java index 8db9621e35..802d453229 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GrayCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GrayCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,7 +20,7 @@ * escape codes. * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class GrayCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GreenCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GreenCompositeConverter.java index 3d0b10b6b8..244efa99e6 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GreenCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/GreenCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in green using the appropriate ANSI escape codes. + * Encloses a given set of converter output in green using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class GreenCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/MagentaCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/MagentaCompositeConverter.java index c98f41f411..22c00648ae 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/MagentaCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/MagentaCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in magenta using the appropriate ANSI escape codes. + * Encloses a given set of converter output in magenta using the appropriate + * ANSI escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class MagentaCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/RedCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/RedCompositeConverter.java index 35d0af9ad2..27454b4772 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/RedCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/RedCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in red using the appropriate ANSI escape codes. + * Encloses a given set of converter output in red using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class RedCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/WhiteCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/WhiteCompositeConverter.java index 0bee2d7be7..957d6180e5 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/WhiteCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/WhiteCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in white using the appropriate ANSI escape codes. + * Encloses a given set of converter output in white using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class WhiteCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/YellowCompositeConverter.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/YellowCompositeConverter.java index a0b56d50c9..913756b419 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/color/YellowCompositeConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/color/YellowCompositeConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,9 +14,11 @@ package ch.qos.logback.core.pattern.color; /** - * Encloses a given set of converter output in yellow using the appropriate ANSI escape codes. + * Encloses a given set of converter output in yellow using the appropriate ANSI + * escape codes. + * * @param - * @author Ceki Gülcü + * @author Ceki Gülcü * @since 1.0.5 */ public class YellowCompositeConverter extends ForegroundCompositeConverterBase { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/package.html b/logback-core/src/main/java/ch/qos/logback/core/pattern/package.html index a9526beb02..d8ccf04d74 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Compiler.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Compiler.java index 7f8ed27e22..7dc374188d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Compiler.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Compiler.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,6 +14,7 @@ package ch.qos.logback.core.pattern.parser; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.pattern.CompositeConverter; import ch.qos.logback.core.pattern.Converter; @@ -28,9 +29,9 @@ class Compiler extends ContextAwareBase { Converter head; Converter tail; final Node top; - final Map converterMap; + final Map> converterMap; - Compiler(final Node top, final Map converterMap) { + Compiler(final Node top, final Map> converterMap) { this.top = top; this.converterMap = converterMap; } @@ -88,8 +89,7 @@ private void addToList(Converter c) { } /** - * Attempt to create a converter using the information found in - * 'converterMap'. + * Attempt to create a converter using the information found in 'converterMap'. * * @param kn * @return @@ -97,17 +97,12 @@ private void addToList(Converter c) { @SuppressWarnings("unchecked") DynamicConverter createConverter(SimpleKeywordNode kn) { String keyword = (String) kn.getValue(); - String converterClassStr = (String) converterMap.get(keyword); + Supplier supplier = converterMap.get(keyword); - if (converterClassStr != null) { - try { - return (DynamicConverter) OptionHelper.instantiateByClassName(converterClassStr, DynamicConverter.class, context); - } catch (Exception e) { - addError("Failed to instantiate converter class [" + converterClassStr + "] for keyword [" + keyword + "]", e); - return null; - } + if (supplier != null) { + return supplier.get(); } else { - addError("There is no conversion class registered for conversion word [" + keyword + "]"); + addError("There is no conversion supplier registered for conversion word [" + keyword + "]"); return null; } } @@ -122,13 +117,13 @@ DynamicConverter createConverter(SimpleKeywordNode kn) { @SuppressWarnings("unchecked") CompositeConverter createCompositeConverter(CompositeNode cn) { String keyword = (String) cn.getValue(); - String converterClassStr = (String) converterMap.get(keyword); + Supplier supplier = (Supplier) converterMap.get(keyword); - if (converterClassStr != null) { + if (supplier != null) { try { - return (CompositeConverter) OptionHelper.instantiateByClassName(converterClassStr, CompositeConverter.class, context); - } catch (Exception e) { - addError("Failed to instantiate converter class [" + converterClassStr + "] as a composite converter for keyword [" + keyword + "]", e); + return (CompositeConverter) supplier.get(); + } catch(ClassCastException e) { + addError("Failed to cast as CompositeConverter for keyword [" + keyword + "]", e); return null; } } else { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/CompositeNode.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/CompositeNode.java index 5ae8619401..9aab8ef493 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/CompositeNode.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/CompositeNode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/FormattingNode.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/FormattingNode.java index 5cf16f5137..845eaf59d4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/FormattingNode.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/FormattingNode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Node.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Node.java index 7fb543d2ff..591eb32c8f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Node.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Node.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -62,7 +62,8 @@ public boolean equals(Object o) { } Node r = (Node) o; - return (type == r.type) && (value != null ? value.equals(r.value) : r.value == null) && (next != null ? next.equals(r.next) : r.next == null); + return (type == r.type) && (value != null ? value.equals(r.value) : r.value == null) + && (next != null ? next.equals(r.next) : r.next == null); } @Override diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/OptionTokenizer.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/OptionTokenizer.java index 2b2791d374..1dbcadcf05 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/OptionTokenizer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/OptionTokenizer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Parser.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Parser.java index 58a9216b2d..daa9a57655 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Parser.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Parser.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,18 +16,16 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.pattern.Converter; -import ch.qos.logback.core.pattern.FormatInfo; -import ch.qos.logback.core.pattern.IdentityCompositeConverter; -import ch.qos.logback.core.pattern.ReplacingCompositeConverter; +import ch.qos.logback.core.pattern.*; import ch.qos.logback.core.pattern.util.IEscapeUtil; import ch.qos.logback.core.pattern.util.RegularEscapeUtil; import ch.qos.logback.core.spi.ContextAwareBase; import ch.qos.logback.core.spi.ScanException; -// ~=lamda +// ~=lambda // E = TE|T // Left factorization @@ -44,11 +42,12 @@ public class Parser extends ContextAwareBase { public final static String MISSING_RIGHT_PARENTHESIS = CoreConstants.CODES_URL + "#missingRightParenthesis"; - public final static Map DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap(); + public final static Map> DEFAULT_COMPOSITE_CONVERTER_MAP = new HashMap<>(); public final static String REPLACE_CONVERTER_WORD = "replace"; static { - DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(), IdentityCompositeConverter.class.getName()); - DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter.class.getName()); + DEFAULT_COMPOSITE_CONVERTER_MAP.put(Token.BARE_COMPOSITE_KEYWORD_TOKEN.getValue().toString(), + IdentityCompositeConverter::new); + DEFAULT_COMPOSITE_CONVERTER_MAP.put(REPLACE_CONVERTER_WORD, ReplacingCompositeConverter::new); } final List tokenList; @@ -79,7 +78,7 @@ public Parser(String pattern, IEscapeUtil escapeUtil) throws ScanException { * @param converterMap * @return */ - public Converter compile(final Node top, Map converterMap) { + public Converter compile(final Node top, Map> converterMap) { Compiler compiler = new Compiler(top, converterMap); compiler.setContext(context); // compiler.setStatusManager(statusManager); diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/SimpleKeywordNode.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/SimpleKeywordNode.java index 80ec6e9838..f9375ab179 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/SimpleKeywordNode.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/SimpleKeywordNode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Token.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Token.java index 2de6d0a705..dbfc1df8a3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Token.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/Token.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -35,6 +35,7 @@ class Token { static Token EOF_TOKEN = new Token(EOF, "EOF"); static Token RIGHT_PARENTHESIS_TOKEN = new Token(RIGHT_PARENTHESIS); + // BARE as in naked. Used for formatting purposes static Token BARE_COMPOSITE_KEYWORD_TOKEN = new Token(COMPOSITE_KEYWORD, "BARE"); static Token PERCENT_TOKEN = new Token(PERCENT); @@ -49,11 +50,11 @@ public Token(int type) { public Token(int type, String value) { this(type, value, null); } - + public Token(int type, List optionsList) { this(type, null, optionsList); } - + public Token(int type, String value, List optionsList) { this.type = type; this.value = value; @@ -67,11 +68,11 @@ public int getType() { public String getValue() { return value; } - + public List getOptionsList() { return optionsList; } - + public String toString() { String typeStr = null; switch (type) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/TokenStream.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/TokenStream.java index 4cc2c65103..aee0ccdd65 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/TokenStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/parser/TokenStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,8 +31,9 @@ *

*

*

- * The returned tokens are one of: LITERAL, '%', FORMAT_MODIFIER, SIMPLE_KEYWORD, COMPOSITE_KEYWORD - * OPTION, LEFT_PARENTHESIS, and RIGHT_PARENTHESIS. + * The returned tokens are one of: LITERAL, '%', FORMAT_MODIFIER, + * SIMPLE_KEYWORD, COMPOSITE_KEYWORD OPTION, LEFT_PARENTHESIS, and + * RIGHT_PARENTHESIS. *

*

*

diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AlmostAsIsEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AlmostAsIsEscapeUtil.java index 8a51d7fb90..790bb11580 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AlmostAsIsEscapeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AlmostAsIsEscapeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -29,10 +29,10 @@ public class AlmostAsIsEscapeUtil extends RestrictedEscapeUtil { *

* Here is the rationale. First, filename patterns do not include escape * combinations such as \r or \n. Moreover, characters which have special - * meaning in logback parsers, such as '{', or '}' cannot be part of file - * names (so me thinks). The left parenthesis character has special meaning - * only if it is preceded by %. Thus, the only characters that needs escaping - * are '%' and ')'. + * meaning in logback parsers, such as '{', or '}' cannot be part of file names + * (so me thinks). The left parenthesis character has special meaning only if it + * is preceded by %. Thus, the only characters that needs escaping are '%' and + * ')'. * *

* Note that this method assumes that it is called after the escape character diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AsIsEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AsIsEscapeUtil.java index 46d0a2acbe..16a3e2baef 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AsIsEscapeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/AsIsEscapeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/IEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/IEscapeUtil.java index 8f646ffb79..41912cb60a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/IEscapeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/IEscapeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RegularEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RegularEscapeUtil.java index 92b2b3324a..fce934dc67 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RegularEscapeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RegularEscapeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -42,8 +42,9 @@ public void escape(String escapeChars, StringBuffer buf, char next, int pointer) break; default: String commaSeperatedEscapeChars = formatEscapeCharsForListing(escapeChars); - throw new IllegalArgumentException("Illegal char '" + next + " at column " + pointer + ". Only \\\\, \\_" + commaSeperatedEscapeChars - + ", \\t, \\n, \\r combinations are allowed as escape characters."); + throw new IllegalArgumentException("Illegal char '" + next + " at column " + pointer + + ". Only \\\\, \\_" + commaSeperatedEscapeChars + + ", \\t, \\n, \\r combinations are allowed as escape characters."); } } @@ -55,6 +56,8 @@ String formatEscapeCharsForListing(String escapeChars) { return commaSeperatedEscapeChars.toString(); } + // s might be path such as c:\\toto\\file.log + // as of version 1.3.0-beta1 this method is no longer used public static String basicEscape(String s) { char c; int len = s.length(); @@ -63,7 +66,7 @@ public static String basicEscape(String s) { int i = 0; while (i < len) { c = s.charAt(i++); - if (c == '\\') { + if (c == '\\' && i < len ) { c = s.charAt(i++); if (c == 'n') { c = '\n'; @@ -82,9 +85,10 @@ public static String basicEscape(String s) { } else if (c == '\\') { c = '\\'; } + ///// } sbuf.append(c); - } + } // while return sbuf.toString(); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RestrictedEscapeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RestrictedEscapeUtil.java index c440cf4ffe..55194661f3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RestrictedEscapeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/pattern/util/RestrictedEscapeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/property/CanonicalHostNamePropertyDefiner.java b/logback-core/src/main/java/ch/qos/logback/core/property/CanonicalHostNamePropertyDefiner.java index 8493a401e6..ff48caea4e 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/property/CanonicalHostNamePropertyDefiner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/property/CanonicalHostNamePropertyDefiner.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.property; import ch.qos.logback.core.PropertyDefinerBase; diff --git a/logback-core/src/main/java/ch/qos/logback/core/property/FileExistsPropertyDefiner.java b/logback-core/src/main/java/ch/qos/logback/core/property/FileExistsPropertyDefiner.java index 11ce943ed6..de1c5826da 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/property/FileExistsPropertyDefiner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/property/FileExistsPropertyDefiner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,14 +14,15 @@ package ch.qos.logback.core.property; import ch.qos.logback.core.PropertyDefinerBase; +import ch.qos.logback.core.joran.action.PropertyAction; import ch.qos.logback.core.util.OptionHelper; import java.io.File; /** - * In conjunction with {@link ch.qos.logback.core.joran.action.PropertyAction} sets - * the named variable to "true" if the file specified by {@link #setPath(String) path} - * property exists, to "false" otherwise. + * In conjunction with {@link PropertyAction} + * sets the named variable to "true" if the file specified by + * {@link #setPath(String) path} property exists, to "false" otherwise. * * @see #getPropertyValue() * @@ -45,13 +46,13 @@ public void setPath(String path) { } /** - * Returns "true" if the file specified by {@link #setPath(String) path} property exists. - * Returns "false" otherwise. + * Returns "true" if the file specified by {@link #setPath(String) path} + * property exists. Returns "false" otherwise. * * @return "true"|"false" depending on the existence of file */ public String getPropertyValue() { - if (OptionHelper.isEmpty(path)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(path)) { addError("The \"path\" property must be set."); return null; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/property/ResourceExistsPropertyDefiner.java b/logback-core/src/main/java/ch/qos/logback/core/property/ResourceExistsPropertyDefiner.java index 4ba693c05e..67e2277fe3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/property/ResourceExistsPropertyDefiner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/property/ResourceExistsPropertyDefiner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,15 +14,17 @@ package ch.qos.logback.core.property; import ch.qos.logback.core.PropertyDefinerBase; +import ch.qos.logback.core.joran.action.PropertyAction; import ch.qos.logback.core.util.Loader; import ch.qos.logback.core.util.OptionHelper; import java.net.URL; /** - * In conjunction with {@link ch.qos.logback.core.joran.action.PropertyAction} sets - * the named variable to "true" if the {@link #setResource(String) resource} specified - * by the user is available on the class path, "false" otherwise. + * In conjunction with {@link PropertyAction} + * sets the named variable to "true" if the {@link #setResource(String) + * resource} specified by the user is available on the class path, "false" + * otherwise. * * @see #getPropertyValue() * @@ -48,13 +50,14 @@ public void setResource(String resource) { } /** - * Returns the string "true" if the {@link #setResource(String) resource} specified by the - * user is available on the class path, "false" otherwise. + * Returns the string "true" if the {@link #setResource(String) resource} + * specified by the user is available on the class path, "false" otherwise. * - * @return "true"|"false" depending on the availability of resource on the classpath + * @return "true"|"false" depending on the availability of resource on the + * classpath */ public String getPropertyValue() { - if (OptionHelper.isEmpty(resourceStr)) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(resourceStr)) { addError("The \"resource\" property must be set."); return null; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/read/CyclicBufferAppender.java b/logback-core/src/main/java/ch/qos/logback/core/read/CyclicBufferAppender.java index a500c99c5c..ff65eb9594 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/read/CyclicBufferAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/read/CyclicBufferAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,9 @@ import ch.qos.logback.core.helpers.CyclicBuffer; /** - * CyclicBufferAppender stores events in a cyclic buffer of user-specified size. As the - * name suggests, if the size of the buffer is N, only the latest N events are available. + * CyclicBufferAppender stores events in a cyclic buffer of user-specified size. + * As the name suggests, if the size of the buffer is N, only the latest N + * events are available. * * * @author Ceki Gulcu diff --git a/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java b/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java index e8eabaa2b8..9506fb93b4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/read/ListAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryCoordinator.java b/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryCoordinator.java index 2c3c765585..0820fa7d36 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryCoordinator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryCoordinator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryListener.java b/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryListener.java new file mode 100644 index 0000000000..7ee59bcf15 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/recovery/RecoveryListener.java @@ -0,0 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.recovery; + +import java.io.IOException; + +/** + * + * An interface for monitoring new failures and recoveries related to {@link ResilientOutputStreamBase}. + * + * + * @since 1.3.0 + * @author ceki + * + */ +public interface RecoveryListener { + + + void newFailure(IOException e); + + void recoveryOccured(); +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientFileOutputStream.java b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientFileOutputStream.java index 199f125fd0..852b79d717 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientFileOutputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientFileOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientOutputStreamBase.java b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientOutputStreamBase.java index dd33046c62..583a44f878 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientOutputStreamBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientOutputStreamBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,8 @@ import java.io.IOException; import java.io.OutputStream; +import java.util.ArrayList; +import java.util.List; import ch.qos.logback.core.Context; import ch.qos.logback.core.status.ErrorStatus; @@ -35,11 +37,21 @@ abstract public class ResilientOutputStreamBase extends OutputStream { protected OutputStream os; protected boolean presumedClean = true; + List recoveryListeners = new ArrayList<>(0); + private boolean isPresumedInError() { // existence of recoveryCoordinator indicates failed state return (recoveryCoordinator != null && !presumedClean); } + public void addRecoveryListener(RecoveryListener listener) { + recoveryListeners.add(listener); + } + + public void removeRecoveryListener(RecoveryListener listener) { + recoveryListeners.remove(listener); + } + public void write(byte b[], int off, int len) { if (isPresumedInError()) { if (!recoveryCoordinator.isTooSoon()) { @@ -92,6 +104,7 @@ private void postSuccessfulWrite() { if (recoveryCoordinator != null) { recoveryCoordinator = null; statusCount = 0; + recoveryListeners.forEach( listener -> listener.recoveryOccured()); addStatus(new InfoStatus("Recovered from IO failure on " + getDescription(), this)); } } @@ -101,6 +114,7 @@ public void postIOFailure(IOException e) { presumedClean = false; if (recoveryCoordinator == null) { recoveryCoordinator = new RecoveryCoordinator(); + recoveryListeners.forEach( listener -> listener.newFailure(e)); } } @@ -117,7 +131,8 @@ void attemptRecovery() { } catch (IOException e) { } - addStatusIfCountNotOverLimit(new InfoStatus("Attempting to recover from IO failure on " + getDescription(), this)); + addStatusIfCountNotOverLimit( + new InfoStatus("Attempting to recover from IO failure on " + getDescription(), this)); // subsequent writes must always be in append mode try { diff --git a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientSyslogOutputStream.java b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientSyslogOutputStream.java index 064a484f90..814ebd1907 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientSyslogOutputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/recovery/ResilientSyslogOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/DefaultTimeBasedFileNamingAndTriggeringPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/DefaultTimeBasedFileNamingAndTriggeringPolicy.java index 314c0ce79b..8c05881328 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/DefaultTimeBasedFileNamingAndTriggeringPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/DefaultTimeBasedFileNamingAndTriggeringPolicy.java @@ -1,28 +1,33 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.rolling; import java.io.File; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; import java.util.Date; import ch.qos.logback.core.joran.spi.NoAutoStart; import ch.qos.logback.core.rolling.helper.TimeBasedArchiveRemover; /** - * + * Default implementation of {@link TimeBasedFileNamingAndTriggeringPolicy} + * interface extending {@link TimeBasedFileNamingAndTriggeringPolicyBase}. This class is intended to be nested + * within a {@link TimeBasedRollingPolicy}. + * + * * @author Ceki Gülcü - * + * * @param */ @NoAutoStart @@ -33,24 +38,28 @@ public void start() { super.start(); if (!super.isErrorFree()) return; - if(tbrp.fileNamePattern.hasIntegerTokenCOnverter()) { - addError("Filename pattern ["+tbrp.fileNamePattern+"] contains an integer token converter, i.e. %i, INCOMPATIBLE with this configuration. Remove it."); + if (tbrp.fileNamePattern.hasIntegerTokenCOnverter()) { + addError("Filename pattern [" + tbrp.fileNamePattern + + "] contains an integer token converter, i.e. %i, INCOMPATIBLE with this configuration. Please remove it."); return; } - + archiveRemover = new TimeBasedArchiveRemover(tbrp.fileNamePattern, rc); archiveRemover.setContext(context); started = true; } public boolean isTriggeringEvent(File activeFile, final E event) { - long time = getCurrentTime(); - if (time >= nextCheck) { - Date dateOfElapsedPeriod = dateInCurrentPeriod; - addInfo("Elapsed period: " + dateOfElapsedPeriod); - elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convert(dateOfElapsedPeriod); - setDateInCurrentPeriod(time); - computeNextCheck(); + long currentTime = getCurrentTime(); + long localNextCheck = atomicNextCheck.get(); + if (currentTime >= localNextCheck) { + long nextCheck = computeNextCheck(currentTime); + atomicNextCheck.set(nextCheck); + Instant instantOfElapsedPeriod = dateInCurrentPeriod; + ZonedDateTime ztd = instantOfElapsedPeriod.atZone(zoneId); + addInfo("Elapsed period: " + ztd.toString()); + this.elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convert(instantOfElapsedPeriod); + setDateInCurrentPeriod(currentTime); return true; } else { return false; @@ -61,4 +70,5 @@ public boolean isTriggeringEvent(File activeFile, final E event) { public String toString() { return "c.q.l.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy"; } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.java index fcf0a23ba4..5bde1afd2a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -32,8 +32,8 @@ */ public class FixedWindowRollingPolicy extends RollingPolicyBase { static final String FNP_NOT_SET = "The \"FileNamePattern\" property must be set before using FixedWindowRollingPolicy. "; - static final String PRUDENT_MODE_UNSUPPORTED = "See also "+CODES_URL+"#tbr_fnp_prudent_unsupported"; - static final String SEE_PARENT_FN_NOT_SET = "Please refer to "+CODES_URL+"#fwrp_parentFileName_not_set"; + static final String PRUDENT_MODE_UNSUPPORTED = "See also " + CODES_URL + "#tbr_fnp_prudent_unsupported"; + static final String SEE_PARENT_FN_NOT_SET = "Please refer to " + CODES_URL + "#fwrp_parentFileName_not_set"; int maxIndex; int minIndex; RenameUtil util = new RenameUtil(); @@ -55,8 +55,9 @@ public void start() { util.setContext(this.context); if (fileNamePatternStr != null) { - fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); determineCompressionMode(); + adjustCompressionModeAndFileNamePatternStrIfNecessary(); + fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); } else { addError(FNP_NOT_SET); addError(CoreConstants.SEE_FNP_NOT_SET); @@ -91,7 +92,8 @@ public void start() { IntegerTokenConverter itc = fileNamePattern.getIntegerTokenConverter(); if (itc == null) { - throw new IllegalStateException("FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken"); + throw new IllegalStateException( + "FileNamePattern [" + fileNamePattern.getPattern() + "] does not contain a valid IntegerToken"); } if (compressionMode == CompressionMode.ZIP) { @@ -104,8 +106,9 @@ public void start() { } /** - * Subclasses can override this method to increase the max window size, if required. This is to - * address LOGBACK-266. + * Subclasses can override this method to increase the max window size, if + * required. This is to address LOGBACK-266. + * * @return */ protected int getMaxWindowSize() { @@ -135,7 +138,7 @@ public void rollover() throws RolloverFailure { for (int i = maxIndex - 1; i >= minIndex; i--) { String toRenameStr = fileNamePattern.convertInt(i); File toRename = new File(toRenameStr); - // no point in trying to rename an inexistent file + // no point in trying to rename a nonexistent file if (toRename.exists()) { util.rename(toRenameStr, fileNamePattern.convertInt(i + 1)); } else { @@ -149,10 +152,12 @@ public void rollover() throws RolloverFailure { util.rename(getActiveFileName(), fileNamePattern.convertInt(minIndex)); break; case GZ: + case XZ: compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), null); break; case ZIP: - compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), zipEntryFileNamePattern.convert(new Date())); + compressor.compress(getActiveFileName(), fileNamePattern.convertInt(minIndex), + zipEntryFileNamePattern.convert(new Date())); break; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounter.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounter.java new file mode 100644 index 0000000000..cd39046036 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounter.java @@ -0,0 +1,22 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling; + +public interface LengthCounter { + + void add(long len); + long getLength(); + void reset(); +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounterBase.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounterBase.java new file mode 100644 index 0000000000..758d9d2f5b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/LengthCounterBase.java @@ -0,0 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling; + +import java.util.concurrent.atomic.LongAdder; + +public class LengthCounterBase implements LengthCounter { + + LongAdder counter = new LongAdder(); + + @Override + public void add(long len) { + counter.add(len); + } + + @Override + public long getLength() { + return counter.longValue(); + } + + @Override + public void reset() { + counter.reset(); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingFileAppender.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingFileAppender.java index 8462703e6c..1f981f6eaf 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingFileAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingFileAppender.java @@ -1,15 +1,13 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.rolling; @@ -20,6 +18,8 @@ import java.io.IOException; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.FileAppender; @@ -28,9 +28,9 @@ import ch.qos.logback.core.util.ContextUtil; /** - * RollingFileAppender extends {@link FileAppender} to backup the + * RollingFileAppender extends {@link FileAppender} to back up the * log files depending on {@link RollingPolicy} and {@link TriggeringPolicy}. - * + * *

* For more information about this appender, please refer to the online manual * at http://logback.qos.ch/manual/appenders.html#RollingFileAppender @@ -43,10 +43,13 @@ public class RollingFileAppender extends FileAppender { TriggeringPolicy triggeringPolicy; RollingPolicy rollingPolicy; + Lock triggeringPolicyLock = new ReentrantLock(); + static private String RFA_NO_TP_URL = CODES_URL + "#rfa_no_tp"; static private String RFA_NO_RP_URL = CODES_URL + "#rfa_no_rp"; static private String COLLISION_URL = CODES_URL + "#rfa_collision"; static private String RFA_LATE_FILE_URL = CODES_URL + "#rfa_file_after"; + static private String RFA_RESET_RP_OR_TP = CODES_URL + "#rfa_reset_rp_or_tp"; public void start() { if (triggeringPolicy == null) { @@ -59,11 +62,11 @@ public void start() { return; } - if (checkForCollisionsInPreviousRollingFileAppenders()) { - addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting."); - addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL); - return; - } +// if (checkForCollisionsInPreviousRollingFileAppenders()) { +// addError("Collisions detected with FileAppender/RollingAppender instances defined earlier. Aborting."); +// addError(MORE_INFO_PREFIX + COLLISION_WITH_EARLIER_APPENDER_URL); +// return; +// } // we don't want to void existing log files if (!append) { @@ -95,11 +98,14 @@ public void start() { } } - currentlyActiveFile = new File(getFile()); addInfo("Active log file name: " + getFile()); + currentlyActiveFile = new File(getFile()); + initializeLengthCounter(); super.start(); } + + private boolean checkForFileAndPatternCollisions() { if (triggeringPolicy instanceof RollingPolicyBase) { final RollingPolicyBase base = (RollingPolicyBase) triggeringPolicy; @@ -113,50 +119,25 @@ private boolean checkForFileAndPatternCollisions() { return false; } - private boolean checkForCollisionsInPreviousRollingFileAppenders() { - boolean collisionResult = false; - if (triggeringPolicy instanceof RollingPolicyBase) { - final RollingPolicyBase base = (RollingPolicyBase) triggeringPolicy; - final FileNamePattern fileNamePattern = base.fileNamePattern; - boolean collisionsDetected = innerCheckForFileNamePatternCollisionInPreviousRFA(fileNamePattern); - if (collisionsDetected) - collisionResult = true; - } - return collisionResult; - } - - private boolean innerCheckForFileNamePatternCollisionInPreviousRFA(FileNamePattern fileNamePattern) { - boolean collisionsDetected = false; - @SuppressWarnings("unchecked") - Map map = (Map) context.getObject(CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP); - if (map == null) { - return collisionsDetected; - } - for (Entry entry : map.entrySet()) { - if (fileNamePattern.equals(entry.getValue())) { - addErrorForCollision("FileNamePattern", entry.getValue().toString(), entry.getKey()); - collisionsDetected = true; - } - } - if (name != null) { - map.put(getName(), fileNamePattern); + private void initializeLengthCounter() { + if(getLengthCounter() != null && currentlyActiveFile.exists()) { + long currentFileLength = currentlyActiveFile.length(); + addInfo("Setting currentFileLength to "+currentFileLength+ " for "+currentlyActiveFile); + incrementByteCount(currentFileLength); } - return collisionsDetected; } @Override public void stop() { + if (!isStarted()) { + return; + } super.stop(); - + if (rollingPolicy != null) rollingPolicy.stop(); if (triggeringPolicy != null) triggeringPolicy.stop(); - - Map map = ContextUtil.getFilenamePatternCollisionMap(context); - if (map != null && getName() != null) - map.remove(getName()); - } @Override @@ -179,7 +160,7 @@ public String getFile() { * Implemented by delegating most of the rollover work to a rolling policy. */ public void rollover() { - lock.lock(); + streamWriteLock.lock(); try { // Note: This method needs to be synchronized because it needs exclusive // access while it closes and then re-opens the target file. @@ -190,7 +171,7 @@ public void rollover() { attemptRollover(); attemptOpenFile(); } finally { - lock.unlock(); + streamWriteLock.unlock(); } } @@ -199,7 +180,8 @@ private void attemptOpenFile() { // update the currentlyActiveFile LOGBACK-64 currentlyActiveFile = new File(rollingPolicy.getActiveFileName()); - // This will also close the file. This is OK since multiple close operations are safe. + // This will also close the file. This is OK since multiple close operations are + // safe. this.openFile(rollingPolicy.getActiveFileName()); } catch (IOException e) { addError("setFile(" + fileName + ", false) call failed.", e); @@ -217,19 +199,25 @@ private void attemptRollover() { } /** - * This method differentiates RollingFileAppender from its super class. - */ + * This method differentiates RollingFileAppender from its super class. + */ @Override protected void subAppend(E event) { + + // We need to synchronize on triggeringPolicy so that only one rollover + // occurs at a time. We should also ensure that the triggeringPolicy.isTriggeringEvent + // method can ensure that it updates itself properly when isTriggeringEvent returns true + // The roll-over check must precede actual writing. This is the // only correct behavior for time driven triggers. - // We need to synchronize on triggeringPolicy so that only one rollover - // occurs at a time - synchronized (triggeringPolicy) { + triggeringPolicyLock.lock(); + try { if (triggeringPolicy.isTriggeringEvent(currentlyActiveFile, event)) { rollover(); } + } finally { + triggeringPolicyLock.unlock(); } super.subAppend(event); @@ -252,17 +240,51 @@ public TriggeringPolicy getTriggeringPolicy() { */ @SuppressWarnings("unchecked") public void setRollingPolicy(RollingPolicy policy) { - rollingPolicy = policy; - if (rollingPolicy instanceof TriggeringPolicy) { - triggeringPolicy = (TriggeringPolicy) policy; + if (this.rollingPolicy instanceof TriggeringPolicy) { + String className = rollingPolicy.getClass().getSimpleName(); + addWarn("A rolling policy of type " + className + " was already set."); + addWarn("Note that " + className + " doubles as a TriggeringPolicy"); + addWarn("See also " + RFA_RESET_RP_OR_TP); + } + this.rollingPolicy = policy; + if (this.rollingPolicy instanceof TriggeringPolicy) { + this.triggeringPolicy = (TriggeringPolicy) policy; } } public void setTriggeringPolicy(TriggeringPolicy policy) { + if (triggeringPolicy instanceof RollingPolicy) { + String className = triggeringPolicy.getClass().getSimpleName(); + addWarn("A triggering policy of type " + className + " was already set."); + addWarn("Note that " + className + " doubles as a RollingPolicy"); + addWarn("See also " + RFA_RESET_RP_OR_TP); + } triggeringPolicy = policy; if (policy instanceof RollingPolicy) { rollingPolicy = (RollingPolicy) policy; } } + + @Override + protected void updateByteCount(byte[] byteArray) { + if(byteArray == null) + return; + incrementByteCount(byteArray.length); + } + + void incrementByteCount(long increment) { + LengthCounter lengthCounter = getLengthCounter(); + if (lengthCounter == null) + return; + + if (increment > 0) { + lengthCounter.add(increment); + } + } + + private LengthCounter getLengthCounter() { + return triggeringPolicy.getLengthCounter(); + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicy.java index e2daf388d0..b8f5d242b7 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -30,22 +30,25 @@ public interface RollingPolicy extends LifeCycle { /** * Rolls over log files according to implementation policy. * - *

This method is invoked by {@link RollingFileAppender}, usually at the - * behest of its {@link TriggeringPolicy}. + *

+ * This method is invoked by {@link RollingFileAppender}, usually at the behest + * of its {@link TriggeringPolicy}. * - * @throws RolloverFailure - * Thrown if the rollover operation fails for any reason. + * @throws RolloverFailure Thrown if the rollover operation fails for any + * reason. */ void rollover() throws RolloverFailure; /** * Get the name of the active log file. * - *

With implementations such as {@link TimeBasedRollingPolicy}, this - * method returns a new file name, where the actual output will be sent. + *

+ * With implementations such as {@link TimeBasedRollingPolicy}, this method + * returns a new file name, where the actual output will be sent. * - *

On other implementations, this method might return the FileAppender's - * file property. + *

+ * On other implementations, this method might return the FileAppender's file + * property. */ String getActiveFileName(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicyBase.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicyBase.java index 5cedb0feff..12dc7ed6fc 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicyBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/RollingPolicyBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,10 +18,12 @@ import ch.qos.logback.core.rolling.helper.FileNamePattern; import ch.qos.logback.core.spi.ContextAwareBase; +import static ch.qos.logback.core.util.Loader.isClassLoadable; + /** * Implements methods common to most, it not all, rolling policies. Currently * such methods are limited to a compression mode getter/setter. - * + * * @author Ceki Gülcü */ public abstract class RollingPolicyBase extends ContextAwareBase implements RollingPolicy { @@ -38,25 +40,52 @@ public abstract class RollingPolicyBase extends ContextAwareBase implements Roll private boolean started; /** - * Given the FileNamePattern string, this method determines the compression - * mode depending on last letters of the fileNamePatternStr. Patterns ending - * with .gz imply GZIP compression, endings with '.zip' imply ZIP compression. - * Otherwise and by default, there is no compression. - * + * Given the FileNamePattern string, this method determines the compression mode + * depending on last letters of the fileNamePatternStr. Patterns ending with .gz + * imply GZIP compression, endings with '.zip' imply ZIP compression, endings with + * .xz imply XZ compression. Otherwise and by default, there is no compression. + * */ protected void determineCompressionMode() { - if (fileNamePatternStr.endsWith(".gz")) { + if (fileNamePatternStr.endsWith(CompressionMode.GZ_SUFFIX)) { addInfo("Will use gz compression"); compressionMode = CompressionMode.GZ; - } else if (fileNamePatternStr.endsWith(".zip")) { + } else if (fileNamePatternStr.endsWith(CompressionMode.ZIP_SUFFIX)) { addInfo("Will use zip compression"); compressionMode = CompressionMode.ZIP; + } else if (fileNamePatternStr.endsWith(CompressionMode.XZ_SUFFIX)) { + addInfo("Will use xz compression"); + compressionMode = CompressionMode.XZ; } else { addInfo("No compression will be used"); compressionMode = CompressionMode.NONE; } } + /** + * If compression mode is XZ but the XZ library is missing, then fallback to GZ compression. + */ + protected void adjustCompressionModeAndFileNamePatternStrIfNecessary() { + if (compressionMode == compressionMode.XZ) { + boolean xzLibraryLoadable = isClassLoadable("org.tukaani.xz.XZOutputStream", getContext()); + if (!xzLibraryLoadable) { + addWarn("XZ library missing, falling back to GZ compression"); + compressionMode = CompressionMode.GZ; + fileNamePatternStr = replaceSuffix(fileNamePatternStr, CompressionMode.XZ_SUFFIX, CompressionMode.GZ_SUFFIX); + } + } + } + + private String replaceSuffix(String input, String existingSuffix, String newSuffix) { + int existingSuffixLen = existingSuffix.length(); + if (input.endsWith(existingSuffix)) { + return input.substring(0, input.length() - existingSuffixLen) + newSuffix; + } else { + // unreachable code + throw new IllegalArgumentException("[" + input + "] should end with "+existingSuffix); + } + } + public void setFileNamePattern(String fnp) { fileNamePatternStr = fnp; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/RolloverFailure.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/RolloverFailure.java index 4086b3f4c7..f2d948a5be 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/RolloverFailure.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/RolloverFailure.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP.java old mode 100755 new mode 100644 index 75068e1aca..fdd7673436 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,173 +11,29 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ + package ch.qos.logback.core.rolling; import static ch.qos.logback.core.CoreConstants.MANUAL_URL_PREFIX; -import java.io.File; -import java.util.Date; - -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.spi.NoAutoStart; -import ch.qos.logback.core.rolling.helper.ArchiveRemover; -import ch.qos.logback.core.rolling.helper.CompressionMode; -import ch.qos.logback.core.rolling.helper.FileFilterUtil; -import ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover; -import ch.qos.logback.core.util.FileSize; -import ch.qos.logback.core.util.DefaultInvocationGate; -import ch.qos.logback.core.util.InvocationGate; - -@NoAutoStart -public class SizeAndTimeBasedFNATP extends TimeBasedFileNamingAndTriggeringPolicyBase { - - enum Usage {EMBEDDED, DIRECT}; +/** + *

{@link SizeAndTimeBasedFNATP} class was renamed as {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy} + * in version 1.5.8. In version 1.5.16 it was reintroduced to preserve backward compatibility with existing + * configurations.

+ * + * + * + * @since removed in 1.5.8 and reintroduced in 1.5.16 + */ - - int currentPeriodsCounter = 0; - FileSize maxFileSize; - // String maxFileSizeAsString; +public class SizeAndTimeBasedFNATP extends SizeAndTimeBasedFileNamingAndTriggeringPolicy { - long nextSizeCheck = 0; - static String MISSING_INT_TOKEN = "Missing integer token, that is %i, in FileNamePattern ["; - static String MISSING_DATE_TOKEN = "Missing date token, that is %d, in FileNamePattern ["; - private final Usage usage; - - public SizeAndTimeBasedFNATP() { - this(Usage.DIRECT); - } - - public SizeAndTimeBasedFNATP(Usage usage) { - this.usage = usage; - } - @Override public void start() { - // we depend on certain fields having been initialized in super class + addWarn("SizeAndTimeBasedFNATP class was renamed as SizeAndTimeBasedFileNamingAndTriggeringPolicy."); super.start(); - - if(usage == Usage.DIRECT) { - addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); - addWarn("For more information see "+MANUAL_URL_PREFIX+"appenders.html#SizeAndTimeBasedRollingPolicy"); - } - - if (!super.isErrorFree()) - return; - - - if (maxFileSize == null) { - addError("maxFileSize property is mandatory."); - withErrors(); - } - - if (!validateDateAndIntegerTokens()) { - withErrors(); - return; - } - - archiveRemover = createArchiveRemover(); - archiveRemover.setContext(context); - - // we need to get the correct value of currentPeriodsCounter. - // usually the value is 0, unless the appender or the application - // is stopped and restarted within the same period - String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod); - String stemRegex = FileFilterUtil.afterLastSlash(regex); - - computeCurrentPeriodsHighestCounterValue(stemRegex); - - if (isErrorFree()) { - started = true; - } - } - - private boolean validateDateAndIntegerTokens() { - boolean inError = false; - if (tbrp.fileNamePattern.getIntegerTokenConverter() == null) { - inError = true; - addError(MISSING_INT_TOKEN + tbrp.fileNamePatternStr + "]"); - addError(CoreConstants.SEE_MISSING_INTEGER_TOKEN); - } - if (tbrp.fileNamePattern.getPrimaryDateTokenConverter() == null) { - inError = true; - addError(MISSING_DATE_TOKEN + tbrp.fileNamePatternStr + "]"); - } - - return !inError; } - protected ArchiveRemover createArchiveRemover() { - return new SizeAndTimeBasedArchiveRemover(tbrp.fileNamePattern, rc); - } - - void computeCurrentPeriodsHighestCounterValue(final String stemRegex) { - File file = new File(getCurrentPeriodsFileNameWithoutCompressionSuffix()); - File parentDir = file.getParentFile(); - - File[] matchingFileArray = FileFilterUtil.filesInFolderMatchingStemRegex(parentDir, stemRegex); - - if (matchingFileArray == null || matchingFileArray.length == 0) { - currentPeriodsCounter = 0; - return; - } - currentPeriodsCounter = FileFilterUtil.findHighestCounter(matchingFileArray, stemRegex); - - // if parent raw file property is not null, then the next - // counter is max found counter+1 - if (tbrp.getParentsRawFileProperty() != null || (tbrp.compressionMode != CompressionMode.NONE)) { - // TODO test me - currentPeriodsCounter++; - } - } - - InvocationGate invocationGate = new DefaultInvocationGate(); - - @Override - public boolean isTriggeringEvent(File activeFile, final E event) { - - long time = getCurrentTime(); - - // first check for roll-over based on time - if (time >= nextCheck) { - Date dateInElapsedPeriod = dateInCurrentPeriod; - elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInElapsedPeriod, currentPeriodsCounter); - currentPeriodsCounter = 0; - setDateInCurrentPeriod(time); - computeNextCheck(); - return true; - } - - // next check for roll-over based on size - if (invocationGate.isTooSoon(time)) { - return false; - } - - if (activeFile == null) { - addWarn("activeFile == null"); - return false; - } - if (maxFileSize == null) { - addWarn("maxFileSize = null"); - return false; - } - if (activeFile.length() >= maxFileSize.getSize()) { - - elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter); - currentPeriodsCounter++; - return true; - } - - return false; - } - - @Override - public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { - return tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, currentPeriodsCounter); - } - - public void setMaxFileSize(FileSize aMaxFileSize) { - this.maxFileSize = aMaxFileSize; - } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFileNamingAndTriggeringPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFileNamingAndTriggeringPolicy.java new file mode 100644 index 0000000000..d03a6680a6 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFileNamingAndTriggeringPolicy.java @@ -0,0 +1,231 @@ +/** + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. + * + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. + */ +package ch.qos.logback.core.rolling; + +import static ch.qos.logback.core.CoreConstants.MANUAL_URL_PREFIX; + +import java.io.File; +import java.time.Instant; + +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.joran.spi.NoAutoStart; +import ch.qos.logback.core.rolling.helper.ArchiveRemover; +import ch.qos.logback.core.rolling.helper.CompressionMode; +import ch.qos.logback.core.rolling.helper.FileFilterUtil; +import ch.qos.logback.core.rolling.helper.SizeAndTimeBasedArchiveRemover; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.FileSize; + +/** + * This class implement {@link TimeBasedFileNamingAndTriggeringPolicy} + * interface extending {@link TimeBasedFileNamingAndTriggeringPolicyBase}. This class is intended to be nested + * within a {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy} instance. However, it can also be + * instantiated directly for testing purposes. + * + *

{@link SizeAndTimeBasedFNATP} class was renamed as {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy} + * in version 1.5.8.

+ * + * @author Ceki Gülcü + * + * @param + * @since 1.5.8 + */ +@NoAutoStart +public class SizeAndTimeBasedFileNamingAndTriggeringPolicy extends TimeBasedFileNamingAndTriggeringPolicyBase { + + enum Usage { + EMBEDDED, DIRECT + } + + volatile int currentPeriodsCounter = 0; + FileSize maxFileSize; + + Duration checkIncrement = null; + + static String MISSING_INT_TOKEN = "Missing integer token, that is %i, in FileNamePattern ["; + static String MISSING_DATE_TOKEN = "Missing date token, that is %d, in FileNamePattern ["; + + private final Usage usage; + + //InvocationGate invocationGate = new SimpleInvocationGate(); + + public SizeAndTimeBasedFileNamingAndTriggeringPolicy() { + this(Usage.DIRECT); + } + + public SizeAndTimeBasedFileNamingAndTriggeringPolicy(Usage usage) { + this.usage = usage; + } + + public LengthCounter lengthCounter = new LengthCounterBase(); + + + + @Override + public void start() { + // we depend on certain fields having been initialized in super class + super.start(); + + if (usage == Usage.DIRECT) { + addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED); + addWarn(CoreConstants.SIZE_AND_TIME_BASED_FNATP_IS_DEPRECATED_BIS); + addWarn("For more information see " + MANUAL_URL_PREFIX + "appenders.html#SizeAndTimeBasedRollingPolicy"); + } + + if (!super.isErrorFree()) + return; + + if (maxFileSize == null) { + addError("maxFileSize property is mandatory."); + withErrors(); + } + + //if (checkIncrement != null) + // invocationGate = new SimpleInvocationGate(checkIncrement); + + if (!validateDateAndIntegerTokens()) { + withErrors(); + return; + } + + archiveRemover = createArchiveRemover(); + archiveRemover.setContext(context); + + // we need to get the correct value of currentPeriodsCounter. + // usually the value is 0, unless the appender or the application + // is stopped and restarted within the same period + String regex = tbrp.fileNamePattern.toRegexForFixedDate(dateInCurrentPeriod); + String stemRegex = FileFilterUtil.afterLastSlash(regex); + + computeCurrentPeriodsHighestCounterValue(stemRegex); + + if (isErrorFree()) { + started = true; + } + } + + private boolean validateDateAndIntegerTokens() { + boolean inError = false; + if (tbrp.fileNamePattern.getIntegerTokenConverter() == null) { + inError = true; + addError(MISSING_INT_TOKEN + tbrp.fileNamePatternStr + "]"); + addError(CoreConstants.SEE_MISSING_INTEGER_TOKEN); + } + if (tbrp.fileNamePattern.getPrimaryDateTokenConverter() == null) { + inError = true; + addError(MISSING_DATE_TOKEN + tbrp.fileNamePatternStr + "]"); + } + + return !inError; + } + + protected ArchiveRemover createArchiveRemover() { + return new SizeAndTimeBasedArchiveRemover(tbrp.fileNamePattern, rc); + } + + void computeCurrentPeriodsHighestCounterValue(final String stemRegex) { + File file = new File(getCurrentPeriodsFileNameWithoutCompressionSuffix()); + File parentDir = file.getParentFile(); + + File[] matchingFileArray = FileFilterUtil.filesInFolderMatchingStemRegex(parentDir, stemRegex); + + if (matchingFileArray == null || matchingFileArray.length == 0) { + currentPeriodsCounter = 0; + return; + } + currentPeriodsCounter = FileFilterUtil.findHighestCounter(matchingFileArray, stemRegex); + + // if parent raw file property is not null, then the next + // counter is max found counter+1 + if (tbrp.getParentsRawFileProperty() != null || (tbrp.compressionMode != CompressionMode.NONE)) { + // TODO test me + currentPeriodsCounter++; + } + } + + @Override + public boolean isTriggeringEvent(File activeFile, final E event) { + + long currentTime = getCurrentTime(); + long localNextCheck = atomicNextCheck.get(); + + // first check for roll-over based on time + if (currentTime >= localNextCheck) { + long nextCheckCandidate = computeNextCheck(currentTime); + atomicNextCheck.set(nextCheckCandidate); + Instant instantInElapsedPeriod = dateInCurrentPeriod; + elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments( + instantInElapsedPeriod, currentPeriodsCounter); + currentPeriodsCounter = 0; + setDateInCurrentPeriod(currentTime); + lengthCounter.reset(); + return true; + } + + boolean result = checkSizeBasedTrigger(activeFile, currentTime); + if(result) + lengthCounter.reset(); + return result; + } + + private boolean checkSizeBasedTrigger(File activeFile, long currentTime) { + // next check for roll-over based on size + //if (invocationGate.isTooSoon(currentTime)) { + // return false; + //} + + if (activeFile == null) { + addWarn("activeFile == null"); + return false; + } + if (maxFileSize == null) { + addWarn("maxFileSize = null"); + return false; + } + + + + if (lengthCounter.getLength() >= maxFileSize.getSize()) { + + elapsedPeriodsFileName = tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, + currentPeriodsCounter); + currentPeriodsCounter++; + + return true; + } + + return false; + } + + public Duration getCheckIncrement() { + return null; + } + + public void setCheckIncrement(Duration checkIncrement) { + addWarn("Since version 1.5.8, 'checkIncrement' property has no effect"); + } + + @Override + public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { + return tbrp.fileNamePatternWithoutCompSuffix.convertMultipleArguments(dateInCurrentPeriod, + currentPeriodsCounter); + } + + public void setMaxFileSize(FileSize aMaxFileSize) { + this.maxFileSize = aMaxFileSize; + } + + @Override + public LengthCounter getLengthCounter() { + return lengthCounter; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedRollingPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedRollingPolicy.java index 38319c66e3..515af5bb51 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedRollingPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeAndTimeBasedRollingPolicy.java @@ -1,43 +1,55 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ package ch.qos.logback.core.rolling; -import ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP.Usage; +import ch.qos.logback.core.rolling.SizeAndTimeBasedFileNamingAndTriggeringPolicy.Usage; import ch.qos.logback.core.util.FileSize; - - public class SizeAndTimeBasedRollingPolicy extends TimeBasedRollingPolicy { + static long MAX_COMPRESSION_FACTOR = 20; + FileSize maxFileSize; - + @Override public void start() { - SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP(Usage.EMBEDDED); - if(maxFileSize == null) { + SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy(Usage.EMBEDDED); + if (maxFileSize == null) { addError("maxFileSize property is mandatory."); return; } else { - addInfo("Archive files will be limited to ["+maxFileSize+"] each."); + addInfo("Archive files will be limited to [" + maxFileSize + "] each."); } - + sizeAndTimeBasedFNATP.setMaxFileSize(maxFileSize); timeBasedFileNamingAndTriggeringPolicy = sizeAndTimeBasedFNATP; - - if(!isUnboundedTotalSizeCap() && totalSizeCap.getSize() < maxFileSize.getSize()) { - addError("totalSizeCap of ["+totalSizeCap+"] is smaller than maxFileSize ["+maxFileSize+"] which is non-sensical"); - return; + + if (!isUnboundedTotalSizeCap() && totalSizeCap.getSize() < maxFileSize.getSize()/MAX_COMPRESSION_FACTOR) { + addWarn("totalSizeCap of [" + totalSizeCap + "] is much smaller than maxFileSize [" + maxFileSize + + "] which is non-sensical, even taking compression into account."); } - + // most work is done by the parent super.start(); } - - + public void setMaxFileSize(FileSize aMaxFileSize) { this.maxFileSize = aMaxFileSize; } - + @Override public String toString() { - return "c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@"+this.hashCode(); + return "c.q.l.core.rolling.SizeAndTimeBasedRollingPolicy@" + this.hashCode(); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.java index c9b5cd03d0..cea94555bd 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,9 +15,11 @@ import java.io.File; +import ch.qos.logback.core.util.Duration; import ch.qos.logback.core.util.FileSize; import ch.qos.logback.core.util.DefaultInvocationGate; import ch.qos.logback.core.util.InvocationGate; +import ch.qos.logback.core.util.SimpleInvocationGate; /** * SizeBasedTriggeringPolicy looks at size of the file being currently written @@ -39,11 +41,18 @@ public class SizeBasedTriggeringPolicy extends TriggeringPolicyBase { public static final long DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024; // 10 MB FileSize maxFileSize = new FileSize(DEFAULT_MAX_FILE_SIZE); + InvocationGate invocationGate = new SimpleInvocationGate(); + Duration checkIncrement = null; public SizeBasedTriggeringPolicy() { } - InvocationGate invocationGate = new DefaultInvocationGate(); + public void start() { + if(checkIncrement != null) + invocationGate = new SimpleInvocationGate(checkIncrement); + super.start(); + } + public boolean isTriggeringEvent(final File activeFile, final E event) { long now = System.currentTimeMillis(); @@ -53,14 +62,19 @@ public boolean isTriggeringEvent(final File activeFile, final E event) { return (activeFile.length() >= maxFileSize.getSize()); } - public FileSize getMaxFileSize() { return this.maxFileSize; } - public void setMaxFileSize(FileSize aMaxFileSize) { this.maxFileSize = aMaxFileSize; } + public Duration getCheckIncrement() { + return checkIncrement; + } + + public void setCheckIncrement(Duration checkIncrement) { + this.checkIncrement = checkIncrement; + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicy.java index ae2724524e..44e6b91a27 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,7 +19,13 @@ /** * This interface lists the set of methods that need to be implemented by * triggering policies which are nested within a {@link TimeBasedRollingPolicy}. - * + * + *

This interface should be considered as an extension of {@link TimeBasedRollingPolicy} with file naming + * support methods. + *

+ * + * @see TimeBasedFileNamingAndTriggeringPolicyBase + * * @author Ceki Gülcü * * @param @@ -29,8 +35,7 @@ public interface TimeBasedFileNamingAndTriggeringPolicy extends TriggeringPol /** * Set the host/parent {@link TimeBasedRollingPolicy}. * - * @param tbrp - * parent TimeBasedRollingPolicy + * @param tbrp parent TimeBasedRollingPolicy */ void setTimeBasedRollingPolicy(TimeBasedRollingPolicy tbrp); @@ -56,8 +61,8 @@ public interface TimeBasedFileNamingAndTriggeringPolicy extends TriggeringPol /** * Return the current time which is usually the value returned by - * System.currentMillis(). However, for testing purposed this value - * may be different than the real time. + * System.currentMillis(). However, for testing purposed this value may + * be different than the real time. * * @return current time value */ @@ -74,8 +79,8 @@ public interface TimeBasedFileNamingAndTriggeringPolicy extends TriggeringPol * Set some date in the current period. Only unit tests should invoke this * method. * - * WARNING: method removed. A unit test should not set the - * date in current period. It is the job of the FNATP to compute that. + * WARNING: method removed. A unit test should not set the date in current + * period. It is the job of the FNATP to compute that. * * @param date */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBase.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBase.java index 47f3ab32eb..75e55573ec 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,8 +16,11 @@ import static ch.qos.logback.core.CoreConstants.CODES_URL; import java.io.File; -import java.util.Date; +import java.time.Instant; +import java.time.ZoneId; import java.util.Locale; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicLong; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.rolling.helper.ArchiveRemover; @@ -25,7 +28,18 @@ import ch.qos.logback.core.rolling.helper.RollingCalendar; import ch.qos.logback.core.spi.ContextAwareBase; -abstract public class TimeBasedFileNamingAndTriggeringPolicyBase extends ContextAwareBase implements TimeBasedFileNamingAndTriggeringPolicy { +/** + * Base implementation of {@link TimeBasedFileNamingAndTriggeringPolicy}. + * + *

See also derived classes {@link DefaultTimeBasedFileNamingAndTriggeringPolicy} and + * {@link SizeAndTimeBasedFileNamingAndTriggeringPolicy}. + * + *

+ * + * @param + */ +abstract public class TimeBasedFileNamingAndTriggeringPolicyBase extends ContextAwareBase + implements TimeBasedFileNamingAndTriggeringPolicy { static private String COLLIDING_DATE_FORMAT_URL = CODES_URL + "#rfa_collision_in_dateFormat"; @@ -36,12 +50,15 @@ abstract public class TimeBasedFileNamingAndTriggeringPolicyBase extends Cont protected RollingCalendar rc; protected long artificialCurrentTime = -1; - protected Date dateInCurrentPeriod = null; - protected long nextCheck; + protected AtomicLong atomicNextCheck = new AtomicLong(0); + protected Instant dateInCurrentPeriod = null; + protected boolean started = false; protected boolean errorFree = true; + protected ZoneId zoneId = ZoneId.systemDefault(); + public boolean isStarted() { return started; } @@ -49,51 +66,50 @@ public boolean isStarted() { public void start() { DateTokenConverter dtc = tbrp.fileNamePattern.getPrimaryDateTokenConverter(); if (dtc == null) { - throw new IllegalStateException("FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken"); + throw new IllegalStateException( + "FileNamePattern [" + tbrp.fileNamePattern.getPattern() + "] does not contain a valid DateToken"); } - if (dtc.getTimeZone() != null) { - rc = new RollingCalendar(dtc.getDatePattern(), dtc.getTimeZone(), Locale.getDefault()); + if (dtc.getZoneId() != null) { + this.zoneId = dtc.getZoneId(); + TimeZone tz = TimeZone.getTimeZone(zoneId); + rc = new RollingCalendar(dtc.getDatePattern(), tz, Locale.getDefault()); } else { rc = new RollingCalendar(dtc.getDatePattern()); } - addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" + tbrp.fileNamePattern.getPattern() + "'."); + addInfo("The date pattern is '" + dtc.getDatePattern() + "' from file name pattern '" + + tbrp.fileNamePattern.getPattern() + "'."); rc.printPeriodicity(this); if (!rc.isCollisionFree()) { - addError("The date format in FileNamePattern will result in collisions in the names of archived log files."); + addError( + "The date format in FileNamePattern will result in collisions in the names of archived log files."); addError(CoreConstants.MORE_INFO_PREFIX + COLLIDING_DATE_FORMAT_URL); withErrors(); return; } - setDateInCurrentPeriod(new Date(getCurrentTime())); + long timestamp = getCurrentTime(); + setDateInCurrentPeriod(timestamp); + if (tbrp.getParentsRawFileProperty() != null) { File currentFile = new File(tbrp.getParentsRawFileProperty()); - if (currentFile.exists() && currentFile.canRead()) { - setDateInCurrentPeriod(new Date(currentFile.lastModified())); + if (currentFile.canRead()) { + timestamp = currentFile.lastModified(); + setDateInCurrentPeriod(timestamp); } } addInfo("Setting initial period to " + dateInCurrentPeriod); - computeNextCheck(); + long nextCheck = computeNextCheck(timestamp); + atomicNextCheck.set(nextCheck); } public void stop() { started = false; } - protected void computeNextCheck() { - nextCheck = rc.getNextTriggeringDate(dateInCurrentPeriod).getTime(); - } - - protected void setDateInCurrentPeriod(long now) { - dateInCurrentPeriod.setTime(now); - } - - // allow Test classes to act on the dateInCurrentPeriod field to simulate old - // log files needing rollover - public void setDateInCurrentPeriod(Date _dateInCurrentPeriod) { - this.dateInCurrentPeriod = _dateInCurrentPeriod; + protected long computeNextCheck(long timestamp) { + return rc.getNextTriggeringDate(Instant.ofEpochMilli(timestamp)).toEpochMilli(); } public String getElapsedPeriodsFileName() { @@ -104,6 +120,10 @@ public String getCurrentPeriodsFileNameWithoutCompressionSuffix() { return tbrp.fileNamePatternWithoutCompSuffix.convert(dateInCurrentPeriod); } + protected void setDateInCurrentPeriod(long timestamp) { + dateInCurrentPeriod = Instant.ofEpochMilli(timestamp); + } + public void setCurrentTime(long timeInMillis) { artificialCurrentTime = timeInMillis; } @@ -129,7 +149,7 @@ public ArchiveRemover getArchiveRemover() { protected void withErrors() { errorFree = false; } - + protected boolean isErrorFree() { return errorFree; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java index 0879b0ced5..46742c54ea 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,11 @@ */ package ch.qos.logback.core.rolling; -import static ch.qos.logback.core.CoreConstants.UNBOUND_HISTORY; +import static ch.qos.logback.core.CoreConstants.UNBOUNDED_HISTORY; import static ch.qos.logback.core.CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP; import java.io.File; -import java.util.Date; +import java.time.Instant; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; @@ -33,10 +33,11 @@ /** * TimeBasedRollingPolicy is both easy to configure and quite - * powerful. It allows the roll over to be made based on time. It is possible to - * specify that the roll over occur once per day, per week or per month. + * powerful. It allows the rollover to be made based on time. It is possible to + * specify that the rollover occur once per day, per week or per month. * - *

For more information, please refer to the online manual at + *

+ * For more information, please refer to the online manual at * http://logback.qos.ch/manual/appenders.html#TimeBasedRollingPolicy * * @author Ceki Gülcü @@ -51,7 +52,7 @@ public class TimeBasedRollingPolicy extends RollingPolicyBase implements Trig Future compressionFuture; Future cleanUpFuture; - private int maxHistory = UNBOUND_HISTORY; + private int maxHistory = UNBOUNDED_HISTORY; protected FileSize totalSizeCap = new FileSize(UNBOUNDED_TOTAL_SIZE_CAP); private ArchiveRemover archiveRemover; @@ -66,8 +67,9 @@ public void start() { // find out period from the filename pattern if (fileNamePatternStr != null) { - fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); determineCompressionMode(); + adjustCompressionModeAndFileNamePatternStrIfNecessary(); + fileNamePattern = new FileNamePattern(fileNamePatternStr, this.context); } else { addWarn(FNP_NOT_SET); addWarn(CoreConstants.SEE_FNP_NOT_SET); @@ -78,7 +80,8 @@ public void start() { compressor.setContext(context); // wcs : without compression suffix - fileNamePatternWithoutCompSuffix = new FileNamePattern(Compressor.computeFileNameStrWithoutCompSuffix(fileNamePatternStr, compressionMode), this.context); + fileNamePatternWithoutCompSuffix = new FileNamePattern( + Compressor.computeFileNameStrWithoutCompSuffix(fileNamePatternStr, compressionMode), this.context); addInfo("Will use the pattern " + fileNamePatternWithoutCompSuffix + " for the active file"); @@ -88,7 +91,7 @@ public void start() { } if (timeBasedFileNamingAndTriggeringPolicy == null) { - timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy(); + timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy<>(); } timeBasedFileNamingAndTriggeringPolicy.setContext(context); timeBasedFileNamingAndTriggeringPolicy.setTimeBasedRollingPolicy(this); @@ -102,17 +105,17 @@ public void start() { // the maxHistory property is given to TimeBasedRollingPolicy instead of to // the TimeBasedFileNamingAndTriggeringPolicy. This makes it more convenient // for the user at the cost of inconsistency here. - if (maxHistory != UNBOUND_HISTORY) { + if (maxHistory != UNBOUNDED_HISTORY) { archiveRemover = timeBasedFileNamingAndTriggeringPolicy.getArchiveRemover(); archiveRemover.setMaxHistory(maxHistory); archiveRemover.setTotalSizeCap(totalSizeCap.getSize()); if (cleanHistoryOnStart) { addInfo("Cleaning on start up"); - Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); + Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); cleanUpFuture = archiveRemover.cleanAsynchronously(now); } } else if (!isUnboundedTotalSizeCap()) { - addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value ["+totalSizeCap+"]"); + addWarn("'maxHistory' is not set, ignoring 'totalSizeCap' option with value [" + totalSizeCap + "]"); } super.start(); @@ -148,7 +151,8 @@ private String transformFileNamePattern2ZipEntry(String fileNamePatternStr) { return FileFilterUtil.afterLastSlash(slashified); } - public void setTimeBasedFileNamingAndTriggeringPolicy(TimeBasedFileNamingAndTriggeringPolicy timeBasedTriggering) { + public void setTimeBasedFileNamingAndTriggeringPolicy( + TimeBasedFileNamingAndTriggeringPolicy timeBasedTriggering) { this.timeBasedFileNamingAndTriggeringPolicy = timeBasedTriggering; } @@ -168,17 +172,19 @@ public void rollover() throws RolloverFailure { if (compressionMode == CompressionMode.NONE) { if (getParentsRawFileProperty() != null) { renameUtil.rename(getParentsRawFileProperty(), elapsedPeriodsFileName); - } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == null } + } // else { nothing to do if CompressionMode == NONE and parentsRawFileProperty == + // null } } else { if (getParentsRawFileProperty() == null) { - compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, elapsedPeriodStem); + compressionFuture = compressor.asyncCompress(elapsedPeriodsFileName, elapsedPeriodsFileName, + elapsedPeriodStem); } else { compressionFuture = renameRawAndAsyncCompress(elapsedPeriodsFileName, elapsedPeriodStem); } } if (archiveRemover != null) { - Date now = new Date(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); + Instant now = Instant.ofEpochMilli(timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()); this.cleanUpFuture = archiveRemover.cleanAsynchronously(now); } } @@ -197,17 +203,19 @@ Future renameRawAndAsyncCompress(String nameOfCompressedFile, String innerEnt * file equals the file name for the current period as computed by the * FileNamePattern option. * - *

The RollingPolicy must know whether it is responsible for changing the - * name of the active file or not. If the active file name is set by the user - * via the configuration file, then the RollingPolicy must let it like it is. - * If the user does not specify an active file name, then the RollingPolicy - * generates one. + *

+ * The RollingPolicy must know whether it is responsible for changing the name + * of the active file or not. If the active file name is set by the user via the + * configuration file, then the RollingPolicy must let it like it is. If the + * user does not specify an active file name, then the RollingPolicy generates + * one. * - *

To be sure that the file name used by the parent class has been - * generated by the RollingPolicy and not specified by the user, we keep track - * of the last generated name object and compare its reference to the parent - * file name. If they match, then the RollingPolicy knows it's responsible for - * the change of the file name. + *

+ * To be sure that the file name used by the parent class has been generated by + * the RollingPolicy and not specified by the user, we keep track of the last + * generated name object and compare its reference to the parent file name. If + * they match, then the RollingPolicy knows it's responsible for the change of + * the file name. * */ public String getActiveFileName() { @@ -219,10 +227,22 @@ public String getActiveFileName() { } } + /** + * Delegates to the underlying timeBasedFileNamingAndTriggeringPolicy. + * + * @param activeFile A reference to the currently active log file. + * @param event A reference to the current event. + * @return + */ public boolean isTriggeringEvent(File activeFile, final E event) { return timeBasedFileNamingAndTriggeringPolicy.isTriggeringEvent(activeFile, event); } + @Override + public LengthCounter getLengthCounter() { + return timeBasedFileNamingAndTriggeringPolicy.getLengthCounter(); + } + /** * Get the number of archive files to keep. * @@ -235,8 +255,7 @@ public int getMaxHistory() { /** * Set the maximum number of archive files to keep. * - * @param maxHistory - * number of archive files to keep + * @param maxHistory number of archive files to keep */ public void setMaxHistory(int maxHistory) { this.maxHistory = maxHistory; @@ -247,7 +266,9 @@ public boolean isCleanHistoryOnStart() { } /** - * Should archive removal be attempted on application start up? Default is false. + * Should archive removal be attempted on application start up? Default is + * false. + * * @since 1.0.1 * @param cleanHistoryOnStart */ @@ -257,11 +278,11 @@ public void setCleanHistoryOnStart(boolean cleanHistoryOnStart) { @Override public String toString() { - return "c.q.l.core.rolling.TimeBasedRollingPolicy@"+this.hashCode(); + return "c.q.l.core.rolling.TimeBasedRollingPolicy@" + this.hashCode(); } public void setTotalSizeCap(FileSize totalSizeCap) { - addInfo("setting totalSizeCap to "+totalSizeCap.toString()); + addInfo("setting totalSizeCap to " + totalSizeCap.toString()); this.totalSizeCap = totalSizeCap; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicy.java index ba24604f80..f06468d252 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,24 +14,37 @@ package ch.qos.logback.core.rolling; import java.io.File; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.LongAdder; import ch.qos.logback.core.spi.LifeCycle; /** * A TriggeringPolicy controls the conditions under which roll-over - * occurs. Such conditions include time of day, file size, an - * external event, the log request or a combination thereof. + * occurs. Such conditions include time of day, file size, an external event, + * the log request or a combination thereof. * * @author Ceki Gülcü - * */ + */ public interface TriggeringPolicy extends LifeCycle { + /** + * Return the {@link LengthCounter} instance associated with this triggering + * policy. The returned value may be null. + * + * @return a LengthCounter instance, may be null + * @since 1.5.8 + */ + default LengthCounter getLengthCounter() { + return null; + } + /** * Should roll-over be triggered at this time? * - * @param activeFile A reference to the currently active log file. - * @param event A reference to the currently event. + * @param activeFile A reference to the currently active log file. + * @param event A reference to the current event. * @return true if a roll-over should occur. */ boolean isTriggeringEvent(final File activeFile, final E event); diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicyBase.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicyBase.java index 852a6061c3..1186b8ccf3 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicyBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/TriggeringPolicyBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,8 +16,8 @@ import ch.qos.logback.core.spi.ContextAwareBase; /** - * SizeBasedTriggeringPolicy looks at size of the file being - * currently written to. + * SizeBasedTriggeringPolicy looks at size of the file being currently written + * to. * * @author Ceki Gülcü * diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ArchiveRemover.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ArchiveRemover.java index fcdc52a803..abe4ab4626 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ArchiveRemover.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ArchiveRemover.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,7 @@ */ package ch.qos.logback.core.rolling.helper; +import java.time.Instant; import java.util.Date; import java.util.concurrent.Future; @@ -24,8 +25,11 @@ * @author Ceki Gülcü */ public interface ArchiveRemover extends ContextAware { - void clean(Date now); + void clean(Instant instant); + void setMaxHistory(int maxHistory); + void setTotalSizeCap(long totalSizeCap); - Future cleanAsynchronously(Date now); + + Future cleanAsynchronously(Instant now); } \ No newline at end of file diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionMode.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionMode.java index e2fd3cd91a..35ddc42b85 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionMode.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionMode.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,5 +14,12 @@ package ch.qos.logback.core.rolling.helper; public enum CompressionMode { - NONE, GZ, ZIP; + NONE, GZ, ZIP, XZ; + + static public String GZ_SUFFIX = ".gz"; + static public String XZ_SUFFIX = ".xz"; + static public String ZIP_SUFFIX = ".zip"; + + + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategy.java new file mode 100644 index 0000000000..749d0f8c3b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategy.java @@ -0,0 +1,29 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling.helper; + +import ch.qos.logback.core.spi.ContextAware; + +/** + * This interface was introduced in order to support for pluggable + * compression methods. + * + * @author Ceki Gülcü + * @since 1.5.18 + */ +public interface CompressionStrategy extends ContextAware { + + void compress(String originalFileName, String compressedFileName, String innerEntryName); +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategyBase.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategyBase.java new file mode 100644 index 0000000000..cd5affa92c --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/CompressionStrategyBase.java @@ -0,0 +1,32 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling.helper; + +import ch.qos.logback.core.spi.ContextAwareBase; +import ch.qos.logback.core.util.FileUtil; + +import java.io.File; + +abstract public class CompressionStrategyBase extends ContextAwareBase implements CompressionStrategy { + + static final int BUFFER_SIZE = 65536; + + void createMissingTargetDirsIfNecessary(File file) { + boolean result = FileUtil.createMissingParentDirectories(file); + if (!result) { + addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]"); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/Compressor.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/Compressor.java index cd2ea3d780..c39211bbaa 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/Compressor.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/Compressor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,21 +13,15 @@ */ package ch.qos.logback.core.rolling.helper; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.zip.GZIPOutputStream; -import java.util.zip.ZipEntry; -import java.util.zip.ZipOutputStream; import ch.qos.logback.core.rolling.RolloverFailure; import ch.qos.logback.core.spi.ContextAwareBase; -import ch.qos.logback.core.status.ErrorStatus; -import ch.qos.logback.core.status.WarnStatus; -import ch.qos.logback.core.util.FileUtil; +import ch.qos.logback.core.util.DynamicClassLoadingException; +import ch.qos.logback.core.util.IncompatibleClassException; + +import static ch.qos.logback.core.util.OptionHelper.instantiateByClassName; /** * The Compression class implements ZIP and GZ file @@ -37,6 +31,9 @@ */ public class Compressor extends ContextAwareBase { + public static final String COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE = "Could not obtain compression strategy"; + public static final String XZ_COMPRESSION_STRATEGY_CLASS_NAME = "ch.qos.logback.core.rolling.helper.XZCompressionStrategy"; + final CompressionMode compressionMode; static final int BUFFER_SIZE = 8192; @@ -46,141 +43,43 @@ public Compressor(CompressionMode compressionMode) { } /** - * @param nameOfFile2Compress - * @param nameOfCompressedFile - * @param innerEntryName - * The name of the file within the zip file. Use for ZIP compression. + * @param originalFileName + * @param compressedFileName + * @param innerEntryName The name of the file within the zip file. Use for + * ZIP compression. */ - public void compress(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) { + public void compress(String originalFileName, String compressedFileName, String innerEntryName) { + CompressionStrategy compressionStrategy = makeCompressionStrategy(compressionMode); + if (compressionStrategy == null) { + addWarn(COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE); + return; + } + compressionStrategy.setContext(getContext()); + compressionStrategy.compress(originalFileName, compressedFileName, innerEntryName); + } + + CompressionStrategy makeCompressionStrategy(CompressionMode compressionMode) { switch (compressionMode) { case GZ: - gzCompress(nameOfFile2Compress, nameOfCompressedFile); - break; + return new GZCompressionStrategy(); case ZIP: - zipCompress(nameOfFile2Compress, nameOfCompressedFile, innerEntryName); - break; + return new ZipCompressionStrategy(); + case XZ: + return dynamicInstantiation(XZ_COMPRESSION_STRATEGY_CLASS_NAME); case NONE: throw new UnsupportedOperationException("compress method called in NONE compression mode"); + default: + return null; } } - private void zipCompress(String nameOfFile2zip, String nameOfZippedFile, String innerEntryName) { - File file2zip = new File(nameOfFile2zip); - - if (!file2zip.exists()) { - addStatus(new WarnStatus("The file to compress named [" + nameOfFile2zip + "] does not exist.", this)); - - return; - } - - if (innerEntryName == null) { - addStatus(new WarnStatus("The innerEntryName parameter cannot be null", this)); - return; - } - - if (!nameOfZippedFile.endsWith(".zip")) { - nameOfZippedFile = nameOfZippedFile + ".zip"; + private CompressionStrategy dynamicInstantiation(String className) { + try { + return (CompressionStrategy) instantiateByClassName(className, CompressionStrategy.class, getContext()); + } catch (IncompatibleClassException | DynamicClassLoadingException e) { + addError("Could not instantiate " + className, e); + return null; } - - File zippedFile = new File(nameOfZippedFile); - - if (zippedFile.exists()) { - addStatus(new WarnStatus("The target compressed file named [" + nameOfZippedFile + "] exist already.", this)); - - return; - } - - addInfo("ZIP compressing [" + file2zip + "] as [" + zippedFile + "]"); - createMissingTargetDirsIfNecessary(zippedFile); - - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(nameOfFile2zip)); - ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(nameOfZippedFile))) { - - ZipEntry zipEntry = computeZipEntry(innerEntryName); - zos.putNextEntry(zipEntry); - - byte[] inbuf = new byte[BUFFER_SIZE]; - int n; - - while ((n = bis.read(inbuf)) != -1) { - zos.write(inbuf, 0, n); - } - - addInfo("Done ZIP compressing [" + file2zip + "] as [" + zippedFile + "]"); - } catch (Exception e) { - addStatus(new ErrorStatus("Error occurred while compressing [" + nameOfFile2zip + "] into [" + nameOfZippedFile + "].", this, e)); - } - if (!file2zip.delete()) { - addStatus(new WarnStatus("Could not delete [" + nameOfFile2zip + "].", this)); - } - } - - // http://jira.qos.ch/browse/LBCORE-98 - // The name of the compressed file as nested within the zip archive - // - // Case 1: RawFile = null, Patern = foo-%d.zip - // nestedFilename = foo-${current-date} - // - // Case 2: RawFile = hello.txt, Pattern = = foo-%d.zip - // nestedFilename = foo-${current-date} - // - // in both cases, the strategy consisting of removing the compression - // suffix of zip file works reasonably well. The alternative strategy - // whereby the nested file name was based on the value of the raw file name - // (applicable to case 2 only) has the disadvantage of the nested files - // all having the same name, which could make it harder for the user - // to unzip the file without collisions - ZipEntry computeZipEntry(File zippedFile) { - return computeZipEntry(zippedFile.getName()); - } - - ZipEntry computeZipEntry(String filename) { - String nameOfFileNestedWithinArchive = computeFileNameStrWithoutCompSuffix(filename, compressionMode); - return new ZipEntry(nameOfFileNestedWithinArchive); - } - - private void gzCompress(String nameOfFile2gz, String nameOfgzedFile) { - File file2gz = new File(nameOfFile2gz); - - if (!file2gz.exists()) { - addStatus(new WarnStatus("The file to compress named [" + nameOfFile2gz + "] does not exist.", this)); - - return; - } - - if (!nameOfgzedFile.endsWith(".gz")) { - nameOfgzedFile = nameOfgzedFile + ".gz"; - } - - File gzedFile = new File(nameOfgzedFile); - - if (gzedFile.exists()) { - addWarn("The target compressed file named [" + nameOfgzedFile + "] exist already. Aborting file compression."); - return; - } - - addInfo("GZ compressing [" + file2gz + "] as [" + gzedFile + "]"); - createMissingTargetDirsIfNecessary(gzedFile); - - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(nameOfFile2gz)); - GZIPOutputStream gzos = new GZIPOutputStream(new FileOutputStream(nameOfgzedFile))) { - - byte[] inbuf = new byte[BUFFER_SIZE]; - int n; - - while ((n = bis.read(inbuf)) != -1) { - gzos.write(inbuf, 0, n); - } - - addInfo("Done ZIP compressing [" + file2gz + "] as [" + gzedFile + "]"); - } catch (Exception e) { - addStatus(new ErrorStatus("Error occurred while compressing [" + nameOfFile2gz + "] into [" + nameOfgzedFile + "].", this, e)); - } - - if (!file2gz.delete()) { - addStatus(new WarnStatus("Could not delete [" + nameOfFile2gz + "].", this)); - } - } static public String computeFileNameStrWithoutCompSuffix(String fileNamePatternStr, CompressionMode compressionMode) { @@ -196,19 +95,17 @@ static public String computeFileNameStrWithoutCompSuffix(String fileNamePatternS return fileNamePatternStr.substring(0, len - 4); else return fileNamePatternStr; + case XZ: + if (fileNamePatternStr.endsWith(".xz")) + return fileNamePatternStr.substring(0, len - 3); + else + return fileNamePatternStr; case NONE: return fileNamePatternStr; } throw new IllegalStateException("Execution should not reach this point"); } - void createMissingTargetDirsIfNecessary(File file) { - boolean result = FileUtil.createMissingParentDirectories(file); - if (!result) { - addError("Failed to create parent directories for [" + file.getAbsolutePath() + "]"); - } - } - @Override public String toString() { return this.getClass().getName(); @@ -216,7 +113,7 @@ public String toString() { public Future asyncCompress(String nameOfFile2Compress, String nameOfCompressedFile, String innerEntryName) throws RolloverFailure { CompressionRunnable runnable = new CompressionRunnable(nameOfFile2Compress, nameOfCompressedFile, innerEntryName); - ExecutorService executorService = context.getScheduledExecutorService(); + ExecutorService executorService = context.getExecutorService(); Future future = executorService.submit(runnable); return future; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/DateTokenConverter.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/DateTokenConverter.java index f50a89f797..4a59dc51d6 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/DateTokenConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/DateTokenConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,10 @@ */ package ch.qos.logback.core.rolling.helper; +import java.time.Instant; +import java.time.ZoneId; import java.util.Date; import java.util.List; -import java.util.TimeZone; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.pattern.DynamicConverter; @@ -37,7 +38,7 @@ public class DateTokenConverter extends DynamicConverter implements MonoTy public static final String DEFAULT_DATE_PATTERN = CoreConstants.DAILY_DATE_PATTERN; private String datePattern; - private TimeZone timeZone; + private ZoneId zoneId; private CachingDateFormatter cdf; // is this token converter primary or auxiliary? Only the primary converter // determines the rolling period @@ -56,21 +57,22 @@ public void start() { if (AUXILIARY_TOKEN.equalsIgnoreCase(option)) { primary = false; } else { - timeZone = TimeZone.getTimeZone(option); + zoneId = ZoneId.of(option); } } } - cdf = new CachingDateFormatter(datePattern); - if (timeZone != null) { - cdf.setTimeZone(timeZone); - } + cdf = new CachingDateFormatter(datePattern, zoneId); } public String convert(Date date) { return cdf.format(date.getTime()); } + public String convert(Instant instant) { + return cdf.format(instant.toEpochMilli()); + } + public String convert(Object o) { if (o == null) { throw new IllegalArgumentException("Null argument forbidden"); @@ -78,6 +80,10 @@ public String convert(Object o) { if (o instanceof Date) { return convert((Date) o); } + if (o instanceof Instant) { + return convert((Instant) o); + } + throw new IllegalArgumentException("Cannot convert " + o + " of type" + o.getClass().getName()); } @@ -88,12 +94,16 @@ public String getDatePattern() { return datePattern; } - public TimeZone getTimeZone() { - return timeZone; + public ZoneId getZoneId() { + return zoneId; } public boolean isApplicable(Object o) { - return (o instanceof Date); + if(o instanceof Date) + return true; + if(o instanceof Instant) + return true; + return false; } public String toRegex() { diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileFilterUtil.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileFilterUtil.java index 8e3004085f..f1794a430d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileFilterUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileFilterUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; +import static ch.qos.logback.core.CoreConstants.EMPTY_FILE_ARRAY; + public class FileFilterUtil { + + public static void sortFileArrayByName(File[] fileArray) { Arrays.sort(fileArray, new Comparator() { public int compare(File o1, File o2) { @@ -74,16 +78,15 @@ static public boolean isEmptyDirectory(File dir) { public static File[] filesInFolderMatchingStemRegex(File file, final String stemRegex) { if (file == null) { - return new File[0]; + return EMPTY_FILE_ARRAY; } - if (!file.exists() || !file.isDirectory()) { - return new File[0]; + if (!file.isDirectory()) { + return EMPTY_FILE_ARRAY; } - return file.listFiles(new FilenameFilter() { - public boolean accept(File dir, String name) { - return name.matches(stemRegex); - } - }); + + // better compile the regex. See also LOGBACK-1409 + Pattern pattern = Pattern.compile(stemRegex); + return file.listFiles((dir, name) -> pattern.matcher(name).matches()); } static public int findHighestCounter(File[] matchingFileArray, final String stemRegex) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileNamePattern.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileNamePattern.java index 941267f582..01bf01b33a 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileNamePattern.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileNamePattern.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,16 @@ */ package ch.qos.logback.core.rolling.helper; +import java.time.Instant; import java.util.Date; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; import ch.qos.logback.core.Context; import ch.qos.logback.core.pattern.Converter; import ch.qos.logback.core.pattern.ConverterUtil; +import ch.qos.logback.core.pattern.DynamicConverter; import ch.qos.logback.core.pattern.LiteralConverter; import ch.qos.logback.core.pattern.parser.Node; import ch.qos.logback.core.pattern.parser.Parser; @@ -37,10 +40,10 @@ */ public class FileNamePattern extends ContextAwareBase { - static final Map CONVERTER_MAP = new HashMap(); + static final Map> CONVERTER_MAP = new HashMap<>(); static { - CONVERTER_MAP.put(IntegerTokenConverter.CONVERTER_KEY, IntegerTokenConverter.class.getName()); - CONVERTER_MAP.put(DateTokenConverter.CONVERTER_KEY, DateTokenConverter.class.getName()); + CONVERTER_MAP.put(IntegerTokenConverter.CONVERTER_KEY, IntegerTokenConverter::new); + CONVERTER_MAP.put(DateTokenConverter.CONVERTER_KEY, DateTokenConverter::new); } String pattern; @@ -54,12 +57,13 @@ public FileNamePattern(String patternArg, Context contextArg) { ConverterUtil.startConverters(this.headTokenConverter); } - void parse() { try { // http://jira.qos.ch/browse/LOGBACK-197 - // we escape ')' for parsing purposes. Note that the original pattern is preserved - // because it is shown to the user in status messages. We don't want the escaped version + // we escape ')' for parsing purposes. Note that the original pattern is + // preserved + // because it is shown to the user in status messages. We don't want the escaped + // version // to leak out. String patternForParsing = escapeRightParantesis(pattern); Parser p = new Parser(patternForParsing, new AlmostAsIsEscapeUtil()); @@ -88,7 +92,6 @@ public int hashCode() { return result; } - @Override public boolean equals(Object obj) { if (this == obj) @@ -106,7 +109,6 @@ public boolean equals(Object obj) { return true; } - public DateTokenConverter getPrimaryDateTokenConverter() { Converter p = headTokenConverter; @@ -141,7 +143,7 @@ public boolean hasIntegerTokenCOnverter() { IntegerTokenConverter itc = getIntegerTokenConverter(); return itc != null; } - + public String convertMultipleArguments(Object... objectList) { StringBuilder buf = new StringBuilder(); Converter c = headTokenConverter; @@ -186,12 +188,11 @@ public String getPattern() { return pattern; } - /** * Given date, convert this instance to a regular expression. * - * Used to compute sub-regex when the pattern has both %d and %i, and the - * date is known. + * Used to compute sub-regex when the pattern has both %d and %i, and the date + * is known. * * @param date - known date */ @@ -211,6 +212,32 @@ public String toRegexForFixedDate(Date date) { return buf.toString(); } + + /** + * Given date, convert this instance to a regular expression. + * + * Used to compute sub-regex when the pattern has both %d and %i, and the date + * is known. + * + * @param instant - known instant + */ + public String toRegexForFixedDate(Instant instant) { + StringBuilder buf = new StringBuilder(); + Converter p = headTokenConverter; + while (p != null) { + if (p instanceof LiteralConverter) { + buf.append(p.convert(null)); + } else if (p instanceof IntegerTokenConverter) { + buf.append("(\\d+)"); + } else if (p instanceof DateTokenConverter) { + buf.append(p.convert(instant)); + } + p = p.getNext(); + } + return buf.toString(); + } + + /** * Given date, convert this instance to a regular expression */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileStoreUtil.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileStoreUtil.java index 129038bffd..cdf242c445 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileStoreUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/FileStoreUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/GZCompressionStrategy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/GZCompressionStrategy.java new file mode 100644 index 0000000000..f32fc03441 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/GZCompressionStrategy.java @@ -0,0 +1,72 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling.helper; + +import ch.qos.logback.core.status.ErrorStatus; +import ch.qos.logback.core.status.WarnStatus; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.zip.GZIPOutputStream; + +public class GZCompressionStrategy extends CompressionStrategyBase { + + + @Override + public void compress(String originalFileName, String compressedFileName, String innerEntryName) { + + File file2gz = new File(originalFileName); + + if (!file2gz.exists()) { + addStatus(new WarnStatus("The file to compress named [" + originalFileName + "] does not exist.", this)); + + return; + } + + if (!compressedFileName.endsWith(".gz")) { + compressedFileName = compressedFileName + ".gz"; + } + + File gzedFile = new File(compressedFileName); + + if (gzedFile.exists()) { + addWarn("The target compressed file named [" + compressedFileName + "] exist already. Aborting file compression."); + return; + } + + addInfo("GZ compressing [" + file2gz + "] as [" + gzedFile + "]"); + createMissingTargetDirsIfNecessary(gzedFile); + try (FileInputStream fis = new FileInputStream(originalFileName); + GZIPOutputStream gzos = new GZIPOutputStream(new FileOutputStream(compressedFileName), BUFFER_SIZE)) { + + byte[] inbuf = new byte[BUFFER_SIZE]; + int n; + + while ((n = fis.read(inbuf)) != -1) { + gzos.write(inbuf, 0, n); + } + + addInfo("Done GZ compressing [" + file2gz + "] as [" + gzedFile + "]"); + } catch (Exception e) { + addStatus(new ErrorStatus("Error occurred while compressing [" + originalFileName + "] into [" + compressedFileName + "].", this, e)); + } + + if (!file2gz.delete()) { + addStatus(new WarnStatus("Could not delete [" + originalFileName + "].", this)); + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/IntegerTokenConverter.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/IntegerTokenConverter.java index 3bc57d21d1..c97faa1cdf 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/IntegerTokenConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/IntegerTokenConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/MonoTypedConverter.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/MonoTypedConverter.java index 0af4b0ea91..490e3ae902 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/MonoTypedConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/MonoTypedConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java index 786e9fb6e8..bc900f964f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/PeriodicityType.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,11 +15,12 @@ public enum PeriodicityType { - ERRONEOUS, TOP_OF_MILLISECOND, TOP_OF_SECOND, TOP_OF_MINUTE, TOP_OF_HOUR, HALF_DAY, TOP_OF_DAY, TOP_OF_WEEK, TOP_OF_MONTH; + ERRONEOUS, TOP_OF_MILLISECOND, TOP_OF_SECOND, TOP_OF_MINUTE, TOP_OF_HOUR, HALF_DAY, TOP_OF_DAY, TOP_OF_WEEK, + TOP_OF_MONTH; - // The followed list consists of valid periodicy types in increasing period - // lengths - static PeriodicityType[] VALID_ORDERED_LIST = new PeriodicityType[] { TOP_OF_MILLISECOND, PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, - PeriodicityType.TOP_OF_HOUR, PeriodicityType.TOP_OF_DAY, PeriodicityType.TOP_OF_WEEK, PeriodicityType.TOP_OF_MONTH }; + // The followed list consists of valid periodicity types in increasing period lengths + static PeriodicityType[] VALID_ORDERED_LIST = new PeriodicityType[] { TOP_OF_MILLISECOND, + PeriodicityType.TOP_OF_SECOND, PeriodicityType.TOP_OF_MINUTE, PeriodicityType.TOP_OF_HOUR, + PeriodicityType.TOP_OF_DAY, PeriodicityType.TOP_OF_WEEK, PeriodicityType.TOP_OF_MONTH }; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RenameUtil.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RenameUtil.java index e83a02bb9e..9915467af8 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RenameUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RenameUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -32,9 +32,8 @@ public class RenameUtil extends ContextAwareBase { static String RENAMING_ERROR_URL = CoreConstants.CODES_URL + "#renamingError"; /** - * A relatively robust file renaming method which in case of failure due to - * src and target being on different volumes, falls back onto - * renaming by copying. + * A relatively robust file renaming method which in case of failure due to src + * and target being on different volumes, falls back onto renaming by copying. * * @param src * @param target @@ -59,11 +58,13 @@ public void rename(String src, String target) throws RolloverFailure { addWarn("Failed to rename file [" + srcFile + "] as [" + targetFile + "]."); Boolean areOnDifferentVolumes = areOnDifferentVolumes(srcFile, targetFile); if (Boolean.TRUE.equals(areOnDifferentVolumes)) { - addWarn("Detected different file systems for source [" + src + "] and target [" + target + "]. Attempting rename by copying."); + addWarn("Detected different file systems for source [" + src + "] and target [" + target + + "]. Attempting rename by copying."); renameByCopying(src, target); return; } else { - addWarn("Please consider leaving the [file] option of " + RollingFileAppender.class.getSimpleName() + " empty."); + addWarn("Please consider leaving the [file] option of " + RollingFileAppender.class.getSimpleName() + + " empty."); addWarn("See also " + RENAMING_ERROR_URL); } } @@ -72,11 +73,10 @@ public void rename(String src, String target) throws RolloverFailure { } } - - /** - * Attempts to determine whether both files are on different volumes. Returns true if we could determine that - * the files are on different volumes. Returns false otherwise or if an error occurred while doing the check. + * Attempts to determine whether both files are on different volumes. Returns + * true if we could determine that the files are on different volumes. Returns + * false otherwise or if an error occurred while doing the check. * * @param srcFile * @param targetFile @@ -86,18 +86,19 @@ Boolean areOnDifferentVolumes(File srcFile, File targetFile) throws RolloverFail if (!EnvUtil.isJDK7OrHigher()) return false; - // target file is not certain to exist but its parent has to exist given the call hierarchy of this method + // target file is not certain to exist but its parent has to exist given the + // call hierarchy of this method File parentOfTarget = targetFile.getAbsoluteFile().getParentFile(); - - if(parentOfTarget == null) { - addWarn("Parent of target file ["+targetFile+"] is null"); + + if (parentOfTarget == null) { + addWarn("Parent of target file [" + targetFile + "] is null"); return null; } - if(!parentOfTarget.exists()) { - addWarn("Parent of target file ["+targetFile+"] does not exist"); + if (!parentOfTarget.exists()) { + addWarn("Parent of target file [" + targetFile + "] does not exist"); return null; } - + try { boolean onSameFileStore = FileStoreUtil.areOnSameFileStore(srcFile, parentOfTarget); return !onSameFileStore; diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java index ab362aa993..44022812db 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/RollingCalendar.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,6 +20,9 @@ import static ch.qos.logback.core.CoreConstants.MILLIS_IN_ONE_DAY; import java.text.SimpleDateFormat; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.Calendar; import java.util.Date; import java.util.GregorianCalendar; @@ -62,9 +65,9 @@ public PeriodicityType getPeriodicityType() { return periodicityType; } - // This method computes the roll over period by looping over the + // This method computes the roll-over period by looping over the // periods, starting with the shortest, and stopping when the r0 is - // different from from r1, where r0 is the epoch formatted according + // different from r1, where r0 is the epoch formatted according // the datePattern (supplied by the user) and r1 is the // epoch+nextMillis(i) formatted according to datePattern. All date // formatting is done in GMT and not local format because the test @@ -75,17 +78,19 @@ public PeriodicityType computePeriodicityType() { GregorianCalendar calendar = new GregorianCalendar(GMT_TIMEZONE, Locale.getDefault()); // set sate to 1970-01-01 00:00:00 GMT - Date epoch = new Date(0); - + Instant epoch = Instant.ofEpochMilli(0); + ZoneId gmtZone = ZoneId.of("UTC"); if (datePattern != null) { for (PeriodicityType i : PeriodicityType.VALID_ORDERED_LIST) { - SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); - simpleDateFormat.setTimeZone(GMT_TIMEZONE); // all date formatting done in GMT + DateTimeFormatter dtf = DateTimeFormatter.ofPattern(datePattern).withZone(gmtZone); - String r0 = simpleDateFormat.format(epoch); + //SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern); + //simpleDateFormat.setTimeZone(GMT_TIMEZONE); // all date formatting done in GMT - Date next = innerGetEndOfThisPeriod(calendar, i, epoch); - String r1 = simpleDateFormat.format(next); + String r0 = dtf.format(epoch); + + Instant next = innerGetEndOfThisPeriod(calendar, i, epoch); + String r1 = dtf.format(next); // System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1); if ((r0 != null) && (r1 != null) && !r0.equals(r1)) { @@ -195,8 +200,7 @@ public long periodBarriersCrossed(long start, long end) { case TOP_OF_MINUTE: return diff / MILLIS_IN_ONE_MINUTE; case TOP_OF_HOUR: - - return (int) diff / MILLIS_IN_ONE_HOUR; + return diff / MILLIS_IN_ONE_HOUR; case TOP_OF_DAY: return diff / MILLIS_IN_ONE_DAY; case TOP_OF_WEEK: @@ -220,12 +224,13 @@ public static int diffInMonths(long startTime, long endTime) { return yearDiff * 12 + monthDiff; } - static private Date innerGetEndOfThisPeriod(Calendar cal, PeriodicityType periodicityType, Date now) { - return innerGetEndOfNextNthPeriod(cal, periodicityType, now, 1); + static private Instant innerGetEndOfThisPeriod(Calendar cal, PeriodicityType periodicityType, Instant instant) { + return innerGetEndOfNextNthPeriod(cal, periodicityType, instant, 1); } - static private Date innerGetEndOfNextNthPeriod(Calendar cal, PeriodicityType periodicityType, Date now, int numPeriods) { - cal.setTime(now); + static private Instant innerGetEndOfNextNthPeriod(Calendar cal, PeriodicityType periodicityType, Instant instant, + int numPeriods) { + cal.setTimeInMillis(instant.toEpochMilli()); switch (periodicityType) { case TOP_OF_MILLISECOND: cal.add(Calendar.MILLISECOND, numPeriods); @@ -279,30 +284,31 @@ static private Date innerGetEndOfNextNthPeriod(Calendar cal, PeriodicityType per throw new IllegalStateException("Unknown periodicity type."); } - return cal.getTime(); + return Instant.ofEpochMilli(cal.getTimeInMillis()); } - public Date getEndOfNextNthPeriod(Date now, int periods) { - return innerGetEndOfNextNthPeriod(this, this.periodicityType, now, periods); + public Instant getEndOfNextNthPeriod(Instant instant, int periods) { + return innerGetEndOfNextNthPeriod(this, this.periodicityType, instant, periods); } - public Date getNextTriggeringDate(Date now) { - return getEndOfNextNthPeriod(now, 1); + public Instant getNextTriggeringDate(Instant instant) { + return getEndOfNextNthPeriod(instant, 1); } public long getStartOfCurrentPeriodWithGMTOffsetCorrection(long now, TimeZone timezone) { - Date toppedDate; + Instant toppedInstant; // there is a bug in Calendar which prevents it from // computing the correct DST_OFFSET when the time changes { Calendar aCal = Calendar.getInstance(timezone); aCal.setTimeInMillis(now); - toppedDate = getEndOfNextNthPeriod(aCal.getTime(), 0); + Instant instant = Instant.ofEpochMilli(aCal.getTimeInMillis()); + toppedInstant = getEndOfNextNthPeriod(instant, 0); } Calendar secondCalendar = Calendar.getInstance(timezone); - secondCalendar.setTimeInMillis(toppedDate.getTime()); + secondCalendar.setTimeInMillis(toppedInstant.toEpochMilli()); long gmtOffset = secondCalendar.get(Calendar.ZONE_OFFSET) + secondCalendar.get(Calendar.DST_OFFSET); - return toppedDate.getTime() + gmtOffset; + return toppedInstant.toEpochMilli() + gmtOffset; } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.java index 7b1d54585a..09e998880b 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemover.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,6 +14,7 @@ package ch.qos.logback.core.rolling.helper; import java.io.File; +import java.time.Instant; import java.util.Arrays; import java.util.Comparator; import java.util.Date; @@ -28,23 +29,19 @@ public SizeAndTimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCa super(fileNamePattern, rc); } - protected File[] getFilesInPeriod(Date dateOfPeriodToClean) { - File archive0 = new File(fileNamePattern.convertMultipleArguments(dateOfPeriodToClean, 0)); + @Override + protected File[] getFilesInPeriod(Instant instantOfPeriodToClean) { + File archive0 = new File(fileNamePattern.convertMultipleArguments(instantOfPeriodToClean, 0)); File parentDir = getParentDir(archive0); - String stemRegex = createStemRegex(dateOfPeriodToClean); + String stemRegex = createStemRegex(instantOfPeriodToClean); File[] matchingFileArray = FileFilterUtil.filesInFolderMatchingStemRegex(parentDir, stemRegex); return matchingFileArray; } - private String createStemRegex(final Date dateOfPeriodToClean) { - String regex = fileNamePattern.toRegexForFixedDate(dateOfPeriodToClean); - return FileFilterUtil.afterLastSlash(regex); - } - @Override - protected void descendingSort(File[] matchingFileArray, Date date) { + protected void descendingSort(File[] matchingFileArray, Instant instant) { - String regexForIndexExtreaction = createStemRegex(date); + String regexForIndexExtreaction = createStemRegex(instant); final Pattern pattern = Pattern.compile(regexForIndexExtreaction); Arrays.sort(matchingFileArray, new Comparator() { @@ -67,7 +64,7 @@ private int extractIndex(Pattern pattern, File f1) { Matcher matcher = pattern.matcher(f1.getName()); if (matcher.find()) { String indexAsStr = matcher.group(1); - + if (indexAsStr == null || indexAsStr.isEmpty()) return NO_INDEX; // unreachable code? else @@ -78,4 +75,9 @@ private int extractIndex(Pattern pattern, File f1) { }); } + private String createStemRegex(final Instant instantOfPeriodToClean) { + String regex = fileNamePattern.toRegexForFixedDate(instantOfPeriodToClean); + return FileFilterUtil.afterLastSlash(regex); + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java index 38c8ace7e2..9156ab416c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TimeBasedArchiveRemover.java @@ -1,22 +1,20 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.rolling.helper; import static ch.qos.logback.core.CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP; import java.io.File; -import java.util.Date; +import java.time.Instant; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; @@ -29,13 +27,14 @@ public class TimeBasedArchiveRemover extends ContextAwareBase implements ArchiveRemover { static protected final long UNINITIALIZED = -1; - // aim for 32 days, except in case of hourly rollover + // aim for 32 days, except in case of hourly rollover, see + // MAX_VALUE_FOR_INACTIVITY_PERIODS static protected final long INACTIVITY_TOLERANCE_IN_MILLIS = 32L * (long) CoreConstants.MILLIS_IN_ONE_DAY; static final int MAX_VALUE_FOR_INACTIVITY_PERIODS = 14 * 24; // 14 days in case of hourly rollover final FileNamePattern fileNamePattern; final RollingCalendar rc; - private int maxHistory = CoreConstants.UNBOUND_HISTORY; + private int maxHistory = CoreConstants.UNBOUNDED_HISTORY; private long totalSizeCap = CoreConstants.UNBOUNDED_TOTAL_SIZE_CAP; final boolean parentClean; long lastHeartBeat = UNINITIALIZED; @@ -47,24 +46,39 @@ public TimeBasedArchiveRemover(FileNamePattern fileNamePattern, RollingCalendar } int callCount = 0; - public void clean(Date now) { - - long nowInMillis = now.getTime(); + + public Future cleanAsynchronously(Instant now) { + ArchiveRemoverRunnable runnable = new ArchiveRemoverRunnable(now); + ExecutorService alternateExecutorService = context.getAlternateExecutorService(); + Future future = alternateExecutorService.submit(runnable); + return future; + } + + /** + * Called from the cleaning thread. + * + * @param now + */ + @Override + public void clean(Instant now) { + + long nowInMillis = now.toEpochMilli(); // for a live appender periodsElapsed is expected to be 1 int periodsElapsed = computeElapsedPeriodsSinceLastClean(nowInMillis); lastHeartBeat = nowInMillis; if (periodsElapsed > 1) { - addInfo("Multiple periods, i.e. " + periodsElapsed + " periods, seem to have elapsed. This is expected at application start."); + addInfo("Multiple periods, i.e. " + periodsElapsed + + " periods, seem to have elapsed. This can happen at application start."); } for (int i = 0; i < periodsElapsed; i++) { int offset = getPeriodOffsetForDeletionTarget() - i; - Date dateOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset); - cleanPeriod(dateOfPeriodToClean); + Instant instantOfPeriodToClean = rc.getEndOfNextNthPeriod(now, offset); + cleanPeriod(instantOfPeriodToClean); } } - protected File[] getFilesInPeriod(Date dateOfPeriodToClean) { - String filenameToDelete = fileNamePattern.convert(dateOfPeriodToClean); + protected File[] getFilesInPeriod(Instant instantOfPeriodToClean) { + String filenameToDelete = fileNamePattern.convert(instantOfPeriodToClean); File file2Delete = new File(filenameToDelete); if (fileExistsAndIsFile(file2Delete)) { @@ -78,12 +92,12 @@ private boolean fileExistsAndIsFile(File file2Delete) { return file2Delete.exists() && file2Delete.isFile(); } - public void cleanPeriod(Date dateOfPeriodToClean) { - File[] matchingFileArray = getFilesInPeriod(dateOfPeriodToClean); + public void cleanPeriod(Instant instantOfPeriodToClean) { + File[] matchingFileArray = getFilesInPeriod(instantOfPeriodToClean); for (File f : matchingFileArray) { - addInfo("deleting " + f); - f.delete(); + addInfo("deleting historically stale " + f); + checkAndDeleteFile(f); } if (parentClean && matchingFileArray.length > 0) { @@ -92,28 +106,63 @@ public void cleanPeriod(Date dateOfPeriodToClean) { } } - void capTotalSize(Date now) { + private boolean checkAndDeleteFile(File f) { + + if (f == null) { + addWarn("Cannot delete empty file"); + return false; + } else if (!f.exists()) { + addWarn("Cannot delete non existent file"); + return false; + } + + boolean result = f.delete(); + if (!result) { + addWarn("Failed to delete file " + f.toString()); + } + return result; + } + + void capTotalSize(Instant now) { long totalSize = 0; long totalRemoved = 0; + int successfulDeletions = 0; + int failedDeletions = 0; + for (int offset = 0; offset < maxHistory; offset++) { - Date date = rc.getEndOfNextNthPeriod(now, -offset); - File[] matchingFileArray = getFilesInPeriod(date); - descendingSort(matchingFileArray, date); + Instant instant = rc.getEndOfNextNthPeriod(now, -offset); + File[] matchingFileArray = getFilesInPeriod(instant); + descendingSort(matchingFileArray, instant); for (File f : matchingFileArray) { long size = f.length(); - if (totalSize + size > totalSizeCap) { - addInfo("Deleting [" + f + "]" + " of size " + new FileSize(size)); - totalRemoved += size; - f.delete(); - } + //System.out.println("File: " + f + " size=" + size); totalSize += size; + if (totalSize > totalSizeCap) { + //addInfo("Deleting [" + f + "]" + " of size " + new FileSize(size) + " on account of totalSizeCap " + totalSizeCap); + addInfo("Deleting [" + f + "]" + " of size " + size + " on account of totalSizeCap " + totalSizeCap); + + boolean success = checkAndDeleteFile(f); + + if (success) { + successfulDeletions++; + totalRemoved += size; + } else { + failedDeletions++; + } + } + } + } + if ((successfulDeletions + failedDeletions) == 0) { + addInfo("No removal attempts were made on account of totalSizeCap="+totalSizeCap); + } else { + addInfo("Removed " + new FileSize(totalRemoved) + " of files in " + successfulDeletions + " files on account of totalSizeCap=" + totalSizeCap); + if (failedDeletions != 0) { + addInfo("There were " + failedDeletions + " failed deletion attempts."); } } - addInfo("Removed " + new FileSize(totalRemoved) + " of files"); } - - protected void descendingSort(File[] matchingFileArray, Date date) { + protected void descendingSort(File[] matchingFileArray, Instant instant) { // nothing to do in super class } @@ -138,6 +187,7 @@ int computeElapsedPeriodsSinceLastClean(long nowInMillis) { /** * Computes whether the fileNamePattern may create sub-folders. + * * @param fileNamePattern * @return */ @@ -147,7 +197,7 @@ boolean computeParentCleaningFlag(FileNamePattern fileNamePattern) { if (dtc.getDatePattern().indexOf('/') != -1) { return true; } - // if the literal string subsequent to the dtc contains a /, we also + // if the literal string after the dtc contains a /, we also // need parent cleaning Converter p = fileNamePattern.headTokenConverter; @@ -179,9 +229,8 @@ void removeFolderIfEmpty(File dir) { } /** - * Will remove the directory passed as parameter if empty. After that, if the - * parent is also becomes empty, remove the parent dir as well but at most 3 - * times. + * Will remove the directory passed as parameter if empty. After that, if the parent is also becomes empty, remove + * the parent dir as well but at most 3 times. * * @param dir * @param depth @@ -193,7 +242,7 @@ private void removeFolderIfEmpty(File dir, int depth) { } if (dir.isDirectory() && FileFilterUtil.isEmptyDirectory(dir)) { addInfo("deleting folder [" + dir + "]"); - dir.delete(); + checkAndDeleteFile(dir); removeFolderIfEmpty(dir.getParentFile(), depth + 1); } } @@ -214,17 +263,10 @@ public String toString() { return "c.q.l.core.rolling.helper.TimeBasedArchiveRemover"; } - public Future cleanAsynchronously(Date now) { - ArhiveRemoverRunnable runnable = new ArhiveRemoverRunnable(now); - ExecutorService executorService = context.getScheduledExecutorService(); - Future future = executorService.submit(runnable); - return future; - } - - public class ArhiveRemoverRunnable implements Runnable { - Date now; + public class ArchiveRemoverRunnable implements Runnable { + Instant now; - ArhiveRemoverRunnable(Date now) { + ArchiveRemoverRunnable(Instant now) { this.now = now; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TokenConverter.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TokenConverter.java index aa4daa508c..982840dc8a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TokenConverter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/TokenConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,10 +14,10 @@ package ch.qos.logback.core.rolling.helper; /** - * TokenConverter offers some basic functionality used by more - * specific token converters. + * TokenConverter offers some basic functionality used by more + * specific token converters. *

- * It basically sets up the chained architecture for tokens. It also forces + * It basically sets up the chained architecture for tokens. It also forces * derived classes to fix their type. * * @author Ceki diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/XZCompressionStrategy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/XZCompressionStrategy.java new file mode 100644 index 0000000000..f4fe6210b1 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/XZCompressionStrategy.java @@ -0,0 +1,73 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.rolling.helper; + +import java.io.*; + +import org.tukaani.xz.LZMA2Options; +import org.tukaani.xz.XZOutputStream; + +/** + * Compresses files using tukaani.org/xz library. + * + *

Note that

+ * + * @author Marian Kazimir + * @author Ceki Gülcü + * @since 1.5.18 + */ +public class XZCompressionStrategy extends CompressionStrategyBase { + + @Override + public void compress(String nameOfFile2xz, String nameOfxzedFile, String innerEntryName) { + File file2xz = new File(nameOfFile2xz); + + if (!file2xz.exists()) { + addWarn("The file to compress named [" + nameOfFile2xz + "] does not exist."); + + return; + } + + if (!nameOfxzedFile.endsWith(".xz")) { + nameOfxzedFile = nameOfxzedFile + ".xz"; + } + + File xzedFile = new File(nameOfxzedFile); + + if (xzedFile.exists()) { + addWarn("The target compressed file named [" + nameOfxzedFile + "] exist already. Aborting file compression."); + return; + } + + addInfo("XZ compressing [" + file2xz + "] as [" + xzedFile + "]"); + createMissingTargetDirsIfNecessary(xzedFile); + + try (FileInputStream fis = new FileInputStream(nameOfFile2xz); + XZOutputStream xzos = new XZOutputStream(new BufferedOutputStream(new FileOutputStream(nameOfxzedFile), BUFFER_SIZE), new LZMA2Options())) { + + byte[] inbuf = new byte[BUFFER_SIZE]; + int n; + + while ((n = fis.read(inbuf)) != -1) { + xzos.write(inbuf, 0, n); + } + } catch (Exception e) { + addError("Error occurred while compressing [" + nameOfFile2xz + "] into [" + nameOfxzedFile + "].", e); + } + + if (!file2xz.delete()) { + addWarn("Could not delete [" + nameOfFile2xz + "]."); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ZipCompressionStrategy.java b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ZipCompressionStrategy.java new file mode 100644 index 0000000000..d6b1c5ef5d --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/ZipCompressionStrategy.java @@ -0,0 +1,110 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.rolling.helper; + +import ch.qos.logback.core.status.ErrorStatus; +import ch.qos.logback.core.status.WarnStatus; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +/** + * Compresses files using JDK's Zip compression algorithm. + * + * @author Ceki Gülcü + * @since 1.5.18 + */ +public class ZipCompressionStrategy extends CompressionStrategyBase { + + @Override + public void compress(String originalFileName, String compressedFileName, String innerEntryName) { + + File file2zip = new File(originalFileName); + + if (!file2zip.exists()) { + addStatus(new WarnStatus("The file to compress named [" + originalFileName + "] does not exist.", this)); + + return; + } + + if (innerEntryName == null) { + addStatus(new WarnStatus("The innerEntryName parameter cannot be null", this)); + return; + } + + if (!compressedFileName.endsWith(".zip")) { + compressedFileName = compressedFileName + ".zip"; + } + + File zippedFile = new File(compressedFileName); + + if (zippedFile.exists()) { + addStatus(new WarnStatus("The target compressed file named [" + compressedFileName + "] exist already.", this)); + + return; + } + + addInfo("ZIP compressing [" + file2zip + "] as [" + zippedFile + "]"); + createMissingTargetDirsIfNecessary(zippedFile); + + try (FileInputStream fis = new FileInputStream(originalFileName); + ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(compressedFileName), BUFFER_SIZE))) { + + ZipEntry zipEntry = computeZipEntry(innerEntryName); + zos.putNextEntry(zipEntry); + + byte[] inbuf = new byte[BUFFER_SIZE]; + int n; + + while ((n = fis.read(inbuf)) != -1) { + zos.write(inbuf, 0, n); + } + + addInfo("Done ZIP compressing [" + file2zip + "] as [" + zippedFile + "]"); + } catch (Exception e) { + addStatus(new ErrorStatus("Error occurred while compressing [" + originalFileName + "] into [" + compressedFileName + "].", this, e)); + } + if (!file2zip.delete()) { + addStatus(new WarnStatus("Could not delete [" + originalFileName + "].", this)); + } + } + + // http://jira.qos.ch/browse/LBCORE-98 + // The name of the compressed file as nested within the zip archive + // + // Case 1: RawFile = null, Pattern = foo-%d.zip + // nestedFilename = foo-${current-date} + // + // Case 2: RawFile = hello.txt, Pattern = = foo-%d.zip + // nestedFilename = foo-${current-date} + // + // in both cases, the strategy consisting of removing the compression + // suffix of zip file works reasonably well. The alternative strategy + // whereby the nested file name was based on the value of the raw file name + // (applicable to case 2 only) has the disadvantage of the nested files + // all having the same name, which could make it harder for the user + // to unzip the file without collisions + //ZipEntry computeZipEntry(File zippedFile) { + // return computeZipEntry(zippedFile.getName()); + //} + + ZipEntry computeZipEntry(String filename) { + String nameOfFileNestedWithinArchive = Compressor.computeFileNameStrWithoutCompSuffix(filename, CompressionMode.ZIP); + return new ZipEntry(nameOfFileNestedWithinArchive); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/package.html b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/package.html index 6c5e455880..7f8bd831fd 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/helper/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/rolling/package.html b/logback-core/src/main/java/ch/qos/logback/core/rolling/package.html index b34dcc19b2..2c50066b70 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/rolling/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/rolling/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractAppenderFactoryUsingJoran.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractAppenderFactoryUsingJoran.java deleted file mode 100644 index 60e18a6cac..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractAppenderFactoryUsingJoran.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.sift; - -import java.util.List; -import java.util.Map; - -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.Context; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.JoranException; - -/** - * Builds new appenders dynamically by running SiftingJoranConfigurator instance, - * a custom configurator tailored for the contents of the sift element. - * @param - */ -public abstract class AbstractAppenderFactoryUsingJoran implements AppenderFactory { - - final List eventList; - protected String key; - protected Map parentPropertyMap; - - protected AbstractAppenderFactoryUsingJoran(List eventList, String key, Map parentPropertyMap) { - this.eventList = removeSiftElement(eventList); - this.key = key; - this.parentPropertyMap = parentPropertyMap; - - } - - List removeSiftElement(List eventList) { - return eventList.subList(1, eventList.size() - 1); - } - - public abstract SiftingJoranConfiguratorBase getSiftingJoranConfigurator(String k); - - public Appender buildAppender(Context context, String discriminatingValue) throws JoranException { - SiftingJoranConfiguratorBase sjc = getSiftingJoranConfigurator(discriminatingValue); - sjc.setContext(context); - sjc.doConfigure(eventList); - return sjc.getAppender(); - } - - public List getEventList() { - return eventList; - } - -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractDiscriminator.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractDiscriminator.java index a8c634a38e..72684a47d4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractDiscriminator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AbstractDiscriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,7 +16,8 @@ import ch.qos.logback.core.spi.ContextAwareBase; /** - * Base implementation of {@link Discriminator} that provides basic lifecycle management + * Base implementation of {@link Discriminator} that provides basic lifecycle + * management * * @author Tomasz Nurkiewicz * @since 3/29/13, 3:28 PM diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java index fcc916caa5..09daa7a519 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,11 +18,8 @@ import ch.qos.logback.core.joran.spi.JoranException; /** - * Created with IntelliJ IDEA. - * User: ceki - * Date: 25.04.13 - * Time: 19:00 - * To change this template use File | Settings | File Templates. + * Created with IntelliJ IDEA. User: ceki Date: 25.04.13 Time: 19:00 To change + * this template use File | Settings | File Templates. */ public interface AppenderFactory { Appender buildAppender(Context context, String discriminatingValue) throws JoranException; diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactoryUsingSiftModel.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactoryUsingSiftModel.java new file mode 100644 index 0000000000..d46fbddcb7 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderFactoryUsingSiftModel.java @@ -0,0 +1,98 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.sift; + +import java.util.Collection; +import java.util.Map; + +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.joran.JoranConstants; +import ch.qos.logback.core.joran.ParamModelHandler; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.ParamModel; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.SiftModel; +import ch.qos.logback.core.model.processor.AppenderModelHandler; +import ch.qos.logback.core.model.processor.ImplicitModelHandler; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.model.processor.PropertyModelHandler; + +/** + * Builds new appenders dynamically by running SiftingJoranConfigurator instance, + * a custom configurator tailored for the contents of the sift element. + * @param + */ +public class AppenderFactoryUsingSiftModel implements AppenderFactory { + + Context context; + final Model siftModel; + protected String discriminatingKey; + protected ModelInterpretationContext parentMic; + protected DefaultNestedComponentRegistry registry; + + public AppenderFactoryUsingSiftModel(ModelInterpretationContext parentMic, Model aSiftModel, String discriminatingKey) { + this.siftModel = Model.duplicate(aSiftModel); + this.discriminatingKey = discriminatingKey; + this.parentMic = parentMic; + this.context = parentMic.getContext(); + + } + + + public SiftProcessor getSiftingModelProcessor(String value) { + ModelInterpretationContext smic = new ModelInterpretationContext(parentMic) { + @Override + public boolean hasDependers(String dependeeName) { + return true; + } + }; + SiftProcessor siftProcessor = new SiftProcessor<>(context, smic); + siftProcessor.addHandler(ParamModel.class, ParamModelHandler::makeInstance); + siftProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance); + siftProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); + siftProcessor.addHandler(AppenderModel.class, AppenderModelHandler::makeInstance); + siftProcessor.addHandler(SiftModel.class, NOPSiftModelHandler::makeInstance); + + return siftProcessor; + + } + + public Appender buildAppender(Context context, String discriminatingValue) throws JoranException { + + SiftProcessor sp = getSiftingModelProcessor(discriminatingValue); + ModelInterpretationContext mic = sp.getModelInterpretationContext(); + sp.setContext(context); + Model duplicate = Model.duplicate(siftModel); + mic.addSubstitutionProperty(discriminatingKey, discriminatingValue); + sp.process(duplicate); + @SuppressWarnings("unchecked") + Map> appenderBag = (Map>) mic.getObjectMap() + .get(JoranConstants.APPENDER_BAG); + Collection> values = appenderBag.values(); + if (values.size() == 0) { + return null; + } + return values.iterator().next(); + } + + public Model getSiftModel() { + return siftModel; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java index acb9752bec..be81a47102 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/AppenderTracker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,8 +22,8 @@ import ch.qos.logback.core.spi.ContextAwareImpl; /** - * Track appenders by key. When an appender is not used for - * longer than {@link #DEFAULT_TIMEOUT} it is stopped and removed. + * Track appenders by key. When an appender is not used for longer than + * {@link #DEFAULT_TIMEOUT} it is stopped and removed. * * @author Tommy Becker * @author Ceki Gulcu diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java b/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java index c891f2ca9c..42bd61cd4c 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/DefaultDiscriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/Discriminator.java b/logback-core/src/main/java/ch/qos/logback/core/sift/Discriminator.java index 18be83447c..c261a85215 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/Discriminator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/Discriminator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,9 @@ * Implement this interface in order to compute a discriminating value for a * given event of type <E>. * - *

The returned value can depend on any data available at the time of the - * call, including data contained within the currently running thread. + *

+ * The returned value can depend on any data available at the time of the call, + * including data contained within the currently running thread. * * @author Ceki Gülcü * @@ -38,7 +39,7 @@ public interface Discriminator extends LifeCycle { /** * The key or variable name under which the discriminating value should be - * exported into the host environment. + * exported into the host environment. * * @return */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/NOPSiftModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/sift/NOPSiftModelHandler.java new file mode 100644 index 0000000000..964193cdf2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/NOPSiftModelHandler.java @@ -0,0 +1,43 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.sift; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SiftModel; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class NOPSiftModelHandler extends ModelHandlerBase { + + public NOPSiftModelHandler(Context context) { + super(context); + } + + static public NOPSiftModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new NOPSiftModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return SiftModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftModelHandler.java b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftModelHandler.java new file mode 100644 index 0000000000..5a0c9b5a56 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftModelHandler.java @@ -0,0 +1,87 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.sift; + +import java.util.stream.Stream; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.SiftModel; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class SiftModelHandler extends ModelHandlerBase { + final static String ONE_AND_ONLY_ONE_URL = CoreConstants.CODES_URL + "#1andOnly1"; + + public SiftModelHandler(Context context) { + super(context); + } + + static public SiftModelHandler makeInstance(Context context, ModelInterpretationContext ic) { + return new SiftModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return SiftModel.class; + } + + @SuppressWarnings("unchecked") + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + SiftModel siftModel = (SiftModel) model; + // don't let the processor handle sub-models + siftModel.markAsSkipped(); + + long appenderModelCount = computeAppenderModelCount(siftModel); + + if(appenderModelCount == 0) { + String errMsg = "No nested appenders found within the element in SiftingAppender."; + addError(errMsg); + return; + } + if(appenderModelCount > 1) { + String errMsg = "Only and only one appender can be nested the element in SiftingAppender. See also " + ONE_AND_ONLY_ONE_URL; + addError(errMsg); + return; + } + + + Object o = mic.peekObject(); + if (o instanceof SiftingAppenderBase) { + @SuppressWarnings("rawtypes") + SiftingAppenderBase sa = (SiftingAppenderBase) o; + + String key = sa.getDiscriminatorKey(); + @SuppressWarnings("rawtypes") + AppenderFactoryUsingSiftModel afusm = new AppenderFactoryUsingSiftModel(mic, siftModel, key); + + sa.setAppenderFactory(afusm); + + } else { + addError("Unexpected object "+ o); + } + } + + private long computeAppenderModelCount(SiftModel siftModel) { + Stream stream = siftModel.getSubModels().stream(); + long count = stream.filter((Model m) -> m instanceof AppenderModel).count(); + return count; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftProcessor.java b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftProcessor.java new file mode 100644 index 0000000000..4c2cce2db5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftProcessor.java @@ -0,0 +1,68 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.sift; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class SiftProcessor extends DefaultProcessor { + + + public SiftProcessor(Context context, ModelInterpretationContext mic) { + super(mic.getContext(), mic); + } + + ModelInterpretationContext getModelInterpretationContext() { + return mic; + } + +// final static String ONE_AND_ONLY_ONE_URL = CoreConstants.CODES_URL + "#1andOnly1"; + + +// void foo() { +// this.modelInterpretationContext = new ModelInterpretationContext(context); +// buildModelInterpretationContext(); +// DefaultProcessor defaultProcessor = new DefaultProcessor(context, this.modelInterpretationContext); +// addModelHandlerAssociations(defaultProcessor); +// +// } + + + //abstract public Appender getAppender(); + +// int errorEmmissionCount = 0; +// +// protected void oneAndOnlyOneCheck(Map appenderMap) { +// String errMsg = null; +// if (appenderMap.size() == 0) { +// errorEmmissionCount++; +// errMsg = "No nested appenders found within the element in SiftingAppender."; +// } else if (appenderMap.size() > 1) { +// errorEmmissionCount++; +// errMsg = "Only and only one appender can be nested the element in SiftingAppender. See also " +// + ONE_AND_ONLY_ONE_URL; +// } +// +// if (errMsg != null && errorEmmissionCount < CoreConstants.MAX_ERROR_COUNT) { +// addError(errMsg); +// } +// } + +// @Override +// public String toString() { +// return this.getClass().getName() + "{" + key + "=" + value + '}'; +// } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java index 9880e74467..e65fb3a571 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,6 +15,7 @@ import ch.qos.logback.core.Appender; import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.model.SiftModel; import ch.qos.logback.core.util.Duration; /** @@ -25,7 +26,7 @@ * processed. The appender to build (dynamically) is specified as part of a * configuration file. * - * @author Ceki Gulcu + * @author Ceki Gülcü */ public abstract class SiftingAppenderBase extends AppenderBase { @@ -34,6 +35,7 @@ public abstract class SiftingAppenderBase extends AppenderBase { Duration timeout = new Duration(AppenderTracker.DEFAULT_TIMEOUT); int maxAppenderCount = AppenderTracker.DEFAULT_MAX_COMPONENTS; + SiftModel siftModel; Discriminator discriminator; public Duration getTimeout() { @@ -44,6 +46,14 @@ public void setTimeout(Duration timeout) { this.timeout = timeout; } + public SiftModel getSiftModel() { + return siftModel; + } + + public void setSiftModel(SiftModel siftModel) { + this.siftModel = siftModel; + } + public int getMaxAppenderCount() { return maxAppenderCount; } @@ -53,8 +63,8 @@ public void setMaxAppenderCount(int maxAppenderCount) { } /** - * This setter is intended to be invoked by SiftAction. Customers have no reason to invoke - * this method directly. + * This setter is intended to be invoked by SiftModelHandler. Users have no + * reason to invoke this method directly. */ public void setAppenderFactory(AppenderFactory appenderFactory) { this.appenderFactory = appenderFactory; @@ -86,6 +96,8 @@ public void start() { @Override public void stop() { + if(!isStarted()) + return; for (Appender appender : appenderTracker.allComponents()) { appender.stop(); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfiguratorBase.java b/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfiguratorBase.java deleted file mode 100644 index 8914a1f4c6..0000000000 --- a/logback-core/src/main/java/ch/qos/logback/core/sift/SiftingJoranConfiguratorBase.java +++ /dev/null @@ -1,89 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.sift; - -import java.util.List; -import java.util.Map; - -import ch.qos.logback.core.Appender; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.joran.GenericConfigurator; -import ch.qos.logback.core.joran.action.*; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.Interpreter; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.joran.spi.RuleStore; - -public abstract class SiftingJoranConfiguratorBase extends GenericConfigurator { - - protected final String key; - protected final String value; - // properties inherited from the main joran run - protected final Map parentPropertyMap; - - protected SiftingJoranConfiguratorBase(String key, String value, Map parentPropertyMap) { - this.key = key; - this.value = value; - this.parentPropertyMap = parentPropertyMap; - } - - final static String ONE_AND_ONLY_ONE_URL = CoreConstants.CODES_URL + "#1andOnly1"; - - @Override - protected void addImplicitRules(Interpreter interpreter) { - NestedComplexPropertyIA nestedComplexIA = new NestedComplexPropertyIA(getBeanDescriptionCache()); - nestedComplexIA.setContext(context); - interpreter.addImplicitAction(nestedComplexIA); - - NestedBasicPropertyIA nestedSimpleIA = new NestedBasicPropertyIA(getBeanDescriptionCache()); - nestedSimpleIA.setContext(context); - interpreter.addImplicitAction(nestedSimpleIA); - } - - @Override - protected void addInstanceRules(RuleStore rs) { - rs.addRule(new ElementSelector("configuration/property"), new PropertyAction()); - rs.addRule(new ElementSelector("configuration/timestamp"), new TimestampAction()); - rs.addRule(new ElementSelector("configuration/define"), new DefinePropertyAction()); - } - - abstract public Appender getAppender(); - - int errorEmmissionCount = 0; - - protected void oneAndOnlyOneCheck(Map appenderMap) { - String errMsg = null; - if (appenderMap.size() == 0) { - errorEmmissionCount++; - errMsg = "No nested appenders found within the element in SiftingAppender."; - } else if (appenderMap.size() > 1) { - errorEmmissionCount++; - errMsg = "Only and only one appender can be nested the element in SiftingAppender. See also " + ONE_AND_ONLY_ONE_URL; - } - - if (errMsg != null && errorEmmissionCount < CoreConstants.MAX_ERROR_COUNT) { - addError(errMsg); - } - } - - public void doConfigure(final List eventList) throws JoranException { - super.doConfigure(eventList); - } - - @Override - public String toString() { - return this.getClass().getName() + "{" + key + "=" + value + '}'; - } -} diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/AbstractComponentTracker.java b/logback-core/src/main/java/ch/qos/logback/core/spi/AbstractComponentTracker.java index 3624a2c9e0..4992b0368c 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/AbstractComponentTracker.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/AbstractComponentTracker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,8 +18,9 @@ import java.util.*; /** - * An abstract implementation of the ComponentTracker interface. Derived classes must implement - * {@link #buildComponent(String)}, {@link #processPriorToRemoval(Object)}, and {@link #isComponentStale(Object)} + * An abstract implementation of the ComponentTracker interface. Derived classes + * must implement {@link #buildComponent(String)}, + * {@link #processPriorToRemoval(Object)}, and {@link #isComponentStale(Object)} * methods as appropriate for their component type. * * @param component type @@ -35,17 +36,20 @@ abstract public class AbstractComponentTracker implements ComponentTracker final public static long LINGERING_TIMEOUT = 10 * CoreConstants.MILLIS_IN_ONE_SECOND; /** - * The minimum amount of time that has to elapse between successive removal iterations. + * The minimum amount of time that has to elapse between successive removal + * iterations. */ final public static long WAIT_BETWEEN_SUCCESSIVE_REMOVAL_ITERATIONS = CoreConstants.MILLIS_IN_ONE_SECOND; protected int maxComponents = DEFAULT_MAX_COMPONENTS; protected long timeout = DEFAULT_TIMEOUT; - // an access ordered map. Least recently accessed element will be removed after a 'timeout' + // an access-ordered map. Least recently accessed element will be removed after + // a 'timeout' LinkedHashMap> liveMap = new LinkedHashMap>(32, .75f, ACCESS_ORDERED); - // an access ordered map. Least recently accessed element will be removed after LINGERING_TIMEOUT + // an access-ordered map. Least recently accessed element will be removed after + // LINGERING_TIMEOUT LinkedHashMap> lingerersMap = new LinkedHashMap>(16, .75f, ACCESS_ORDERED); long lastCheck = 0; @@ -65,8 +69,8 @@ abstract public class AbstractComponentTracker implements ComponentTracker abstract protected C buildComponent(String key); /** - * Components can declare themselves stale. Such components may be - * removed before they time out. + * Components can declare themselves stale. Such components may be removed + * before they time out. * * @param c * @return @@ -95,7 +99,9 @@ private Entry getFromEitherMap(String key) { /** * {@inheritDoc} * - *

Note that this method is synchronized.

+ *

+ * Note that this method is synchronized. + *

* * @param key {@inheritDoc} * @return {@inheritDoc} @@ -110,11 +116,13 @@ public synchronized C find(String key) { } /** - * {@inheritDoc} + * {@inheritDoc} * - *

Note that this method is atomic, i.e. synchronized.

+ *

+ * Note that this method is atomic, i.e. synchronized. + *

* - * @param key {@inheritDoc} + * @param key {@inheritDoc} * @param timestamp {@inheritDoc} * @return {@inheritDoc} */ @@ -144,8 +152,8 @@ public void endOfLife(String key) { } /** - * Clear (and detach) components which are stale. Components which have not - * been accessed for more than a user-specified duration are deemed stale. + * Clear (and detach) components which are stale. Components which have not been + * accessed for more than a user-specified duration are deemed stale. * * @param now */ @@ -169,7 +177,8 @@ private void removeStaleComponentsFromLingerersMap(long now) { genericStaleComponentRemover(lingerersMap, now, byLingering); } - private void genericStaleComponentRemover(LinkedHashMap> map, long now, RemovalPredicator removalPredicator) { + private void genericStaleComponentRemover(LinkedHashMap> map, long now, + RemovalPredicator removalPredicator) { Iterator>> iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry> mapEntry = iter.next(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachable.java b/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachable.java old mode 100644 new mode 100755 index f805d36384..fd997938fd --- a/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachable.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -40,7 +40,7 @@ public interface AppenderAttachable { /** * Returns true if the specified appender is in list of - * attached attached, false otherwise. + * attached, false otherwise. */ boolean isAttached(Appender appender); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java b/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java index 746b295bdf..021d999299 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/AppenderAttachableImpl.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,8 +19,8 @@ import ch.qos.logback.core.util.COWArrayList; /** - * A ReentrantReadWriteLock based implementation of the - * {@link AppenderAttachable} interface. + * A {@link COWArrayList} based implementation of the {@link AppenderAttachable} + * interface. * * @author Ceki Gülcü */ @@ -30,8 +30,8 @@ public class AppenderAttachableImpl implements AppenderAttachable { final private COWArrayList> appenderList = new COWArrayList>(new Appender[0]); /** - * Attach an appender. If the appender is already in the list in won't be - * added again. + * Attach an appender. If the appender is already in the list in won't be added + * again. */ public void addAppender(Appender newAppender) { if (newAppender == null) { @@ -67,8 +67,8 @@ public Iterator> iteratorForAppenders() { /** * Look for an attached appender named as name. * - *

Return the appender with that name if in the list. Return null - * otherwise. + *

+ * Return the appender with that name if in the list. Return null otherwise. */ public Appender getAppender(String name) { if (name == null) { @@ -109,11 +109,8 @@ public void detachAndStopAllAppenders() { appenderList.clear(); } - static final long START = System.currentTimeMillis(); - /** - * Remove the appender passed as parameter form the list of attached - * appenders. + * Remove the appender passed as parameter form the list of attached appenders. */ public boolean detachAppender(Appender appender) { if (appender == null) { @@ -133,7 +130,7 @@ public boolean detachAppender(String name) { return false; } boolean removed = false; - for (Appender a : appenderList) { + for (Appender a : appenderList.asTypedArray()) { if (name.equals((a).getName())) { removed = appenderList.remove(a); break; diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/BasicSequenceNumberGenerator.java b/logback-core/src/main/java/ch/qos/logback/core/spi/BasicSequenceNumberGenerator.java index 9be51cf187..d214071646 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/BasicSequenceNumberGenerator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/BasicSequenceNumberGenerator.java @@ -1,18 +1,33 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.spi; import java.util.concurrent.atomic.AtomicLong; /** * - * A very simple {@link SequenceNumberGenerator} based on an {@link AtomicLong} variable. + * A very simple {@link SequenceNumberGenerator} based on an {@link AtomicLong} + * variable. * * @author Ceki Gülcü * @since 1.3.0 */ -public class BasicSequenceNumberGenerator extends ContextAwareBase implements SequenceNumberGenerator { +public class BasicSequenceNumberGenerator extends ContextAwareBase implements SequenceNumberGenerator { private final AtomicLong atomicLong = new AtomicLong(); - + @Override public long nextSequenceNumber() { return atomicLong.incrementAndGet(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ComponentTracker.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ComponentTracker.java index 70321d02b2..6f72e67eb3 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/ComponentTracker.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ComponentTracker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -45,13 +45,14 @@ public interface ComponentTracker { /** * Returns the number of components tracked. + * * @return number of components */ int getComponentCount(); /** - * Find the component identified by 'key', without updating the timestamp. Returns null if no - * corresponding component could be found. + * Find the component identified by 'key', without updating the timestamp. + * Returns null if no corresponding component could be found. * * @param key * @return corresponding component, may be null @@ -59,8 +60,8 @@ public interface ComponentTracker { C find(String key); /** - * Get the component identified by 'key', updating its timestamp in the - * process. If the corresponding component could not be found, it is created. + * Get the component identified by 'key', updating its timestamp in the process. + * If the corresponding component could not be found, it is created. * * @param key * @param timestamp @@ -69,22 +70,27 @@ public interface ComponentTracker { C getOrCreate(String key, long timestamp); /** - * Remove components which are deemed stale. Components which have not - * been accessed for more than a user-specified duration are deemed stale. + * Remove components which are deemed stale. Components which have not been + * accessed for more than a user-specified duration are deemed stale. * - *

If the number of components exceeds, {@link #getComponentCount()}, - * components in excess will be removed.

+ *

+ * If the number of components exceeds, {@link #getComponentCount()}, components + * in excess will be removed. + *

* - *

Depending on the component type, components will be cleared or stopped - * (as appropriate) right before removal.

+ *

+ * Depending on the component type, components will be cleared or stopped (as + * appropriate) right before removal. + *

* - * @param now current time in milliseconds + * @param now current time in milliseconds */ void removeStaleComponents(long now); /** - * Mark component identified by 'key' as having reached its end-of-life. End-of-lifed - * components will linger for a few more seconds before being removed. + * Mark component identified by 'key' as having reached its end-of-life. + * End-of-lifed components will linger for a few more seconds before being + * removed. * * @param key */ @@ -92,7 +98,8 @@ public interface ComponentTracker { /** * Returns the collection of all components tracked by this instance. - * @return collection of components + * + * @return collection of components */ Collection allComponents(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEvent.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEvent.java new file mode 100644 index 0000000000..bc3cf28287 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEvent.java @@ -0,0 +1,90 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.spi; + +/** + * This class configuration events which can be of various types such as + * CHANGE_DETECTED, CONFIGURATION_STARTED and CONFIGURATION_ENDED. + * + * Configuration events can be accompanied by supplemental data which can be null. + * + * @since 1.3.6/1.4.6 + */ + +public class ConfigurationEvent { + + + public enum EventType { + CHANGE_DETECTOR_REGISTERED, + + CHANGE_DETECTOR_RUNNING, + CHANGE_DETECTED, + CONFIGURATION_STARTED, + PARTIAL_CONFIGURATION_ENDED_SUCCESSFULLY, + CONFIGURATION_ENDED_SUCCESSFULLY, + CONFIGURATION_ENDED_WITH_XML_PARSING_ERRORS; + } + final EventType eventType; + final Object data; + + /** + * Construct a ConfigurationEvent instance. + * + * @param eventType + * @param data supplemental data, can be null + */ + private ConfigurationEvent(EventType eventType, Object data) { + this.eventType = eventType; + this.data = data; + } + + static public ConfigurationEvent newConfigurationChangeDetectorRunningEvent(Object data) { + return new ConfigurationEvent(EventType.CHANGE_DETECTOR_RUNNING, data); + } + + static public ConfigurationEvent newConfigurationChangeDetectorRegisteredEvent(Object data) { + return new ConfigurationEvent(EventType.CHANGE_DETECTOR_REGISTERED, data); + } + static public ConfigurationEvent newConfigurationChangeDetectedEvent(Object data) { + return new ConfigurationEvent(EventType.CHANGE_DETECTED, data); + } + static public ConfigurationEvent newConfigurationStartedEvent(Object data) { + return new ConfigurationEvent(EventType.CONFIGURATION_STARTED, data); + } + static public ConfigurationEvent newPartialConfigurationEndedSuccessfullyEvent(Object data) { + return new ConfigurationEvent(EventType.PARTIAL_CONFIGURATION_ENDED_SUCCESSFULLY, data); + } + + + static public ConfigurationEvent newConfigurationEndedSuccessfullyEvent(Object data) { + return new ConfigurationEvent(EventType.CONFIGURATION_ENDED_SUCCESSFULLY, data); + } + static public ConfigurationEvent newConfigurationEndedWithXMLParsingErrorsEvent(Object data) { + return new ConfigurationEvent(EventType.CONFIGURATION_ENDED_WITH_XML_PARSING_ERRORS, data); + } + public EventType getEventType() { + return eventType; + } + + public Object getData() { + return data; + } + + + @Override + public String toString() { + return "ConfigurationEvent{" + "eventType=" + eventType + ", data=" + data + '}'; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEventListener.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEventListener.java new file mode 100644 index 0000000000..ae3c6d93e0 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ConfigurationEventListener.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.spi; + +/** + * A listener of {@link ConfigurationEvent configuration events}. + * + * @since 1.3.6/1.4.6 + */ +public interface ConfigurationEventListener { + + void listen(ConfigurationEvent configurationEvent); + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAware.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAware.java index 6854ad60a2..adcbf281db 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAware.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAware.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,8 @@ import ch.qos.logback.core.status.Status; /** - * An object which has a context and add methods for updating internal status messages. + * An object which has a context and add methods for updating internal status + * messages. */ public interface ContextAware { diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareBase.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareBase.java index 051d1f0507..1417dee492 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -59,8 +59,8 @@ public StatusManager getStatusManager() { } /** - * The declared origin of status messages. By default 'this'. Derived classes may override this - * method to declare other origin. + * The declared origin of status messages. By default 'this'. Derived classes + * may override this method to declare other origin. * * @return the declared origin, by default 'this' */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareImpl.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareImpl.java index 1ae51103a0..dc0c6c5342 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareImpl.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwareImpl.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwarePropertyContainer.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwarePropertyContainer.java new file mode 100644 index 0000000000..b3fbb60b84 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ContextAwarePropertyContainer.java @@ -0,0 +1,50 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.spi; + +import ch.qos.logback.core.joran.GenericXMLConfigurator; + +import java.util.function.Supplier; + +/** + * An interface extending both {@link PropertyContainer} and {@link ContextAware} + * + * @since 1.5.1 + */ +public interface ContextAwarePropertyContainer extends PropertyContainer, ContextAware { + + + /** + * This method is used tp perform variable substitution. + * + * @param input + * @return a new string after variable substitution, if any. + */ + String subst(String input); + + + /** + * Returns a supplier of {@link GenericXMLConfigurator} instance. The returned value may be null. + * + *

This method could/should have been part of a new interface. It is added here for reasons + * of commodity and not coherence.

+ * + * @return a supplier of {@link GenericXMLConfigurator} instance, may be null + * @since 1.5.11 + */ + default public Supplier getConfiguratorSupplier() { + return null; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java index 284ae1b668..f4b6322ea3 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/CyclicBufferTracker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,7 +18,7 @@ import java.util.*; /** - * CyclicBufferTracker tracks {@link CyclicBuffer} instances. + * CyclicBufferTracker tracks {@link CyclicBuffer} instances. * * @author Ceki Gülcü */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/DeferredProcessingAware.java b/logback-core/src/main/java/ch/qos/logback/core/spi/DeferredProcessingAware.java index 6e10640b97..558f5d0ae4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/DeferredProcessingAware.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/DeferredProcessingAware.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ErrorCodes.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ErrorCodes.java new file mode 100644 index 0000000000..2f14f8f9af --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ErrorCodes.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.spi; + +public class ErrorCodes { + + + public static final String EMPTY_MODEL_STACK = "Could not find valid configuration instructions. Exiting."; + public static final String MISSING_IF_EMPTY_MODEL_STACK = "Unexpected empty model stack. Have you omitted the part?"; + + public static final String PARENT_MODEL_NOT_FOUND = "Could not find parent model."; + public static final String SKIPPING_IMPLICIT_MODEL_ADDITION = " Will not add current implicit model as subModel."; + public static final String ROOT_LEVEL_CANNOT_BE_SET_TO_NULL = "The level for the ROOT logger cannot be set to NULL or INHERITED. Ignoring."; + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachable.java b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachable.java index 0358302845..41bf8407e4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachable.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,17 +31,16 @@ public interface FilterAttachable { void clearAllFilters(); /** - * Get a copy of all the filters contained within this FilterAttachable - * object. + * Get a copy of all the filters contained within this FilterAttachable object. * * @return all attached filters as a list */ List> getCopyOfAttachedFiltersList(); /** - * Loop through the filters in the chain. As soon as a filter decides on - * ACCEPT or DENY, then that value is returned. If all of the filters return - * NEUTRAL, then NEUTRAL is returned. + * Loop through the filters in the chain. As soon as a filter decides on ACCEPT + * or DENY, then that value is returned. If all of the filters return NEUTRAL, + * then NEUTRAL is returned. */ FilterReply getFilterChainDecision(E event); } diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachableImpl.java b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachableImpl.java index ad0557e161..2a25e79516 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachableImpl.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterAttachableImpl.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -44,9 +44,9 @@ public void clearAllFilters() { } /** - * Loop through the filters in the list. As soon as a filter decides on - * ACCEPT or DENY, then that value is returned. If all of the filters return - * NEUTRAL, then NEUTRAL is returned. + * Loop through the filters in the list. As soon as a filter decides on ACCEPT + * or DENY, then that value is returned. If all of the filters return NEUTRAL, + * then NEUTRAL is returned. */ public FilterReply getFilterChainDecision(E event) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterReply.java b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterReply.java index a6e944e9a3..21de1824d5 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/FilterReply.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/FilterReply.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,14 +15,13 @@ /** * - * This enum represents the possible replies that a filtering component - * in logback can return. It is used by implementations of both + * This enum represents the possible replies that a filtering component in + * logback can return. It is used by implementations of both * {@link ch.qos.logback.core.filter.Filter Filter} and * ch.qos.logback.classic.turbo.TurboFilter abstract classes. * * Based on the order that the FilterReply values are declared, - * FilterReply.ACCEPT.compareTo(FilterReply.DENY) will return - * a positive value. + * FilterReply.ACCEPT.compareTo(FilterReply.DENY) will return a positive value. * * @author Sébastien Pennec */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/LifeCycle.java b/logback-core/src/main/java/ch/qos/logback/core/spi/LifeCycle.java index 5f970bcabc..2397fa1a91 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/LifeCycle.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/LifeCycle.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,11 @@ */ package ch.qos.logback.core.spi; +/** + * Components supporting start/stop implement this interface. + * + * @since 1.0.0 + */ public interface LifeCycle { void start(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/LogbackLock.java b/logback-core/src/main/java/ch/qos/logback/core/spi/LogbackLock.java index 8789c27bea..23d9c53a7d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/LogbackLock.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/LogbackLock.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/PreSerializationTransformer.java b/logback-core/src/main/java/ch/qos/logback/core/spi/PreSerializationTransformer.java index 23b6ea047b..6b40be5441 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/PreSerializationTransformer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/PreSerializationTransformer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java index 521909b650..e32708654b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyContainer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,10 +14,21 @@ package ch.qos.logback.core.spi; import java.util.Map; +import java.util.Properties; public interface PropertyContainer { + + void addSubstitutionProperty(String key, String value); + String getProperty(String key); Map getCopyOfPropertyMap(); + + default void addSubstitutionProperties(Properties props) { + if (props == null) { + return; + } + props.forEach((k, v) -> addSubstitutionProperty((String) k, (String) v)); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java index 593f7619c0..520f512a10 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/PropertyDefiner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/ScanException.java b/logback-core/src/main/java/ch/qos/logback/core/spi/ScanException.java index c7b42471c1..7dd773f16b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/ScanException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/ScanException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/SequenceNumberGenerator.java b/logback-core/src/main/java/ch/qos/logback/core/spi/SequenceNumberGenerator.java index 73ab27e4cb..7887682467 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/SequenceNumberGenerator.java +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/SequenceNumberGenerator.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.spi; /** @@ -8,7 +22,6 @@ */ public interface SequenceNumberGenerator extends ContextAware { - long nextSequenceNumber(); - + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/spi/package.html b/logback-core/src/main/java/ch/qos/logback/core/spi/package.html index a8ee444aeb..95c48e399a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/spi/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/spi/package.html @@ -1,3 +1,17 @@ + + @@ -7,9 +21,9 @@ -

Contains core functionnalities of logback, such as +

Contains core functionalities of logback, such as {@link ch.qos.logback.core.spi.AppenderAttachable} and {@link ch.qos.logback.core.spi.LifeCycle}.

- \ No newline at end of file + diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/ErrorStatus.java b/logback-core/src/main/java/ch/qos/logback/core/status/ErrorStatus.java index f1c095c28c..cd1f746037 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/ErrorStatus.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/ErrorStatus.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/InfoStatus.java b/logback-core/src/main/java/ch/qos/logback/core/status/InfoStatus.java index a03ff6ea9b..48f538e8b9 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/InfoStatus.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/InfoStatus.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/NopStatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/status/NopStatusListener.java index 89f8ee6ddb..fd521110b5 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/NopStatusListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/NopStatusListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/OnConsoleStatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/status/OnConsoleStatusListener.java index 057f050a63..3113ec6168 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/status/OnConsoleStatusListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/OnConsoleStatusListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,8 +21,7 @@ * @author Ceki Gülcü */ public class OnConsoleStatusListener extends OnPrintStreamStatusListenerBase { - - + @Override protected PrintStream getPrintStream() { return System.out; diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/OnErrorConsoleStatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/status/OnErrorConsoleStatusListener.java index 1f0730dd37..bc63b81c69 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/OnErrorConsoleStatusListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/OnErrorConsoleStatusListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/OnFileStatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/status/OnFileStatusListener.java new file mode 100644 index 0000000000..8b9bf5d5c9 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/status/OnFileStatusListener.java @@ -0,0 +1,68 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.status; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +public class OnFileStatusListener extends OnPrintStreamStatusListenerBase { + + String filename; + PrintStream ps; + + @Override + public void start() { + if (filename == null) { + addInfo("File option not set. Defaulting to \"status.txt\""); + filename = "status.txt"; + } + + try { + FileOutputStream fos = new FileOutputStream(filename, true); + ps = new PrintStream(fos, true); + } catch (FileNotFoundException e) { + addError("Failed to open [" + filename + "]", e); + return; + } + + super.start(); + + } + + @Override + public void stop() { + if (!isStarted) { + return; + } + if (ps != null) + ps.close(); + super.stop(); + } + + public String getFilename() { + return filename; + } + + public void setFilename(String filename) { + this.filename = filename; + } + + @Override + protected PrintStream getPrintStream() { + return ps; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/OnPrintStreamStatusListenerBase.java b/logback-core/src/main/java/ch/qos/logback/core/status/OnPrintStreamStatusListenerBase.java index 108947e707..e4a5540516 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/OnPrintStreamStatusListenerBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/OnPrintStreamStatusListenerBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,7 +21,8 @@ import java.util.List; /** - * Print all new incoming status messages on the on the designated PrintStream. + * Print all new incoming status messages on the designated PrintStream. + * * @author Ceki Gülcü */ abstract public class OnPrintStreamStatusListenerBase extends ContextAwareBase implements StatusListener, LifeCycle { @@ -30,24 +31,28 @@ abstract public class OnPrintStreamStatusListenerBase extends ContextAwareBase i static final long DEFAULT_RETROSPECTIVE = 300; long retrospectiveThresold = DEFAULT_RETROSPECTIVE; - + + boolean resetResistant = false; + /** * The prefix to place before each status message + * * @since 1.1.10 */ String prefix; - + /** * The PrintStream used by derived classes + * * @return */ abstract protected PrintStream getPrintStream(); private void print(Status status) { StringBuilder sb = new StringBuilder(); - if(prefix != null) + if (prefix != null) sb.append(prefix); - + StatusPrinter.buildStr(sb, "", status); getPrintStream().print(sb); } @@ -68,7 +73,7 @@ private void retrospectivePrint() { StatusManager sm = context.getStatusManager(); List statusList = sm.getCopyOfStatusList(); for (Status status : statusList) { - long timestampOfStatusMesage = status.getDate(); + long timestampOfStatusMesage = status.getTimestamp(); if (isElapsedTimeLongerThanThreshold(now, timestampOfStatusMesage)) { print(status); } @@ -81,8 +86,8 @@ private boolean isElapsedTimeLongerThanThreshold(long now, long timestamp) { } /** - * Invoking the start method can cause the instance to print status messages created less than - * value of retrospectiveThresold. + * Invoking the start method can cause the instance to print status messages + * created less than value of retrospectiveThresold. */ public void start() { isStarted = true; @@ -98,7 +103,7 @@ public String getPrefix() { public void setPrefix(String prefix) { this.prefix = prefix; } - + public void setRetrospective(long retrospective) { this.retrospectiveThresold = retrospective; } @@ -115,4 +120,14 @@ public boolean isStarted() { return isStarted; } + @Override + public boolean isResetResistant() { + return resetResistant; + } + public void setResetResistant(boolean resetResistant) { + this.resetResistant = resetResistant; + } + + + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/Status.java b/logback-core/src/main/java/ch/qos/logback/core/status/Status.java index aa08f59e93..93e053448b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/Status.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/Status.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -31,7 +31,16 @@ public interface Status { Throwable getThrowable(); - Long getDate(); + /** + * @deprecated Use getTimestamp instead. + * @return the date as a long value + */ + @Deprecated + default Long getDate() { + return getTimestamp(); + } + + long getTimestamp(); boolean hasChildren(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java b/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java index f50a679afb..e2d46e6c19 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,6 +16,7 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Objects; abstract public class StatusBase implements Status { @@ -26,7 +27,7 @@ abstract public class StatusBase implements Status { final Object origin; List childrenList; Throwable throwable; - long date; + long timestamp; StatusBase(int level, String msg, Object origin) { this(level, msg, origin, null); @@ -37,7 +38,7 @@ abstract public class StatusBase implements Status { this.message = msg; this.origin = origin; this.throwable = t; - this.date = System.currentTimeMillis(); + this.timestamp = System.currentTimeMillis(); } public synchronized void add(Status child) { @@ -74,7 +75,7 @@ public int getLevel() { return level; } - // status messages are not supposed to contains cycles. + // status messages are not supposed to contain cycles. // cyclic status arrangements are like to cause deadlocks // when this method is called from different thread on // different status objects lying on the same cycle @@ -106,8 +107,8 @@ public Throwable getThrowable() { return throwable; } - public Long getDate() { - return date; + public long getTimestamp() { + return timestamp; } @Override @@ -142,31 +143,20 @@ public String toString() { } @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + level; - result = prime * result + ((message == null) ? 0 : message.hashCode()); - return result; + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + StatusBase that = (StatusBase) o; + return level == that.level && timestamp == that.timestamp && Objects.equals(message, that.message); } + + @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - final StatusBase other = (StatusBase) obj; - if (level != other.level) - return false; - if (message == null) { - if (other.message != null) - return false; - } else if (!message.equals(other.message)) - return false; - return true; + public int hashCode() { + return Objects.hash(level, message, timestamp); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/StatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/status/StatusListener.java index 4cbfa250aa..17517edb17 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/StatusListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/StatusListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,4 +21,14 @@ */ public interface StatusListener { void addStatusEvent(Status status); + + /** + * Reset resistant status listeners return true here. Default is false. + * + * @return whether this listener is reset resistant + * @since 1.3.6/1.4.6 + */ + default boolean isResetResistant() { + return false; + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/StatusListenerAsList.java b/logback-core/src/main/java/ch/qos/logback/core/status/StatusListenerAsList.java index ca2db35282..6b1ae8d2a2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/StatusListenerAsList.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/StatusListenerAsList.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/StatusManager.java b/logback-core/src/main/java/ch/qos/logback/core/status/StatusManager.java index 455c02a186..2274e66e23 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/status/StatusManager.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/StatusManager.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -52,20 +52,21 @@ public interface StatusManager { /** * Add a status listener. + * * @param listener */ /** - * Add a status listener. The StatusManager may decide to skip installation if an - * earlier instance was already installed. + * Add a status listener. The StatusManager may decide to skip installation if + * an earlier instance was already installed. * * @param listener * @return true if actually added, false if skipped */ boolean add(StatusListener listener); - /**); - * Remove a status listener. + /** + * ); Remove a status listener. * * @param listener */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/StatusUtil.java b/logback-core/src/main/java/ch/qos/logback/core/status/StatusUtil.java index cb77d02490..b93042883a 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/status/StatusUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/StatusUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,8 @@ import ch.qos.logback.core.CoreConstants; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -35,9 +35,9 @@ public StatusUtil(Context context) { } /** - * Returns true if the StatusManager associated with the context passed - * as parameter has one or more StatusListener instances registered. Returns - * false otherwise. + * Returns true if the StatusManager associated with the context passed as + * parameter has one or more StatusListener instances registered. Returns false + * otherwise. * * @param context * @return true if one or more StatusListeners registered, false otherwise @@ -57,7 +57,7 @@ static public boolean contextHasStatusListener(Context context) { static public List filterStatusListByTimeThreshold(List rawList, long threshold) { List filteredList = new ArrayList(); for (Status s : rawList) { - if (s.getDate() >= threshold) + if (s.getTimestamp() >= threshold) filteredList.add(s); } return filteredList; @@ -100,13 +100,13 @@ public int getHighestLevel(long threshold) { } public boolean isErrorFree(long threshold) { - return Status.ERROR > getHighestLevel(threshold); + return getHighestLevel(threshold) < Status.ERROR; } public boolean isWarningOrErrorFree(long threshold) { return Status.WARN > getHighestLevel(threshold); } - + public boolean containsMatch(long threshold, int level, String regex) { List filteredList = filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), threshold); Pattern p = Pattern.compile(regex); @@ -140,6 +140,18 @@ public boolean containsMatch(String regex) { return false; } + + public int levelCount(int level, long threshold) { + List filteredList = filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), threshold); + + int count = 0; + for (Status status : filteredList) { + if (status.getLevel() == level) + count++; + } + return count; + } + public int matchCount(String regex) { int count = 0; Pattern p = Pattern.compile(regex); @@ -154,13 +166,19 @@ public int matchCount(String regex) { } public boolean containsException(Class exceptionType) { - Iterator stati = sm.getCopyOfStatusList().iterator(); - while (stati.hasNext()) { - Status status = stati.next(); + return containsException(exceptionType, null); + } + + public boolean containsException(Class exceptionType, String msgRegex) { + for (Status status : sm.getCopyOfStatusList()) { Throwable t = status.getThrowable(); while (t != null) { if (t.getClass().getName().equals(exceptionType.getName())) { - return true; + if (msgRegex == null) { + return true; + } else if (checkRegexMatch(t.getMessage(), msgRegex)) { + return true; + } } t = t.getCause(); } @@ -168,6 +186,13 @@ public boolean containsException(Class exceptionType) { return false; } + private boolean checkRegexMatch(String message, String msgRegex) { + Pattern p = Pattern.compile(msgRegex); + Matcher matcher = p.matcher(message); + return matcher.lookingAt(); + } + + /** * Return the time of last reset. -1 if last reset time could not be found * @@ -182,10 +207,24 @@ public long timeOfLastReset() { for (int i = len - 1; i >= 0; i--) { Status s = statusList.get(i); if (CoreConstants.RESET_MSG_PREFIX.equals(s.getMessage())) { - return s.getDate(); + return s.getTimestamp(); } } return -1; } + public static String diff(Status left, Status right) { + StringBuilder sb = new StringBuilder(); + if( left.getLevel() != right.getLevel()) { + sb.append(" left.level ").append(left.getLevel()).append(" != right.level ").append(right.getLevel()); + } + if( left.getTimestamp() != right.getTimestamp()) { + sb.append(" left.timestamp ").append(left.getTimestamp()).append(" != right.timestamp ").append(right.getTimestamp()); + } + if( !Objects.equals(left.getMessage(), right.getMessage())) { + sb.append(" left.message ").append(left.getMessage()).append(" != right.message ").append(right.getMessage()); + } + + return sb.toString(); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/ViewStatusMessagesServletBase.java b/logback-core/src/main/java/ch/qos/logback/core/status/ViewStatusMessagesServletBase.java index e1c483b1ab..3ade47df66 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/ViewStatusMessagesServletBase.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/ViewStatusMessagesServletBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,10 +20,10 @@ import java.io.StringWriter; import java.util.List; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.helpers.Transform; @@ -62,7 +62,7 @@ protected void service(HttpServletRequest req, HttpServletResponse resp) throws output.append(""); output.append("\r\n"); - if (CLEAR.equalsIgnoreCase(req.getParameter(SUBMIT))) { + if ("POST".equals(req.getMethod()) && CLEAR.equalsIgnoreCase(req.getParameter(SUBMIT))) { sm.clear(); sm.add(new InfoStatus("Cleared all status messages", this)); } @@ -160,7 +160,7 @@ private void printStatus(StringBuilder buf, Status s) { trClass = "odd"; } buf.append(" \r\n"); - String dateStr = SDF.format(s.getDate()); + String dateStr = SDF.format(s.getTimestamp()); buf.append(" ").append(dateStr).append("\r\n"); buf.append(" ").append(statusLevelAsString(s)).append("\r\n"); buf.append(" ").append(abbreviatedOrigin(s)).append("\r\n"); diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/WarnStatus.java b/logback-core/src/main/java/ch/qos/logback/core/status/WarnStatus.java index a9d7ab23e2..0c5f9996d2 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/WarnStatus.java +++ b/logback-core/src/main/java/ch/qos/logback/core/status/WarnStatus.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/status/package.html b/logback-core/src/main/java/ch/qos/logback/core/status/package.html index cf1c5c5c89..e7486edc49 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/status/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/status/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/ch/qos/logback/core/subst/Node.java b/logback-core/src/main/java/ch/qos/logback/core/subst/Node.java index ba5815c9d9..ab4e6eacfd 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/subst/Node.java +++ b/logback-core/src/main/java/ch/qos/logback/core/subst/Node.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/subst/NodeToStringTransformer.java b/logback-core/src/main/java/ch/qos/logback/core/subst/NodeToStringTransformer.java index e805b7ac68..e3c768292a 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/subst/NodeToStringTransformer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/subst/NodeToStringTransformer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,6 +16,7 @@ import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.PropertyContainer; import ch.qos.logback.core.spi.ScanException; +import ch.qos.logback.core.subst.Node.Type; import ch.qos.logback.core.util.OptionHelper; import java.util.List; @@ -28,11 +29,13 @@ */ public class NodeToStringTransformer { + public static final String CIRCULAR_VARIABLE_REFERENCE_DETECTED = "Circular variable reference detected while parsing input ["; final Node node; final PropertyContainer propertyContainer0; final PropertyContainer propertyContainer1; - public NodeToStringTransformer(Node node, PropertyContainer propertyContainer0, PropertyContainer propertyContainer1) { + public NodeToStringTransformer(Node node, PropertyContainer propertyContainer0, + PropertyContainer propertyContainer1) { this.node = node; this.propertyContainer0 = propertyContainer0; this.propertyContainer1 = propertyContainer1; @@ -42,7 +45,8 @@ public NodeToStringTransformer(Node node, PropertyContainer propertyContainer0) this(node, propertyContainer0, null); } - public static String substituteVariable(String input, PropertyContainer pc0, PropertyContainer pc1) throws ScanException { + public static String substituteVariable(String input, PropertyContainer pc0, PropertyContainer pc1) + throws ScanException { Node node = tokenizeAndParseString(input); NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, pc0, pc1); return nodeToStringTransformer.transform(); @@ -61,7 +65,8 @@ public String transform() throws ScanException { return stringBuilder.toString(); } - private void compileNode(Node inputNode, StringBuilder stringBuilder, Stack cycleCheckStack) throws ScanException { + private void compileNode(Node inputNode, StringBuilder stringBuilder, Stack cycleCheckStack) + throws ScanException { Node n = inputNode; while (n != null) { switch (n.type) { @@ -92,6 +97,7 @@ private void handleVariable(Node n, StringBuilder stringBuilder, Stack cyc String key = keyBuffer.toString(); String value = lookupKey(key); + // empty values are considered valid if (value != null) { Node innerNode = tokenizeAndParseString(value); compileNode(innerNode, stringBuilder, cycleCheckStack); @@ -99,6 +105,7 @@ private void handleVariable(Node n, StringBuilder stringBuilder, Stack cyc return; } + // empty default literal is a valid value if (n.defaultPart == null) { stringBuilder.append(key + CoreConstants.UNDEFINED_PROPERTY_SUFFIX); cycleCheckStack.pop(); @@ -141,12 +148,23 @@ private void handleLiteral(Node n, StringBuilder stringBuilder) { } private String variableNodeValue(Node variableNode) { - Node literalPayload = (Node) variableNode.payload; - return (String) literalPayload.payload; + Node payload = (Node) variableNode.payload; + if(payload == null) { + return CoreConstants.EMPTY_STRING; + } + + if(payload.type == Type.LITERAL) { + return (String) payload.payload; + } + + if(payload.type == Type.VARIABLE) { + return " ? " + variableNodeValue(payload); + } + throw new IllegalStateException("unreachable code"); } private String constructRecursionErrorMessage(Stack recursionNodes) { - StringBuilder errorBuilder = new StringBuilder("Circular variable reference detected while parsing input ["); + StringBuilder errorBuilder = new StringBuilder(CIRCULAR_VARIABLE_REFERENCE_DETECTED); for (Node stackNode : recursionNodes) { errorBuilder.append("${").append(variableNodeValue(stackNode)).append("}"); @@ -159,9 +177,10 @@ private String constructRecursionErrorMessage(Stack recursionNodes) { } /** - * Determine if a node has already been visited already by checking the cycleDetectionStack - * for it's existence. This method is used -- rather than Stack.contains() -- because - * we want to ignore the Node's 'next' attribute when comparing for equality. + * Determine if a node has already been visited already by checking the + * cycleDetectionStack for its existence. This method is used -- rather than + * Stack.contains() -- because we want to ignore the Node's 'next' attribute + * when comparing for equality. */ private boolean haveVisitedNodeAlready(Node node, Stack cycleDetectionStack) { for (Node cycleNode : cycleDetectionStack) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/subst/Parser.java b/logback-core/src/main/java/ch/qos/logback/core/subst/Parser.java index 5c935c6f19..ce65ca5a64 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/subst/Parser.java +++ b/logback-core/src/main/java/ch/qos/logback/core/subst/Parser.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,13 +27,20 @@ // V = E|E :- E // = E(':-'E|~) +// new definition + +// V = E | E :- Eopt +// = E (~| :- Eopt) + /** * Parse a token list returning a node chain. * * @author Ceki Gulcu */ public class Parser { - + + static final public String EXPECTING_DATA_AFTER_LEFT_ACCOLADE = "Expecting at least a literal between left accolade and ':-'"; + final List tokenList; int pointer = 0; @@ -72,7 +79,9 @@ private Node Eopt() throws ScanException { // T = LITERAL | '${' V '}' private Node T() throws ScanException { Token t = peekAtCurentToken(); - + if(t == null) { + return null; + } switch (t.type) { case LITERAL: advanceTokenPointer(); @@ -103,26 +112,34 @@ private Node makeNewLiteralNode(String s) { return new Node(Node.Type.LITERAL, s); } - // V = E(':='E|~) + // V = E (~| :- Eopt) private Node V() throws ScanException { Node e = E(); Node variable = new Node(Node.Type.VARIABLE, e); Token t = peekAtCurentToken(); if (isDefaultToken(t)) { advanceTokenPointer(); - Node def = E(); - variable.defaultPart = def; + Node def = Eopt(); + if(def != null) { + variable.defaultPart = def; + } else { + variable.defaultPart = makeNewLiteralNode(CoreConstants.EMPTY_STRING); + } } return variable; } - // C = E(':='E|~) + + // C = E(':-'E|~) private Node C() throws ScanException { Node e0 = E(); Token t = peekAtCurentToken(); if (isDefaultToken(t)) { advanceTokenPointer(); Node literal = makeNewLiteralNode(CoreConstants.DEFAULT_VALUE_SEPARATOR); + if(e0 == null) { + throw new ScanException(EXPECTING_DATA_AFTER_LEFT_ACCOLADE); + } e0.append(literal); Node e1 = E(); e0.append(e1); diff --git a/logback-core/src/main/java/ch/qos/logback/core/subst/Token.java b/logback-core/src/main/java/ch/qos/logback/core/subst/Token.java index 42767bd01e..2cdb8eac00 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/subst/Token.java +++ b/logback-core/src/main/java/ch/qos/logback/core/subst/Token.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/subst/Tokenizer.java b/logback-core/src/main/java/ch/qos/logback/core/subst/Tokenizer.java index 1f31dc3a95..30b6b6fc2b 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/subst/Tokenizer.java +++ b/logback-core/src/main/java/ch/qos/logback/core/subst/Tokenizer.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/contention/AbstractMultiThreadedHarness.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/AbstractMultiThreadedHarness.java similarity index 80% rename from logback-core/src/test/java/ch/qos/logback/core/contention/AbstractMultiThreadedHarness.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/AbstractMultiThreadedHarness.java index 3e2e389266..1eea39abe4 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/contention/AbstractMultiThreadedHarness.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/AbstractMultiThreadedHarness.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,19 +11,17 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.contention; +package ch.qos.logback.core.testUtil; abstract public class AbstractMultiThreadedHarness { - RunnableWithCounterAndDone[] runnableArray; - abstract public void waitUntilEndCondition() throws InterruptedException; public void execute(RunnableWithCounterAndDone[] runnableArray) throws InterruptedException { - this.runnableArray = runnableArray; Thread[] threadArray = new Thread[runnableArray.length]; for (int i = 0; i < runnableArray.length; i++) { + System.out.println("Starting "+runnableArray[i]); threadArray[i] = new Thread(runnableArray[i], "Harness[" + i + "]"); } for (Thread t : threadArray) { diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/CoreTestConstants.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/CoreTestConstants.java old mode 100755 new mode 100644 similarity index 83% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/CoreTestConstants.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/CoreTestConstants.java index 6e01871843..9418d93c12 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/CoreTestConstants.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/CoreTestConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -29,4 +29,5 @@ public class CoreTestConstants { public static final String BASH_PATH_ON_LINUX = "bash"; public static final String SLOW_JENKINS = "slowJenkins"; + public static final String JAVA_NAMING_FACTORY_INITIAL = "java.naming.factory.initial"; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/DelayingListAppender.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/DelayingListAppender.java similarity index 87% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/DelayingListAppender.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/DelayingListAppender.java index 527fd28af8..6b788a4e6e 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/DelayingListAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/DelayingListAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -35,4 +35,3 @@ public void append(E e) { super.append(e); } } - diff --git a/logback-core/src/test/java/ch/qos/logback/core/encoder/DummyEncoder.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/DummyEncoder.java similarity index 88% rename from logback-core/src/test/java/ch/qos/logback/core/encoder/DummyEncoder.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/DummyEncoder.java index 4e0ab9f999..3449a4c3d5 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/encoder/DummyEncoder.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/DummyEncoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,11 +11,12 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.encoder; +package ch.qos.logback.core.testUtil; import java.nio.charset.Charset; import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.encoder.EncoderBase; public class DummyEncoder extends EncoderBase { @@ -40,7 +41,7 @@ public DummyEncoder(String val) { this.val = val; } - public byte[] encode(E event) { + public byte[] encode(E event) { return encodeString(val); } @@ -74,7 +75,7 @@ public byte[] headerBytes() { return header(); } - public byte[] footerBytes() { + public byte[] footerBytes() { if (fileFooter == null) { return null; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java similarity index 75% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java index 2989cda09b..98c63db050 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/EnvUtilForTests.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,6 +18,23 @@ public class EnvUtilForTests { + static String GITHUB_HOME = "/home/runner"; + + static String LOCAL_REPOSITORY_PREFIX = GITHUB_HOME; + + static public boolean isGithubAction() { + String userHome = System.getProperty("user.home"); + String localRepository = System.getProperty("localRepository"); + + if (GITHUB_HOME.equals(userHome)) + return true; + + if (localRepository != null && localRepository.startsWith(LOCAL_REPOSITORY_PREFIX)) + return true; + + return false; + } + static public boolean isWindows() { return System.getProperty("os.name").indexOf("Windows") != -1; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/testUtil/FileTestUtil.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/FileTestUtil.java new file mode 100644 index 0000000000..1263c7ee9b --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/FileTestUtil.java @@ -0,0 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.testUtil; + + +import java.io.File; + +/** + * @author Ceki Gülcü + */ +public class FileTestUtil { + + public static void makeTestOutputDir() { + File target = new File(CoreTestConstants.TARGET_DIR); + if (target.exists() && target.isDirectory()) { + File testoutput = new File(CoreTestConstants.OUTPUT_DIR_PREFIX); + if (!testoutput.exists()) { + boolean result = testoutput.mkdir(); + if(!result) + throw new IllegalStateException("Failed to create "+testoutput); + } + } else { + throw new IllegalStateException(CoreTestConstants.TARGET_DIR + " does not exist"); + } + } +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java similarity index 95% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java index 82f2f36164..a892b14a02 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/FileToBufferUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContext.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContext.java similarity index 84% rename from logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContext.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContext.java index e478da6c34..6e0cad893e 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContext.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.util; +package ch.qos.logback.core.testUtil; import java.util.HashMap; import java.util.Map; diff --git a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContextFactory.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContextFactory.java similarity index 87% rename from logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContextFactory.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContextFactory.java index 7c3b0e124a..e5dc4e3ab7 100644 --- a/logback-classic/src/test/java/ch/qos/logback/classic/util/MockInitialContextFactory.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/MockInitialContextFactory.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.classic.util; +package ch.qos.logback.core.testUtil; import java.util.Hashtable; diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/NPEAppender.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/NPEAppender.java similarity index 82% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/NPEAppender.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/NPEAppender.java index defa6f9467..a6b6c2f067 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/NPEAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/NPEAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/RandomUtil.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/RandomUtil.java similarity index 86% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/RandomUtil.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/RandomUtil.java index 9bdff9d200..117d97dff1 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/RandomUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/RandomUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/contention/RunnableWithCounterAndDone.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/RunnableWithCounterAndDone.java similarity index 82% rename from logback-core/src/test/java/ch/qos/logback/core/contention/RunnableWithCounterAndDone.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/RunnableWithCounterAndDone.java index 10bab5aba0..cbc2897207 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/contention/RunnableWithCounterAndDone.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/RunnableWithCounterAndDone.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.contention; +package ch.qos.logback.core.testUtil; /** * A runnable with 'done' and 'counter' fields. diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/StringListAppender.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/StringListAppender.java similarity index 90% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/StringListAppender.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/StringListAppender.java index 0a6640abbb..333ba6a52b 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/StringListAppender.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/StringListAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/TeeOutputStream.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/TeeOutputStream.java similarity index 91% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/TeeOutputStream.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/TeeOutputStream.java index d7a9291b88..10814ae15f 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/TeeOutputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/TeeOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java similarity index 89% rename from logback-core/src/test/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java index bcc076e111..fb100b352a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/TrivialStatusListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/XTeeOutputStream.java b/logback-core/src/main/java/ch/qos/logback/core/testUtil/XTeeOutputStream.java similarity index 83% rename from logback-core/src/test/java/ch/qos/logback/core/appender/XTeeOutputStream.java rename to logback-core/src/main/java/ch/qos/logback/core/testUtil/XTeeOutputStream.java index c06acfe242..8ebd10e898 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/XTeeOutputStream.java +++ b/logback-core/src/main/java/ch/qos/logback/core/testUtil/XTeeOutputStream.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,7 +11,7 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core.appender; +package ch.qos.logback.core.testUtil; import java.io.IOException; import java.io.PrintStream; diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/AggregationType.java b/logback-core/src/main/java/ch/qos/logback/core/util/AggregationType.java index 4ce5021b0a..615141bf22 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/AggregationType.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/AggregationType.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,13 +14,12 @@ package ch.qos.logback.core.util; /** - * AggregationType classifies how one object is contained within - * another object. + * AggregationType classifies how one object is contained within another object. * * * - * See also http://en.wikipedia.org/wiki/Class_diagram - * and http://en.wikipedia.org/wiki/Object_composition + * See also http://en.wikipedia.org/wiki/Class_diagram and + * http://en.wikipedia.org/wiki/Object_composition * * @author Ceki Gulcu */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/COWArrayList.java b/logback-core/src/main/java/ch/qos/logback/core/util/COWArrayList.java index 693a3ce047..395d428270 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/COWArrayList.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/COWArrayList.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.util; import java.util.Collection; @@ -8,11 +22,17 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * A GC-free lock-free thread-safe implementation of the {@link List} interface for use cases where iterations over the list vastly out-number modifications on the list. + * A GC-free lock-free thread-safe implementation of the {@link List} interface + * for use cases where iterations over the list vastly out-number modifications + * on the list. * - *

Underneath, it wraps an instance of {@link CopyOnWriteArrayList} and exposes a copy of the array used by that instance. + *

+ * Underneath, it wraps an instance of {@link CopyOnWriteArrayList} and exposes + * a copy of the array used by that instance. * - *

Typical use:

+ *

+ * Typical use: + *

* *
  *   COWArrayList<Integer> list = new COWArrayList(new Integer[0]);
@@ -27,21 +47,28 @@
  *   for(int i = 0; i < intArray.length; i++) {
  *     sum != intArray[i];
  *   }
- * 
- * - *

If the list is not modified, then repetitive calls to {@link #asTypedArray()}, {@link #toArray()} and - * {@link #toArray(Object[])} are guaranteed to be GC-free. Note that iterating over the list using - * {@link COWArrayList#iterator()} and {@link COWArrayList#listIterator()} are not GC-free.

- * + * + * + *

+ * If the list is not modified, then repetitive calls to + * {@link #asTypedArray()}, {@link #toArray()} and {@link #toArray(Object[])} + * are guaranteed to be GC-free. Note that iterating over the list using + * {@link COWArrayList#iterator()} and {@link COWArrayList#listIterator()} are + * not GC-free. + *

+ * * @author Ceki Gulcu * @since 1.1.10 */ public class COWArrayList implements List { - // Implementation note: markAsStale() should always be invoked *after* list-modifying actions. - // If not, readers might get a stale array until the next write. The potential problem is nicely - // explained by Rob Eden. See https://github.com/qos-ch/logback/commit/32a2047a1adfc#commitcomment-20791176 - + // Implementation note: markAsStale() should always be invoked *after* + // list-modifying actions. + // If not, readers might get a stale array until the next write. The potential + // problem is nicely + // explained by Rob Eden. See + // https://github.com/qos-ch/logback/commit/32a2047a1adfc#commitcomment-20791176 + AtomicBoolean fresh = new AtomicBoolean(false); CopyOnWriteArrayList underlyingList = new CopyOnWriteArrayList(); E[] ourCopy; @@ -100,9 +127,9 @@ public T[] toArray(T[] a) { } /** - * Return an array of type E[]. The returned array is intended to be iterated over. - * If the list is modified, subsequent calls to this method will return different/modified - * array instances. + * Return an array of type E[]. The returned array is intended to be iterated + * over. If the list is modified, subsequent calls to this method will return + * different/modified array instances. * * @return */ @@ -110,11 +137,11 @@ public E[] asTypedArray() { refreshCopyIfNecessary(); return ourCopy; } - + private void markAsStale() { fresh.set(false); } - + public void addIfAbsent(E e) { underlyingList.addIfAbsent(e); markAsStale(); @@ -141,36 +168,36 @@ public boolean containsAll(Collection c) { @Override public boolean addAll(Collection c) { - boolean result = underlyingList.addAll(c); markAsStale(); + boolean result = underlyingList.addAll(c); return result; } @Override public boolean addAll(int index, Collection col) { - boolean result = underlyingList.addAll(index, col); markAsStale(); + boolean result = underlyingList.addAll(index, col); return result; } @Override public boolean removeAll(Collection col) { - boolean result = underlyingList.removeAll(col); markAsStale(); + boolean result = underlyingList.removeAll(col); return result; } @Override public boolean retainAll(Collection col) { - boolean result = underlyingList.retainAll(col); markAsStale(); + boolean result = underlyingList.retainAll(col); return result; } @Override public void clear() { - underlyingList.clear(); markAsStale(); + underlyingList.clear(); } @Override @@ -181,21 +208,21 @@ public E get(int index) { @Override public E set(int index, E element) { - E e = underlyingList.set(index, element); markAsStale(); + E e = underlyingList.set(index, element); return e; } @Override public void add(int index, E element) { - underlyingList.add(index, element); markAsStale(); + underlyingList.add(index, element); } @Override public E remove(int index) { - E e = (E) underlyingList.remove(index); markAsStale(); + E e = (E) underlyingList.remove(index); return e; } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/CachingDateFormatter.java b/logback-core/src/main/java/ch/qos/logback/core/util/CachingDateFormatter.java index 841026fef9..e226f8adb5 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/CachingDateFormatter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/CachingDateFormatter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,45 +13,70 @@ */ package ch.qos.logback.core.util; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import java.util.concurrent.atomic.AtomicReference; /** - * A synchronized implementation of SimpleDateFormat which uses caching internally. - * + * A CAS implementation of DateTimeFormatter (previously SimpleDateFormat) which + * caches results for the duration of a millisecond. + * * @author Ceki Gülcü * @since 0.9.29 */ public class CachingDateFormatter { - long lastTimestamp = -1; - String cachedStr = null; - final SimpleDateFormat sdf; + final DateTimeFormatter dtf; + final ZoneId zoneId; + final AtomicReference atomicReference; + + static class CacheTuple { + final long lastTimestamp; + final String cachedStr; + + public CacheTuple(long lastTimestamp, String cachedStr) { + super(); + this.lastTimestamp = lastTimestamp; + this.cachedStr = cachedStr; + } + } public CachingDateFormatter(String pattern) { - sdf = new SimpleDateFormat(pattern); + this(pattern, null); } - public final String format(long now) { + public CachingDateFormatter(String pattern, ZoneId aZoneId) { + this(pattern, aZoneId, null); + } + + public CachingDateFormatter(String pattern, ZoneId aZoneId, Locale aLocale) { + if (aZoneId == null) { + this.zoneId = ZoneId.systemDefault(); + } else { + this.zoneId = aZoneId; + } + Locale locale = aLocale != null ? aLocale : Locale.getDefault(); - // SimpleDateFormat is not thread safe. + dtf = DateTimeFormatter.ofPattern(pattern).withZone(this.zoneId).withLocale(locale); + CacheTuple cacheTuple = new CacheTuple(-1, null); + this.atomicReference = new AtomicReference<>(cacheTuple); + } - // See also the discussion in http://jira.qos.ch/browse/LBCLASSIC-36 - // DateFormattingThreadedThroughputCalculator and SelectiveDateFormattingRunnable - // are also noteworthy + public final String format(long now) { + CacheTuple localCacheTuple = atomicReference.get(); + CacheTuple oldCacheTuple = localCacheTuple; - // The now == lastTimestamp guard minimizes synchronization - synchronized (this) { - if (now != lastTimestamp) { - lastTimestamp = now; - cachedStr = sdf.format(new Date(now)); - } - return cachedStr; + if (now != localCacheTuple.lastTimestamp) { + Instant instant = Instant.ofEpochMilli(now); + String result = dtf.format(instant); + localCacheTuple = new CacheTuple(now, result); + // allow a single thread to update the cache reference + atomicReference.compareAndSet(oldCacheTuple, localCacheTuple); } + return localCacheTuple.cachedStr; } - public void setTimeZone(TimeZone tz) { - sdf.setTimeZone(tz); - } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceState.java b/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceState.java index c5dbe3fe93..dabec25b09 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceState.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceState.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,8 +14,9 @@ package ch.qos.logback.core.util; /** - * Used to keep state about a char sequence. - * @author Ceki Gulcu + * Used to keep state about a char sequence. + * + * @author Ceki Gulcu */ class CharSequenceState { final char c; diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceToRegexMapper.java b/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceToRegexMapper.java index 66ccd2207d..5428ef78a6 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceToRegexMapper.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/CharSequenceToRegexMapper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,8 +16,8 @@ import java.text.DateFormatSymbols; /** - * This class supports mapping character sequences to - * regular expressions as appropriate for SimpleDateFormatter. + * This class supports mapping character sequences to regular expressions as + * appropriate for SimpleDateFormatter. * * @author Ceki */ @@ -115,7 +115,8 @@ static int[] findMinMaxLengthsInSymbols(String[] symbols) { int max = 0; for (String symbol : symbols) { int len = symbol.length(); - // some SENTINEL values can be empty strings, the month at index 12 or the weekday at index 0 + // some SENTINEL values can be empty strings, the month at index 12 or the + // weekday at index 0 if (len == 0) continue; min = Math.min(min, len); diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/CloseUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/CloseUtil.java index 8c0f91b569..c4996608c1 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/CloseUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/CloseUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,6 +27,7 @@ public class CloseUtil { /** * Closes a closeable while suppressing any {@code IOException} that occurs. + * * @param closeable the socket to close */ public static void closeQuietly(Closeable closeable) { @@ -41,6 +42,7 @@ public static void closeQuietly(Closeable closeable) { /** * Closes a socket while suppressing any {@code IOException} that occurs. + * * @param socket the socket to close */ public static void closeQuietly(Socket socket) { @@ -54,8 +56,8 @@ public static void closeQuietly(Socket socket) { } /** - * Closes a server socket while suppressing any {@code IOException} that - * occurs. + * Closes a server socket while suppressing any {@code IOException} that occurs. + * * @param serverSocket the socket to close */ public static void closeQuietly(ServerSocket serverSocket) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/ContentTypeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/ContentTypeUtil.java index 2e06573649..8e16a98589 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/ContentTypeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/ContentTypeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/ContextUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/ContextUtil.java index 68caad7cf3..5ed44cfe26 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/ContextUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/ContextUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,56 +13,40 @@ */ package ch.qos.logback.core.util; -import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP; -import static ch.qos.logback.core.CoreConstants.RFA_FILENAME_PATTERN_COLLISION_MAP; - -import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Properties; import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.hook.ShutdownHook; import ch.qos.logback.core.rolling.helper.FileNamePattern; import ch.qos.logback.core.spi.ContextAwareBase; public class ContextUtil extends ContextAwareBase { + static final String GROOVY_RUNTIME_PACKAGE = "org.codehaus.groovy.runtime"; + // static final String SYSTEM_LOGGER_FQCN = "java.lang.System$Logger"; + public ContextUtil(Context context) { setContext(context); } - public void addProperties(Properties props) { if (props == null) { return; } - @SuppressWarnings("rawtypes") - Iterator i = props.keySet().iterator(); - while (i.hasNext()) { - String key = (String) i.next(); - context.putProperty(key, props.getProperty(key)); + + for (Entry e : props.entrySet()) { + String key = (String) e.getKey(); + context.putProperty(key, (String) e.getValue()); } - } - public static Map getFilenameCollisionMap(Context context) { - if (context == null) - return null; - @SuppressWarnings("unchecked") - Map map = (Map) context.getObject(FA_FILENAME_COLLISION_MAP); - return map; } - public static Map getFilenamePatternCollisionMap(Context context) { - if (context == null) - return null; - @SuppressWarnings("unchecked") - Map map = (Map) context.getObject(RFA_FILENAME_PATTERN_COLLISION_MAP); - return map; - } - public void addGroovyPackages(List frameworkPackages) { - // addFrameworkPackage(frameworkPackages, "groovy.lang"); - addFrameworkPackage(frameworkPackages, "org.codehaus.groovy.runtime"); + addFrameworkPackage(frameworkPackages, GROOVY_RUNTIME_PACKAGE); } public void addFrameworkPackage(List frameworkPackages, String packageName) { @@ -71,4 +55,27 @@ public void addFrameworkPackage(List frameworkPackages, String packageNa } } + /** + * Add a shutdown hook thread with the JVM runtime. + * + * If a previous shutdown hook thread was registered, it is replaced. + * @param hook + * @since 1.5.7 + */ + public void addOrReplaceShutdownHook(ShutdownHook hook) { + Runtime runtime = Runtime.getRuntime(); + + Thread oldShutdownHookThread = (Thread) context.getObject(CoreConstants.SHUTDOWN_HOOK_THREAD); + if(oldShutdownHookThread != null) { + addInfo("Removing old shutdown hook from JVM runtime"); + runtime.removeShutdownHook(oldShutdownHookThread); + } + + Thread hookThread = new Thread(hook, "Logback shutdown hook [" + context.getName() + "]"); + addInfo("Registering shutdown hook with JVM runtime."); + context.putObject(CoreConstants.SHUTDOWN_HOOK_THREAD, hookThread); + runtime.addShutdownHook(hookThread); + + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/CoreVersionUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/CoreVersionUtil.java new file mode 100644 index 0000000000..625db826a2 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/CoreVersionUtil.java @@ -0,0 +1,40 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import ch.qos.logback.core.CoreConstants; + +/** + * Utility class for retrieving version information of the "logback-core" module. + * + * @since 1.5.26 + */ +public class CoreVersionUtil { + /** + * Retrieves the version of the "logback-core" module using a properties file + * associated with the module. + * + *

The method locates and reads a properties file named "logback-core-version.properties" + * in the package of the {@code CoreConstants.class}. It then extracts the version + * information using the key "logback-core-version". + *

+ * + * @return the version of the "logback-core" module as a string, or null if the version cannot be determined + * @since 1.5.26 + */ + static public String getCoreVersionBySelfDeclaredProperties() { + return VersionUtil.getArtifactVersionBySelfDeclaredProperties(CoreConstants.class, "logback-core"); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/DatePatternToRegexUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/DatePatternToRegexUtil.java index b3d11df350..b362ac7e7f 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/DatePatternToRegexUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/DatePatternToRegexUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/DefaultInvocationGate.java b/logback-core/src/main/java/ch/qos/logback/core/util/DefaultInvocationGate.java index bc03296664..38c313e61e 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/DefaultInvocationGate.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/DefaultInvocationGate.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,7 +14,8 @@ package ch.qos.logback.core.util; /** - * This class serves as a gateway for invocations of a "costly" operation on a critical execution path. + * This class serves as a gateway for invocations of a "costly" operation on a + * critical execution path. * * @author Ceki Gülcü */ @@ -22,13 +23,14 @@ public class DefaultInvocationGate implements InvocationGate { static final int MASK_DECREASE_RIGHT_SHIFT_COUNT = 2; - // experiments indicate that even for the most CPU intensive applications with 200 or more threads MASK + // experiments indicate that even for the most CPU intensive applications with + // 200 or more threads MASK // values in the order of 0xFFFF is appropriate private static final int MAX_MASK = 0xFFFF; static final int DEFAULT_MASK = 0xF; private volatile long mask = DEFAULT_MASK; - //private volatile long lastMaskCheck = System.currentTimeMillis(); + // private volatile long lastMaskCheck = System.currentTimeMillis(); // IMPORTANT: This field can be updated by multiple threads. It follows that // its values may *not* be incremented sequentially. However, we don't care @@ -36,26 +38,27 @@ public class DefaultInvocationGate implements InvocationGate { // expression (invocationCounter++ & mask) == mask) should be true. private long invocationCounter = 0; - // if less than thresholdForMaskIncrease milliseconds elapse between invocations of updateMaskIfNecessary() + // if less than thresholdForMaskIncrease milliseconds elapse between invocations + // of updateMaskIfNecessary() // method, then the mask should be increased private static final long MASK_INCREASE_THRESHOLD = 100; - // if more than thresholdForMaskDecrease milliseconds elapse between invocations of updateMaskIfNecessary() method, + // if more than thresholdForMaskDecrease milliseconds elapse between invocations + // of updateMaskIfNecessary() method, // then the mask should be decreased private static final long MASK_DECREASE_THRESHOLD = MASK_INCREASE_THRESHOLD * 8; - public DefaultInvocationGate() { this(MASK_INCREASE_THRESHOLD, MASK_DECREASE_THRESHOLD, System.currentTimeMillis()); } - - public DefaultInvocationGate(long minDelayThreshold, long maxDelayThreshold, long currentTime) { + + public DefaultInvocationGate(long minDelayThreshold, long maxDelayThreshold, long currentTime) { this.minDelayThreshold = minDelayThreshold; - this.maxDelayThreshold = maxDelayThreshold; + this.maxDelayThreshold = maxDelayThreshold; this.lowerLimitForMaskMatch = currentTime + minDelayThreshold; this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold; } - + private long minDelayThreshold; private long maxDelayThreshold; @@ -91,12 +94,11 @@ private void updateLimits(long currentTime) { this.upperLimitForNoMaskMatch = currentTime + maxDelayThreshold; } - // package private, for testing purposes only long getMask() { return mask; } - + private void increaseMask() { if (mask >= MAX_MASK) return; diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/DelayStrategy.java b/logback-core/src/main/java/ch/qos/logback/core/util/DelayStrategy.java index 183a0c0042..055b21de28 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/DelayStrategy.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/DelayStrategy.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -22,6 +22,7 @@ public interface DelayStrategy { /** * The value computed by this {@code DelayStrategy} for the next delay. + * * @return a delay value */ long nextDelay(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/DirectJson.java b/logback-core/src/main/java/ch/qos/logback/core/util/DirectJson.java new file mode 100644 index 0000000000..9473bcc783 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/DirectJson.java @@ -0,0 +1,259 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import java.math.BigDecimal; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +/** + * This is a utility class for writing json logs. + * It is imported from (and in collaboration with) penna. + * + * @author Henry John Kupty + * @see penna + */ +public final class DirectJson { + private static final int INITIAL_BUFFER_SIZE = 1024; + private static final byte QUOTE = '"'; + private static final byte ENTRY_SEP = ':'; + private static final byte KV_SEP = ','; + private static final byte DOT = '.'; + private static final byte OPEN_OBJ = '{'; + private static final byte CLOSE_OBJ = '}'; + private static final byte OPEN_ARR = '['; + private static final byte CLOSE_ARR = ']'; + + private static final byte[] NEWLINE = new byte[] { + '\\', + 'n', + }; + private static final byte[] ESCAPE = new byte[] { + '\\', + '\\', + }; + private static final byte[] LINEBREAK = new byte[] { + '\\', + 'r', + }; + private static final byte[] TAB = new byte[] { + '\\', + 't', + }; + private static final byte[] TRUE = new byte[] { + 't', + 'r', + 'u', + 'e' + }; + private static final byte[] FALSE = new byte[] { + 'f', + 'a', + 'l', + 's', + 'e' + }; + private static final byte[] NULL = new byte[] { + 'n', + 'u', + 'l', + 'l' + }; + + private ByteBuffer buffer; + + public DirectJson() { + buffer = ByteBuffer.allocateDirect(INITIAL_BUFFER_SIZE); + } + + public void openObject() { buffer.put(OPEN_OBJ); } + public void openArray() { buffer.put(OPEN_ARR); } + + public void openObject(String str) { + writeString(str); + writeEntrySep(); + buffer.put(OPEN_OBJ); + } + + public void openArray(String str) { + writeString(str); + writeEntrySep(); + buffer.put(OPEN_ARR); + } + + public void closeObject() { + var target = buffer.position() - 1; + if (',' == buffer.get(target)) { + buffer.put(target, CLOSE_OBJ); + } else { + buffer.put(CLOSE_OBJ); + } + } + + public void closeArray() { + var target = buffer.position() - 1; + if (',' == buffer.get(target)) { + buffer.put(target, CLOSE_ARR); + } else { + buffer.put(CLOSE_ARR); + } + } + + public void writeRaw(String str) { + for(int i = 0; i < str.length(); i++ ){ + var chr = str.codePointAt(i); + switch (chr) { + case '\\': + buffer.put(ESCAPE); + break; + case '\n': + buffer.put(NEWLINE); + break; + case '\r': + buffer.put(LINEBREAK); + break; + case '\t': + buffer.put(TAB); + break; + default: + if (chr >= 0x80 && chr <= 0x10FFFF) { + buffer.put(String.valueOf(str.charAt(i)).getBytes()); + } else if (chr > 0x1F) buffer.put((byte) chr); + } + + } + } + + public void writeRaw(char chr) { buffer.put((byte) chr); } + public void writeRaw(byte[] chr) { buffer.put(chr); } + + public void writeQuote() { buffer.put(QUOTE); } + public void writeString(String str) { + checkSpace(str.length() + 3); + buffer.put(QUOTE); + writeRaw(str); + buffer.put(QUOTE); + buffer.put(KV_SEP); + } + public void writeSep() { buffer.put(KV_SEP); } + + public void writeNumberRaw(final long data) { + final int pos = buffer.position(); + final int sz = (int) Math.log10(data) + 1; + long dataPointer = data; + + for (int i = sz - 1; i >= 0; i--) { + byte chr = (byte) (dataPointer % 10); + dataPointer = dataPointer / 10; + chr += 48; + buffer.put(pos + i, chr); + } + + buffer.position(pos + sz); + } + + public void writeNumber(final long data) { + final int pos = buffer.position(); + final int sz = data == 0 ? 1 : (int) Math.log10(data) + 1; + long dataPointer = data; + + for (int i = sz - 1; i >= 0; i--) { + byte chr = (byte) (dataPointer % 10); + dataPointer = dataPointer / 10; + chr += 48; + buffer.put(pos + i, chr); + } + + buffer.position(pos + sz); + buffer.put(KV_SEP); + } + + public void writeNumber(final double data) { + int pos = buffer.position(); + long whole = (long) data; + final int sz = (int) Math.log10(whole) + 1; + + for (int i = sz - 1; i >= 0; i--) { + byte chr = (byte) (whole % 10); + whole = whole / 10; + chr += 48; + buffer.put(pos + i, chr); + } + buffer.position(pos + sz); + buffer.put(DOT); + pos = buffer.position(); + BigDecimal fractional = BigDecimal.valueOf(data).remainder(BigDecimal.ONE); + int decs = 0; + while (!fractional.equals(BigDecimal.ZERO)) { + fractional = fractional.movePointRight(1); + byte chr = (byte) (fractional.intValue() + 48); + fractional = fractional.remainder(BigDecimal.ONE); + decs += 1; + buffer.put(chr); + } + + buffer.position(pos + decs); + buffer.put(KV_SEP); + } + + public void writeEntrySep() { buffer.put(buffer.position() - 1, ENTRY_SEP); } + + public void writeStringValue(String key, String value) { + writeString(key); + writeEntrySep(); + writeString(value); + } + + public void writeNumberValue(String key, long value) { + writeString(key); + writeEntrySep(); + writeNumber(value); + } + + public void writeNumberValue(String key, double value) { + writeString(key); + writeEntrySep(); + writeNumber(value); + } + + public void writeBoolean(boolean value) { + buffer.put(value ? TRUE : FALSE); + buffer.put(KV_SEP); + } + + public void writeNull() { + buffer.put(NULL); + buffer.put(KV_SEP); + } + + public void checkSpace(int size) { + if (buffer.position() + size >= buffer.capacity()) { + var newSize = (buffer.capacity() + size) * 2; + ByteBuffer newBuffer = ByteBuffer.allocateDirect(newSize); + buffer.flip(); + newBuffer.put(buffer); + buffer = newBuffer; + } + } + + public byte[] flush() { + byte[] result = new byte[buffer.position()]; + buffer.flip(); + buffer.get(result); + buffer.clear(); + + return result; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/Duration.java b/logback-core/src/main/java/ch/qos/logback/core/util/Duration.java index 8e156dea8b..fb13eb697a 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/Duration.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/Duration.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,16 +18,20 @@ /** * Duration instances represent a lapse of time. Internally, the duration is - * stored in milliseconds. However, whenever a parameter of type Duration is expected, Joran - * (logback's configuration system) will automatically convert strings such as "20 seconds" - * "3.5 minutes" or "5 hours" into Duration instances. + * stored in milliseconds. However, whenever a parameter of type Duration is + * expected, Joran (logback's configuration system) will automatically convert + * strings such as "20 seconds" "3.5 minutes" or "5 hours" into Duration + * instances. * - *

The recognized units of time are the "millisecond", "second", "minute" "hour" and "day". - * The unit name may be followed by an "s". Thus, "2 day" and "2 days" are equivalent. In the - * absence of a time unit specification, milliseconds are assumed. + *

+ * The recognized units of time are the "millisecond", "second", "minute" "hour" + * and "day". The unit name may be followed by an "s". Thus, "2 day" and "2 + * days" are equivalent. In the absence of a time unit specification, + * milliseconds are assumed. * - *

Note: the conversion magic is entirely due to the fact that this class follows the - * {@link #valueOf} convention. + *

+ * Note: the conversion magic is entirely due to the fact that this class + * follows the {@link #valueOf} convention. * * @author Ceki Gulcu */ @@ -39,7 +43,8 @@ public class Duration { private final static String UNIT_PART = "(|milli(second)?|second(e)?|minute|hour|day)s?"; private final static int UNIT_GROUP = 3; - private static final Pattern DURATION_PATTERN = Pattern.compile(DOUBLE_PART + "\\s*" + UNIT_PART, Pattern.CASE_INSENSITIVE); + private static final Pattern DURATION_PATTERN = Pattern.compile(DOUBLE_PART + "\\s*" + UNIT_PART, + Pattern.CASE_INSENSITIVE); static final long SECONDS_COEFFICIENT = 1000; static final long MINUTES_COEFFICIENT = 60 * SECONDS_COEFFICIENT; diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/DynamicClassLoadingException.java b/logback-core/src/main/java/ch/qos/logback/core/util/DynamicClassLoadingException.java index 7d7f9c4862..e1c5b936a4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/DynamicClassLoadingException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/DynamicClassLoadingException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/EnvUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/EnvUtil.java index 8151ccf7d1..d78947cca4 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/EnvUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/EnvUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,11 @@ */ package ch.qos.logback.core.util; +import ch.qos.logback.core.CoreConstants; + +import java.lang.module.ModuleDescriptor; +import java.util.Optional; + /** * @author Ceki Gülcü */ @@ -21,6 +26,20 @@ public class EnvUtil { private EnvUtil() { } + + /** + *

Returns the current version of logback-core, or null if data is not + * available. + *

+ * + * @since 1.3.0 + * @return current version or null if missing version data + * @deprecated See {@link VersionUtil#getVersionOfArtifact(Class)} + */ + static public String logbackVersion() { + return VersionUtil.getVersionOfArtifact(CoreConstants.class); + } + static public int getJDKVersion(String javaVersionStr) { int version = 0; @@ -57,6 +76,22 @@ static public boolean isJDK7OrHigher() { return isJDK_N_OrHigher(7); } + static public boolean isJDK16OrHigher() { + return isJDK_N_OrHigher(16); + } + + static public boolean isJDK18OrHigher() { + return isJDK_N_OrHigher(18); + } + + /** + * @since logback 1.3.12/1.4.12 + * @return true if runtime JDK is version 21 or higher + */ + static public boolean isJDK21OrHigher() { + return isJDK_N_OrHigher(21); + } + static public boolean isJaninoAvailable() { ClassLoader classLoader = EnvUtil.class.getClassLoader(); try { @@ -67,9 +102,25 @@ static public boolean isJaninoAvailable() { } } + public static boolean isMacOs() { + String os = System.getProperty("os.name"); + // expected value is "Mac OS X" + return os.toLowerCase().contains("mac"); + } + public static boolean isWindows() { String os = System.getProperty("os.name"); return os.startsWith("Windows"); } + static public boolean isClassAvailable(Class callerClass, String className) { + ClassLoader classLoader = Loader.getClassLoaderOfClass(callerClass); + try { + Class bindingClass = classLoader.loadClass(className); + return (bindingClass != null); + } catch (ClassNotFoundException e) { + return false; + } + } + } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/ExecutorServiceUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/ExecutorServiceUtil.java index 0477fe3c9c..2b043f922a 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/ExecutorServiceUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/ExecutorServiceUtil.java @@ -1,43 +1,47 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.util; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledThreadPoolExecutor; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import ch.qos.logback.core.CoreConstants; /** * Static utility methods for manipulating an {@link ExecutorService}. - * + * * @author Carl Harris * @author Mikhail Mazursky */ public class ExecutorServiceUtil { - private static final ThreadFactory THREAD_FACTORY = new ThreadFactory() { + private static final ThreadFactory THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE = new ThreadFactory() { - private final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); private final AtomicInteger threadNumber = new AtomicInteger(1); + private final ThreadFactory defaultFactory = makeThreadFactory(); + + /** + * A thread factory which may be a virtual thread factory the JDK supports it. + * + * @return + */ + private ThreadFactory makeThreadFactory() { + return Executors.defaultThreadFactory(); + } + + @Override public Thread newThread(Runnable r) { Thread thread = defaultFactory.newThread(r); if (!thread.isDaemon()) { @@ -49,26 +53,58 @@ public Thread newThread(Runnable r) { }; static public ScheduledExecutorService newScheduledExecutorService() { - return new ScheduledThreadPoolExecutor(CoreConstants.SCHEDULED_EXECUTOR_POOL_SIZE, THREAD_FACTORY); + return new ScheduledThreadPoolExecutor(CoreConstants.SCHEDULED_EXECUTOR_POOL_SIZE, THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE); } - /** - * Creates an executor service suitable for use by logback components. - * @return executor service + * @deprecated replaced by {@link #newThreadPoolExecutor()} */ static public ExecutorService newExecutorService() { - return new ThreadPoolExecutor(CoreConstants.CORE_POOL_SIZE, CoreConstants.MAX_POOL_SIZE, 0L, TimeUnit.MILLISECONDS, new SynchronousQueue(), - THREAD_FACTORY); + return newThreadPoolExecutor(); + } + + /** + * Creates an ThreadPoolExecutor suitable for use by logback components. + * + * @since 1.4.7 + * @return ThreadPoolExecutor + */ + static public ThreadPoolExecutor newThreadPoolExecutor() { + + // irrelevant parameter when LinkedBlockingQueue is in use + final int maximumPoolSize = CoreConstants.CORE_POOL_SIZE + 1; + final long keepAliveMillis = 100L; + + // As of version 1.5.13, the SynchronousQueue was replaced by LinkedBlockingQueue + // This has the effect of queueing jobs immediately and have them run by CORE_POOL_SIZE + // threads. We expect jobs to arrive at a relatively slow pace compared to their duration. + // Note that threads are removed if idle more than keepAliveMillis + ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(CoreConstants.CORE_POOL_SIZE, maximumPoolSize, keepAliveMillis, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue<>(), THREAD_FACTORY_FOR_SCHEDULED_EXECUTION_SERVICE); + threadPoolExecutor.allowCoreThreadTimeOut(true); + return threadPoolExecutor; + } /** * Shuts down an executor service. *

+ * * @param executorService the executor service to shut down */ static public void shutdown(ExecutorService executorService) { - executorService.shutdownNow(); + if (executorService != null) { + executorService.shutdownNow(); + } } + /** + * An alternate implementation of {@link #newThreadPoolExecutor} which returns a virtual thread per task executor when + * available. + * + * @since 1.3.12/1.4.12 + */ + static public ExecutorService newAlternateThreadPoolExecutor() { + return newThreadPoolExecutor(); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/FileSize.java b/logback-core/src/main/java/ch/qos/logback/core/util/FileSize.java index c516dd10c1..c3f7a12114 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/FileSize.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/FileSize.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,7 @@ */ package ch.qos.logback.core.util; +import java.util.Objects; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -20,12 +21,13 @@ * Instances of this class represent the size of a file. Internally, the size is * stored as long. * - *

The {@link #valueOf} method can convert strings such as "3 kb", "5 mb", into + *

+ * The {@link #valueOf} method can convert strings such as "3 kb", "5 mb", into * FileSize instances. The recognized unit specifications for file size are the * "kb", "mb", and "gb". The unit name may be followed by an "s". Thus, "2 kbs" * and "2 kb" are equivalent. In the absence of a time unit specification, byte * is assumed. - * + * * @author Ceki Gülcü * */ @@ -37,7 +39,8 @@ public class FileSize { private final static String UNIT_PART = "(|kb|mb|gb)s?"; private final static int UNIT_GROUP = 2; - private static final Pattern FILE_SIZE_PATTERN = Pattern.compile(LENGTH_PART + "\\s*" + UNIT_PART, Pattern.CASE_INSENSITIVE); + private static final Pattern FILE_SIZE_PATTERN = Pattern.compile(LENGTH_PART + "\\s*" + UNIT_PART, + Pattern.CASE_INSENSITIVE); static public final long KB_COEFFICIENT = 1024; static public final long MB_COEFFICIENT = 1024 * KB_COEFFICIENT; @@ -78,25 +81,40 @@ static public FileSize valueOf(String fileSizeStr) { throw new IllegalArgumentException("String value [" + fileSizeStr + "] is not in the expected format."); } } - + @Override public String toString() { long inKB = size / KB_COEFFICIENT; - - if(inKB == 0) + + if (inKB == 0) return size + " Bytes"; - + long inMB = size / MB_COEFFICIENT; - if(inMB == 0) { + if (inMB == 0) { return inKB + " KB"; } - + long inGB = size / GB_COEFFICIENT; - if(inGB == 0) { + if (inGB == 0) { return inMB + " MB"; } - + return inGB + " GB"; - + + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + FileSize fileSize = (FileSize) o; + return size == fileSize.size; + } + + @Override + public int hashCode() { + return Objects.hashCode(size); } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/FileUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/FileUtil.java index 2c335ffe7c..d4f321e67b 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/FileUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/FileUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -37,13 +37,12 @@ public static URL fileToURL(File file) { } /** - * Creates the parent directories of a file. If parent directories not - * specified in file's path, then nothing is done and this returns - * gracefully. + * Creates the parent directories of a file. If parent directories not specified + * in file's path, then nothing is done and this returns gracefully. * * @param file file whose parent directories (if any) should be created - * @return {@code true} if either no parents were specified, or if all - * parent directories were created successfully; {@code false} otherwise + * @return {@code true} if either no parents were specified, or if all parent + * directories were created successfully; {@code false} otherwise */ static public boolean createMissingParentDirectories(File file) { File parent = file.getParentFile(); diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/FixedDelay.java b/logback-core/src/main/java/ch/qos/logback/core/util/FixedDelay.java index 9fe3c29b5a..c389702d01 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/FixedDelay.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/FixedDelay.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -37,8 +37,8 @@ public FixedDelay(long initialDelay, long subsequentDelay) { } /** - * Initialize a new {@code FixedDelay} with fixed delay value given by {@code delay} - * parameter. + * Initialize a new {@code FixedDelay} with fixed delay value given by + * {@code delay} parameter. * * @param delay value for all delays */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/IncompatibleClassException.java b/logback-core/src/main/java/ch/qos/logback/core/util/IncompatibleClassException.java index 33412a7035..a866f801bb 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/IncompatibleClassException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/IncompatibleClassException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/IntHolder.java b/logback-core/src/main/java/ch/qos/logback/core/util/IntHolder.java new file mode 100644 index 0000000000..b05bbb01d5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/IntHolder.java @@ -0,0 +1,61 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +/** + * A simple mutable holder for an integer value, providing basic operations + * like incrementing, setting, and retrieving the value. This class is not + * thread-safe and should be used in single-threaded contexts or with external + * synchronization. + * + * @since 1.5.24 + */ +public class IntHolder { + public int value; + + /** + * Constructs an IntHolder with the specified initial value. + * + * @param value the initial integer value to hold + */ + public IntHolder(int value) { + this.value = value; + } + + /** + * Increments the held value by 1. + */ + public void inc() { + value++; + } + + /** + * Sets the held value to the specified new value. + * + * @param newValue the new integer value to set + */ + public void set(int newValue) { + value = newValue; + } + + /** + * Returns the current held value. + * + * @return the current integer value + */ + public int get(){ + return value; + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/InterruptUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/InterruptUtil.java index 6529ccc5da..de2d5829eb 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/InterruptUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/InterruptUtil.java @@ -1,10 +1,25 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.util; import ch.qos.logback.core.Context; import ch.qos.logback.core.spi.ContextAwareBase; /** - * Allows masking of interrupt flag if previously the flag is already set. Does nothing otherwise. + * Allows masking of interrupt flag if previously the flag is already set. Does + * nothing otherwise. * * Typical use: * @@ -12,16 +27,19 @@ * InterruptUtil interruptUtil = new InterruptUtil(context); * * try { - * interruptUtil.maskInterruptFlag(); - * someOtherThread.join(delay); - * } catch(InterruptedException e) { - * // reachable only if join does not succeed within delay. - * // Without the maskInterruptFlag() call, the join() would have returned immediately - * // had the current thread been interrupted previously, i.e. before entering the above block + * interruptUtil.maskInterruptFlag(); + * someOtherThread.join(delay); + * } catch (InterruptedException e) { + * // reachable only if join does not succeed within delay. + * // Without the maskInterruptFlag() call, the join() would have returned + * // immediately + * // had the current thread been interrupted previously, i.e. before entering + * // the above block * } finally { - * interruptUtil.unmaskInterruptFlag(); + * interruptUtil.unmaskInterruptFlag(); * } * + * * @author Ceki Gulcu * @since 1.2.2 */ @@ -46,7 +64,7 @@ public void unmaskInterruptFlag() { try { Thread.currentThread().interrupt(); } catch (SecurityException se) { - addError("Failed to intrreupt current thread", se); + addError("Failed to interrupt current thread", se); } } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/InvocationGate.java b/logback-core/src/main/java/ch/qos/logback/core/util/InvocationGate.java index 7b27fa936a..5fe91fa6b6 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/InvocationGate.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/InvocationGate.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.util; public interface InvocationGate { @@ -5,11 +19,14 @@ public interface InvocationGate { final long TIME_UNAVAILABLE = -1; /** - * The caller of this method can decide to skip further work if the returned value is true. + * The caller of this method can decide to skip further work if the returned + * value is true. * - * Implementations should be able to give a reasonable answer even if current time date is unavailable. + * Implementations should be able to give a reasonable answer even if current + * time date is unavailable. * - * @param currentTime can be TIME_UNAVAILABLE (-1) to signal that time is not available + * @param currentTime can be TIME_UNAVAILABLE (-1) to signal that time is not + * available * @return if true, caller should skip further work */ public abstract boolean isTooSoon(long currentTime); diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/JNDIUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/JNDIUtil.java new file mode 100644 index 0000000000..55ff44e9ff --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/JNDIUtil.java @@ -0,0 +1,71 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.util; + +import static ch.qos.logback.core.CoreConstants.JNDI_JAVA_NAMESPACE; + +import java.util.Hashtable; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; + +/** + * A simple utility class to create and use a JNDI Context. + * + * @author Ceki Gülcü + * @author Michael Osipov + * @author Sébastien Pennec + * + */ + +public class JNDIUtil { + + static final String RESTRICTION_MSG = "JNDI name must start with " + JNDI_JAVA_NAMESPACE + " but was "; + + public static Context getInitialContext() throws NamingException { + return new InitialContext(); + } + + public static Context getInitialContext(Hashtable props) throws NamingException { + return new InitialContext(props); + } + + public static Object lookupObject(Context ctx, String name) throws NamingException { + if (ctx == null) { + return null; + } + + if (OptionHelper.isNullOrEmptyOrAllSpaces(name)) { + return null; + } + + jndiNameSecurityCheck(name); + + Object lookup = ctx.lookup(name); + return lookup; + } + + public static void jndiNameSecurityCheck(String name) throws NamingException { + if (!name.startsWith(JNDI_JAVA_NAMESPACE)) { + throw new NamingException(RESTRICTION_MSG + name); + } + } + + public static String lookupString(Context ctx, String name) throws NamingException { + Object lookup = lookupObject(ctx, name); + return (String) lookup; + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java b/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java index 31c4951e74..2fda571990 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/Loader.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,8 +15,6 @@ import java.io.IOException; import java.net.URL; -import java.security.AccessController; -import java.security.PrivilegedAction; import java.util.Enumeration; import java.util.Set; import java.util.HashSet; @@ -33,7 +31,6 @@ public class Loader { private static boolean ignoreTCL = false; public static final String IGNORE_TCL_PROPERTY_NAME = "logback.ignoreTCL"; - private static boolean HAS_GET_CLASS_LOADER_PERMISSION = false; static { String ignoreTCLProp = OptionHelper.getSystemProperty(IGNORE_TCL_PROPERTY_NAME, null); @@ -41,31 +38,31 @@ public class Loader { if (ignoreTCLProp != null) { ignoreTCL = OptionHelper.toBoolean(ignoreTCLProp, true); } + } - HAS_GET_CLASS_LOADER_PERMISSION = AccessController.doPrivileged(new PrivilegedAction() { - public Boolean run() { - try { - AccessController.checkPermission(new RuntimePermission("getClassLoader")); - return true; - } catch (SecurityException e) { - // Using SecurityException instead of AccessControlException. - // See bug LOGBACK-760. - return false; - } - } - }); + /** + * This method is used to sanitize the cl argument in case it is null. + * + * @param cl a class loader, may be null + * @return the system class loader if the cl argument is null, return cl otherwise. + * + * @since 1.4.12 + */ + public static ClassLoader systemClassloaderIfNull(ClassLoader cl) { + if(cl == null) + return ClassLoader.getSystemClassLoader(); + else + return cl; } /** - * Compute the number of occurrences a resource can be found by a class - * loader. + * Compute the number of occurrences a resource can be found by a class loader. * * @param resource * @param classLoader * @return * @throws IOException */ - public static Set getResources(String resource, ClassLoader classLoader) throws IOException { // See LBCLASSIC-159 Set urlSet = new HashSet(); @@ -108,8 +105,7 @@ public static URL getResourceBySelfClassLoader(String resource) { /** * Get the Thread Context Loader which is a JDK 1.2 feature. If we are running - * under JDK 1.1 or anything else goes wrong the method returns - * {@code null}. + * under JDK 1.1 or anything else goes wrong the method returns {@code null}. */ public static ClassLoader getTCL() { return Thread.currentThread().getContextClassLoader(); @@ -135,36 +131,33 @@ public static ClassLoader getClassLoaderOfObject(Object o) { } /** - * Returns the class loader of clazz in an access privileged section. + * Check whether a given class is loadable by the class loader that loaded the context parameter. * - * @param clazz - * @return + * @param className the class to check for availability + * @param context the context object used to find the class loader + * @return true if className is available, false otherwise + * @since 1.5.18 */ - public static ClassLoader getClassLoaderAsPrivileged(final Class clazz) { - if (!HAS_GET_CLASS_LOADER_PERMISSION) - return null; - else - return AccessController.doPrivileged(new PrivilegedAction() { - public ClassLoader run() { - return clazz.getClassLoader(); - } - }); + public static boolean isClassLoadable(String className, Context context) { + try { + Class aClass = Loader.loadClass(className, context); + return true; + } catch (ClassNotFoundException e) { + return false; + } } + /** - * Return the class loader which loaded the class passed as argument. Return - * the system class loader if appropriate. + * Return the class loader which loaded the class passed as argument. Return the + * system class loader if the class loader of 'clazz' argument is null. * * @param clazz * @return */ public static ClassLoader getClassLoaderOfClass(final Class clazz) { ClassLoader cl = clazz.getClassLoader(); - if (cl == null) { - return ClassLoader.getSystemClassLoader(); - } else { - return cl; - } + return systemClassloaderIfNull(cl); } /** diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/LocationUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/LocationUtil.java index 9c86a0fd40..ceb5fbdb76 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/LocationUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/LocationUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,8 +18,8 @@ import java.net.URL; /** - * A static utility method that converts a string that describes the - * location of a resource into a {@link URL} object. + * A static utility method that converts a string that describes the location of + * a resource into a {@link URL} object. * * @author Carl Harris */ @@ -33,13 +33,14 @@ public class LocationUtil { /** * Converts a string describing the location of a resource into a URL object. + * * @param location String describing the location * @return URL object that refers to {@code location} - * @throws MalformedURLException if {@code location} is not a syntatically - * valid URL + * @throws MalformedURLException if {@code location} is not a syntactically valid + * URL * @throws FileNotFoundException if {@code location} specifies a non-existent - * classpath resource - * @throws NullPointerException if {@code location} is {@code null} + * classpath resource + * @throws NullPointerException if {@code location} is {@code null} */ public static URL urlForResource(String location) throws MalformedURLException, FileNotFoundException { if (location == null) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/MD5Util.java b/logback-core/src/main/java/ch/qos/logback/core/util/MD5Util.java new file mode 100644 index 0000000000..cd587763c5 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/MD5Util.java @@ -0,0 +1,61 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class MD5Util { + + + static String MD5_ALGORITHM_KEY = "MD5"; + final MessageDigest md5; + + public MD5Util() throws NoSuchAlgorithmException { + md5 = MessageDigest.getInstance(MD5_ALGORITHM_KEY); + + } + + + public boolean equalsHash(byte[] b0, byte[] b1) { + return Arrays.equals(b0, b1); + } + + /** + * Compute hash for input string. The hash is computed on the input alone + * with no previous or subsequent data. + * + * @param input + * @return + */ + public byte[] md5Hash(String input) { + byte[] messageDigest = md5.digest(input.getBytes()); + md5.reset(); + return messageDigest; + } + + public String asHexString(byte[] messageDigest) { + BigInteger number = new BigInteger(1, messageDigest); + StringBuilder hexString = new StringBuilder(number.toString(16)); + // Pad with zeros if necessary + while (hexString.length() < 32) { + hexString.insert(0, '0'); + } + return hexString.toString(); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/NetworkAddressUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/NetworkAddressUtil.java index a5de41a5c1..e2f27a8def 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/NetworkAddressUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/NetworkAddressUtil.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.util; import java.net.InetAddress; @@ -49,7 +63,8 @@ private static String getLocalAddressAsString() throws UnknownHostException, Soc } private static boolean acceptableAddress(InetAddress address) { - return address != null && !address.isLoopbackAddress() && !address.isAnyLocalAddress() && !address.isLinkLocalAddress(); + return address != null && !address.isLoopbackAddress() && !address.isAnyLocalAddress() + && !address.isLinkLocalAddress(); } /** diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/OptionHelper.java b/logback-core/src/main/java/ch/qos/logback/core/util/OptionHelper.java index 895e0cf835..c233518b85 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/OptionHelper.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/OptionHelper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,10 +14,10 @@ package ch.qos.logback.core.util; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.Properties; import ch.qos.logback.core.Context; -import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.spi.ContextAware; import ch.qos.logback.core.spi.PropertyContainer; import ch.qos.logback.core.spi.ScanException; @@ -28,25 +28,40 @@ */ public class OptionHelper { - public static Object instantiateByClassName(String className, Class superClass, Context context) throws IncompatibleClassException, - DynamicClassLoadingException { + public static Object instantiateByClassName(String className, Class superClass, Context context) + throws IncompatibleClassException, DynamicClassLoadingException { ClassLoader classLoader = Loader.getClassLoaderOfObject(context); return instantiateByClassName(className, superClass, classLoader); } - public static Object instantiateByClassNameAndParameter(String className, Class superClass, Context context, Class type, Object param) - throws IncompatibleClassException, DynamicClassLoadingException { + public static Object instantiateByClassNameAndParameter(String className, Class superClass, Context context, + Class type, Object param) throws IncompatibleClassException, DynamicClassLoadingException { ClassLoader classLoader = Loader.getClassLoaderOfObject(context); return instantiateByClassNameAndParameter(className, superClass, classLoader, type, param); } - public static Object instantiateByClassName(String className, Class superClass, ClassLoader classLoader) throws IncompatibleClassException, - DynamicClassLoadingException { + public static Object instantiateByClassName(String className, Class superClass, ClassLoader classLoader) + throws IncompatibleClassException, DynamicClassLoadingException { return instantiateByClassNameAndParameter(className, superClass, classLoader, null, null); } - public static Object instantiateByClassNameAndParameter(String className, Class superClass, ClassLoader classLoader, Class type, Object parameter) - throws IncompatibleClassException, DynamicClassLoadingException { + + public static Object instantiateClassWithSuperclassRestriction(Class classObj, Class superClass) + throws IncompatibleClassException, DynamicClassLoadingException { + if (!superClass.isAssignableFrom(classObj)) { + throw new IncompatibleClassException(superClass, classObj); + } + + try { + return classObj.getConstructor().newInstance(); + } catch (NoSuchMethodException|InstantiationException|IllegalAccessException|InvocationTargetException e) { + throw new DynamicClassLoadingException("Failed to instantiate type " + classObj.getName(), e); + } + } + + public static Object instantiateByClassNameAndParameter(String className, Class superClass, + ClassLoader classLoader, Class type, Object parameter) + throws IncompatibleClassException, DynamicClassLoadingException { if (className == null) { throw new NullPointerException(); @@ -71,8 +86,8 @@ public static Object instantiateByClassNameAndParameter(String className, Class< } /** - * Find the value corresponding to key in props. - * Then perform variable substitution on the found value. + * Find the value corresponding to key in props. Then + * perform variable substitution on the found value. */ // public static String findAndSubst(String key, Properties props) { // String value = props.getProperty(key); @@ -100,21 +115,33 @@ public static Object instantiateByClassNameAndParameter(String className, Class< /** * @see #substVars(String, PropertyContainer, PropertyContainer) */ - public static String substVars(String val, PropertyContainer pc1) { + public static String substVars(String val, PropertyContainer pc1) throws ScanException { return substVars(val, pc1, null); } /** - * See http://logback.qos.ch/manual/configuration.html#variableSubstitution + * See http://logback.qos.ch/manual/configuration.html#variableSubstitution */ - public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) { - try { - return NodeToStringTransformer.substituteVariable(input, pc0, pc1); - } catch (ScanException e) { - throw new IllegalArgumentException("Failed to parse input [" + input + "]", e); - } + public static String substVars(String input, PropertyContainer pc0, PropertyContainer pc1) throws ScanException { + // may throw IllegalArgumentException or ScanException + return NodeToStringTransformer.substituteVariable(input, pc0, pc1); + } + /** + * Try to lookup the property in the following order: + *

    + *
  1. pc1 (the local property container, usually the {@link ch.qos.logback.core.model.processor.ModelInterpretationContext ModelInterpretationContext})
  2. + *
  3. pc2 (usually the {@link Context context})
  4. + *
  5. System properties
  6. + *
  7. Environment variables
  8. + *
+ * + * @param key the property key + * @param pc1 the first property container to search + * @param pc2 the second property container to search + * @return the property value or null if not found + */ public static String propertyLookup(String key, PropertyContainer pc1, PropertyContainer pc2) { String value = null; // first try the props passed as parameter @@ -211,9 +238,9 @@ public static Properties getSystemProperties() { } /** - * Return a String[] of size two. The first item containing the key part and the second item - * containing a default value specified by the user. The second item will be null if no default value - * is specified. + * Return a String[] of size two. The first item containing the key part and the + * second item containing a default value specified by the user. The second item + * will be null if no default value is specified. * * @param key * @return @@ -232,16 +259,47 @@ static public String[] extractDefaultReplacement(String key) { return result; } + + /** + * Converts a string representation of a boolean value to a {@link Boolean} object. + * If the input string is "true" (case-insensitive), returns {@code Boolean.TRUE}. + * If the input string is "false" (case-insensitive), returns {@code Boolean.FALSE}. + * Returns {@code null} if the input string is null, empty, or does not match "true" or "false". + * + * @param value the string representation of a boolean value, may be null or empty + * @return a {@link Boolean} object corresponding to the input string, or {@code null} if the input is invalid + */ + public static Boolean toBooleanObject(String value) { + if (OptionHelper.isNullOrEmptyOrAllSpaces(value)) { + return null; + } + + String trimmedVal = value.trim(); + + if ("true".equalsIgnoreCase(trimmedVal)) { + return true; + } + + if ("false".equalsIgnoreCase(trimmedVal)) { + return false; + } + return null; + } + /** - * If value is "true", then true is returned. If - * value is "false", then true is returned. - * Otherwise, default is returned. - *

- * Case of value is unimportant. + *

If value is "true", then true is returned. If + * value is "false", then true is returned. Otherwise, + * defaultVal is returned. + *

+ * + *

Comparisons are case-insensitive.

+ * + * @param value a string representation of a boolean value + * @param defaultVal the default value to return if value is null, empty or not a valid boolean value */ - public static boolean toBoolean(String value, boolean dEfault) { + public static boolean toBoolean(String value, boolean defaultVal) { if (value == null) { - return dEfault; + return defaultVal; } String trimmedVal = value.trim(); @@ -254,11 +312,46 @@ public static boolean toBoolean(String value, boolean dEfault) { return false; } - return dEfault; + return defaultVal; } + /** + * @deprecated + * @since 1.3 + */ public static boolean isEmpty(String str) { - return ((str == null) || CoreConstants.EMPTY_STRING.equals(str)); + return isNullOrEmptyOrAllSpaces(str); } + /** + * Returns true if input str is null or empty. + * + * @param str + * @return + */ + public static boolean isNullOrEmpty(String str) { + return ((str == null) || str.trim().length() == 0); + } + + /** + * isNullOrEmpty xisNullOrEmptyOrAllSpaces + * @param str + * @return + * @since 1.5.0 + */ + public static boolean isNullOrEmptyOrAllSpaces(String str) { + return ((str == null) || str.trim().length() == 0); + } + + + final public static boolean isNullOrEmpty(Object[] array) { + if(array == null || array.length == 0) + return true; + else + return false; + } + + final public static boolean isNotEmtpy(Object[] array) { + return !isNullOrEmpty(array); + } } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/PropertySetterException.java b/logback-core/src/main/java/ch/qos/logback/core/util/PropertySetterException.java index b97142703f..61da479d94 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/PropertySetterException.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/PropertySetterException.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,8 +14,8 @@ package ch.qos.logback.core.util; /** - * Thrown when an error is encountered whilst attempting to set a property - * using the {@link ch.qos.logback.core.joran.util.PropertySetter} utility class. + * Thrown when an error is encountered whilst attempting to set a property using + * the {@link ch.qos.logback.core.joran.util.PropertySetter} utility class. * * @author Anders Kristensen */ diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuard.java b/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuard.java new file mode 100644 index 0000000000..93922fed04 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuard.java @@ -0,0 +1,137 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +/** + * Guard used to prevent re-entrant (recursive) appender invocations on a per-thread basis. + * + *

Implementations are used by appenders and other components that must avoid + * recursively calling back into themselves (for example when an error causes + * logging while handling a logging event). Typical usage: check {@link #isLocked()} + * before proceeding and call {@link #lock()} / {@link #unlock()} around the + * guarded region.

+ * + *

Concurrency: guards operate on a per-thread basis; callers should treat the + * guard as thread-local state. Implementations must document their semantics; + * the provided {@link ReentryGuardImpl} uses a {@link ThreadLocal} to track the + * locked state for the current thread.

+* + * @since 1.5.21 + */ +public interface ReentryGuard { + + /** + * Return true if the current thread holds the guard (i.e. is inside a guarded region). + * + *

Implementations typically return {@code false} if the current thread has not + * previously called {@link #lock()} or if the stored value is {@code null}.

+ * + * @return {@code true} if the guard is locked for the current thread, {@code false} otherwise + */ + boolean isLocked(); + + /** + * Mark the guard as locked for the current thread. + * + *

Callers must ensure {@link #unlock()} is invoked in a finally block to + * avoid leaving the guard permanently locked for the thread.

+ */ + void lock(); + + /** + * Release the guard for the current thread. + * + *

After calling {@code unlock()} the {@link #isLocked()} should return + * {@code false} for the current thread (unless {@code lock()} is called again).

+ */ + void unlock(); + + + /** + * Default per-thread implementation backed by a {@link ThreadLocal}. + * + *

Semantics: a value of {@link Boolean#TRUE} indicates the current thread + * is inside a guarded region. If the ThreadLocal has no value ({@code null}), + * {@link #isLocked()} treats this as unlocked (returns {@code false}).

+ * + *

Note: this implementation intentionally uses {@code ThreadLocal} + * to avoid global synchronization. The initial state is unlocked.

+ * + * Typical usage: + *
+     * if (!guard.isLocked()) {
+     *   guard.lock();
+     *   try {
+     *     // guarded work
+     *   } finally {
+     *     guard.unlock();
+     *   }
+     * }
+     * 
+ * + */ + class ReentryGuardImpl implements ReentryGuard { + + private ThreadLocal guard = new ThreadLocal(); + + + @Override + public boolean isLocked() { + // the guard is considered locked if the ThreadLocal contains Boolean.TRUE + // note that initially the ThreadLocal contains null + return (Boolean.TRUE.equals(guard.get())); + } + + @Override + public void lock() { + guard.set(Boolean.TRUE); + } + + @Override + public void unlock() { + guard.set(Boolean.FALSE); + } + } + + /** + * No-op implementation that never locks. Useful in contexts where re-entrancy + * protection is not required. + * + *

{@link #isLocked()} always returns {@code false}. {@link #lock()} and + * {@link #unlock()} are no-ops.

+ * + *

Use this implementation when the caller explicitly wants to disable + * reentrancy protection (for example in tests or in environments where the + * cost of thread-local checks is undesirable and re-entrancy cannot occur).

+ * + */ + class NOPRentryGuard implements ReentryGuard { + @Override + public boolean isLocked() { + return false; + } + + @Override + public void lock() { + // NOP + } + + @Override + public void unlock() { + // NOP + } + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuardFactory.java b/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuardFactory.java new file mode 100644 index 0000000000..275f500fcc --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/ReentryGuardFactory.java @@ -0,0 +1,66 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +/** + * Factory that creates {@link ReentryGuard} instances according to a requested type. + * + *

This class centralizes creation of the built-in guard implementations. + * Consumers can use the factory to obtain either a per-thread guard or a no-op + * guard depending on their needs.

+ * + * @since 1.5.21 + */ +public class ReentryGuardFactory { + + /** + * Types of guards that can be produced by this factory. + * + * THREAD_LOCAL - returns a {@link ReentryGuard.ReentryGuardImpl} backed by a ThreadLocal. + * NOP - returns a {@link ReentryGuard.NOPRentryGuard} which never locks. + */ + public enum GuardType { + THREAD_LOCAL, + NOP + } + + + /** + * Create a {@link ReentryGuard} for the given {@link GuardType}. + * + *

Returns a fresh instance of the requested guard implementation. The + * factory does not cache instances; callers may obtain separate instances + * as required.

+ * + *

Thread-safety: this method is stateless and may be called concurrently + * from multiple threads.

+ * + * @param guardType the type of guard to create; must not be {@code null} + * @return a new {@link ReentryGuard} instance implementing the requested semantics + * @throws NullPointerException if {@code guardType} is {@code null} + * @throws IllegalArgumentException if an unknown guard type is provided + * @since 1.5.21 + */ + public static ReentryGuard makeGuard(GuardType guardType) { + switch (guardType) { + case THREAD_LOCAL: + return new ReentryGuard.ReentryGuardImpl(); + case NOP: + return new ReentryGuard.NOPRentryGuard(); + default: + throw new IllegalArgumentException("Unknown GuardType: " + guardType); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/SimpleInvocationGate.java b/logback-core/src/main/java/ch/qos/logback/core/util/SimpleInvocationGate.java new file mode 100644 index 0000000000..92c59f38c1 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/SimpleInvocationGate.java @@ -0,0 +1,79 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.LongBinaryOperator; +import java.util.function.LongUnaryOperator; +import java.util.function.UnaryOperator; + +/** + * An invocation gate using very simple logic. + * + * @since 1.3.6/1.4.6 + */ +public class SimpleInvocationGate implements InvocationGate { + + //volatile long next = 0; + + AtomicLong atomicNext = new AtomicLong(0); + final Duration increment; + + // 60 seconds by default + final public static Duration DEFAULT_INCREMENT = Duration.buildBySeconds(60); + + public SimpleInvocationGate() { + this(DEFAULT_INCREMENT); + } + + public SimpleInvocationGate(Duration anIncrement) { + this.increment = anIncrement; + } + + @Override + public boolean isTooSoon(long currentTime) { + if (currentTime == -1) + return false; + + long localNext = atomicNext.get(); + if (currentTime >= localNext) { + long next2 = currentTime+increment.getMilliseconds(); + // if success, we were able to set the variable, otherwise some other thread beat us to it + boolean success = atomicNext.compareAndSet(localNext, next2); + // while we have crossed 'next', the other thread already returned true. There is + // no point in letting more than one thread per duration. + return !success; + } else { + return true; + } + + } + + +} + +// private final boolean isTooSoonSynchronized(long currentTime) { +// if (currentTime == -1) +// return false; +// +// synchronized (this) { +// if (currentTime >= next) { +// next = currentTime + increment; +// return false; +// } +// } +// return true; +// } + diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/SimpleTimeBasedGuard.java b/logback-core/src/main/java/ch/qos/logback/core/util/SimpleTimeBasedGuard.java new file mode 100644 index 0000000000..743ce10046 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/SimpleTimeBasedGuard.java @@ -0,0 +1,180 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import java.util.concurrent.atomic.AtomicLong; + +/** + * A simple time-based guard that limits the number of allowed operations within a sliding time window. + * This class is useful for rate limiting or preventing excessive actions over time periods. + * It supports time injection for testing purposes. + * + * @author Ceki Gülcü + * @since 1.5.22 + */ +public class SimpleTimeBasedGuard { + + private final long windowDurationMs; + private final int maxAllows; + + /** + * Default window duration in milliseconds: 30 minutes. + */ + public static final long DEFAULT_WINDOW_MS = 30*60_000L; // 30 minutes + + /** + * Default maximum number of allows per window: 2. + */ + public static final int DEFAULT_MAX_ALLOWS = 2; + + // Injectable time + private final AtomicLong artificialTime = new AtomicLong(-1L); + + // Current window state + private volatile long windowStartMs = 0; + private volatile int allowsUsed = 0; + + /** + * Creates a guard with custom limits. + * + * @param windowDurationMs how many millis per window (e.g. 30_000 for 30 minutes) + * @param maxAllows how many allows per window (e.g. 2) + */ + public SimpleTimeBasedGuard(long windowDurationMs, int maxAllows) { + if (windowDurationMs <= 0) throw new IllegalArgumentException("windowDurationMs must be > 0"); + if (maxAllows < 1) throw new IllegalArgumentException("maxAllows must be >= 1"); + + this.windowDurationMs = windowDurationMs; + this.maxAllows = maxAllows; + } + + /** + * Convenience: uses defaults — 2 allows every 30 minutes + */ + public SimpleTimeBasedGuard() { + this(DEFAULT_WINDOW_MS, DEFAULT_MAX_ALLOWS); + } + + /** + * Checks if an operation is allowed based on the current time window. + * If allowed, increments the usage count for the current window. + * If the window has expired, resets the window and allows the operation. + * + * @return true if the operation is allowed, false otherwise + */ + public synchronized boolean allow() { + long now = currentTimeMillis(); + + // First call ever + if (windowStartMs == 0) { + windowStartMs = now; + allowsUsed = 1; + return true; + } + + // Still in current window? + if (now < windowStartMs + windowDurationMs) { + if (allowsUsed < maxAllows) { + allowsUsed++; + return true; + } + return false; + } + + // New window → reset + windowStartMs = now; + allowsUsed = 1; + return true; + } + + // --- Time injection for testing --- + + /** + * Sets the artificial current time for testing purposes. + * When set, {@link #currentTimeMillis()} will return this value instead of {@link System#currentTimeMillis()}. + * + * @param timestamp the artificial timestamp in milliseconds + */ + public void setCurrentTimeMillis(long timestamp) { + this.artificialTime.set(timestamp); + } + + /** + * Clears the artificial time, reverting to using {@link System#currentTimeMillis()}. + */ + public void clearCurrentTime() { + this.artificialTime.set(-1L); + } + + private long currentTimeMillis() { + long t = artificialTime.get(); + return t >= 0 ? t : System.currentTimeMillis(); + } + + void incCurrentTimeMillis(long increment) { + artificialTime.getAndAdd(increment); + } + + // --- Helpful getters --- + + /** + * Returns the number of allows used in the current window. + * + * @return the number of allows used + */ + public int getAllowsUsed() { + return allowsUsed; + } + + /** + * Returns the number of allows remaining in the current window. + * + * @return the number of allows remaining + */ + public int getAllowsRemaining() { + return Math.max(0, maxAllows - allowsUsed); + } + + /** + * Returns the window duration in milliseconds. + * + * @return the window duration in milliseconds + */ + public long getWindowDuration() { + return windowDurationMs; + } + + /** + * Returns the maximum number of allows per window. + * + * @return the maximum number of allows + */ + public int getMaxAllows() { + return maxAllows; + } + + /** + * Returns the number of milliseconds until the next window starts. + * If no window has started yet, returns the full window duration. + * + * @return milliseconds until next window + */ + public long getMillisUntilNextWindow() { + if (windowStartMs == 0) return windowDurationMs; + long nextWindowStart = windowStartMs + windowDurationMs; + long now = currentTimeMillis(); + return Math.max(0, nextWindowStart - now); + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StatusListenerConfigHelper.java b/logback-core/src/main/java/ch/qos/logback/core/util/StatusListenerConfigHelper.java index 6234450b5a..ba10ba0be8 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/StatusListenerConfigHelper.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StatusListenerConfigHelper.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,20 +18,24 @@ import ch.qos.logback.core.spi.ContextAware; import ch.qos.logback.core.spi.LifeCycle; import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.status.OnErrorConsoleStatusListener; import ch.qos.logback.core.status.StatusListener; +import static ch.qos.logback.core.CoreConstants.STDOUT; +import static ch.qos.logback.core.CoreConstants.SYSOUT; + public class StatusListenerConfigHelper { public static void installIfAsked(Context context) { String slClass = OptionHelper.getSystemProperty(CoreConstants.STATUS_LISTENER_CLASS_KEY); - if (!OptionHelper.isEmpty(slClass)) { + if (!OptionHelper.isNullOrEmptyOrAllSpaces(slClass)) { addStatusListener(context, slClass); } } private static void addStatusListener(Context context, String listenerClassName) { StatusListener listener = null; - if (CoreConstants.SYSOUT.equalsIgnoreCase(listenerClassName)) { + if (SYSOUT.equalsIgnoreCase(listenerClassName) || STDOUT.equalsIgnoreCase(listenerClassName)) { listener = new OnConsoleStatusListener(); } else { listener = createListenerPerClassName(context, listenerClassName); @@ -62,12 +66,12 @@ private static StatusListener createListenerPerClassName(Context context, String } /** - * This utility method adds a new OnConsoleStatusListener to the context - * passed as parameter. - * - * @param context - * @since 1.0.1 - */ + * This utility method adds a new OnConsoleStatusListener to the context passed + * as parameter. + * + * @param context + * @since 1.0.1 + */ static public void addOnConsoleListenerInstance(Context context, OnConsoleStatusListener onConsoleStatusListener) { onConsoleStatusListener.setContext(context); boolean effectivelyAdded = context.getStatusManager().add(onConsoleStatusListener); diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java b/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java index 8c07be0e1c..004bc38d59 100755 --- a/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,176 +13,89 @@ */ package ch.qos.logback.core.util; -import java.io.PrintStream; -import java.util.Iterator; -import java.util.List; - import ch.qos.logback.core.Context; -import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.helpers.ThrowableToStringArray; -import ch.qos.logback.core.status.*; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusManager; -import static ch.qos.logback.core.status.StatusUtil.filterStatusListByTimeThreshold; +import java.io.PrintStream; +import java.util.List; +/** + * This class print status messages of a given {@link Context}. However, all its methods are + * static. Use {@link StatusPrinter2} instead + * + * @deprecated replaced by {@link StatusPrinter2} + */ public class StatusPrinter { - private static PrintStream ps = System.out; - - static CachingDateFormatter cachingDateFormat = new CachingDateFormatter("HH:mm:ss,SSS"); + private final static StatusPrinter2 SINGLETON = new StatusPrinter2(); public static void setPrintStream(PrintStream printStream) { - ps = printStream; + SINGLETON.setPrintStream(printStream); } /** - * Print the contents of the context statuses, but only if they contain - * warnings or errors. + * Print the contents of the context statuses, but only if they contain warnings + * or errors. * - * @param context + * @param context a context to print */ public static void printInCaseOfErrorsOrWarnings(Context context) { - printInCaseOfErrorsOrWarnings(context, 0); + SINGLETON.printInCaseOfErrorsOrWarnings(context, 0); } /** - * Print the contents of the context status, but only if they contain - * warnings or errors occurring later then the threshold. + * Print the contents of the context status, but only if they contain warnings + * or errors occurring later than the threshold. * - * @param context + * @param context a context to print + * @param threshold filter events later than the threshold */ public static void printInCaseOfErrorsOrWarnings(Context context, long threshold) { - if (context == null) { - throw new IllegalArgumentException("Context argument cannot be null"); - } - - StatusManager sm = context.getStatusManager(); - if (sm == null) { - ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); - } else { - StatusUtil statusUtil = new StatusUtil(context); - if (statusUtil.getHighestLevel(threshold) >= ErrorStatus.WARN) { - print(sm, threshold); - } - } + SINGLETON.printInCaseOfErrorsOrWarnings(context, threshold); } /** - * Print the contents of the context statuses, but only if they contain - * errors. + * Print the contents of the context statuses, but only if they contain errors. * - * @param context + * @param context a context to print */ public static void printIfErrorsOccured(Context context) { - if (context == null) { - throw new IllegalArgumentException("Context argument cannot be null"); - } - - StatusManager sm = context.getStatusManager(); - if (sm == null) { - ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); - } else { - StatusUtil statusUtil = new StatusUtil(context); - if (statusUtil.getHighestLevel(0) == ErrorStatus.ERROR) { - print(sm); - } - } + SINGLETON.printIfErrorsOccured(context); } /** * Print the contents of the context's status data. * - * @param context + * @param context a context to print */ public static void print(Context context) { - print(context, 0); + SINGLETON.print(context, 0); } /** - * Print context's status data with a timestamp higher than the threshold. - * @param context - */ + * Print context's status data with a timestamp higher than the threshold. + * + * @param context a context to print + * @param threshold filter events later than the threshold + */ public static void print(Context context, long threshold) { - if (context == null) { - throw new IllegalArgumentException("Context argument cannot be null"); - } - - StatusManager sm = context.getStatusManager(); - if (sm == null) { - ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); - } else { - print(sm, threshold); - } + SINGLETON.print(context, threshold); } public static void print(StatusManager sm) { - print(sm, 0); + SINGLETON.print(sm, 0); } public static void print(StatusManager sm, long threshold) { - StringBuilder sb = new StringBuilder(); - List filteredList = filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), threshold); - buildStrFromStatusList(sb, filteredList); - ps.println(sb.toString()); + SINGLETON.print(sm, threshold); } public static void print(List statusList) { - StringBuilder sb = new StringBuilder(); - buildStrFromStatusList(sb, statusList); - ps.println(sb.toString()); - } - - private static void buildStrFromStatusList(StringBuilder sb, List statusList) { - if (statusList == null) - return; - for (Status s : statusList) { - buildStr(sb, "", s); - } - } - - // private static void buildStrFromStatusManager(StringBuilder sb, StatusManager sm) { - // } - - private static void appendThrowable(StringBuilder sb, Throwable t) { - String[] stringRep = ThrowableToStringArray.convert(t); - - for (String s : stringRep) { - if (s.startsWith(CoreConstants.CAUSED_BY)) { - // nothing - } else if (Character.isDigit(s.charAt(0))) { - // if line resembles "48 common frames omitted" - sb.append("\t... "); - } else { - // most of the time. just add a tab+"at" - sb.append("\tat "); - } - sb.append(s).append(CoreConstants.LINE_SEPARATOR); - } + SINGLETON.print(statusList); } public static void buildStr(StringBuilder sb, String indentation, Status s) { - String prefix; - if (s.hasChildren()) { - prefix = indentation + "+ "; - } else { - prefix = indentation + "|-"; - } - - if (cachingDateFormat != null) { - String dateStr = cachingDateFormat.format(s.getDate()); - sb.append(dateStr).append(" "); - } - sb.append(prefix).append(s).append(CoreConstants.LINE_SEPARATOR); - - if (s.getThrowable() != null) { - appendThrowable(sb, s.getThrowable()); - } - if (s.hasChildren()) { - Iterator ite = s.iterator(); - while (ite.hasNext()) { - Status child = ite.next(); - buildStr(sb, indentation + " ", child); - } - } + SINGLETON.buildStr(sb, indentation, s); } - } diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter2.java b/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter2.java new file mode 100644 index 0000000000..cf41d4df89 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StatusPrinter2.java @@ -0,0 +1,196 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.helpers.ThrowableToStringArray; +import ch.qos.logback.core.status.ErrorStatus; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.StatusManager; +import ch.qos.logback.core.status.StatusUtil; + +import java.io.PrintStream; +import java.util.Iterator; +import java.util.List; + +import static ch.qos.logback.core.status.StatusUtil.filterStatusListByTimeThreshold; + +/** + * + * Same as StatusPrinter but with instance methods instead of static. + * + * @since 1.5.4 + */ +public class StatusPrinter2 { + + + private PrintStream ps = System.out; + + static CachingDateFormatter cachingDateFormat = new CachingDateFormatter("HH:mm:ss,SSS"); + + public void setPrintStream(PrintStream printStream) { + ps = printStream; + } + + /** + * Print the contents of the context statuses, but only if they contain warnings + * or errors. + * + * @param context a context to print + */ + public void printInCaseOfErrorsOrWarnings(Context context) { + printInCaseOfErrorsOrWarnings(context, 0); + } + + /** + * Print the contents of the context status, but only if they contain warnings + * or errors occurring later than the threshold. + * + * @param context a context to print + * @param threshold filter events later than the threshold + */ + public void printInCaseOfErrorsOrWarnings(Context context, long threshold) { + if (context == null) { + throw new IllegalArgumentException("Context argument cannot be null"); + } + + StatusManager sm = context.getStatusManager(); + if (sm == null) { + ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); + } else { + StatusUtil statusUtil = new StatusUtil(context); + if (statusUtil.getHighestLevel(threshold) >= ErrorStatus.WARN) { + print(sm, threshold); + } + } + } + + /** + * Print the contents of the context statuses, but only if they contain errors. + * + * @param context a context to print + */ + public void printIfErrorsOccured(Context context) { + if (context == null) { + throw new IllegalArgumentException("Context argument cannot be null"); + } + + StatusManager sm = context.getStatusManager(); + if (sm == null) { + ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); + } else { + StatusUtil statusUtil = new StatusUtil(context); + if (statusUtil.getHighestLevel(0) == ErrorStatus.ERROR) { + print(sm); + } + } + } + + /** + * Print the contents of the context's status data. + * + * @param context a context to print + */ + public void print(Context context) { + print(context, 0); + } + + /** + * Print context's status data with a timestamp higher than the threshold. + * + * @param context a context to print + */ + public void print(Context context, long threshold) { + if (context == null) { + throw new IllegalArgumentException("Context argument cannot be null"); + } + + StatusManager sm = context.getStatusManager(); + if (sm == null) { + ps.println("WARN: Context named \"" + context.getName() + "\" has no status manager"); + } else { + print(sm, threshold); + } + } + + public void print(StatusManager sm) { + print(sm, 0); + } + + public void print(StatusManager sm, long threshold) { + StringBuilder sb = new StringBuilder(); + List filteredList = filterStatusListByTimeThreshold(sm.getCopyOfStatusList(), threshold); + buildStrFromStatusList(sb, filteredList); + ps.println(sb.toString()); + } + + public void print(List statusList) { + StringBuilder sb = new StringBuilder(); + buildStrFromStatusList(sb, statusList); + ps.println(sb.toString()); + } + + private void buildStrFromStatusList(StringBuilder sb, List statusList) { + if (statusList == null) + return; + for (Status s : statusList) { + buildStr(sb, "", s); + } + } + + private void appendThrowable(StringBuilder sb, Throwable t) { + String[] stringRep = ThrowableToStringArray.convert(t); + + for (String s : stringRep) { + if (s.startsWith(CoreConstants.CAUSED_BY)) { + // nothing + } else if (Character.isDigit(s.charAt(0))) { + // if line resembles "48 common frames omitted" + sb.append("\t... "); + } else { + // most of the time. just add a tab+"at" + sb.append("\tat "); + } + sb.append(s).append(CoreConstants.LINE_SEPARATOR); + } + } + + public void buildStr(StringBuilder sb, String indentation, Status s) { + String prefix; + if (s.hasChildren()) { + prefix = indentation + "+ "; + } else { + prefix = indentation + "|-"; + } + + if (cachingDateFormat != null) { + String dateStr = cachingDateFormat.format(s.getTimestamp()); + sb.append(dateStr).append(" "); + } + sb.append(prefix).append(s).append(CoreConstants.LINE_SEPARATOR); + + if (s.getThrowable() != null) { + appendThrowable(sb, s.getThrowable()); + } + if (s.hasChildren()) { + Iterator ite = s.iterator(); + while (ite.hasNext()) { + Status child = ite.next(); + buildStr(sb, indentation + " ", child); + } + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StringCollectionUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/StringCollectionUtil.java index f0230b09dc..59401ddbce 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/StringCollectionUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StringCollectionUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -27,13 +27,13 @@ public class StringCollectionUtil { /** - * Retains all values in the subject collection that are matched by - * at least one of a collection of regular expressions. + * Retains all values in the subject collection that are matched by at least one + * of a collection of regular expressions. *

* This method is a convenience overload for * {@link #retainMatching(Collection, Collection)}. - * - * @param values subject value collection + * + * @param values subject value collection * @param patterns patterns to match */ public static void retainMatching(Collection values, String... patterns) { @@ -41,14 +41,14 @@ public static void retainMatching(Collection values, String... patterns) } /** - * Retains all values in the subject collection that are matched by - * at least one of a collection of regular expressions. + * Retains all values in the subject collection that are matched by at least one + * of a collection of regular expressions. *

* The semantics of this method are conceptually similar to - * {@link Collection#retainAll(Collection)}, but uses pattern matching - * instead of exact matching. + * {@link Collection#retainAll(Collection)}, but uses pattern matching instead + * of exact matching. * - * @param values subject value collection + * @param values subject value collection * @param patterns patterns to match */ public static void retainMatching(Collection values, Collection patterns) { @@ -67,13 +67,13 @@ public static void retainMatching(Collection values, Collection } /** - * Removes all values in the subject collection that are matched by - * at least one of a collection of regular expressions. + * Removes all values in the subject collection that are matched by at least one + * of a collection of regular expressions. *

* This method is a convenience overload for * {@link #removeMatching(Collection, Collection)}. * - * @param values subject value collection + * @param values subject value collection * @param patterns patterns to match */ public static void removeMatching(Collection values, String... patterns) { @@ -81,14 +81,14 @@ public static void removeMatching(Collection values, String... patterns) } /** - * Removes all values in the subject collection that are matched by - * at least one of a collection of regular expressions. + * Removes all values in the subject collection that are matched by at least one + * of a collection of regular expressions. *

* The semantics of this method are conceptually similar to - * {@link Collection#removeAll(Collection)}, but uses pattern matching - * instead of exact matching. + * {@link Collection#removeAll(Collection)}, but uses pattern matching instead + * of exact matching. * - * @param values subject value collection + * @param values subject value collection * @param patterns patterns to match */ public static void removeMatching(Collection values, Collection patterns) { diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java new file mode 100644 index 0000000000..c0a519ab3e --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/StringUtil.java @@ -0,0 +1,71 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import ch.qos.logback.core.CoreConstants; + +/** + * @since 1.5.0 + */ +public class StringUtil { + + public static String nullStringToEmpty(String input) { + if (input != null) + return input; + else + return CoreConstants.EMPTY_STRING; + } + + /** + * Returns true if input str is null or empty. + * + * @param str + * @return + */ + public static boolean isNullOrEmpty(String str) { + return ((str == null) || str.isEmpty()); + } + + /** + * Returns true if input str is not null nor empty. + * + * @param str + * @return + */ + public static boolean notNullNorEmpty(String str) { + return !isNullOrEmpty(str); + } + + public static String capitalizeFirstLetter(String name) { + if (isNullOrEmpty(name)) + return name; + + if (name.length() == 1) { + return name.toUpperCase(); + } else + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + public static String lowercaseFirstLetter(String name) { + if (isNullOrEmpty(name)) + return name; + + if (name.length() == 1) { + return name.toLowerCase(); + } else + return name.substring(0, 1).toLowerCase() + name.substring(1); + } + +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/SystemInfo.java b/logback-core/src/main/java/ch/qos/logback/core/util/SystemInfo.java index 2e8134abb9..8d0364c25a 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/SystemInfo.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/SystemInfo.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/TimeUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/TimeUtil.java index de6740dd49..7cdeecfc5d 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/TimeUtil.java +++ b/logback-core/src/main/java/ch/qos/logback/core/util/TimeUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/VersionUtil.java b/logback-core/src/main/java/ch/qos/logback/core/util/VersionUtil.java new file mode 100644 index 0000000000..b09b0f8079 --- /dev/null +++ b/logback-core/src/main/java/ch/qos/logback/core/util/VersionUtil.java @@ -0,0 +1,231 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.util; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.status.InfoStatus; +import ch.qos.logback.core.status.WarnStatus; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.module.ModuleDescriptor; +import java.util.Optional; +import java.util.Properties; + +import static ch.qos.logback.core.CoreConstants.NA; + + +/** + * Utility class for handling and validating version information of various artifacts. + * + *

It is used by logback-classic, logback-access-common, logback-access-jetty11, logback-access-tomcat, etc. + * to alert users about version discrepancies between dependent and dependee artifacts. + *

+ * + * @since 1.5.25 + */ +public class VersionUtil { + + /** + * Retrieves the version of an artifact, such as logback-core.jar, logback-access-common.jar etc. + * + *

The aClass parameter is assumed to be part of the artifact. + *

+ * + *

The method first attempts to get the version from the module information. If the module version + * is not available, it falls back to retrieving the implementation version from the package. + *

+ * + * @param aClass the class from which to retrieve the version information + * @return the version of the artifact where aClass is found, or null if the version cannot be determined + */ + static public String getVersionOfArtifact(Class aClass) { + String moduleVersion = getVersionOfClassByModule(aClass); + if (moduleVersion != null) + return moduleVersion; + + Package pkg = aClass.getPackage(); + if (pkg == null) { + return null; + } + return pkg.getImplementationVersion(); + } + + static public String nonNull(String input) { + if (input == null) { + return NA; + } else { + return input; + } + } + + /** + * Retrieves the version of an artifact from the artifact's module metadata. + * + *

If the module or its descriptor does not provide a version, the method returns null. + *

+ * + * @param aClass a class from which to retrieve the version information + * @return the version of class' module as a string, or null if the version cannot be determined + */ + static private String getVersionOfClassByModule(Class aClass) { + Module module = aClass.getModule(); + if (module == null) + return null; + + ModuleDescriptor md = module.getDescriptor(); + if (md == null) + return null; + Optional opt = md.rawVersion(); + return opt.orElse(null); + } + + /** + * Retrieves the version of a module using a properties file associated with the module. + * + *

Unfortunately, this code cannot be called by other modules. It needs to be duplicated.

+ * + *

The method looks for a properties file with a name derived from the moduleName parameter, + * in the same location, e.g. package, as the aClass parameter. It attempts to load the properties file + * and fetch the version information using a specific key. + *

+ * + *

The properties file is expected to be in the same package as the class provided, and named + * moduleName-version.properties. The properties file should contain a single key-value pair, + * where the key is moduleName-version, and the value is the module version. + * + * @param aClass the class used to locate the resource file, the properties file is expected to be in the same package + * @param moduleName the name of the module, which is used to construct the properties file name and the key + * @return the version of the module as a string, or null if the version cannot be determined + * @since 1.5.26 + */ + static public String getArtifactVersionBySelfDeclaredProperties(Class aClass, String moduleName) { + Properties props = new Properties(); + // example propertiesFileName: logback-core-version.properties + // + String propertiesFileName = moduleName + "-version.properties"; + String propertyKey = moduleName + "-version"; + try (InputStream is = aClass.getResourceAsStream(propertiesFileName)) { + if (is != null) { + props.load(is); + return props.getProperty(propertyKey); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + + // dependency synonym dependee + // depender synonym dependent + + static String getExpectedVersionOfDependencyByProperties(Class dependerClass, String propertiesFileName, String dependencyNameAsKey) { + Properties props = new Properties(); + // propertiesFileName : logback-access-common-dependees.properties + try (InputStream is = dependerClass.getClassLoader() + .getResourceAsStream(propertiesFileName)) { + if (is != null) { + props.load(is); + return props.getProperty(dependencyNameAsKey); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } + + + static public void checkForVersionEquality(Context context, Class dependerClass, Class dependencyClass, String dependentName, String dependencyName) { + // the depender depends on the dependency + String dependerVersion = nonNull(getVersionOfArtifact(dependerClass)); + String dependencyVersion = nonNull(getVersionOfArtifact(dependencyClass)); + + checkForVersionEquality(context, dependerVersion, dependencyVersion, dependentName, dependencyName); + } + + // depender depends on dependency + // dependency synonym dependee + // depender synonym dependent + static public void checkForVersionEquality(Context context, Class dependerClass, String dependencyVersion, String dependentName, String dependencyName) { + String dependerVersion = nonNull(getVersionOfArtifact(dependerClass)); + checkForVersionEquality(context, dependerVersion, dependencyVersion, dependentName, dependencyName); + } + + + /** + * Compares the versions of a dependent and a dependency to determine if they are equal. + * Updates the context's status manager with version information and logs a warning if the versions differ. + * + * @param context the logging context to which status messages are added + * @param dependentVersion the version string of the dependent component + * @param dependencyVersion the version string of the dependency component + * @param dependentName the name of the dependent component + * @param dependencyName the name of the dependency component + * @since 1.5.26 + */ + static public void checkForVersionEquality(Context context, String dependentVersion, String dependencyVersion, String dependentName, String dependencyName) { + // the dependent depends on the dependency + addFoundVersionStatus(context, dependentName, dependentVersion); + + if (dependentVersion.equals(NA) || !dependentVersion.equals(dependencyVersion)) { + addFoundVersionStatus(context, dependencyName, dependencyVersion); + String discrepancyMsg = String.format("Versions of %s and %s are different!", dependencyName, dependentName); + context.getStatusManager().add(new WarnStatus(discrepancyMsg, context)); + } + } + + + + + + private static void addFoundVersionStatus(Context context, String name, String version) { + String foundDependent = String.format("Found %s version %s", name, version); + context.getStatusManager().add(new InfoStatus(foundDependent, context)); + } + + + private static String nameToFilename(String name) { + return name + "-dependencies.properties"; + } + +// // dependency synonym dependee +// // depender synonym dependent +// static public void compareExpectedAndFoundVersion(Context context, Class dependerClass, Class dependencyClass, +// String dependerName, String dependencyName) { +// String actualDependencyVersion = nonNull(getVersionOfArtifact(dependencyClass)); +// String dependerVersion = nonNull(getVersionOfArtifact(dependerClass)); +// +// compareExpectedAndFoundVersion(context, actualDependencyVersion, dependerClass, dependerVersion, dependerName, dependencyName); +// } + + // dependency synonym dependee + // depender synonym dependent + static public void compareExpectedAndFoundVersion(Context context, String actualDependencyVersion, ClassdependerClass, String dependerVersion, + String dependerName, String dependencyName) { + + String expectedDependencyVersion = nonNull(getExpectedVersionOfDependencyByProperties(dependerClass, nameToFilename(dependerName), dependencyName)); + + addFoundVersionStatus(context, dependencyName, actualDependencyVersion); + addFoundVersionStatus(context, dependerName, dependerVersion); + + if (!expectedDependencyVersion.equals(actualDependencyVersion)) { + String discrepancyMsg = String.format("Expected version of %s is %s but found %s", dependencyName, expectedDependencyVersion, actualDependencyVersion); + context.getStatusManager().add(new WarnStatus(discrepancyMsg, context)); + } + } +} diff --git a/logback-core/src/main/java/ch/qos/logback/core/util/package.html b/logback-core/src/main/java/ch/qos/logback/core/util/package.html index 311d89f0b0..48e2353869 100644 --- a/logback-core/src/main/java/ch/qos/logback/core/util/package.html +++ b/logback-core/src/main/java/ch/qos/logback/core/util/package.html @@ -1,3 +1,17 @@ + + diff --git a/logback-core/src/main/java/module-info.java b/logback-core/src/main/java/module-info.java old mode 100755 new mode 100644 index a5a6842052..1cac384332 --- a/logback-core/src/main/java/module-info.java +++ b/logback-core/src/main/java/module-info.java @@ -1,51 +1,74 @@ -module ch.qos.logback.core { - requires static transitive java.sql; - requires static transitive java.naming; - requires static transitive java.xml; - requires static javax.mail.api; - requires static javax.servlet.api; +module ch.qos.logback.core { + requires transitive java.xml; + requires static java.sql; + + // required by the optional SMTPAppenderBase component + requires static java.naming; + requires static janino; requires static commons.compiler; - - + + // transitive _imposes_ the presence of jakarta.mail on downstream users, + // let them declare it if they need it + requires static jakarta.mail; + + // jakarta.servlet 5.0 is not modular + requires static jakarta.servlet; + + // optionally require jansi + requires static org.fusesource.jansi; + + // optionally require tukaani + requires static org.tukaani.xz; + exports ch.qos.logback.core; exports ch.qos.logback.core.boolex; - exports ch.qos.logback.core.db; exports ch.qos.logback.core.encoder; exports ch.qos.logback.core.helpers; exports ch.qos.logback.core.html; - - + exports ch.qos.logback.core.filter; + exports ch.qos.logback.core.hook; + exports ch.qos.logback.core.joran; exports ch.qos.logback.core.joran.action; + exports ch.qos.logback.core.joran.conditional; + exports ch.qos.logback.core.joran.event; + exports ch.qos.logback.core.joran.sanity; exports ch.qos.logback.core.joran.spi; - exports ch.qos.logback.core.joran.event; exports ch.qos.logback.core.joran.util; - exports ch.qos.logback.core.joran.conditional; - - + exports ch.qos.logback.core.joran.util.beans; + + + exports ch.qos.logback.core.layout; + + exports ch.qos.logback.core.model; + exports ch.qos.logback.core.model.conditional; + exports ch.qos.logback.core.model.processor; + exports ch.qos.logback.core.model.processor.conditional; + exports ch.qos.logback.core.model.util; + exports ch.qos.logback.core.net; exports ch.qos.logback.core.net.server; exports ch.qos.logback.core.net.ssl; - exports ch.qos.logback.core.pattern; exports ch.qos.logback.core.pattern.color; exports ch.qos.logback.core.pattern.parser; - + + exports ch.qos.logback.core.recovery; + exports ch.qos.logback.core.read; + exports ch.qos.logback.core.rolling; + exports ch.qos.logback.core.rolling.helper; + exports ch.qos.logback.core.sift; exports ch.qos.logback.core.spi; exports ch.qos.logback.core.status; + exports ch.qos.logback.core.testUtil; exports ch.qos.logback.core.util; - - - - - } diff --git a/logback-core/src/main/java21/ch/qos/logback/core/property/ConsoleCharsetPropertyDefiner.java b/logback-core/src/main/java21/ch/qos/logback/core/property/ConsoleCharsetPropertyDefiner.java new file mode 100644 index 0000000000..6a1486fc25 --- /dev/null +++ b/logback-core/src/main/java21/ch/qos/logback/core/property/ConsoleCharsetPropertyDefiner.java @@ -0,0 +1,49 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.property; + +import ch.qos.logback.core.PropertyDefinerBase; +import static ch.qos.logback.core.CoreConstants.NULL_STR; + +import java.io.Console; +import java.nio.charset.Charset; + +/** + * Compute the console's charset. + * + * @since 1.5.7 + */ +public class ConsoleCharsetPropertyDefiner extends PropertyDefinerBase { + @Override + public String getPropertyValue() { + // System.console().charset() requires Java 17 + Console console = System.console(); + if (console != null) { + Charset charset = console.charset(); + if (charset != null) { + String charsetName = charset.name(); + addInfo("Found value '" + charsetName + "' as returned by System.console()."); + return charsetName; + } else { + addInfo("System.console() returned null charset. Returning \"NULL\" string as defined value."); + return NULL_STR; + } + } else { + addWarn("System.console() returned null. Cannot compute console's charset, returning the \"NULL\" string, i.e. the default JVM charset"); + return NULL_STR; + } + } + +} diff --git a/logback-core/src/test/input/compress1.original b/logback-core/src/test/input/compress1.original new file mode 100644 index 0000000000..6ec9d8e577 --- /dev/null +++ b/logback-core/src/test/input/compress1.original @@ -0,0 +1,8 @@ + + LOGBack: the generic, reliable, fast and flexible logging framework. + + Copyright (C) 1999-2006, QOS.ch + + This library is free software, you can redistribute it and/or modify it under + the terms of the GNU Lesser General Public License as published by the Free + Software Foundation. diff --git a/logback-core/src/test/input/compress2.original b/logback-core/src/test/input/compress2.original new file mode 100644 index 0000000000..885e4175d1 --- /dev/null +++ b/logback-core/src/test/input/compress2.original @@ -0,0 +1,34 @@ + + + + + LOGBack Home + + + + + +

LOGBack project

+ +

LOGBack is intended as a successor to the popular log4j + project. It was also designed by Ceki Gülcü, the founder + of the log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. +

+ +

LOGBack's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, LOGBack is + divided into three modules, Core, Classic and Access. +

+ +

The Core module lays the groundwork for the other two + modules. The Classic module can be assimilated to an improved + version of log4j. The Access module integrates with Servlet + containers to provide HTPP-access log functionality. Note that you + can easily build your own modules on top of the Core module. +

+ + + + + diff --git a/logback-core/src/test/input/compress3.copy b/logback-core/src/test/input/compress3.copy deleted file mode 100644 index 87087da0cc..0000000000 --- a/logback-core/src/test/input/compress3.copy +++ /dev/null @@ -1,42 +0,0 @@ - - LOGBack: the generic, reliable, fast and flexible logging framework. - - Copyright (C) 1999-2006, QOS.ch - - This library is free software, you can redistribute it and/or modify it under - the terms of the GNU Lesser General Public License as published by the Free - Software Foundation. - - - - - LOGBack Home - - - - - -

LOGBack project

- -

LOGBack is intended as a successor to the popular log4j - project. It was also designed by Ceki Gülcü, the founder - of the log4j project. It builds upon exerience gained in building - industrial-strength logging systems going back as far as 1999. -

- -

LOGBack's basic architecture is sufficiently generic so as to - apply under different circumstances. At present time, LOGBack is - divided into three modules, Core, Classic and Access. -

- -

The Core module lays the groundwork for the other two - modules. The Classic module can be assimilated to an improved - version of log4j. The Access module integrates with Servlet - containers to provide HTPP-access log functionality. Note that you - can easily build your own modules on top of the Core module. -

- - - -
- diff --git a/logback-core/src/test/input/compress3.original b/logback-core/src/test/input/compress3.original new file mode 100644 index 0000000000..885e4175d1 --- /dev/null +++ b/logback-core/src/test/input/compress3.original @@ -0,0 +1,34 @@ + + + + + LOGBack Home + + + + + +

LOGBack project

+ +

LOGBack is intended as a successor to the popular log4j + project. It was also designed by Ceki Gülcü, the founder + of the log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. +

+ +

LOGBack's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, LOGBack is + divided into three modules, Core, Classic and Access. +

+ +

The Core module lays the groundwork for the other two + modules. The Classic module can be assimilated to an improved + version of log4j. The Access module integrates with Servlet + containers to provide HTPP-access log functionality. Note that you + can easily build your own modules on top of the Core module. +

+ + + +
+ diff --git a/logback-core/src/test/input/compress4.original b/logback-core/src/test/input/compress4.original new file mode 100644 index 0000000000..885e4175d1 --- /dev/null +++ b/logback-core/src/test/input/compress4.original @@ -0,0 +1,34 @@ + + + + + LOGBack Home + + + + + +

LOGBack project

+ +

LOGBack is intended as a successor to the popular log4j + project. It was also designed by Ceki Gülcü, the founder + of the log4j project. It builds upon exerience gained in building + industrial-strength logging systems going back as far as 1999. +

+ +

LOGBack's basic architecture is sufficiently generic so as to + apply under different circumstances. At present time, LOGBack is + divided into three modules, Core, Classic and Access. +

+ +

The Core module lays the groundwork for the other two + modules. The Classic module can be assimilated to an improved + version of log4j. The Access module integrates with Servlet + containers to provide HTPP-access log functionality. Note that you + can easily build your own modules on top of the Core module. +

+ + + +
+ diff --git a/logback-core/src/test/input/joran/define/badclass.xml b/logback-core/src/test/input/joran/define/badclass.xml index fde9b69424..e474ea4f5d 100644 --- a/logback-core/src/test/input/joran/define/badclass.xml +++ b/logback-core/src/test/input/joran/define/badclass.xml @@ -1 +1,3 @@ - + + + \ No newline at end of file diff --git a/logback-core/src/test/input/joran/define/good.xml b/logback-core/src/test/input/joran/define/good.xml index 60e9ca14e3..4e6c059c43 100644 --- a/logback-core/src/test/input/joran/define/good.xml +++ b/logback-core/src/test/input/joran/define/good.xml @@ -1,3 +1,5 @@ - - MONSTER - \ No newline at end of file + + + MONSTER + + diff --git a/logback-core/src/test/input/joran/define/noclass.xml b/logback-core/src/test/input/joran/define/noclass.xml index 60156aa1b0..12c5349745 100644 --- a/logback-core/src/test/input/joran/define/noclass.xml +++ b/logback-core/src/test/input/joran/define/noclass.xml @@ -1,3 +1,4 @@ - - Monster - \ No newline at end of file + + + Monster + \ No newline at end of file diff --git a/logback-core/src/test/input/joran/define/noname.xml b/logback-core/src/test/input/joran/define/noname.xml index ba9793a88c..f503df67aa 100644 --- a/logback-core/src/test/input/joran/define/noname.xml +++ b/logback-core/src/test/input/joran/define/noname.xml @@ -1,3 +1,5 @@ - - Monster - \ No newline at end of file + + + Monster + + \ No newline at end of file diff --git a/logback-core/src/test/input/joran/event-ssrf.xml b/logback-core/src/test/input/joran/event-ssrf.xml new file mode 100644 index 0000000000..5b5b57812f --- /dev/null +++ b/logback-core/src/test/input/joran/event-ssrf.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + XXX& + + \ No newline at end of file diff --git a/logback-core/src/test/input/joran/implicitAction/nestedComplexWithPassword.xml b/logback-core/src/test/input/joran/implicitAction/nestedComplexWithPassword.xml new file mode 100644 index 0000000000..a262f6d7b8 --- /dev/null +++ b/logback-core/src/test/input/joran/implicitAction/nestedComplexWithPassword.xml @@ -0,0 +1,21 @@ + + + + + ${A_PASSWORD} + hello + world + + \ No newline at end of file diff --git a/logback-core/src/test/input/joran/inclusion/intermediaryByFile.xml b/logback-core/src/test/input/joran/inclusion/intermediaryByFile.xml index dce2acb6c3..d5491fef89 100644 --- a/logback-core/src/test/input/joran/inclusion/intermediaryByFile.xml +++ b/logback-core/src/test/input/joran/inclusion/intermediaryByFile.xml @@ -1,5 +1,5 @@ - + diff --git a/logback-core/src/test/input/joran/inclusion/multiIncludeByFile.xml b/logback-core/src/test/input/joran/inclusion/multiIncludeByFile.xml index dd1d158db1..ab5c0bc747 100644 --- a/logback-core/src/test/input/joran/inclusion/multiIncludeByFile.xml +++ b/logback-core/src/test/input/joran/inclusion/multiIncludeByFile.xml @@ -1,7 +1,7 @@ - + - + - + diff --git a/logback-core/src/test/input/joran/inclusion/topByEntity.xml b/logback-core/src/test/input/joran/inclusion/topByEntity.xml index 519219086f..4c340d54d0 100755 --- a/logback-core/src/test/input/joran/inclusion/topByEntity.xml +++ b/logback-core/src/test/input/joran/inclusion/topByEntity.xml @@ -1,10 +1,10 @@ - ]> - + &includedEntity; - + diff --git a/logback-core/src/test/input/joran/inclusion/topByFile.xml b/logback-core/src/test/input/joran/inclusion/topByFile.xml index 4dc521c261..7041261dec 100644 --- a/logback-core/src/test/input/joran/inclusion/topByFile.xml +++ b/logback-core/src/test/input/joran/inclusion/topByFile.xml @@ -1,6 +1,6 @@ - + - + - + diff --git a/logback-core/src/test/input/joran/inclusion/topByResource.xml b/logback-core/src/test/input/joran/inclusion/topByResource.xml index 63e02db68f..03d750731d 100644 --- a/logback-core/src/test/input/joran/inclusion/topByResource.xml +++ b/logback-core/src/test/input/joran/inclusion/topByResource.xml @@ -1,8 +1,8 @@ - + - + - + diff --git a/logback-core/src/test/input/joran/inclusion/topByUrl.xml b/logback-core/src/test/input/joran/inclusion/topByUrl.xml index f49cd974de..93159d1705 100644 --- a/logback-core/src/test/input/joran/inclusion/topByUrl.xml +++ b/logback-core/src/test/input/joran/inclusion/topByUrl.xml @@ -1,6 +1,6 @@ - + - + - + diff --git a/logback-core/src/test/input/joran/inclusion/topOptional.xml b/logback-core/src/test/input/joran/inclusion/topOptional.xml index 3625575e20..f060c6f504 100644 --- a/logback-core/src/test/input/joran/inclusion/topOptional.xml +++ b/logback-core/src/test/input/joran/inclusion/topOptional.xml @@ -1,9 +1,9 @@ - + - + - + diff --git a/logback-core/src/test/input/joran/inclusion/topOptionalResource.xml b/logback-core/src/test/input/joran/inclusion/topOptionalResource.xml index 951b11d95c..439d7687ea 100644 --- a/logback-core/src/test/input/joran/inclusion/topOptionalResource.xml +++ b/logback-core/src/test/input/joran/inclusion/topOptionalResource.xml @@ -1,9 +1,9 @@ - + - + - + diff --git a/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java b/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java deleted file mode 100644 index d5bfbdcc5c..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/AllCoreTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ BasicStatusManagerTest.class, ch.qos.logback.core.status.PackageTest.class, ch.qos.logback.core.util.PackageTest.class, - ch.qos.logback.core.helpers.PackageTest.class, ch.qos.logback.core.subst.PackageTest.class, ch.qos.logback.core.pattern.PackageTest.class, - ch.qos.logback.core.PackageTest.class, ch.qos.logback.core.joran.PackageTest.class, ch.qos.logback.core.appender.PackageTest.class, - ch.qos.logback.core.spi.PackageTest.class, ch.qos.logback.core.rolling.PackageTest.class, ch.qos.logback.core.net.PackageTest.class, - ch.qos.logback.core.sift.PackageTest.class, ch.qos.logback.core.encoder.PackageTest.class, ch.qos.logback.core.recovery.PackageTest.class }) -public class AllCoreTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/AsyncAppenderBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/AsyncAppenderBaseTest.java index 3209be2b42..b2e58d5071 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/AsyncAppenderBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/AsyncAppenderBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -11,21 +11,23 @@ * under the terms of the GNU Lesser General Public License version 2.1 * as published by the Free Software Foundation. */ -package ch.qos.logback.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +package ch.qos.logback.core; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.helpers.NOPAppender; import ch.qos.logback.core.read.ListAppender; import ch.qos.logback.core.status.OnConsoleStatusListener; import ch.qos.logback.core.testUtil.DelayingListAppender; import ch.qos.logback.core.testUtil.NPEAppender; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import org.junit.jupiter.api.Timeout; + +import java.util.concurrent.TimeUnit; /** * @author Ceki Gülcü @@ -41,7 +43,7 @@ public class AsyncAppenderBaseTest { OnConsoleStatusListener onConsoleStatusListener = new OnConsoleStatusListener(); StatusChecker statusChecker = new StatusChecker(context); - @Before + @BeforeEach public void setUp() { onConsoleStatusListener.setContext(context); context.getStatusManager().add(onConsoleStatusListener); @@ -59,7 +61,8 @@ public void setUp() { delayingListAppender.start(); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void smoke() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); @@ -77,16 +80,17 @@ public void exceptionsShouldNotCauseHalting() throws InterruptedException { asyncAppenderBase.addAppender(npeAppender); asyncAppenderBase.start(); - assertTrue(asyncAppenderBase.isStarted()); + Assertions.assertTrue(asyncAppenderBase.isStarted()); for (int i = 0; i < 10; i++) asyncAppenderBase.append(i); asyncAppenderBase.stop(); - assertFalse(asyncAppenderBase.isStarted()); - assertEquals(AppenderBase.ALLOWED_REPEATS, statusChecker.matchCount("Appender \\[bad\\] failed to append.")); + Assertions.assertFalse(asyncAppenderBase.isStarted()); + Assertions.assertEquals(AppenderBase.ALLOWED_REPEATS, statusChecker.matchCount("Appender \\[bad\\] failed to append.")); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void emptyQueueShouldBeStoppable() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); @@ -94,7 +98,8 @@ public void emptyQueueShouldBeStoppable() { verify(listAppender, 0); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void workerShouldStopEvenIfInterruptExceptionConsumedWithinSubappender() { delayingListAppender.delay = 100; asyncAppenderBase.addAppender(delayingListAppender); @@ -102,25 +107,29 @@ public void workerShouldStopEvenIfInterruptExceptionConsumedWithinSubappender() asyncAppenderBase.doAppend(0); asyncAppenderBase.stop(); verify(delayingListAppender, 1); - assertTrue(delayingListAppender.interrupted); + Assertions.assertTrue(delayingListAppender.interrupted); Thread.interrupted(); } - @Test(timeout = 2000) - public void noEventLoss() { + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) + public void noEventLoss() throws InterruptedException { int bufferSize = 10; int loopLen = bufferSize * 2; asyncAppenderBase.addAppender(delayingListAppender); asyncAppenderBase.setQueueSize(bufferSize); + asyncAppenderBase.setMaxFlushTime(2000); asyncAppenderBase.start(); for (int i = 0; i < loopLen; i++) { asyncAppenderBase.doAppend(i); } + asyncAppenderBase.stop(); verify(delayingListAppender, loopLen); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void eventLossIfNeverBlock() { int bufferSize = 10; int loopLen = bufferSize * 200; @@ -138,7 +147,8 @@ public void eventLossIfNeverBlock() { statusChecker.assertIsErrorFree(); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void lossyAppenderShouldOnlyLoseCertainEvents() { int bufferSize = 5; int loopLen = bufferSize * 2; @@ -151,12 +161,13 @@ public void lossyAppenderShouldOnlyLoseCertainEvents() { } lossyAsyncAppender.stop(); // events 0, 3, 6 and 9 are discardable. However, for events 0 and 3 - // the buffer is not not yet full. Thus, only events 6 and 9 will be + // the buffer is not yet full. Thus, only events 6 and 9 will be // effectively discarded. verify(delayingListAppender, loopLen - 2); } - @Test(timeout = 2000) + @Test + @Timeout(value=2, unit = TimeUnit.SECONDS) public void lossyAppenderShouldBeNonLossyIfDiscardingThresholdIsZero() { int bufferSize = 5; int loopLen = bufferSize * 2; @@ -175,15 +186,15 @@ public void lossyAppenderShouldBeNonLossyIfDiscardingThresholdIsZero() { public void invalidQueueCapacityShouldResultInNonStartedAppender() { asyncAppenderBase.addAppender(new NOPAppender()); asyncAppenderBase.setQueueSize(0); - assertEquals(0, asyncAppenderBase.getQueueSize()); + Assertions.assertEquals(0, asyncAppenderBase.getQueueSize()); asyncAppenderBase.start(); - assertFalse(asyncAppenderBase.isStarted()); + Assertions.assertFalse(asyncAppenderBase.isStarted()); statusChecker.assertContainsMatch("Invalid queue size"); } - @SuppressWarnings("deprecation") @Test - public void workerThreadFlushesOnStop() { + @Disabled + public void workerThreadFlushesOnStop() throws InterruptedException { int loopLen = 5; int maxRuntime = (loopLen + 1) * Math.max(1000, delayingListAppender.delay); ListAppender la = delayingListAppender; @@ -196,17 +207,16 @@ public void workerThreadFlushesOnStop() { for (int i = 0; i < loopLen; i++) { asyncAppenderBase.doAppend(i); } - assertEquals(loopLen, asyncAppenderBase.getNumberOfElementsInQueue()); - assertEquals(0, la.list.size()); + Assertions.assertEquals(loopLen, asyncAppenderBase.getNumberOfElementsInQueue()); + Assertions.assertEquals(0, la.list.size()); asyncAppenderBase.worker.resume(); asyncAppenderBase.stop(); - assertEquals(0, asyncAppenderBase.getNumberOfElementsInQueue()); + Assertions.assertEquals(0, asyncAppenderBase.getNumberOfElementsInQueue()); verify(la, loopLen); } - // @SuppressWarnings("deprecation") @Test public void stopExitsWhenMaxRuntimeReached() throws InterruptedException { int maxFlushTime = 1; // runtime of 0 means wait forever, so use 1 ms instead @@ -239,12 +249,12 @@ public void verifyInterruptionIsNotSwallowed() { asyncAppenderBase.start(); Thread.currentThread().interrupt(); asyncAppenderBase.doAppend(Integer.valueOf(0)); - assertTrue(Thread.currentThread().isInterrupted()); + Assertions.assertTrue(Thread.currentThread().isInterrupted()); // clear interrupt flag for subsequent tests Thread.interrupted(); } - // Interruption of current thread should not prevent logging. + // Interruption of current thread should not prevent logging. // See also http://jira.qos.ch/browse/LOGBACK-910 // and https://jira.qos.ch/browse/LOGBACK-1247 @Test @@ -255,43 +265,46 @@ public void verifyInterruptionDoesNotPreventLogging() { Thread.currentThread().interrupt(); asyncAppenderBase.doAppend(Integer.valueOf(1)); asyncAppenderBase.doAppend(Integer.valueOf(1)); - assertTrue(Thread.currentThread().isInterrupted()); + Assertions.assertTrue(Thread.currentThread().isInterrupted()); // the interruption needs to be consumed Thread.interrupted(); asyncAppenderBase.stop(); verify(listAppender, 3); } - + @Test public void verifyInterruptionFlagWhenStopping_INTERUPPTED() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); Thread.currentThread().interrupt(); asyncAppenderBase.stop(); - assertTrue(Thread.currentThread().isInterrupted()); + Assertions.assertTrue(Thread.currentThread().isInterrupted()); Thread.interrupted(); } - + @Test public void verifyInterruptionFlagWhenStopping_NOT_INTERUPPTED() { asyncAppenderBase.addAppender(listAppender); asyncAppenderBase.start(); asyncAppenderBase.stop(); - assertFalse(Thread.currentThread().isInterrupted()); + Assertions.assertFalse(Thread.currentThread().isInterrupted()); } - - + + // In JDK non started threads can be interrupted + @Disabled @Test public void verifyInterruptionOfWorkerIsSwallowed() { asyncAppenderBase.addAppender(delayingListAppender); asyncAppenderBase.start(); + Thread.yield(); asyncAppenderBase.stop(); - assertFalse(asyncAppenderBase.worker.isInterrupted()); + Assertions.assertFalse(asyncAppenderBase.worker.isInterrupted()); } private void verify(ListAppender la, int atLeast) { - assertFalse(la.isStarted()); - assertTrue(atLeast + " <= " + la.list.size(), atLeast <= la.list.size()); + // ListAppender passes as parameter should be stopped at this stage + Assertions.assertFalse(la.isStarted()); + Assertions.assertTrue( atLeast <= la.list.size(), atLeast + " <= " + la.list.size()); statusChecker.assertIsErrorFree(); statusChecker.assertContainsMatch("Worker thread will flush remaining events before exiting."); } @@ -310,10 +323,12 @@ public void checkThatStartMethodIsIdempotent() { // we don't need mockito for this test, but if we did here is how it would look // AsyncAppenderBase spied = Mockito.spy(asyncAppenderBase); - // Mockito.doThrow(new IllegalStateException("non idempotent start")).when((UnsynchronizedAppenderBase) + // Mockito.doThrow(new IllegalStateException("non idempotent + // start")).when((UnsynchronizedAppenderBase) // spied).start(); - // a second invocation of start will cause a IllegalThreadStateException thrown by the asyncAppenderBase.worker + // a second invocation of start will cause a IllegalThreadStateException thrown + // by the asyncAppenderBase.worker // thread asyncAppenderBase.start(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java b/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java index 9d49f48657..140832723f 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/BasicStatusManagerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,15 +15,21 @@ import static ch.qos.logback.core.BasicStatusManager.MAX_HEADER_COUNT; import static ch.qos.logback.core.BasicStatusManager.TAIL_SIZE; -import static org.junit.Assert.*; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.ArrayList; import java.util.List; import ch.qos.logback.core.status.OnConsoleStatusListener; +import ch.qos.logback.core.status.StatusBase; import ch.qos.logback.core.status.StatusListener; -import org.junit.Test; +import ch.qos.logback.core.status.StatusUtil; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.status.ErrorStatus; import ch.qos.logback.core.status.Status; @@ -47,21 +53,44 @@ public void smoke() { public void many() { int margin = 300; int len = MAX_HEADER_COUNT + TAIL_SIZE + margin; + List witness = new ArrayList(); for (int i = 0; i < len; i++) { - bsm.add(new ErrorStatus("" + i, this)); + Status s = new ErrorStatus("" + i, this); + bsm.add(s); + if(i < MAX_HEADER_COUNT) { + witness.add(s); + } + if(i >= MAX_HEADER_COUNT + margin) { + witness.add(s); + } } List statusList = bsm.getCopyOfStatusList(); assertNotNull(statusList); assertEquals(MAX_HEADER_COUNT + TAIL_SIZE, statusList.size()); - List witness = new ArrayList(); - for (int i = 0; i < MAX_HEADER_COUNT; i++) { - witness.add(new ErrorStatus("" + i, this)); - } - for (int i = 0; i < TAIL_SIZE; i++) { - witness.add(new ErrorStatus("" + (MAX_HEADER_COUNT + margin + i), this)); + + arrayDiff(witness, statusList); + } + + private void arrayDiff(List witness, List otherList) { + int witnessSize = witness.size(); + int otherSize = otherList.size(); + boolean diff = false; + for(int i = 0; i < witness.size(); i++) { + + Status w = witness.get(i); + Status o = otherList.get(i); + if(!w.equals(o)) { + System.out.println("at "+i + " differs w.message=" + w.getMessage() + " and o.message=" +o.getMessage()); + String diffMsg = StatusUtil.diff(w, o); + System.out.println(diffMsg); + diff = true; + } } - assertEquals(witness, statusList); + + + assertEquals(witnessSize, otherSize, "witnessSize="+witnessSize+" does not match resultSize="+otherSize); + assertFalse(diff, "diff detected"); } @Test diff --git a/logback-core/src/test/java/ch/qos/logback/core/ContextBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/ContextBaseTest.java index 03ccb88a5d..03621e0ad0 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/ContextBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/ContextBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,19 +13,15 @@ */ package ch.qos.logback.core; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import org.junit.Test; - -import ch.qos.logback.core.spi.LifeCycle; import java.util.ArrayList; import java.util.concurrent.ExecutorService; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import ch.qos.logback.core.spi.LifeCycle; + public class ContextBaseTest { private InstrumentedLifeCycleManager lifeCycleManager = new InstrumentedLifeCycleManager(); @@ -49,7 +45,7 @@ public void renameTest() { context.setName("hello"); try { context.setName("x"); - fail("renaming is not allowed"); + Assertions.fail("renaming is not allowed"); } catch (IllegalStateException ise) { } } @@ -59,26 +55,26 @@ public void resetTest() { context.setName("hello"); context.putProperty("keyA", "valA"); context.putObject("keyA", "valA"); - assertEquals("valA", context.getProperty("keyA")); - assertEquals("valA", context.getObject("keyA")); + Assertions.assertEquals("valA", context.getProperty("keyA")); + Assertions.assertEquals("valA", context.getObject("keyA")); MockLifeCycleComponent component = new MockLifeCycleComponent(); context.register(component); - assertSame(component, lifeCycleManager.getLastComponent()); + Assertions.assertSame(component, lifeCycleManager.getLastComponent()); context.reset(); - assertNull(context.getProperty("keyA")); - assertNull(context.getObject("keyA")); - assertTrue(lifeCycleManager.isReset()); + Assertions.assertNull(context.getProperty("keyA")); + Assertions.assertNull(context.getObject("keyA")); + Assertions.assertTrue(lifeCycleManager.isReset()); } @Test public void contextNameProperty() { - assertNull(context.getProperty(CoreConstants.CONTEXT_NAME_KEY)); + Assertions.assertNull(context.getProperty(CoreConstants.CONTEXT_NAME_KEY)); String HELLO = "hello"; context.setName(HELLO); - assertEquals(HELLO, context.getProperty(CoreConstants.CONTEXT_NAME_KEY)); + Assertions.assertEquals(HELLO, context.getProperty(CoreConstants.CONTEXT_NAME_KEY)); // good to have a raw reference to the "CONTEXT_NAME" as most clients would // not go through CoreConstants - assertEquals(HELLO, context.getProperty("CONTEXT_NAME")); + Assertions.assertEquals(HELLO, context.getProperty("CONTEXT_NAME")); } private static class InstrumentedContextBase extends ContextBase { @@ -141,7 +137,7 @@ public void run() { executingThreads.wait(); } } - assertTrue("executing thread should be a daemon thread.", executingThreads.get(0).isDaemon()); + Assertions.assertTrue(executingThreads.get(0).isDaemon(), "executing thread should be a daemon thread."); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilienceTest.java b/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilienceTest.java index ebc6055972..c9824d066f 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilienceTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilienceTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,22 +17,27 @@ import java.io.IOException; import java.nio.channels.FileChannel; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.recovery.RecoveryCoordinator; +import ch.qos.logback.core.recovery.RecoveryListener; import ch.qos.logback.core.recovery.ResilientFileOutputStream; import ch.qos.logback.core.status.OnConsoleStatusListener; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.util.ResilienceUtil; -public class FileAppenderResilienceTest { +public class FileAppenderResilienceTest implements RecoveryListener { FileAppender fa = new FileAppender(); + + ResilientFileOutputStream resilientFOS; + Context context = new ContextBase(); int diff = RandomUtil.getPositiveInt(); String outputDirStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "resilience-" + diff + "/"; @@ -41,7 +46,13 @@ public class FileAppenderResilienceTest { // "/";; String logfileStr = outputDirStr + "output.log"; - @Before + boolean failedState = false; + + int recoveryCounter = 0; + int failureCounter = 0; + + + @BeforeEach public void setUp() throws InterruptedException { context.getStatusManager().add(new OnConsoleStatusListener()); @@ -54,10 +65,13 @@ public void setUp() throws InterruptedException { fa.setEncoder(new EchoEncoder()); fa.setFile(logfileStr); fa.start(); + resilientFOS = (ResilientFileOutputStream) fa.getOutputStream(); + resilientFOS.addRecoveryListener(this); + } @Test - @Ignore + @Disabled public void manual() throws InterruptedException, IOException { Runner runner = new Runner(fa); Thread t = new Thread(runner); @@ -83,11 +97,29 @@ public void smoke() throws InterruptedException, IOException { t.join(); double bestCaseSuccessRatio = 1 / delayCoefficient; - // expect to loose at most 35% of the events + // expect to lose at most 35% of the events double lossinessFactor = 0.35; double resilianceFactor = (1 - lossinessFactor); - ResilienceUtil.verify(logfileStr, "^hello (\\d{1,5})$", runner.getCounter(), bestCaseSuccessRatio * resilianceFactor); + Assertions.assertTrue(recoveryCounter > 0, "at least one recovery should have occured"); + Assertions.assertTrue(failureCounter > 0, "at least one failure should have occured"); + + System.out.println("recoveryCounter=" + recoveryCounter); + System.out.println("failureCounter=" + failureCounter); + + + + String errmsg0 = "failureCounter="+failureCounter+" must be greater or equal to recoveryCounter="+recoveryCounter; + Assertions.assertTrue(failureCounter >= recoveryCounter, errmsg0); + + String errmsg1 = "Difference between failureCounter="+failureCounter+" and recoveryCounter="+recoveryCounter+" should not exceeed 1"; + Assertions.assertTrue(failureCounter - recoveryCounter <= 1, errmsg1); + + + + int actuallyWritten = ResilienceUtil.countLines(logfileStr, "^hello (\\d{1,5})$"); + long exptectedWrites = runner.getCounter()-recoveryCounter; + Assertions.assertEquals(exptectedWrites, actuallyWritten); } private void closeLogFileOnPurpose() throws IOException { @@ -95,26 +127,42 @@ private void closeLogFileOnPurpose() throws IOException { FileChannel fileChannel = resilientFOS.getChannel(); fileChannel.close(); } -} -class Runner extends RunnableWithCounterAndDone { - FileAppender fa; + @Override + public void newFailure(IOException e) { + failedState = true; + failureCounter++; + + } - Runner(FileAppender fa) { - this.fa = fa; + @Override + public void recoveryOccured() { + failedState = false; + recoveryCounter++; } + + class Runner extends RunnableWithCounterAndDone { + FileAppender fa; + + Runner(FileAppender fa) { + this.fa = fa; + } - public void run() { - while (!isDone()) { - counter++; - fa.doAppend("hello " + counter); - if (counter % 128 == 0) { - try { - Thread.sleep(10); - } catch (InterruptedException e) { + public void run() { + while (!isDone()) { + fa.doAppend("hello " + counter); + if(!FileAppenderResilienceTest.this.failedState) { + counter++; + } + if (counter % 128 == 0) { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + } } } } + } +} -} \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilience_AS_ROOT_Test.java b/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilience_AS_ROOT_Test.java index afbd93a615..2e8869125a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilience_AS_ROOT_Test.java +++ b/logback-core/src/test/java/ch/qos/logback/core/FileAppenderResilience_AS_ROOT_Test.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,15 +18,17 @@ import java.io.IOException; import ch.qos.logback.core.testUtil.EnvUtilForTests; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.util.ResilienceUtil; import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.Test; +@Disabled public class FileAppenderResilience_AS_ROOT_Test { static String MOUNT_POINT = "/mnt/loop/"; @@ -50,7 +52,7 @@ static boolean isConformingHost() { return EnvUtilForTests.isLocalHostNameInList(new String[] { "haro" }); } - @Before + @BeforeEach public void setUp() throws IOException, InterruptedException { if (!isConformingHost()) { return; @@ -87,7 +89,7 @@ void dump(String file) throws IOException { } } - @After + @AfterEach public void tearDown() throws IOException, InterruptedException { if (!isConformingHost()) { return; @@ -114,9 +116,9 @@ public void go() throws IOException, InterruptedException { Thread.sleep(DELAY); } p.waitFor(); - // the extrernal script has the file system ready for IO 50% of the time + // the external script has the file system ready for IO 50% of the time double bestCase = 0.5; - ResilienceUtil.verify(logfileStr, "^(\\d{1,3}) x*$", NUM_STEPS, bestCase * 0.6); + ResilienceUtil.countLines(logfileStr, "^(\\d{1,3}) x*$"); System.out.println("Done go"); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/LifeCycleManagerTest.java b/logback-core/src/test/java/ch/qos/logback/core/LifeCycleManagerTest.java index 0fe44b7733..b19ee9461c 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/LifeCycleManagerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/LifeCycleManagerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,8 @@ */ package ch.qos.logback.core; -import static org.junit.Assert.assertFalse; - -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Unit tests for {@link LifeCycleManager}. @@ -32,7 +31,7 @@ public void testRegisterAndReset() { manager.register(component); component.start(); manager.reset(); - assertFalse(component.isStarted()); + Assertions.assertFalse(component.isStarted()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/MockLifeCycleComponent.java b/logback-core/src/test/java/ch/qos/logback/core/MockLifeCycleComponent.java index b3549af616..e5a9f126f1 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/MockLifeCycleComponent.java +++ b/logback-core/src/test/java/ch/qos/logback/core/MockLifeCycleComponent.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/OutputStreamAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/OutputStreamAppenderTest.java index a0bf43eabb..5875bbbe92 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/OutputStreamAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/OutputStreamAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,12 @@ */ package ch.qos.logback.core; -import static org.junit.Assert.assertTrue; - import java.io.ByteArrayOutputStream; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.encoder.LayoutWrappingEncoder; import ch.qos.logback.core.pattern.parser.SamplePatternLayout; @@ -28,11 +27,11 @@ public class OutputStreamAppenderTest { Context context = new ContextBase(); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } @@ -81,9 +80,10 @@ public void nullFileFooter() { headerFooterCheck(FILE_HEADER, PRESENTATION_HEADER, PRESENTATION_FOOTER, FILE_FOOTER); } - public void headerFooterCheck(String fileHeader, String presentationHeader, String presentationFooter, String fileFooter) { - OutputStreamAppender wa = new OutputStreamAppender(); - wa.setContext(context); + public void headerFooterCheck(String fileHeader, String presentationHeader, String presentationFooter, + String fileFooter) { + OutputStreamAppender osa = new OutputStreamAppender(); + osa.setContext(context); ByteArrayOutputStream baos = new ByteArrayOutputStream(); SamplePatternLayout spl = new SamplePatternLayout(); @@ -99,20 +99,21 @@ public void headerFooterCheck(String fileHeader, String presentationHeader, Stri encoder.setLayout(spl); encoder.setContext(context); - wa.setEncoder(encoder); - wa.setOutputStream(baos); - wa.start(); + osa.setEncoder(encoder); + osa.setOutputStream(baos); + osa.start(); - wa.stop(); + osa.stop(); String result = baos.toString(); String expectedHeader = emtptyIfNull(fileHeader) + emtptyIfNull(presentationHeader); System.out.println(result); - assertTrue(result, result.startsWith(expectedHeader)); + + Assertions.assertTrue(result.startsWith(expectedHeader), result); String expectedFooter = emtptyIfNull(presentationFooter) + emtptyIfNull(fileFooter); - assertTrue(result, result.endsWith(expectedFooter)); + Assertions.assertTrue(result.endsWith(expectedFooter), result); } String emtptyIfNull(String s) { diff --git a/logback-core/src/test/java/ch/qos/logback/core/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/PackageTest.java deleted file mode 100644 index d5ffd24975..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ContextBaseTest.class, OutputStreamAppenderTest.class, FileAppenderResilienceTest.class, FileAppenderResilience_AS_ROOT_Test.class, - AsyncAppenderBaseTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/PrudentFileAppenderInterruptTest.java b/logback-core/src/test/java/ch/qos/logback/core/PrudentFileAppenderInterruptTest.java index 579e083c6e..95a0eb1268 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/PrudentFileAppenderInterruptTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/PrudentFileAppenderInterruptTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,15 +18,16 @@ import java.io.FileReader; import java.io.IOException; import java.util.concurrent.CountDownLatch; -import org.junit.Before; -import org.junit.Test; -import static org.junit.Assert.assertEquals; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; + +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.status.OnConsoleStatusListener; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; +import org.junit.jupiter.api.Test; public class PrudentFileAppenderInterruptTest { @@ -36,7 +37,7 @@ public class PrudentFileAppenderInterruptTest { String outputDirStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "resilience-" + diff + "/"; String logfileStr = outputDirStr + "output.log"; - @Before + @BeforeEach public void setUp() throws InterruptedException { context.getStatusManager().add(new OnConsoleStatusListener()); @@ -53,7 +54,7 @@ public void setUp() throws InterruptedException { @Test public void smoke() throws InterruptedException, IOException { - Runner runner = new Runner(fa); + InterruptAndLogRunner runner = new InterruptAndLogRunner(fa); Thread t = new Thread(runner); t.start(); @@ -71,15 +72,15 @@ public void smoke() throws InterruptedException, IOException { fr.close(); br.close(); - assertEquals("Incorrect number of logged lines", 2, totalLines); + Assertions.assertEquals(2, totalLines, "Incorrect number of logged lines "+outputDirStr); } - class Runner extends RunnableWithCounterAndDone { + class InterruptAndLogRunner extends RunnableWithCounterAndDone { FileAppender fa; CountDownLatch latch = new CountDownLatch(1); // Just to make sure this is executed before we log in the test // method - Runner(FileAppender fa) { + InterruptAndLogRunner(FileAppender fa) { this.fa = fa; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/AbstractAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/appender/AbstractAppenderTest.java index b23be70e04..64b5ea99fa 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/AbstractAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/appender/AbstractAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,13 @@ */ package ch.qos.logback.core.appender; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - import ch.qos.logback.core.Appender; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; abstract public class AbstractAppenderTest { @@ -36,17 +33,17 @@ abstract public class AbstractAppenderTest { public void testNewAppender() { // new appenders should be inactive Appender appender = getAppender(); - assertFalse(appender.isStarted()); + Assertions.assertFalse(appender.isStarted()); } @Test public void testConfiguredAppender() { Appender appender = getConfiguredAppender(); appender.start(); - assertTrue(appender.isStarted()); + Assertions.assertTrue(appender.isStarted()); appender.stop(); - assertFalse(appender.isStarted()); + Assertions.assertFalse(appender.isStarted()); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/ConsoleAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/appender/ConsoleAppenderTest.java index 1b620cc6fa..85e5f56dd5 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/ConsoleAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/appender/ConsoleAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,60 +16,64 @@ import ch.qos.logback.core.Appender; import ch.qos.logback.core.ConsoleAppender; import ch.qos.logback.core.CoreConstants; -import ch.qos.logback.core.encoder.DummyEncoder; +import ch.qos.logback.core.testUtil.DummyEncoder; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.encoder.NopEncoder; import ch.qos.logback.core.layout.DummyLayout; import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.testUtil.XTeeOutputStream; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.io.PrintStream; -import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; +import java.nio.charset.StandardCharsets; /** - * Redirecting System.out is quite messy. Disable this test in Maven bu not in Package.class + * Redirecting System.out is quite messy. Disable this test in Maven but not in + * Package.class */ public class ConsoleAppenderTest extends AbstractAppenderTest { - XTeeOutputStream tee; - PrintStream original; + XTeeOutputStream teeOut; + XTeeOutputStream teeErr; + PrintStream originalOut; + PrintStream originalErr; - @Before + @BeforeEach public void setUp() { - original = System.out; - // tee will output bytes on System out but it will also + originalOut = System.out; + originalErr = System.err; + // teeOut will output bytes on System out but it will also // collect them so that the output can be compared against - // some expected output data - // tee = new TeeOutputStream(original); - + // keep the console quiet - tee = new XTeeOutputStream(null); + teeOut = new XTeeOutputStream(null); + teeErr = new XTeeOutputStream(null); - // redirect System.out to tee - System.setOut(new PrintStream(tee)); + // redirect System.out to teeOut and System.err to teeErr + System.setOut(new PrintStream(teeOut)); + System.setErr(new PrintStream(teeErr)); } - @After + @AfterEach public void tearDown() { - System.setOut(original); + System.setOut(originalOut); + System.setErr(originalErr); } @Override public Appender getAppender() { - return new ConsoleAppender(); + return new ConsoleAppender<>(); } protected Appender getConfiguredAppender() { - ConsoleAppender ca = new ConsoleAppender(); - ca.setEncoder(new NopEncoder()); + ConsoleAppender ca = new ConsoleAppender<>(); + ca.setEncoder(new NopEncoder<>()); ca.start(); return ca; } @@ -77,28 +81,28 @@ protected Appender getConfiguredAppender() { @Test public void smoke() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - ca.setEncoder(new DummyEncoder()); + ca.setEncoder(new DummyEncoder<>()); ca.start(); ca.doAppend(new Object()); - assertEquals(DummyLayout.DUMMY, tee.toString()); + Assertions.assertEquals(DummyLayout.DUMMY, teeOut.toString()); } @Test public void open() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - DummyEncoder dummyEncoder = new DummyEncoder(); + DummyEncoder dummyEncoder = new DummyEncoder<>(); dummyEncoder.setFileHeader("open"); ca.setEncoder(dummyEncoder); ca.start(); ca.doAppend(new Object()); ca.stop(); - assertEquals("open" + CoreConstants.LINE_SEPARATOR + DummyLayout.DUMMY, tee.toString()); + Assertions.assertEquals("open" + CoreConstants.LINE_SEPARATOR + DummyLayout.DUMMY, teeOut.toString()); } @Test public void testClose() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - DummyEncoder dummyEncoder = new DummyEncoder(); + DummyEncoder dummyEncoder = new DummyEncoder<>(); dummyEncoder.setFileFooter("CLOSED"); ca.setEncoder(dummyEncoder); ca.start(); @@ -106,42 +110,42 @@ public void testClose() { ca.stop(); // ConsoleAppender must keep the underlying stream open. // The console is not ours to close. - assertFalse(tee.isClosed()); - assertEquals(DummyLayout.DUMMY + "CLOSED", tee.toString()); + Assertions.assertFalse(teeOut.isClosed()); + Assertions.assertEquals(DummyLayout.DUMMY + "CLOSED", teeOut.toString()); } // See http://jira.qos.ch/browse/LBCORE-143 @Test public void changeInConsole() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - EchoEncoder encoder = new EchoEncoder(); + EchoEncoder encoder = new EchoEncoder<>(); ca.setEncoder(encoder); ca.start(); ca.doAppend("a"); - assertEquals("a" + CoreConstants.LINE_SEPARATOR, tee.toString()); + Assertions.assertEquals("a" + CoreConstants.LINE_SEPARATOR, teeOut.toString()); XTeeOutputStream newTee = new XTeeOutputStream(null); System.setOut(new PrintStream(newTee)); ca.doAppend("b"); - assertEquals("b" + CoreConstants.LINE_SEPARATOR, newTee.toString()); + Assertions.assertEquals("b" + CoreConstants.LINE_SEPARATOR, newTee.toString()); } @Test - public void testUTF16BE() throws UnsupportedEncodingException { + public void testUTF16BE() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - DummyEncoder dummyEncoder = new DummyEncoder(); - Charset utf16BE = Charset.forName("UTF-16BE"); + DummyEncoder dummyEncoder = new DummyEncoder<>(); + Charset utf16BE = StandardCharsets.UTF_16BE; dummyEncoder.setCharset(utf16BE); ca.setEncoder(dummyEncoder); ca.start(); ca.doAppend(new Object()); - assertEquals(DummyLayout.DUMMY, new String(tee.toByteArray(), utf16BE)); + Assertions.assertEquals(DummyLayout.DUMMY, new String(teeOut.toByteArray(), utf16BE)); } @Test public void wrongTarget() { ConsoleAppender ca = (ConsoleAppender) getAppender(); - EchoEncoder encoder = new EchoEncoder(); + EchoEncoder encoder = new EchoEncoder<>(); encoder.setContext(context); ca.setContext(context); ca.setTarget("foo"); @@ -149,14 +153,15 @@ public void wrongTarget() { ca.start(); ca.doAppend("a"); StatusChecker checker = new StatusChecker(context); - // 21:28:01,246 + WARN in ch.qos.logback.core.ConsoleAppender[null] - [foo] should be one of [System.out, + // 21:28:01,246 + WARN in ch.qos.logback.core.ConsoleAppender[null] - [foo] + // should be one of [System.out, // System.err] - // 21:28:01,246 |-WARN in ch.qos.logback.core.ConsoleAppender[null] - Using previously set target, System.out by + // 21:28:01,246 |-WARN in ch.qos.logback.core.ConsoleAppender[null] - Using + // previously set target, System.out by // default. // StatusPrinter.print(context); checker.assertContainsMatch(Status.WARN, "\\[foo\\] should be one of \\[System.out, System.err\\]"); } - } diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/DummyAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/appender/DummyAppenderTest.java index d4ed609364..3f24b079fd 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/DummyAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/appender/DummyAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,18 @@ */ package ch.qos.logback.core.appender; -import static org.junit.Assert.assertEquals; - import java.io.ByteArrayOutputStream; import java.io.IOException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Appender; -import ch.qos.logback.core.encoder.DummyEncoder; +import ch.qos.logback.core.testUtil.DummyEncoder; import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.layout.DummyLayout; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class DummyAppenderTest extends AbstractAppenderTest { ByteArrayOutputStream baos = new ByteArrayOutputStream(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/DummyWriterAppender.java b/logback-core/src/test/java/ch/qos/logback/core/appender/DummyWriterAppender.java index ad6497d511..3d3db1fefd 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/DummyWriterAppender.java +++ b/logback-core/src/test/java/ch/qos/logback/core/appender/DummyWriterAppender.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/FileAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/appender/FileAppenderTest.java index 3267a1f990..00d6ad0e81 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/FileAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/appender/FileAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,24 +13,21 @@ */ package ch.qos.logback.core.appender; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import java.io.File; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Appender; import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.encoder.DummyEncoder; +import ch.qos.logback.core.testUtil.DummyEncoder; import ch.qos.logback.core.encoder.NopEncoder; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.StatusPrinter; public class FileAppenderTest extends AbstractAppenderTest { @@ -66,16 +63,17 @@ public void smoke() { appender.stop(); File file = new File(filename); - assertTrue(file.exists()); - assertTrue("failed to delete " + file.getAbsolutePath(), file.delete()); + Assertions.assertTrue(file.exists()); + Assertions.assertTrue(file.delete(), "failed to delete " + file.getAbsolutePath()); } @Test public void testCreateParentFolders() { - String filename = CoreTestConstants.OUTPUT_DIR_PREFIX + "/fat-testCreateParentFolders-" + diff + "/testCreateParentFolders.txt"; + String filename = CoreTestConstants.OUTPUT_DIR_PREFIX + "/fat-testCreateParentFolders-" + diff + + "/testCreateParentFolders.txt"; File file = new File(filename); - assertFalse(file.getParentFile().exists()); - assertFalse(file.exists()); + Assertions.assertFalse(file.getParentFile().exists()); + Assertions.assertFalse(file.exists()); FileAppender appender = new FileAppender(); appender.setEncoder(new DummyEncoder()); @@ -86,13 +84,13 @@ public void testCreateParentFolders() { appender.start(); appender.doAppend(new Object()); appender.stop(); - assertTrue(file.getParentFile().exists()); - assertTrue(file.exists()); + Assertions.assertTrue(file.getParentFile().exists()); + Assertions.assertTrue(file.exists()); // cleanup - assertTrue("failed to delete " + file.getAbsolutePath(), file.delete()); + Assertions.assertTrue(file.delete(), "failed to delete " + file.getAbsolutePath()); File parent = file.getParentFile(); - assertTrue("failed to delete " + parent.getAbsolutePath(), parent.delete()); + Assertions.assertTrue(parent.delete(), "failed to delete " + parent.getAbsolutePath()); } @Test @@ -109,48 +107,22 @@ public void testPrudentModeLogicalImplications() { appender.setPrudent(true); appender.start(); - assertTrue(appender.isAppend()); + Assertions.assertTrue(appender.isAppend()); StatusManager sm = context.getStatusManager(); // StatusPrinter.print(context); StatusChecker statusChecker = new StatusChecker(context); - assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); + Assertions.assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); List statusList = sm.getCopyOfStatusList(); - assertTrue("Expecting status list size to be 2 or larger, but was " + statusList.size(), statusList.size() >= 2); + Assertions.assertTrue( + statusList.size() >= 2, "Expecting status list size to be 2 or larger, but was " + statusList.size()); String msg1 = statusList.get(1).getMessage(); - assertTrue("Got message [" + msg1 + "]", msg1.startsWith("Setting \"Append\" property")); + Assertions.assertTrue(msg1.startsWith("Setting \"Append\" property"), "Got message [" + msg1 + "]"); appender.doAppend(new Object()); appender.stop(); - assertTrue(file.exists()); - assertTrue("failed to delete " + file.getAbsolutePath(), file.delete()); - } - - @Test - public void fileNameCollision() { - String fileName = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "fileNameCollision"; - - FileAppender appender0 = new FileAppender(); - appender0.setName("FA0"); - appender0.setFile(fileName); - appender0.setContext(context); - appender0.setEncoder(new DummyEncoder()); - appender0.start(); - assertTrue(appender0.isStarted()); - - FileAppender appender1 = new FileAppender(); - appender1.setName("FA1"); - appender1.setFile(fileName); - appender1.setContext(context); - appender1.setEncoder(new DummyEncoder()); - appender1.start(); - - assertFalse(appender1.isStarted()); - - StatusPrinter.print(context); - StatusChecker checker = new StatusChecker(context); - checker.assertContainsMatch(Status.ERROR, "'File' option has the same value"); - + Assertions.assertTrue(file.exists()); + Assertions.assertTrue(file.delete(), "failed to delete " + file.getAbsolutePath()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/appender/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/appender/PackageTest.java deleted file mode 100644 index 7c2883b9ae..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/appender/PackageTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.appender; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ DummyAppenderTest.class, ConsoleAppenderTest.class, FileAppenderTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/boolex/ExpressionPropertyConditionTest.java b/logback-core/src/test/java/ch/qos/logback/core/boolex/ExpressionPropertyConditionTest.java new file mode 100644 index 0000000000..1ac02539a9 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/boolex/ExpressionPropertyConditionTest.java @@ -0,0 +1,120 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.boolex; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ExpressionPropertyConditionTest { + + + Context context = new ContextBase(); + ModelInterpretationContext mic = new ModelInterpretationContext(context); + ExpressionPropertyCondition epc = new ExpressionPropertyCondition(); + + + static final String SMOKE_KEY = "SMOKE_KEY"; + static final String UNDEFINED_KEY = "UNDEFINED_KEY"; + static final String SMOKE_VALUE = "SMOKE_VALUE"; + + @BeforeEach + public void setUp() throws Exception { + epc.setContext(context); + epc.setLocalPropertyContainer(mic); + context.putProperty(SMOKE_KEY, SMOKE_VALUE); + } + + @Test + public void smokeDefined() { + String expression = String.format("isDefined(\"%s\")", SMOKE_KEY); + check(expression, true); + } + + + @Test + public void notSmokeDefined() { + String expression = String.format("!isDefined(\"%s\")", UNDEFINED_KEY); + check(expression, true); + } + + @Test + public void notSmokeDefined_and_nullBoo() { + String expression = String.format("!isDefined(\"%s\") && isNull(\"%s\")", UNDEFINED_KEY, "BOO"); + check(expression, true); + } + + @Test + public void paranthesisSmokeDefined() { + String expression = String.format("(isDefined(\"%s\"))", SMOKE_KEY); + check(expression, true); + } + + @Test + public void SmokeDefinedAndIsNull_AND_IsNull() { + String expression = String.format("(isDefined(\"%s\") || isNull(\"%s\")) && isNull(\"x\")", SMOKE_KEY, UNDEFINED_KEY); + check(expression, true); + } + + @Test + public void NOTSmokeDefinedAndIsNull_AND_IsNull() { + String expression = String.format("!(isDefined(\"%s\") || isNull(\"%s\")) && isNull(\"x\")", SMOKE_KEY, UNDEFINED_KEY); + check(expression, false); + } + + @Test + public void smokeBiFunction() { + String expression = String.format("propertyEquals(\"%s\", \"%s\")" , SMOKE_KEY, SMOKE_VALUE); + check(expression, true); + } + + + @Test + public void propertyContains() { + String expression = String.format("propertyContains(\"%s\", \"%s\")" , SMOKE_KEY, SMOKE_VALUE.substring(0, 2)); + check(expression, true); + } + + + @Test + public void notPropertyContainsX() { + String expression = String.format("!propertyContains(\"%s\", \"%s\")" , SMOKE_KEY, SMOKE_VALUE+"x"); + check(expression, true); + } + + @Test + public void propertyEqualsOrIsNull() { + String expression = String.format("!propertyEquals(\"%s\", \"%s\") || !isNull(\"%s\")" , SMOKE_KEY, SMOKE_VALUE, UNDEFINED_KEY); + check(expression, false); + } + + + void check(String expression, boolean expected) { + epc.setExpression(expression); + epc.start(); + + if(expected) { + assertTrue(epc.evaluate()); + } else { + assertFalse(epc.evaluate()); + } + + } +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/boolex/MatcherTest.java b/logback-core/src/test/java/ch/qos/logback/core/boolex/MatcherTest.java index ffb35c4bdf..90e5d9c7d4 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/boolex/MatcherTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/boolex/MatcherTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,28 +13,34 @@ */ package ch.qos.logback.core.boolex; -import junit.framework.TestCase; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -public class MatcherTest extends TestCase { +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MatcherTest { Context context; Matcher matcher; + @BeforeEach public void setUp() throws Exception { context = new ContextBase(); matcher = new Matcher(); matcher.setContext(context); matcher.setName("testMatcher"); - super.setUp(); } + @AfterEach public void tearDown() throws Exception { matcher = null; - super.tearDown(); } + @Test public void testFullRegion() throws Exception { matcher.setRegex(".*test.*"); matcher.start(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/contention/MultiThreadedHarness.java b/logback-core/src/test/java/ch/qos/logback/core/contention/MultiThreadedHarness.java index 8fdbef5c7c..5bef0a18ee 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/contention/MultiThreadedHarness.java +++ b/logback-core/src/test/java/ch/qos/logback/core/contention/MultiThreadedHarness.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,11 @@ */ package ch.qos.logback.core.contention; +import ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness; + /** - * Useful scaffolding/harness to start and processPriorToRemoval multiple threads. + * Useful scaffolding/harness to start and processPriorToRemoval multiple + * threads. * * @author Joern Huxhorn * @author Ralph Goers diff --git a/logback-core/src/test/java/ch/qos/logback/core/contention/ThreadedThroughputCalculator.java b/logback-core/src/test/java/ch/qos/logback/core/contention/ThreadedThroughputCalculator.java index 126ab283d1..a549f7ee92 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/contention/ThreadedThroughputCalculator.java +++ b/logback-core/src/test/java/ch/qos/logback/core/contention/ThreadedThroughputCalculator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,8 @@ */ package ch.qos.logback.core.contention; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; + /** * Useful scaffolding to measure the throughput of certain operations when * invoked by multiple threads. @@ -27,11 +29,11 @@ public ThreadedThroughputCalculator(long overallDurationInMillis) { super(overallDurationInMillis); } - public void printThroughput(String msg) throws InterruptedException { - printThroughput(msg, false); + public void printThroughput(RunnableWithCounterAndDone[] runnableArray, String msg) throws InterruptedException { + printThroughput(runnableArray, msg, false); } - public void printThroughput(String msg, boolean detailed) throws InterruptedException { + public void printThroughput( RunnableWithCounterAndDone[] runnableArray, String msg, boolean detailed) throws InterruptedException { long sum = 0; for (RunnableWithCounterAndDone r : runnableArray) { if (detailed) { @@ -40,6 +42,7 @@ public void printThroughput(String msg, boolean detailed) throws InterruptedExce sum += r.getCounter(); } - System.out.println(msg + "total of " + sum + " operations, or " + ((sum) / overallDurationInMillis) + " operations per millisecond"); + System.out.println(msg + "total of " + sum + " operations, or " + ((sum) / overallDurationInMillis) + + " operations per millisecond"); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/contention/WaitOnExecutionMultiThreadedHarness.java b/logback-core/src/test/java/ch/qos/logback/core/contention/WaitOnExecutionMultiThreadedHarness.java index a7da102bf6..ecd67fc2be 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/contention/WaitOnExecutionMultiThreadedHarness.java +++ b/logback-core/src/test/java/ch/qos/logback/core/contention/WaitOnExecutionMultiThreadedHarness.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,8 @@ */ package ch.qos.logback.core.contention; +import ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness; + import java.util.concurrent.ThreadPoolExecutor; public class WaitOnExecutionMultiThreadedHarness extends AbstractMultiThreadedHarness { diff --git a/logback-core/src/test/java/ch/qos/logback/core/encoder/ByteArrayUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/encoder/ByteArrayUtilTest.java index 41a6ffb988..d68fa3637c 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/encoder/ByteArrayUtilTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/encoder/ByteArrayUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,11 @@ */ package ch.qos.logback.core.encoder; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.assertEquals; import java.util.Random; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class ByteArrayUtilTest { diff --git a/logback-core/src/test/java/ch/qos/logback/core/encoder/JsonEscapeUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/encoder/JsonEscapeUtilTest.java new file mode 100644 index 0000000000..836a99353a --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/encoder/JsonEscapeUtilTest.java @@ -0,0 +1,74 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.encoder; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class JsonEscapeUtilTest { + + @Test + public void smokeTestEscapeCodesUnder32() { + assertEquals("\\u0001", JsonEscapeUtil.ESCAPE_CODES[1]); + assertEquals("\\u0005", JsonEscapeUtil.ESCAPE_CODES[5]); + assertEquals("\\b", JsonEscapeUtil.ESCAPE_CODES[8]); + assertEquals("\\t", JsonEscapeUtil.ESCAPE_CODES[9]); + assertEquals("\\n", JsonEscapeUtil.ESCAPE_CODES[0x0A]); + assertEquals("\\u000B", JsonEscapeUtil.ESCAPE_CODES[0x0B]); + assertEquals("\\f", JsonEscapeUtil.ESCAPE_CODES[0x0C]); + assertEquals("\\r", JsonEscapeUtil.ESCAPE_CODES[0x0D]); + assertEquals("\\u000E", JsonEscapeUtil.ESCAPE_CODES[0x0E]); + + assertEquals("\\u001A", JsonEscapeUtil.ESCAPE_CODES[0x1A]); + } + + @Test + public void smokeTestEscapeString() { + assertEquals("abc", JsonEscapeUtil.jsonEscapeString("abc")); + assertEquals("{world: \\\"world\\\"}", JsonEscapeUtil.jsonEscapeString("{world: \"world\"}")); + assertEquals("{world: "+'\\'+'"'+"world\\\"}", JsonEscapeUtil.jsonEscapeString("{world: \"world\"}")); + } + + @Test + public void testEscapingLF() { + String input = "{\nhello: \"wo\nrld\"}"; + System.out.println(input); + assertEquals("{\\nhello: "+'\\'+'"'+"wo\\nrld\\\"}", JsonEscapeUtil.jsonEscapeString(input)); + } + + @Test + public void testBackslash() { + String input = "{x:com\\foo}"; + System.out.println(input); + assertEquals("{x:com\\\\foo}", JsonEscapeUtil.jsonEscapeString(input)); + } + + @Test + public void testForwardslash() { + String input = "{x:com/foo}"; + System.out.println(input); + assertEquals("{x:com\\/foo}", JsonEscapeUtil.jsonEscapeString(input)); + } + + + + @Test + public void testEscapingTab() { + String input = "{hello: \"\tworld\"}"; + System.out.println(input); + assertEquals("{hello: "+'\\'+'"'+"\\tworld\\\"}", JsonEscapeUtil.jsonEscapeString(input)); + } +} \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/encoder/NopEncoder.java b/logback-core/src/test/java/ch/qos/logback/core/encoder/NopEncoder.java index 011f85c2b0..16ea5b7b28 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/encoder/NopEncoder.java +++ b/logback-core/src/test/java/ch/qos/logback/core/encoder/NopEncoder.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,16 +15,15 @@ public class NopEncoder extends EncoderBase { - - public byte[] encode(E event) { + public byte[] encode(E event) { return null; } - public byte[] headerBytes() { + public byte[] headerBytes() { return null; } - - public byte[] footerBytes() { + + public byte[] footerBytes() { return null; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/encoder/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/encoder/PackageTest.java deleted file mode 100644 index 04cf4956f8..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/encoder/PackageTest.java +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.encoder; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ ByteArrayUtilTest.class}) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/helpers/CyclicBufferTest.java b/logback-core/src/test/java/ch/qos/logback/core/helpers/CyclicBufferTest.java index e15363d3a4..f5b0cb6543 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/helpers/CyclicBufferTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/helpers/CyclicBufferTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,17 +13,16 @@ */ package ch.qos.logback.core.helpers; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; -import static org.junit.Assert.assertEquals; - public class CyclicBufferTest { void assertSize(CyclicBuffer cb, int size) { - assertEquals(size, cb.length()); + Assertions.assertEquals(size, cb.length()); } @Test @@ -36,9 +35,9 @@ public void smoke() { assertSize(cb, 2); cb.add("two"); assertSize(cb, 2); - assertEquals("one", cb.get()); + Assertions.assertEquals("one", cb.get()); assertSize(cb, 1); - assertEquals("two", cb.get()); + Assertions.assertEquals("two", cb.get()); assertSize(cb, 0); } @@ -54,7 +53,7 @@ public void cloning() { assertSize(cb, 0); List witness = Arrays.asList("zero", "one"); - assertEquals(witness, clone.asList()); + Assertions.assertEquals(witness, clone.asList()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/helpers/FileFilterUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/helpers/FileFilterUtilTest.java index 0d11cb4e3f..656d2579a4 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/helpers/FileFilterUtilTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/helpers/FileFilterUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,15 +17,13 @@ import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.rolling.helper.FileFilterUtil; import ch.qos.logback.core.rolling.helper.FileNamePattern; -import org.junit.Test; - -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import java.io.File; import java.text.ParseException; import java.text.SimpleDateFormat; - public class FileFilterUtilTest { Context context = new ContextBase(); @@ -33,10 +31,12 @@ public class FileFilterUtilTest { // see also http://jira.qos.ch/browse/LBCORE-164 @Test public void findHighestCounterTest() throws ParseException { - String[] sa = new String[] { "c:/log/debug-old-2010-08-10.0.log", "c:/log/debug-old-2010-08-10.1.log", "c:/log/debug-old-2010-08-10.10.log", - "c:/log/debug-old-2010-08-10.11.log", "c:/log/debug-old-2010-08-10.12.log", "c:/log/debug-old-2010-08-10.2.log", - "c:/log/debug-old-2010-08-10.3.log", "c:/log/debug-old-2010-08-10.4.log", "c:/log/debug-old-2010-08-10.5.log", - "c:/log/debug-old-2010-08-10.6.log", "c:/log/debug-old-2010-08-10.7.log", "c:/log/debug-old-2010-08-10.8.log", + String[] sa = new String[] { "c:/log/debug-old-2010-08-10.0.log", "c:/log/debug-old-2010-08-10.1.log", + "c:/log/debug-old-2010-08-10.10.log", "c:/log/debug-old-2010-08-10.11.log", + "c:/log/debug-old-2010-08-10.12.log", "c:/log/debug-old-2010-08-10.2.log", + "c:/log/debug-old-2010-08-10.3.log", "c:/log/debug-old-2010-08-10.4.log", + "c:/log/debug-old-2010-08-10.5.log", "c:/log/debug-old-2010-08-10.6.log", + "c:/log/debug-old-2010-08-10.7.log", "c:/log/debug-old-2010-08-10.8.log", "c:/log/debug-old-2010-08-10.9.log" }; File[] matchingFileArray = new File[sa.length]; @@ -49,6 +49,6 @@ public void findHighestCounterTest() throws ParseException { rexexp = fnp.toRegexForFixedDate(sdf.parse("2010-08-10")); String stemRegex = FileFilterUtil.afterLastSlash(rexexp); int result = FileFilterUtil.findHighestCounter(matchingFileArray, stemRegex); - assertEquals(12, result); + Assertions.assertEquals(12, result); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java deleted file mode 100644 index 411b08c478..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/helpers/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.helpers; - -import ch.qos.logback.core.util.FileUtilTest; -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ThrowableToStringArrayTest.class, FileUtilTest.class, CyclicBufferTest.class }) -public class PackageTest { - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java b/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java index 8c38ebd446..ae13ea8f5d 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/helpers/ThrowableToStringArrayTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,27 +13,27 @@ */ package ch.qos.logback.core.helpers; -import static org.junit.Assert.assertEquals; - import java.io.PrintWriter; import java.io.StringWriter; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.CoreConstants; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class ThrowableToStringArrayTest { StringWriter sw = new StringWriter(); PrintWriter pw = new PrintWriter(sw); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/LBCORE97.java b/logback-core/src/test/java/ch/qos/logback/core/issue/LBCORE97.java index 328a572ece..03073156a1 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/LBCORE97.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/LBCORE97.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,8 @@ import java.util.concurrent.locks.ReentrantLock; /** - * Example code illustrating locking policies in the JDK. - * See http://jira.qos.ch/browse/LBCORE-97 for a discussion. + * Example code illustrating locking policies in the JDK. See + * http://jira.qos.ch/browse/LBCORE-97 for a discussion. * * @author Joern Huxhorn */ diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/LOGBACK_849/Basic.java b/logback-core/src/test/java/ch/qos/logback/core/issue/LOGBACK_849/Basic.java index f15addbe5c..15b134a5a0 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/LOGBACK_849/Basic.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/LOGBACK_849/Basic.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,25 +16,27 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.TimeUnit; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.util.ExecutorServiceUtil; +import org.junit.jupiter.api.Timeout; public class Basic { ExecutorService executor = ExecutorServiceUtil.newScheduledExecutorService(); Context context = new ContextBase(); - @Test(timeout = 100) + @Test + @Timeout(value=100, unit=TimeUnit.MILLISECONDS) public void withNoSubmittedTasksShutdownNowShouldReturnImmediately() throws InterruptedException { executor.shutdownNow(); executor.awaitTermination(5000, TimeUnit.MILLISECONDS); } - @Ignore + @Disabled @Test public void withOneSlowTask() throws InterruptedException { executor.execute(new InterruptIgnoring(1000)); diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/LockThroughput.java b/logback-core/src/test/java/ch/qos/logback/core/issue/LockThroughput.java index 7fc6287309..99461f0670 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/LockThroughput.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/LockThroughput.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,7 @@ */ package ch.qos.logback.core.issue; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.contention.ThreadedThroughputCalculator; import ch.qos.logback.core.issue.SelectiveLockRunnable.LockingModel; @@ -38,14 +39,19 @@ public static void main(String args[]) throws InterruptedException { tp.execute(buildArray(LockingModel.FAIR)); } - tp.execute(buildArray(LockingModel.SYNC)); - tp.printThroughput("Sync: "); - tp.execute(buildArray(LockingModel.UNFAIR)); - tp.printThroughput("Unfair: "); + RunnableWithCounterAndDone[] runnableArraySync = buildArray(LockingModel.SYNC); + tp.execute(runnableArraySync); + tp.printThroughput(runnableArraySync, "Sync: "); + + + RunnableWithCounterAndDone[] runnableArrayUnfair = buildArray(LockingModel.UNFAIR); + tp.execute(runnableArrayUnfair); + tp.printThroughput(runnableArrayUnfair, "Unfair: "); - tp.execute(buildArray(LockingModel.FAIR)); - tp.printThroughput("Fair: "); + RunnableWithCounterAndDone[] runnableArrayFair = buildArray(LockingModel.FAIR); + tp.execute(runnableArrayFair); + tp.printThroughput(runnableArrayFair, "Fair: "); } static SelectiveLockRunnable[] buildArray(LockingModel model) { diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/LockingInJava.java b/logback-core/src/test/java/ch/qos/logback/core/issue/LockingInJava.java index 780d48cec1..cac1e038e5 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/LockingInJava.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/LockingInJava.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockThroughput.java b/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockThroughput.java index a52a187191..5789d61974 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockThroughput.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockThroughput.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,6 +13,7 @@ */ package ch.qos.logback.core.issue; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.contention.ThreadedThroughputCalculator; import ch.qos.logback.core.issue.SelectiveLockRunnable.LockingModel; @@ -36,8 +37,9 @@ public static void main(String args[]) throws InterruptedException { tp.execute(buildArray(LockingModel.NOLOCK)); } - tp.execute(buildArray(LockingModel.NOLOCK)); - tp.printThroughput("No lock: ", true); + RunnableWithCounterAndDone[] runnableArray = buildArray(LockingModel.NOLOCK); + tp.execute(runnableArray); + tp.printThroughput(runnableArray,"No lock: ", true); } static SelectiveLockRunnable[] buildArray(LockingModel model) { diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockingInJava.java b/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockingInJava.java index 719361f670..9ed9b18215 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockingInJava.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/NoLockingInJava.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/SelectiveLockRunnable.java b/logback-core/src/test/java/ch/qos/logback/core/issue/SelectiveLockRunnable.java index b03d47c036..0b727da3ba 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/SelectiveLockRunnable.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/SelectiveLockRunnable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,7 +16,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; /** * A runnable which behaves differently depending on the desired locking model. diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/FileLockSimulator.java b/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/FileLockSimulator.java index 5e71ec79ab..54fe063183 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/FileLockSimulator.java +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/FileLockSimulator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,11 +19,12 @@ import java.nio.channels.FileLock; /** - * FileLockSimulator is a small application intended to simulate FileAppender in prudent mode. - * In this mode, the application obtains an exclusive lock on the file, writes to the file and - * then releases the lock. + * FileLockSimulator is a small application intended to simulate FileAppender in + * prudent mode. In this mode, the application obtains an exclusive lock on the + * file, writes to the file and then releases the lock. * - *
  Usage:
+ * 
+ *   Usage:
  *   java  FileLockSimulator instanceName pathToLogFile delay
  * where
  * "instanceName" is the name given to the current instance of the application
@@ -31,15 +32,18 @@
  * "delay" is the number of milliseconds of sleep observed every 128 writes
  * 
* - * This small application requires only the JDK to compile and to execute. + * This small application requires only the JDK to compile and to + * execute. * - *

FileLockSimulator should be launched as many times and from as many hosts as there will be - * JVMs writing to a log file in prudent mode. Performance should be quite good if - * "pathToLogFile" is on a local file system. On networked file systems such as NFS, performance - * depends on the speed of the network and NFS implementation. It has been observed that file - * locking over NFS is biased so that the current owner of the lock is favored over other processes. - * Thus, while one process hogs the lock for the log file, other processes starve waiting for the - * lock to the point of appearing deadlocked. + *

+ * FileLockSimulator should be launched as many times and from as many hosts as + * there will be JVMs writing to a log file in prudent mode. Performance should + * be quite good if "pathToLogFile" is on a local file system. On networked file + * systems such as NFS, performance depends on the speed of the network and NFS + * implementation. It has been observed that file locking over NFS is biased so + * that the current owner of the lock is favored over other processes. Thus, + * while one process hogs the lock for the log file, other processes starve + * waiting for the lock to the point of appearing deadlocked. * */ public class FileLockSimulator { diff --git a/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/Logback1362.java b/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/Logback1362.java new file mode 100644 index 0000000000..7a24f59bb0 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/issue/lbcore258/Logback1362.java @@ -0,0 +1,105 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.issue.lbcore258; + + +import java.io.IOException; +import java.io.OutputStream; + + +import ch.qos.logback.core.OutputStreamAppender; +import ch.qos.logback.core.encoder.EncoderBase; +import org.junit.jupiter.api.Test; + +/** + * Provided by Alexander Kudrevatykh in LOGBACK-1362 + */ +public class Logback1362 { + + long startNanos = System.nanoTime(); + long DELAY = 20; + long getNanos() { + return (System.nanoTime() - startNanos); + } + + @Test + public void testAppender() throws InterruptedException { + + OutputStreamAppender appender = new OutputStreamAppender() { + @Override + public void addError(String msg, Throwable ex) { + throw new RuntimeException(getNanos()+" "+msg, ex); + } + }; + + appender.setEncoder(new EncoderBase() { + + @Override + public byte[] headerBytes() { + return null; + } + + @Override + public byte[] encode(Object event) { + delay(DELAY*2); + return new byte[]{'A'}; + } + + @Override + public byte[] footerBytes() { + // TODO Auto-generated method stub + return null; + } + }); + appender.setOutputStream(new OutputStream() { + + @Override + public void write(int b) throws IOException { + throw new RuntimeException("not closed appender"); + } + }); + + System.out.println(getNanos() + " About to call appender.start()"); + appender.start(); + System.out.println(getNanos() + " After call to appender.start()"); + + Thread t = new Thread(new Runnable() { + @Override + public void run() { + delay(DELAY); + System.out.println(getNanos() + " About to call appender.stop()"); + appender.stop(); + System.out.println(getNanos() + " After call to appender.stop()"); + } + }); + t.start(); + System.out.println(getNanos() + " Calling appender.doAppend(new Object());"); + appender.doAppend(new Object()); + System.out.println("xxxxxxxxxxxxxxxxxxxxxx"); + System.out.println(getNanos()+ " After call to appender.doAppender(new Object())"); + t.join(); + } + + private void delay(long delayInMillis) { + try { + Thread.sleep(delayInMillis); + } catch (InterruptedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + +} + diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/PackageTest.java deleted file mode 100644 index 4d8593ef51..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/PackageTest.java +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ SkippingInInterpreterTest.class, TrivialConfiguratorTest.class, ch.qos.logback.core.joran.action.PackageTest.class, - ch.qos.logback.core.joran.event.PackageTest.class, ch.qos.logback.core.joran.util.PackageTest.class, ch.qos.logback.core.joran.spi.PackageTest.class, - ch.qos.logback.core.joran.replay.PackageTest.class, ch.qos.logback.core.joran.implicitAction.PackageTest.class, - ch.qos.logback.core.joran.conditional.PackageTest.class }) -public class PackageTest { - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/SimpleConfigurator.java b/logback-core/src/test/java/ch/qos/logback/core/joran/SimpleConfigurator.java index c461e8f1cb..250c0ab651 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/SimpleConfigurator.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/SimpleConfigurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,43 +14,36 @@ package ch.qos.logback.core.joran; import java.util.HashMap; +import java.util.function.Supplier; import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.NestedBasicPropertyIA; -import ch.qos.logback.core.joran.action.NestedComplexPropertyIA; +import ch.qos.logback.core.joran.action.ImplicitModelAction; import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.Interpreter; import ch.qos.logback.core.joran.spi.RuleStore; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; -public class SimpleConfigurator extends GenericConfigurator { +public class SimpleConfigurator extends GenericXMLConfigurator { - HashMap rulesMap; + HashMap> rulesMap; - public SimpleConfigurator(HashMap rules) { + public SimpleConfigurator(HashMap> rules) { this.rulesMap = rules; } @Override - protected void addImplicitRules(Interpreter interpreter) { - NestedComplexPropertyIA nestedIA = new NestedComplexPropertyIA(getBeanDescriptionCache()); - nestedIA.setContext(context); - interpreter.addImplicitAction(nestedIA); - - NestedBasicPropertyIA nestedSimpleIA = new NestedBasicPropertyIA(getBeanDescriptionCache()); - nestedSimpleIA.setContext(context); - interpreter.addImplicitAction(nestedSimpleIA); + protected void setImplicitRuleSupplier(SaxEventInterpreter interpreter) { + interpreter.setImplicitActionSupplier(() -> new ImplicitModelAction()); } - public Interpreter getInterpreter() { - return interpreter; + public SaxEventInterpreter getInterpreter() { + return saxEventInterpreter; } @Override - protected void addInstanceRules(RuleStore rs) { + protected void addElementSelectorAndActionAssociations(RuleStore rs) { for (ElementSelector elementSelector : rulesMap.keySet()) { - Action action = rulesMap.get(elementSelector); - rs.addRule(elementSelector, action); + Supplier actionSupplier = rulesMap.get(elementSelector); + rs.addRule(elementSelector, actionSupplier); } } - } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/SkippingInInterpreterTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/SkippingInInterpreterTest.java index 015006d222..2cf5d40ba9 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/SkippingInInterpreterTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/SkippingInInterpreterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,23 +13,21 @@ */ package ch.qos.logback.core.joran; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.util.HashMap; import java.util.List; +import java.util.function.Supplier; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; import ch.qos.logback.core.joran.spi.ElementSelector; -import org.junit.Test; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.action.NOPAction; +import ch.qos.logback.core.joran.action.TopElementAction; import ch.qos.logback.core.joran.action.ext.BadBeginAction; import ch.qos.logback.core.joran.action.ext.BadEndAction; import ch.qos.logback.core.joran.action.ext.HelloAction; @@ -39,6 +37,10 @@ import ch.qos.logback.core.status.StatusManager; import ch.qos.logback.core.testUtil.CoreTestConstants; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Test the way Interpreter skips child elements in case of exceptions thrown by * Actions. It also tests addition of status messages in case of exceptions. @@ -47,7 +49,7 @@ */ public class SkippingInInterpreterTest { - HashMap rulesMap = new HashMap(); + HashMap> rulesMap = new HashMap<>(); Context context = new ContextBase(); StatusManager sm = context.getStatusManager(); @@ -58,18 +60,18 @@ SAXParser createParser() throws Exception { void doTest(String filename, Integer expectedInt, Class exceptionClass) throws Exception { - rulesMap.put(new ElementSelector("test"), new NOPAction()); - rulesMap.put(new ElementSelector("test/badBegin"), new BadBeginAction()); - rulesMap.put(new ElementSelector("test/badBegin/touch"), new TouchAction()); - rulesMap.put(new ElementSelector("test/badEnd"), new BadEndAction()); - rulesMap.put(new ElementSelector("test/badEnd/touch"), new TouchAction()); - rulesMap.put(new ElementSelector("test/hello"), new HelloAction()); - - rulesMap.put(new ElementSelector("test/isolate"), new NOPAction()); - rulesMap.put(new ElementSelector("test/isolate/badEnd"), new BadEndAction()); - rulesMap.put(new ElementSelector("test/isolate/badEnd/touch"), new TouchAction()); - rulesMap.put(new ElementSelector("test/isolate/touch"), new TouchAction()); - rulesMap.put(new ElementSelector("test/hello"), new HelloAction()); + rulesMap.put(new ElementSelector("test"), () -> new TopElementAction()); + rulesMap.put(new ElementSelector("test/badBegin"), () -> new BadBeginAction()); + rulesMap.put(new ElementSelector("test/badBegin/touch"), () -> new TouchAction()); + rulesMap.put(new ElementSelector("test/badEnd"), () -> new BadEndAction()); + rulesMap.put(new ElementSelector("test/badEnd/touch"), () -> new TouchAction()); + rulesMap.put(new ElementSelector("test/hello"), () -> new HelloAction()); + + rulesMap.put(new ElementSelector("test/isolate"), () -> new NOPAction()); + rulesMap.put(new ElementSelector("test/isolate/badEnd"), () -> new BadEndAction()); + rulesMap.put(new ElementSelector("test/isolate/badEnd/touch"), () -> new TouchAction()); + rulesMap.put(new ElementSelector("test/isolate/touch"), () -> new TouchAction()); + rulesMap.put(new ElementSelector("test/hello"), () -> new HelloAction()); TrivialConfigurator tc = new TrivialConfigurator(rulesMap); tc.setContext(context); diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfigurator.java b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfigurator.java index 6278cfc524..d460e3c7ca 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfigurator.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfigurator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,29 +14,39 @@ package ch.qos.logback.core.joran; import java.util.HashMap; +import java.util.function.Supplier; import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.ImplicitModelAction; import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.Interpreter; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; import ch.qos.logback.core.joran.spi.RuleStore; -public class TrivialConfigurator extends GenericConfigurator { +public class TrivialConfigurator extends GenericXMLConfigurator { - HashMap rulesMap; + HashMap> rulesMap; - public TrivialConfigurator(HashMap rules) { + public TrivialConfigurator(HashMap> rules) { this.rulesMap = rules; } + + public TrivialConfigurator makeAnotherInstance() { + TrivialConfigurator tc = new TrivialConfigurator(rulesMap); + tc.setContext(context); + return tc; + } + @Override - protected void addImplicitRules(Interpreter interpreter) { + protected void setImplicitRuleSupplier(SaxEventInterpreter interpreter) { + interpreter.setImplicitActionSupplier(ImplicitModelAction::new); } @Override - protected void addInstanceRules(RuleStore rs) { + protected void addElementSelectorAndActionAssociations(RuleStore aRuleStore) { for (ElementSelector elementSelector : rulesMap.keySet()) { - Action action = rulesMap.get(elementSelector); - rs.addRule(elementSelector, action); + Supplier actionSupplier = rulesMap.get(elementSelector); + aRuleStore.addRule(elementSelector, actionSupplier); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java index dcb518b464..091cd3650e 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/TrivialConfiguratorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,6 @@ */ package ch.qos.logback.core.joran; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - import java.io.File; import java.io.FileOutputStream; import java.io.IOException; @@ -24,15 +20,19 @@ import java.net.URL; import java.net.URLConnection; import java.util.HashMap; +import java.util.function.Supplier; import java.util.jar.JarOutputStream; import java.util.zip.ZipEntry; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.TopElementAction; import ch.qos.logback.core.joran.action.ext.IncAction; import ch.qos.logback.core.joran.spi.ElementSelector; import ch.qos.logback.core.joran.spi.JoranException; @@ -44,13 +44,17 @@ public class TrivialConfiguratorTest { Context context = new ContextBase(); - HashMap rulesMap = new HashMap(); + HashMap> rulesMap = new HashMap<>(); - public void doTest(String filename) throws Exception { + @BeforeEach + public void setUp() { + // rule store is case-insensitive + rulesMap.put(new ElementSelector("x"), () -> new TopElementAction()); + rulesMap.put(new ElementSelector("x/inc"), () -> new IncAction()); - // rule store is case insensitve - rulesMap.put(new ElementSelector("x/inc"), new IncAction()); + } + public void doTest(String filename) throws Exception { TrivialConfigurator trivialConfigurator = new TrivialConfigurator(rulesMap); trivialConfigurator.setContext(context); @@ -63,9 +67,9 @@ public void smoke() throws Exception { int oldEndCount = IncAction.endCount; int oldErrorCount = IncAction.errorCount; doTest(CoreTestConstants.TEST_SRC_PREFIX + "input/joran/" + "inc.xml"); - assertEquals(oldErrorCount, IncAction.errorCount); - assertEquals(oldBeginCount + 1, IncAction.beginCount); - assertEquals(oldEndCount + 1, IncAction.endCount); + Assertions.assertEquals(oldErrorCount, IncAction.errorCount); + Assertions.assertEquals(oldBeginCount + 1, IncAction.beginCount); + Assertions.assertEquals(oldEndCount + 1, IncAction.endCount); } @Test @@ -77,11 +81,11 @@ public void inexistentFile() { try { doTest(filename); } catch (Exception e) { - assertTrue(e.getMessage().startsWith("Could not open [")); + Assertions.assertTrue(e.getMessage().startsWith("Could not open [")); } - assertTrue(tsl.list.size() + " should be greater than or equal to 1", tsl.list.size() >= 1); + Assertions.assertTrue(tsl.list.size() >= 1, tsl.list.size() + " should be greater than or equal to 1"); Status s0 = tsl.list.get(0); - assertTrue(s0.getMessage().startsWith("Could not open [")); + Assertions.assertTrue(s0.getMessage().startsWith("Could not open [")); } @Test @@ -94,13 +98,13 @@ public void illFormedXML() { doTest(filename); } catch (Exception e) { } - assertEquals(2, tsl.list.size()); + Assertions.assertEquals(2, tsl.list.size()); Status s0 = tsl.list.get(0); - assertTrue(s0.getMessage().startsWith(CoreConstants.XML_PARSING)); + Assertions.assertTrue(s0.getMessage().startsWith(CoreConstants.XML_PARSING)); } @Test - public void lbcore105() throws IOException, JoranException { + public void LOGBACK_117() throws IOException, JoranException { String jarEntry = "buzz.xml"; File jarFile = makeRandomJarFile(); fillInJarFile(jarFile, jarEntry); @@ -109,12 +113,12 @@ public void lbcore105() throws IOException, JoranException { tc.setContext(context); tc.doConfigure(url); // deleting an open file fails - assertTrue(jarFile.delete()); - assertFalse(jarFile.exists()); + Assertions.assertTrue(jarFile.delete()); + Assertions.assertFalse(jarFile.exists()); } @Test - public void lbcore127() throws IOException, JoranException { + public void LOGBACK_163() throws IOException, JoranException { String jarEntry = "buzz.xml"; String jarEntry2 = "lightyear.xml"; @@ -136,8 +140,8 @@ public void lbcore127() throws IOException, JoranException { is.close(); // deleting an open file fails - assertTrue(jarFile.delete()); - assertFalse(jarFile.exists()); + Assertions.assertTrue(jarFile.delete()); + Assertions.assertFalse(jarFile.exists()); } File makeRandomJarFile() { diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/AsLowerCasePropertyDefiner.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/AsLowerCasePropertyDefiner.java index de457545e6..f2f5ddddb9 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/AsLowerCasePropertyDefiner.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/AsLowerCasePropertyDefiner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java deleted file mode 100755 index 1c7b32c028..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/DefinePropertyActionTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - -import java.util.HashMap; - -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.SimpleConfigurator; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.StatusChecker; - -/** - * Test {@link DefinePropertyAction}. - * - * @author Aleksey Didik - */ -public class DefinePropertyActionTest { - - private static final String DEFINE_INPUT_DIR = CoreTestConstants.JORAN_INPUT_PREFIX + "define/"; - private static final String GOOD_XML = "good.xml"; - private static final String NONAME_XML = "noname.xml"; - private static final String NOCLASS_XML = "noclass.xml"; - private static final String BADCLASS_XML = "badclass.xml"; - - SimpleConfigurator simpleConfigurator; - Context context = new ContextBase(); - DefinePropertyAction definerAction; - InterpretationContext ic; - StatusChecker checker = new StatusChecker(context); - - @Before - public void setUp() throws Exception { - - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("define"), new DefinePropertyAction()); - simpleConfigurator = new SimpleConfigurator(rulesMap); - simpleConfigurator.setContext(context); - } - - @After - public void tearDown() throws Exception { - // StatusPrinter.printInCaseOfErrorsOrWarnings(context); - } - - @Test - public void good() throws JoranException { - simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + GOOD_XML); - InterpretationContext ic = simpleConfigurator.getInterpreter().getInterpretationContext(); - String inContextFoo = ic.getProperty("foo"); - assertEquals("monster", inContextFoo); - } - - @Test - public void noName() throws JoranException { - simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + NONAME_XML); - // get from context - String inContextFoo = context.getProperty("foo"); - assertNull(inContextFoo); - // check context errors - checker.assertContainsMatch(Status.ERROR, "Missing property name for property definer. Near \\[define\\] line 1"); - } - - @Test - public void noClass() throws JoranException { - simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + NOCLASS_XML); - String inContextFoo = context.getProperty("foo"); - assertNull(inContextFoo); - checker.assertContainsMatch(Status.ERROR, "Missing class name for property definer. Near \\[define\\] line 1"); - } - - @Test - public void testBadClass() throws JoranException { - simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + BADCLASS_XML); - // get from context - String inContextFoo = context.getProperty("foo"); - assertNull(inContextFoo); - // check context errors - checker.assertContainsMatch(Status.ERROR, "Could not create an PropertyDefiner of type"); - } - - @Ignore // on certain hosts this test takes 5 seconds to complete - @Test - public void canonicalHostNameProperty() throws JoranException { - String configFileAsStr = DEFINE_INPUT_DIR + "canonicalHostname.xml"; - simpleConfigurator.doConfigure(configFileAsStr); - assertNotNull(context.getProperty("CANONICAL_HOST_NAME")); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/DummyAttributes.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/DummyAttributes.java index fb546077ff..001e875102 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/DummyAttributes.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/DummyAttributes.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java deleted file mode 100755 index 862edf1264..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeActionTest.java +++ /dev/null @@ -1,233 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.net.MalformedURLException; -import java.util.Arrays; -import java.util.HashMap; -import java.util.Stack; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.xml.sax.SAXParseException; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.TrivialConfigurator; -import ch.qos.logback.core.joran.action.ext.StackAction; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.FileTestUtil; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -public class IncludeActionTest { - - final static String INCLUDE_KEY = "includeKey"; - final static String SUB_FILE_KEY = "subFileKey"; - final static String SECOND_FILE_KEY = "secondFileKey"; - - Context context = new ContextBase(); - StatusChecker statusChecker = new StatusChecker(context); - TrivialConfigurator tc; - - static final String INCLUSION_DIR_PREFIX = CoreTestConstants.JORAN_INPUT_PREFIX + "inclusion/"; - - static final String TOP_BY_FILE = INCLUSION_DIR_PREFIX + "topByFile.xml"; - - static final String TOP_OPTIONAL = INCLUSION_DIR_PREFIX + "topOptional.xml"; - - static final String TOP_OPTIONAL_RESOURCE = INCLUSION_DIR_PREFIX + "topOptionalResource.xml"; - - static final String INTERMEDIARY_FILE = INCLUSION_DIR_PREFIX + "intermediaryByFile.xml"; - - static final String SUB_FILE = INCLUSION_DIR_PREFIX + "subByFile.xml"; - - static final String MULTI_INCLUDE_BY_FILE = INCLUSION_DIR_PREFIX + "multiIncludeByFile.xml"; - - static final String SECOND_FILE = INCLUSION_DIR_PREFIX + "second.xml"; - - static final String TOP_BY_URL = INCLUSION_DIR_PREFIX + "topByUrl.xml"; - - static final String TOP_BY_ENTITY = INCLUSION_DIR_PREFIX + "topByEntity.xml"; - - static final String INCLUDE_BY_RESOURCE = INCLUSION_DIR_PREFIX + "topByResource.xml"; - - static final String INCLUDED_FILE = INCLUSION_DIR_PREFIX + "included.xml"; - static final String URL_TO_INCLUDE = "file:./" + INCLUDED_FILE; - - static final String INVALID = INCLUSION_DIR_PREFIX + "invalid.xml"; - - static final String INCLUDED_AS_RESOURCE = "asResource/joran/inclusion/includedAsResource.xml"; - - int diff = RandomUtil.getPositiveInt(); - - StackAction stackAction = new StackAction(); - - @Before - public void setUp() throws Exception { - FileTestUtil.makeTestOutputDir(); - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("x"), new NOPAction()); - rulesMap.put(new ElementSelector("x/include"), new IncludeAction()); - rulesMap.put(new ElementSelector("x/stack"), stackAction); - - tc = new TrivialConfigurator(rulesMap); - tc.setContext(context); - } - - @After - public void tearDown() throws Exception { - StatusPrinter.printInCaseOfErrorsOrWarnings(context); - context = null; - System.clearProperty(INCLUDE_KEY); - System.clearProperty(SECOND_FILE_KEY); - System.clearProperty(SUB_FILE_KEY); - // StackAction.reset(); - } - - @Test - public void basicFile() throws JoranException { - System.setProperty(INCLUDE_KEY, INCLUDED_FILE); - tc.doConfigure(TOP_BY_FILE); - verifyConfig(new String[] { "IA", "IB" }); - } - - @Test - public void optionalFile() throws JoranException { - tc.doConfigure(TOP_OPTIONAL); - verifyConfig(new String[] { "IA", "IB" }); - StatusPrinter.print(context); - } - - @Test - public void optionalResource() throws JoranException { - tc.doConfigure(TOP_OPTIONAL_RESOURCE); - verifyConfig(new String[] { "IA", "IB" }); - StatusPrinter.print(context); - assertEquals(Status.INFO, statusChecker.getHighestLevel(0)); - } - - @Test - public void basicResource() throws JoranException { - System.setProperty(INCLUDE_KEY, INCLUDED_AS_RESOURCE); - tc.doConfigure(INCLUDE_BY_RESOURCE); - verifyConfig(new String[] { "AR_A", "AR_B" }); - } - - @Test - public void basicURL() throws JoranException { - System.setProperty(INCLUDE_KEY, URL_TO_INCLUDE); - tc.doConfigure(TOP_BY_URL); - verifyConfig(new String[] { "IA", "IB" }); - } - - @Test - public void noFileFound() throws JoranException { - System.setProperty(INCLUDE_KEY, "toto"); - tc.doConfigure(TOP_BY_FILE); - assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); - } - - @Test - public void withCorruptFile() throws JoranException, IOException { - String tmpOut = copyToTemp(INVALID); - System.setProperty(INCLUDE_KEY, tmpOut); - tc.doConfigure(TOP_BY_FILE); - assertEquals(Status.ERROR, statusChecker.getHighestLevel(0)); - StatusPrinter.print(context); - assertTrue(statusChecker.containsException(SAXParseException.class)); - - // we like to erase the temp file in order to see - // if http://jira.qos.ch/browse/LBCORE-122 was fixed - File f = new File(tmpOut); - assertTrue(f.exists()); - assertTrue(f.delete()); - - } - - String copyToTemp(String in) throws IOException { - FileInputStream fis = new FileInputStream(in); - String out = CoreTestConstants.OUTPUT_DIR_PREFIX + "out" + diff; - FileOutputStream fos = new FileOutputStream(out); - int b; - while ((b = fis.read()) != -1) { - fos.write(b); - } - fis.close(); - fos.close(); - return out; - } - - @Test - public void malformedURL() throws JoranException { - System.setProperty(INCLUDE_KEY, "htp://logback.qos.ch"); - tc.doConfigure(TOP_BY_URL); - assertEquals(Status.ERROR, statusChecker.getHighestLevel(0)); - assertTrue(statusChecker.containsException(MalformedURLException.class)); - } - - @Test - public void unknownURL() throws JoranException { - System.setProperty(INCLUDE_KEY, "http://logback2345.qos.ch"); - tc.doConfigure(TOP_BY_URL); - assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); - } - - @Test - public void nestedInclude() throws JoranException { - System.setProperty(SUB_FILE_KEY, SUB_FILE); - System.setProperty(INCLUDE_KEY, INTERMEDIARY_FILE); - tc.doConfigure(TOP_BY_FILE); - Stack witness = new Stack(); - witness.push("a"); - witness.push("b"); - witness.push("c"); - assertEquals(witness, stackAction.getStack()); - } - - @Test - public void multiInclude() throws JoranException { - System.setProperty(INCLUDE_KEY, INCLUDED_FILE); - System.setProperty(SECOND_FILE_KEY, SECOND_FILE); - tc.doConfigure(MULTI_INCLUDE_BY_FILE); - verifyConfig(new String[] { "IA", "IB", "SECOND" }); - } - - @Test - public void includeAsEntity() throws JoranException { - tc.doConfigure(TOP_BY_ENTITY); - verifyConfig(new String[] { "EA", "EB" }); - } - - void verifyConfig(String[] expected) { - Stack witness = new Stack(); - witness.addAll(Arrays.asList(expected)); - assertEquals(witness, stackAction.getStack()); - } - - - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeModelHandlerTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeModelHandlerTest.java new file mode 100644 index 0000000000..182208a996 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/IncludeModelHandlerTest.java @@ -0,0 +1,274 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.action; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Stack; +import java.util.function.Supplier; + +import ch.qos.logback.core.model.processor.IncludeModelHandler; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.xml.sax.SAXParseException; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.joran.TrivialConfigurator; +import ch.qos.logback.core.joran.action.ext.StackAction; +import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.IncludeModel; +import ch.qos.logback.core.model.StackModel; +import ch.qos.logback.core.model.TopModel; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.NOPModelHandler; +import ch.qos.logback.core.model.processor.StackModelHandler; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.testUtil.CoreTestConstants; +import ch.qos.logback.core.testUtil.FileTestUtil; +import ch.qos.logback.core.testUtil.RandomUtil; +import ch.qos.logback.core.status.testUtil.StatusChecker; + +import static ch.qos.logback.core.joran.JoranConstants.CONFIGURATION_TAG; +import static ch.qos.logback.core.joran.JoranConstants.INCLUDED_TAG; + + +public class IncludeModelHandlerTest { + + final static String INCLUDE_KEY = "includeKey"; + final static String SUB_FILE_KEY = "subFileKey"; + final static String SECOND_FILE_KEY = "secondFileKey"; + + Context context = new ContextBase(); + StatusChecker statusChecker = new StatusChecker(context); + TrivialConfigurator tc; + + static final String INCLUSION_DIR_PREFIX = CoreTestConstants.JORAN_INPUT_PREFIX + "inclusion/"; + + static final String TOP_BY_FILE = INCLUSION_DIR_PREFIX + "topByFile.xml"; + + static final String TOP_OPTIONAL = INCLUSION_DIR_PREFIX + "topOptional.xml"; + + static final String TOP_OPTIONAL_RESOURCE = INCLUSION_DIR_PREFIX + "topOptionalResource.xml"; + + static final String INTERMEDIARY_FILE = INCLUSION_DIR_PREFIX + "intermediaryByFile.xml"; + + static final String SUB_FILE = INCLUSION_DIR_PREFIX + "subByFile.xml"; + + static final String MULTI_INCLUDE_BY_FILE = INCLUSION_DIR_PREFIX + "multiIncludeByFile.xml"; + + static final String SECOND_FILE = INCLUSION_DIR_PREFIX + "second.xml"; + + static final String TOP_BY_URL = INCLUSION_DIR_PREFIX + "topByUrl.xml"; + + static final String TOP_BY_ENTITY = INCLUSION_DIR_PREFIX + "topByEntity.xml"; + + static final String INCLUDE_BY_RESOURCE = INCLUSION_DIR_PREFIX + "topByResource.xml"; + + static final String INCLUDED_FILE = INCLUSION_DIR_PREFIX + "included.xml"; + static final String URL_TO_INCLUDE = "file:./" + INCLUDED_FILE; + + static final String INVALID = INCLUSION_DIR_PREFIX + "invalid.xml"; + + static final String INCLUDED_AS_RESOURCE = "asResource/joran/inclusion/includedAsResource.xml"; + + int diff = RandomUtil.getPositiveInt(); + + + @BeforeEach + public void setUp() throws Exception { + FileTestUtil.makeTestOutputDir(); + HashMap> rulesMap = new HashMap<>(); + rulesMap.put(new ElementSelector(CONFIGURATION_TAG), () -> new TopElementAction()); + rulesMap.put(new ElementSelector(CONFIGURATION_TAG+"/include"), () -> new IncludeAction()); + rulesMap.put(new ElementSelector(CONFIGURATION_TAG+"/stack"), () -> new StackAction()); + + tc = new TrivialConfigurator(rulesMap) { + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + defaultProcessor.addHandler(TopModel.class, NOPModelHandler::makeInstance); + defaultProcessor.addHandler(IncludeModel.class, IncludeModelHandler::makeInstance); + defaultProcessor.addHandler(StackModel.class, StackModelHandler::makeInstance); + } + + public void buildModelInterpretationContext() { + super.buildModelInterpretationContext(); + this.modelInterpretationContext.setConfiguratorSupplier( () -> this.makeAnotherInstance() ); + } + }; + + tc.setContext(context); + tc.getRuleStore().addPathPathMapping(INCLUDED_TAG, CONFIGURATION_TAG); + } + + @AfterEach + public void tearDown() throws Exception { + //StatusPrinter.printInCaseOfErrorsOrWarnings(context); + context = null; + System.clearProperty(INCLUDE_KEY); + System.clearProperty(SECOND_FILE_KEY); + System.clearProperty(SUB_FILE_KEY); + // StackAction.reset(); + } + + @Test + public void basicFile() throws JoranException { + System.setProperty(INCLUDE_KEY, INCLUDED_FILE); + tc.doConfigure(TOP_BY_FILE); + //StatusPrinter.print(context); + verifyConfig(new String[] { "IA", "IB" }); + } + + @Test + public void optionalFile() throws JoranException { + tc.doConfigure(TOP_OPTIONAL); + verifyConfig(new String[] { "IA", "IB" }); + //StatusPrinter.print(context); + } + + @Test + public void optionalResource() throws JoranException { + tc.doConfigure(TOP_OPTIONAL_RESOURCE); + verifyConfig(new String[] { "IA", "IB" }); + //StatusPrinter.print(context); + Assertions.assertEquals(Status.INFO, statusChecker.getHighestLevel(0)); + } + + @Test + public void basicResource() throws JoranException { + System.setProperty(INCLUDE_KEY, INCLUDED_AS_RESOURCE); + tc.doConfigure(INCLUDE_BY_RESOURCE); + verifyConfig(new String[] { "AR_A", "AR_B" }); + } + + @Test + public void basicURL() throws JoranException { + System.setProperty(INCLUDE_KEY, URL_TO_INCLUDE); + tc.doConfigure(TOP_BY_URL); + //StatusPrinter.print(context); + verifyConfig(new String[] { "IA", "IB" }); + } + + @Test + public void noFileFound() throws JoranException { + System.setProperty(INCLUDE_KEY, "toto"); + tc.doConfigure(TOP_BY_FILE); + Assertions.assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); + } + + @Test + public void withCorruptFile() throws JoranException, IOException { + String tmpOut = copyToTemp(INVALID); + System.setProperty(INCLUDE_KEY, tmpOut); + tc.doConfigure(TOP_BY_FILE); + Assertions.assertEquals(Status.ERROR, statusChecker.getHighestLevel(0)); + //StatusPrinter.print(context); + Assertions.assertTrue(statusChecker.containsException(SAXParseException.class)); + + // we like to erase the temp file in order to see + // if http://jira.qos.ch/browse/LBCORE-122 was fixed + File f = new File(tmpOut); + Assertions.assertTrue(f.exists()); + Assertions.assertTrue(f.delete()); + + } + + String copyToTemp(String in) throws IOException { + FileInputStream fis = new FileInputStream(in); + String out = CoreTestConstants.OUTPUT_DIR_PREFIX + "out" + diff; + FileOutputStream fos = new FileOutputStream(out); + int b; + while ((b = fis.read()) != -1) { + fos.write(b); + } + fis.close(); + fos.close(); + return out; + } + + @Test + public void malformedURL() throws JoranException { + String MALFORMED = "htp://logback.qos.ch"; + + System.setProperty(INCLUDE_KEY, MALFORMED); + tc.doConfigure(TOP_BY_URL); + Assertions.assertEquals(Status.ERROR, statusChecker.getHighestLevel(0)); + Assertions.assertTrue(statusChecker.containsException(MalformedURLException.class)); + } + + @Test + public void unknownURL() throws JoranException { + System.setProperty(INCLUDE_KEY, "http://logback2345.qos.ch"); + tc.doConfigure(TOP_BY_URL); + Assertions.assertEquals(Status.WARN, statusChecker.getHighestLevel(0)); + } + + @Test + public void nestedInclude() throws JoranException { + System.setProperty(SUB_FILE_KEY, SUB_FILE); + System.setProperty(INCLUDE_KEY, INTERMEDIARY_FILE); + tc.doConfigure(TOP_BY_FILE); + Stack expected = new Stack(); + expected.push("a"); + expected.push("b"); + expected.push("c"); + @SuppressWarnings({ "unchecked", "rawtypes" }) + Stack aStack = (Stack) context.getObject(StackModelHandler.STACK_TEST); + Assertions.assertEquals(expected, aStack); + } + + @Test + public void multiInclude() throws JoranException { + System.setProperty(INCLUDE_KEY, INCLUDED_FILE); + System.setProperty(SECOND_FILE_KEY, SECOND_FILE); + tc.doConfigure(MULTI_INCLUDE_BY_FILE); + verifyConfig(new String[] { "IA", "IB", "SECOND" }); + } + + // See LOGBACK-1465 - xxe vulnerability + @Test + public void includeAsEntity() throws JoranException { + tc.doConfigure(TOP_BY_ENTITY); + // when entity inclusion is enabled + // verifyConfig(new String[] { "EA", "EB" }); + + // when entity inclusion disabled + verifyConfig(null); + } + + void verifyConfig(String[] expected) { + @SuppressWarnings({ "unchecked", "rawtypes" }) + Stack aStack = (Stack) context.getObject(StackModelHandler.STACK_TEST); + + if(expected == null) { + Assertions.assertNull(aStack); + return; + } + + Stack witness = new Stack(); + witness.addAll(Arrays.asList(expected)); + + Assertions.assertEquals(witness, aStack); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/PackageTest.java deleted file mode 100644 index 591bcee898..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.action; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ PropertyActionTest.class, IncludeActionTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/PropertyActionTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/PropertyActionTest.java old mode 100755 new mode 100644 index b9e51254d2..efce631411 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/PropertyActionTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/PropertyActionTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,27 @@ */ package ch.qos.logback.core.joran.action; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.Iterator; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.model.ModelConstants; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.ActionException; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpreter; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.TopModel; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ImplicitModelHandler; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; +import ch.qos.logback.core.model.processor.NOPModelHandler; +import ch.qos.logback.core.model.processor.PropertyModelHandler; import ch.qos.logback.core.status.ErrorStatus; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.testUtil.CoreTestConstants; @@ -32,126 +41,164 @@ /** * Test {@link PropertyAction}. + * * @author Ceki Gülcü */ public class PropertyActionTest { Context context; - InterpretationContext ec; + SaxEventInterpretationContext interpretationContext; + ModelInterpretationContext mic; + SaxEventInterpreter x; + PropertyAction propertyAction; DummyAttributes atts = new DummyAttributes(); + DefaultProcessor defaultProcessor; + TopModel topModel = new TopModel(); + String tagName = "property"; - @Before + @BeforeEach public void setUp() throws Exception { context = new ContextBase(); - ec = new InterpretationContext(context, null); + interpretationContext = new SaxEventInterpretationContext(context, null); + mic = new ModelInterpretationContext(context); + topModel.setTag("top"); + interpretationContext.pushModel(topModel); + mic.pushModel(topModel); propertyAction = new PropertyAction(); propertyAction.setContext(context); + defaultProcessor = new DefaultProcessor(context, mic); + defaultProcessor.addHandler(TopModel.class, NOPModelHandler::makeInstance); + defaultProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance); + defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); } - @After + @AfterEach public void tearDown() throws Exception { + StatusPrinter.print(context); context = null; propertyAction = null; atts = null; } @Test - public void nameValuePair() { + public void nameValuePair() throws ActionException { atts.setValue("name", "v1"); atts.setValue("value", "work"); - propertyAction.begin(ec, null, atts); - assertEquals("work", ec.getProperty("v1")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("work", mic.getProperty("v1")); } @Test - public void nameValuePairWithPrerequisiteSubsitution() { + public void nameValuePairWithPrerequisiteSubsitution() throws ActionException { context.putProperty("w", "wor"); atts.setValue("name", "v1"); atts.setValue("value", "${w}k"); - propertyAction.begin(ec, null, atts); - assertEquals("work", ec.getProperty("v1")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("work", mic.getProperty("v1")); } @Test - public void noValue() { + public void noValue() throws ActionException { atts.setValue("name", "v1"); - propertyAction.begin(ec, null, atts); - assertEquals(1, context.getStatusManager().getCount()); - assertTrue(checkError()); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals(2, context.getStatusManager().getCount()); + Assertions.assertTrue(checkError()); } @Test - public void noName() { + public void noName() throws ActionException { atts.setValue("value", "v1"); - propertyAction.begin(ec, null, atts); - assertEquals(1, context.getStatusManager().getCount()); - assertTrue(checkError()); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals(2, context.getStatusManager().getCount()); + Assertions.assertTrue(checkError()); } @Test - public void noAttributes() { - propertyAction.begin(ec, null, atts); - assertEquals(1, context.getStatusManager().getCount()); - assertTrue(checkError()); + public void noAttributes() throws ActionException { + propertyAction.begin(interpretationContext, "noAttributes", atts); + propertyAction.end(interpretationContext, "noAttributes"); + defaultProcessor.process(topModel); + Assertions.assertEquals(2, context.getStatusManager().getCount()); + Assertions.assertTrue(checkError()); StatusPrinter.print(context); } @Test - public void testFileNotLoaded() { + public void testFileNotLoaded() throws ActionException { atts.setValue("file", "toto"); atts.setValue("value", "work"); - propertyAction.begin(ec, null, atts); - assertEquals(1, context.getStatusManager().getCount()); - assertTrue(checkError()); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals(2, context.getStatusManager().getCount()); + Assertions.assertTrue(checkError()); } @Test - public void testLoadFileWithPrerequisiteSubsitution() { + public void testLoadFileWithPrerequisiteSubsitution() throws ActionException { context.putProperty("STEM", CoreTestConstants.TEST_SRC_PREFIX + "input/joran"); atts.setValue("file", "${STEM}/propertyActionTest.properties"); - propertyAction.begin(ec, null, atts); - assertEquals("tata", ec.getProperty("v1")); - assertEquals("toto", ec.getProperty("v2")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("tata", mic.getProperty("v1")); + Assertions.assertEquals("toto", mic.getProperty("v2")); } @Test - public void testLoadFile() { + public void testLoadFile() throws ActionException { atts.setValue("file", CoreTestConstants.TEST_SRC_PREFIX + "input/joran/propertyActionTest.properties"); - propertyAction.begin(ec, null, atts); - assertEquals("tata", ec.getProperty("v1")); - assertEquals("toto", ec.getProperty("v2")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("tata", mic.getProperty("v1")); + Assertions.assertEquals("toto", mic.getProperty("v2")); } @Test - public void testLoadResource() { + public void testLoadResource() throws ActionException { atts.setValue("resource", "asResource/joran/propertyActionTest.properties"); - propertyAction.begin(ec, null, atts); - assertEquals("tata", ec.getProperty("r1")); - assertEquals("toto", ec.getProperty("r2")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("tata", mic.getProperty("r1")); + Assertions.assertEquals("toto", mic.getProperty("r2")); } @Test - public void testLoadResourceWithPrerequisiteSubsitution() { + public void testLoadResourceWithPrerequisiteSubsitution() throws ActionException { context.putProperty("STEM", "asResource/joran"); atts.setValue("resource", "${STEM}/propertyActionTest.properties"); - propertyAction.begin(ec, null, atts); - assertEquals("tata", ec.getProperty("r1")); - assertEquals("toto", ec.getProperty("r2")); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals("tata", mic.getProperty("r1")); + Assertions.assertEquals("toto", mic.getProperty("r2")); } @Test - public void testLoadNotPossible() { + public void testLoadNotPossible() throws ActionException { atts.setValue("file", "toto"); - propertyAction.begin(ec, null, atts); - assertEquals(1, context.getStatusManager().getCount()); - assertTrue(checkFileErrors()); + propertyAction.begin(interpretationContext, tagName, atts); + propertyAction.end(interpretationContext, tagName); + defaultProcessor.process(topModel); + Assertions.assertEquals(2, context.getStatusManager().getCount()); + Assertions.assertTrue(checkFileErrors()); } private boolean checkError() { Iterator it = context.getStatusManager().getCopyOfStatusList().iterator(); ErrorStatus es = (ErrorStatus) it.next(); - return PropertyAction.INVALID_ATTRIBUTES.equals(es.getMessage()); + return ModelConstants.INVALID_ATTRIBUTES.equals(es.getMessage()); } private boolean checkFileErrors() { diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/TopElementAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/TopElementAction.java new file mode 100755 index 0000000000..2cf8c2c1c7 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/TopElementAction.java @@ -0,0 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.action; + +import org.xml.sax.Attributes; + +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.TopModel; + +/** + * Add a Model instance at the top of the InterpretationContext stack + * + * @author Ceki Gulcu + */ +public class TopElementAction extends BaseModelAction { + + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + TopModel topModel = new TopModel(); + return topModel; + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadBeginAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadBeginAction.java index 460b836ee4..4f3de7595f 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadBeginAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadBeginAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class BadBeginAction extends Action { @@ -27,7 +27,7 @@ public class BadBeginAction extends Action { int type; - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) throws ActionException { String exType = attributes.getValue(EXCEPTION_TYPE); type = RUNTIME_EDXCEPTION; @@ -44,6 +44,6 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) } - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadEndAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadEndAction.java index a141c5293a..865fd076b8 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadEndAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/BadEndAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class BadEndAction extends Action { @@ -27,7 +27,7 @@ public class BadEndAction extends Action { int type; - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { String exType = attributes.getValue(EXCEPTION_TYPE); type = RUNTIME_EXCEPTION; if ("ActionException".equals(exType)) { @@ -35,7 +35,7 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) } } - public void end(InterpretationContext ec, String name) throws ActionException { + public void end(SaxEventInterpretationContext ec, String name) throws ActionException { switch (type) { case ACTION_EXCEPTION: throw new ActionException(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/HelloAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/HelloAction.java index 85e0027c63..9ebc6b34d7 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/HelloAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/HelloAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,25 +16,25 @@ import org.xml.sax.Attributes; import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class HelloAction extends Action { static final public String PROPERTY_KEY = "name"; /** - * Instantiates an layout of the given class and sets its name. + * Instantiates a layout of the given class and sets its name. * */ - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { String str = "Hello " + attributes.getValue("name") + "."; ec.getContext().putProperty(PROPERTY_KEY, str); } /** - * Once the children elements are also parsed, now is the time to activate - * the appender options. + * Once the children elements are also parsed, now is the time to activate the + * appender options. */ - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/IncAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/IncAction.java index ae5f9a7b3e..7c07f4096a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/IncAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/IncAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,7 +17,7 @@ import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class IncAction extends Action { @@ -32,10 +32,10 @@ static public void reset() { } /** - * Instantiates an layout of the given class and sets its name. + * Instantiates a layout of the given class and sets its name. * */ - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) throws ActionException { // System.out.println("IncAction Begin called"); beginCount++; String val = attributes.getValue("increment"); @@ -46,10 +46,10 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) } /** - * Once the children elements are also parsed, now is the time to activate - * the appender options. + * Once the children elements are also parsed, now is the time to activate the + * appender options. */ - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { endCount++; } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/StackAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/StackAction.java index d765a8627b..6d55983794 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/StackAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/StackAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,29 +13,31 @@ */ package ch.qos.logback.core.joran.action.ext; -import java.util.Stack; - import org.xml.sax.Attributes; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -public class StackAction extends Action { +import ch.qos.logback.core.joran.action.BaseModelAction; +import ch.qos.logback.core.joran.action.PreconditionValidator; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.StackModel; - Stack stack = new Stack(); - - public Stack getStack() { - return stack; - } +public class StackAction extends BaseModelAction { - public void begin(InterpretationContext ec, String name, Attributes attributes) { - stack.push(attributes.getValue("name")); + + @Override + protected boolean validPreconditions(SaxEventInterpretationContext ic, String name, Attributes attributes) { + PreconditionValidator validator = new PreconditionValidator(this, ic, name, attributes); + validator.validateNameAttribute(); + return validator.isValid(); } - public void end(InterpretationContext ec, String name) { + @Override + protected Model buildCurrentModel(SaxEventInterpretationContext interpretationContext, String name, + Attributes attributes) { + StackModel stackModel = new StackModel(); + stackModel.setName(attributes.getValue(NAME_ATTRIBUTE)); + return stackModel; } + - // static public void reset() { - // stack.clear(); - // } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/TouchAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/TouchAction.java index 7e3fad8ea2..7efde4352b 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/TouchAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/action/ext/TouchAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,17 +16,17 @@ import org.xml.sax.Attributes; import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; public class TouchAction extends Action { public static final String KEY = "touched"; /** - * Instantiates an layout of the given class and sets its name. + * Instantiates a layout of the given class and sets its name. * */ - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { Integer i = (Integer) ec.getContext().getObject(KEY); if (i == null) { ec.getContext().putObject(KEY, Integer.valueOf(1)); @@ -36,9 +36,9 @@ public void begin(InterpretationContext ec, String name, Attributes attributes) } /** - * Once the children elements are also parsed, now is the time to activate - * the appender options. + * Once the children elements are also parsed, now is the time to activate the + * appender options. */ - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseAndIncludeCompositionTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseAndIncludeCompositionTest.java deleted file mode 100644 index 2fa229d570..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseAndIncludeCompositionTest.java +++ /dev/null @@ -1,92 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.conditional; - -import static org.junit.Assert.assertEquals; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Stack; - -import ch.qos.logback.core.joran.spi.ElementSelector; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.TrivialConfigurator; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.IncludeAction; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.joran.action.ext.StackAction; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.util.StatusPrinter; - -public class IfThenElseAndIncludeCompositionTest { - - Context context = new ContextBase(); - TrivialConfigurator tc; - int diff = RandomUtil.getPositiveInt(); - static final String CONDITIONAL_DIR_PREFIX = CoreTestConstants.JORAN_INPUT_PREFIX + "conditional/"; - - final static String THEN_FILE_TO_INCLUDE_KEY = "thenFileToInclude"; - final static String ELSE_FILE_TO_INCLUDE_KEY = "elseFileToInclude"; - - static final String NESTED_INCLUDE_FILE = CONDITIONAL_DIR_PREFIX + "nestedInclude.xml"; - static final String THEN_FILE_TO_INCLUDE = CONDITIONAL_DIR_PREFIX + "includedA.xml"; - static final String ELSE_FILE_TO_INCLUDE = CONDITIONAL_DIR_PREFIX + "includedB.xml"; - - StackAction stackAction = new StackAction(); - - @Before - public void setUp() throws Exception { - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("x"), new NOPAction()); - rulesMap.put(new ElementSelector("x/stack"), stackAction); - rulesMap.put(new ElementSelector("*/if"), new IfAction()); - rulesMap.put(new ElementSelector("*/if/then"), new ThenAction()); - rulesMap.put(new ElementSelector("*/if/then/*"), new NOPAction()); - rulesMap.put(new ElementSelector("*/if/else"), new ElseAction()); - rulesMap.put(new ElementSelector("*/if/else/*"), new NOPAction()); - rulesMap.put(new ElementSelector("x/include"), new IncludeAction()); - - tc = new TrivialConfigurator(rulesMap); - tc.setContext(context); - } - - @After - public void tearDown() throws Exception { - StatusPrinter.printInCaseOfErrorsOrWarnings(context); - context = null; - // StackAction.reset(); - } - - @Test - public void includeNestedWithinIf() throws JoranException { - context.putProperty(THEN_FILE_TO_INCLUDE_KEY, THEN_FILE_TO_INCLUDE); - context.putProperty(ELSE_FILE_TO_INCLUDE_KEY, ELSE_FILE_TO_INCLUDE); - tc.doConfigure(NESTED_INCLUDE_FILE); - verifyConfig(new String[] { "BEGIN", "e0", "IncludedB0", "e1", "END" }); - } - - void verifyConfig(String[] expected) { - Stack witness = new Stack(); - witness.addAll(Arrays.asList(expected)); - assertEquals(witness, stackAction.getStack()); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseTest.java deleted file mode 100644 index 4cf73e5d93..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/IfThenElseTest.java +++ /dev/null @@ -1,142 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.conditional; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Stack; - -import ch.qos.logback.core.joran.action.PropertyAction; -import ch.qos.logback.core.joran.spi.ElementSelector; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.TrivialConfigurator; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.joran.action.ext.StackAction; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -import static org.junit.Assert.*; - -public class IfThenElseTest { - - Context context = new ContextBase(); - StatusChecker checker = new StatusChecker(context); - TrivialConfigurator tc; - int diff = RandomUtil.getPositiveInt(); - static final String CONDITIONAL_DIR_PREFIX = CoreTestConstants.JORAN_INPUT_PREFIX + "conditional/"; - - String ki1 = "ki1"; - String val1 = "val1"; - String sysKey = "sysKey"; - String dynaKey = "dynaKey"; - - StackAction stackAction = new StackAction(); - - @Before - public void setUp() throws Exception { - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("x"), new NOPAction()); - rulesMap.put(new ElementSelector("x/stack"), stackAction); - rulesMap.put(new ElementSelector("x/property"), new PropertyAction()); - rulesMap.put(new ElementSelector("*/if"), new IfAction()); - rulesMap.put(new ElementSelector("*/if/then"), new ThenAction()); - rulesMap.put(new ElementSelector("*/if/then/*"), new NOPAction()); - rulesMap.put(new ElementSelector("*/if/else"), new ElseAction()); - rulesMap.put(new ElementSelector("*/if/else/*"), new NOPAction()); - - tc = new TrivialConfigurator(rulesMap); - tc.setContext(context); - } - - @After - public void tearDown() throws Exception { - StatusPrinter.printIfErrorsOccured(context); - System.clearProperty(sysKey); - } - - @Test - public void whenContextPropertyIsSet_IfThenBranchIsEvaluated() throws JoranException { - context.putProperty(ki1, val1); - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "if0.xml"); - verifyConfig(new String[] { "BEGIN", "a", "END" }); - } - - @Test - public void whenLocalPropertyIsSet_IfThenBranchIsEvaluated() throws JoranException { - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "if_localProperty.xml"); - verifyConfig(new String[] { "BEGIN", "a", "END" }); - } - - @Test - public void whenNoPropertyIsDefined_ElseBranchIsEvaluated() throws JoranException { - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "if0.xml"); - verifyConfig(new String[] { "BEGIN", "b", "END" }); - } - - @Test - public void whenContextPropertyIsSet_IfThenBranchIsEvaluated_NO_ELSE_DEFINED() throws JoranException { - context.putProperty(ki1, val1); - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse.xml"); - verifyConfig(new String[] { "BEGIN", "a", "END" }); - } - - @Test - public void whenNoPropertyIsDefined_IfThenBranchIsNotEvaluated_NO_ELSE_DEFINED() throws JoranException { - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "ifWithoutElse.xml"); - verifyConfig(new String[] { "BEGIN", "END" }); - assertTrue(checker.isErrorFree(0)); - } - - @Test - public void nestedIf() throws JoranException { - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "nestedIf.xml"); - verifyConfig(new String[] { "BEGIN", "a", "c", "END" }); - assertTrue(checker.isErrorFree(0)); - } - - @Test - public void useNonExistenceOfSystemPropertyToDefineAContextProperty() throws JoranException { - assertNull(System.getProperty(sysKey)); - assertNull(context.getProperty(dynaKey)); - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem.xml"); - System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); - assertNotNull(context.getProperty(dynaKey)); - } - - @Test - public void noContextPropertyShouldBeDefinedIfSystemPropertyExists() throws JoranException { - System.setProperty(sysKey, "a"); - assertNull(context.getProperty(dynaKey)); - System.out.println("before " + dynaKey + "=" + context.getProperty(dynaKey)); - tc.doConfigure(CONDITIONAL_DIR_PREFIX + "ifSystem.xml"); - System.out.println(dynaKey + "=" + context.getProperty(dynaKey)); - assertNull(context.getProperty(dynaKey)); - } - - private void verifyConfig(String[] expected) { - Stack witness = new Stack(); - witness.addAll(Arrays.asList(expected)); - assertEquals(witness, stackAction.getStack()); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PackageTest.java deleted file mode 100644 index a2f99dfa4e..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/conditional/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.conditional; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ PropertyEvalScriptBuilderTest.class, IfThenElseTest.class, IfThenElseAndIncludeCompositionTest.class }) -public class PackageTest { - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/event/InPlayFireTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/event/InPlayFireTest.java deleted file mode 100755 index aa4dcb8b50..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/event/InPlayFireTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; - -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.TrivialConfigurator; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.testUtil.CoreTestConstants; - -public class InPlayFireTest { - - Context context = new ContextBase(); - HashMap rulesMap = new HashMap(); - - @Test - public void testBasic() throws JoranException { - ListenAction listenAction = new ListenAction(); - - rulesMap.put(new ElementSelector("fire"), listenAction); - TrivialConfigurator gc = new TrivialConfigurator(rulesMap); - - gc.setContext(context); - gc.doConfigure(CoreTestConstants.TEST_SRC_PREFIX + "input/joran/fire1.xml"); - - // for(SaxEvent se: listenAction.getSeList()) { - // System.out.println(se); - // } - assertEquals(5, listenAction.getSeList().size()); - assertTrue(listenAction.getSeList().get(0) instanceof StartEvent); - assertTrue(listenAction.getSeList().get(1) instanceof StartEvent); - assertTrue(listenAction.getSeList().get(2) instanceof BodyEvent); - assertTrue(listenAction.getSeList().get(3) instanceof EndEvent); - } - - @Test - public void testReplay() throws JoranException { - ListenAction listenAction = new ListenAction(); - - rulesMap.put(new ElementSelector("fire"), listenAction); - TrivialConfigurator gc = new TrivialConfigurator(rulesMap); - - gc.setContext(context); - gc.doConfigure(CoreTestConstants.TEST_SRC_PREFIX + "input/joran/fire1.xml"); - - // for(SaxEvent se: listenAction.getSeList()) { - // System.out.println(se); - // } - assertEquals(5, listenAction.getSeList().size()); - assertTrue(listenAction.getSeList().get(0) instanceof StartEvent); - assertTrue(listenAction.getSeList().get(1) instanceof StartEvent); - assertTrue(listenAction.getSeList().get(2) instanceof BodyEvent); - assertTrue(listenAction.getSeList().get(3) instanceof EndEvent); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/event/ListenAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/event/ListenAction.java deleted file mode 100644 index fce55ca745..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/event/ListenAction.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event; - -import java.util.ArrayList; -import java.util.List; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -public class ListenAction extends Action implements InPlayListener { - - List seList = new ArrayList(); - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - ec.addInPlayListener(this); - } - - @Override - public void end(InterpretationContext ec, String name) throws ActionException { - ec.removeInPlayListener(this); - - } - - public void inPlay(SaxEvent event) { - seList.add(event); - } - - public List getSeList() { - return seList; - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/event/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/event/PackageTest.java deleted file mode 100644 index 5408423a4d..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/event/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ SaxEventRecorderTest.class, InPlayFireTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/event/SaxEventRecorderTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/event/SaxEventRecorderTest.java index 9f2362cae6..6cbed1fe26 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/event/SaxEventRecorderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/event/SaxEventRecorderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,28 +13,29 @@ */ package ch.qos.logback.core.joran.event; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.io.FileInputStream; import java.util.List; +import java.util.concurrent.TimeUnit; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; -import org.junit.Test; +import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; import org.xml.sax.Attributes; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; /** * Test whether SaxEventRecorder does a good job. - * + * * @author Ceki Gulcu */ public class SaxEventRecorderTest { @@ -62,43 +63,58 @@ public void dump(List seList) { } @Test - public void test1() throws Exception { + public void testEvent1() throws Exception { + System.out.println("test1"); List seList = doTest("event1.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); + StatusPrinter.print(context); + Assertions.assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); // dump(seList); - assertEquals(11, seList.size()); + Assertions.assertEquals(11, seList.size()); + } + + @Test() + @Timeout(value = 500, unit = TimeUnit.MILLISECONDS) // timeout in case attack is not prevented + public void testEventSSRF() throws Exception { + try { + List seList = doTest("event-ssrf.xml"); + Assertions.assertTrue(statusChecker.getHighestLevel(0) == Status.WARN); + statusChecker.assertContainsMatch(Status.WARN, "Document Type Declaration"); + Assertions.assertEquals(11, seList.size()); + } finally { + StatusPrinter.print(context); + } } @Test - public void test2() throws Exception { + public void testEventAmp() throws Exception { List seList = doTest("ampEvent.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); + Assertions.assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); // dump(seList); - assertEquals(3, seList.size()); + Assertions.assertEquals(3, seList.size()); BodyEvent be = (BodyEvent) seList.get(1); - assertEquals("xxx & yyy", be.getText()); + Assertions.assertEquals("xxx & yyy", be.getText()); } @Test - public void test3() throws Exception { + public void testInc() throws Exception { List seList = doTest("inc.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); + Assertions.assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); // dump(seList); - assertEquals(4, seList.size()); + Assertions.assertEquals(4, seList.size()); StartEvent se = (StartEvent) seList.get(1); Attributes attr = se.getAttributes(); - assertNotNull(attr); - assertEquals("1", attr.getValue("increment")); + Assertions.assertNotNull(attr); + Assertions.assertEquals("1", attr.getValue("increment")); } @Test public void bodyWithSpacesAndQuotes() throws Exception { List seList = doTest("spacesAndQuotes.xml"); - assertEquals(3, seList.size()); + Assertions.assertEquals(3, seList.size()); BodyEvent be = (BodyEvent) seList.get(1); - assertEquals("[x][x] \"xyz\"%n", be.getText()); + Assertions.assertEquals("[x][x] \"xyz\"%n", be.getText()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorderTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorderTest.java deleted file mode 100755 index d8938b4691..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/event/stax/StaxEventRecorderTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.event.stax; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.io.FileInputStream; -import java.util.List; - -import javax.xml.stream.events.Attribute; - -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.StatusChecker; - -public class StaxEventRecorderTest { - - Context context = new ContextBase(); - StatusChecker statusChecker = new StatusChecker(context); - - public List doTest(String filename) throws Exception { - StaxEventRecorder recorder = new StaxEventRecorder(context); - FileInputStream fis = new FileInputStream(CoreTestConstants.TEST_SRC_PREFIX + "input/joran/" + filename); - recorder.recordEvents(fis); - return recorder.getEventList(); - } - - public void dump(List seList) { - for (StaxEvent se : seList) { - System.out.println(se); - } - } - - @Test - public void testParsingOfXMLWithAttributesAndBodyText() throws Exception { - List seList = doTest("event1.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); - // dump(seList); - assertEquals(11, seList.size()); - assertEquals("test", seList.get(0).getName()); - assertEquals("badBegin", seList.get(1).getName()); - StartEvent startEvent = (StartEvent) seList.get(7); - assertEquals("John Doe", startEvent.getAttributeByName("name").getValue()); - assertEquals("XXX&", ((BodyEvent) seList.get(8)).getText()); - } - - @Test - public void testProcessingOfTextWithEntityCharacters() throws Exception { - List seList = doTest("ampEvent.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); - // dump(seList); - assertEquals(3, seList.size()); - - BodyEvent be = (BodyEvent) seList.get(1); - assertEquals("xxx & yyy", be.getText()); - } - - @Test - public void testAttributeProcessing() throws Exception { - List seList = doTest("inc.xml"); - assertTrue(statusChecker.getHighestLevel(0) == Status.INFO); - assertEquals(4, seList.size()); - StartEvent se = (StartEvent) seList.get(1); - Attribute attr = se.getAttributeByName("increment"); - assertNotNull(attr); - assertEquals("1", attr.getValue()); - } - - @Test - public void bodyWithSpacesAndQuotes() throws Exception { - List seList = doTest("spacesAndQuotes.xml"); - assertEquals(3, seList.size()); - BodyEvent be = (BodyEvent) seList.get(1); - assertEquals("[x][x] \"xyz\"%n", be.getText()); - } -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Cake.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Cake.java index d8cd7a6f9e..f5638ebf75 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Cake.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Cake.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Fruit.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Fruit.java index 7c1a5803e9..f52de911cf 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Fruit.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/Fruit.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContext.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContext.java index d8b028564f..9a30c1811e 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContext.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextAction.java index 1b061c8f1d..6d50b685dd 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextAction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextAction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,40 +17,30 @@ import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; +import ch.qos.logback.core.joran.spi.SaxEventInterpretationContext; +import ch.qos.logback.core.model.Model; public class FruitContextAction extends Action { - private boolean inError = false; + FruitContextModel parentModel; @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - - inError = false; - - try { - ec.pushObject(context); - } catch (Exception oops) { - inError = true; - addError("Could not push context", oops); - throw new ActionException(oops); - } + public void begin(SaxEventInterpretationContext ic, String name, Attributes attributes) throws ActionException { + parentModel = new FruitContextModel(); + parentModel.setTag(name); + ic.pushModel(parentModel); } @Override - public void end(InterpretationContext ec, String name) throws ActionException { - if (inError) { - return; - } + public void end(SaxEventInterpretationContext ic, String name) throws ActionException { - Object o = ec.peekObject(); + Model m = ic.peekModel(); - if (o != context) { - addWarn("The object at the of the stack is not the context named [" + context.getName() + "] pushed earlier."); - } else { - addInfo("Popping context named [" + context.getName() + "] from the object stack"); - ec.popObject(); + if (m != parentModel) { + addWarn("The object at the of the stack is not the model named [" + parentModel.getTag() + + "] pushed earlier."); } + // NOTE: top level model is NOT popped } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModel.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModel.java new file mode 100755 index 0000000000..adb492e43a --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModel.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.joran.implicitAction; + +import ch.qos.logback.core.model.Model; + +public class FruitContextModel extends Model { + + private static final long serialVersionUID = 548563161422738332L; + + @Override + protected FruitContextModel makeNewInstance() { + return new FruitContextModel(); + } +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModelHandler.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModelHandler.java new file mode 100755 index 0000000000..8307f261aa --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/FruitContextModelHandler.java @@ -0,0 +1,52 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.implicitAction; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.processor.ModelHandlerBase; +import ch.qos.logback.core.model.processor.ModelHandlerException; +import ch.qos.logback.core.model.processor.ModelInterpretationContext; + +public class FruitContextModelHandler extends ModelHandlerBase { + + public FruitContextModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new FruitContextModelHandler(context); + } + + @Override + public void handle(ModelInterpretationContext interpretationContext, Model model) throws ModelHandlerException { + interpretationContext.pushObject(context); + } + + @Override + public void postHandle(ModelInterpretationContext ec, Model model) throws ModelHandlerException { + + Object o = ec.peekObject(); + + if (o != context) { + addWarn("The object [" + o + "] at top of the stack is not the context named [" + context.getName() + + "] pushed earlier."); + } else { + addInfo("Popping context named [" + context.getName() + "] from the object stack"); + ec.popObject(); + } + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/ImplicitActionTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/ImplicitActionTest.java index 73a4a6f58d..68c45b0660 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/ImplicitActionTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/ImplicitActionTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,23 +13,31 @@ */ package ch.qos.logback.core.joran.implicitAction; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - import java.util.HashMap; import java.util.List; +import java.util.function.Supplier; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.joran.SimpleConfigurator; import ch.qos.logback.core.joran.action.Action; import ch.qos.logback.core.joran.action.StatusListenerAction; import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.PropertyModel; +import ch.qos.logback.core.model.StatusListenerModel; +import ch.qos.logback.core.model.processor.DefaultProcessor; +import ch.qos.logback.core.model.processor.ImplicitModelHandler; +import ch.qos.logback.core.model.processor.PropertyModelHandler; +import ch.qos.logback.core.model.processor.StatusListenerModelHandler; import ch.qos.logback.core.testUtil.CoreTestConstants; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.StatusPrinter; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + public class ImplicitActionTest { static final String IMPLCIT_DIR = CoreTestConstants.TEST_SRC_PREFIX + "input/joran/implicitAction/"; @@ -37,14 +45,24 @@ public class ImplicitActionTest { FruitContext fruitContext = new FruitContext(); SimpleConfigurator simpleConfigurator; StatusChecker checker = new StatusChecker(fruitContext); - - @Before + + @BeforeEach public void setUp() throws Exception { fruitContext.setName("fruits"); - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("/context/"), new FruitContextAction()); - rulesMap.put(new ElementSelector("/context/statusListener"), new StatusListenerAction()); - simpleConfigurator = new SimpleConfigurator(rulesMap); + HashMap> rulesMap = new HashMap<>(); + rulesMap.put(new ElementSelector("/context/"), () -> new FruitContextAction()); + rulesMap.put(new ElementSelector("/context/statusListener"), () -> new StatusListenerAction()); + simpleConfigurator = new SimpleConfigurator(rulesMap) { + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + defaultProcessor.addHandler(FruitContextModel.class, FruitContextModelHandler::makeInstance); + defaultProcessor.addHandler(PropertyModel.class, PropertyModelHandler::makeInstance); + defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); + defaultProcessor.addHandler(StatusListenerModel.class, StatusListenerModelHandler::makeInstance); + } + + }; simpleConfigurator.setContext(fruitContext); } @@ -64,6 +82,25 @@ void verifyFruit() { public void nestedComplex() throws Exception { try { simpleConfigurator.doConfigure(IMPLCIT_DIR + "nestedComplex.xml"); + StatusPrinter.print(fruitContext); + verifyFruit(); + + } catch (Exception je) { + StatusPrinter.print(fruitContext); + throw je; + } + } + + @Test + public void nestedComplexWithPassword() throws Exception { + try { + fruitContext.addSubstitutionProperty("A_PASSWORD", "blue"); + + simpleConfigurator.doConfigure(IMPLCIT_DIR + "nestedComplexWithPassword.xml"); + StatusPrinter.print(fruitContext); + StatusChecker checker = new StatusChecker(fruitContext); + + checker.assertContainsMatch("value \"\\*{6}\" substituted for \"\\$\\{A_PASSWORD\\}\""); verifyFruit(); } catch (Exception je) { @@ -124,7 +161,7 @@ public void nestedComplexCollectionWithoutClassAtrribute() throws Exception { @Test public void statusListenerWithPrefix() throws Exception { - try { + try { simpleConfigurator.doConfigure(IMPLCIT_DIR + "statusListenerWithPrefix.xml"); StatusPrinter.print(fruitContext); checker.assertIsErrorFree(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/PackageTest.java deleted file mode 100644 index ced90551f9..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/implicitAction/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.implicitAction; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ImplicitActionTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/Fruit.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/Fruit.java deleted file mode 100644 index ce5130634a..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/Fruit.java +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import java.util.ArrayList; -import java.util.List; - -public class Fruit { - - String name; - List textList = new ArrayList(); - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public String toString() { - final String TAB = " "; - - StringBuilder retValue = new StringBuilder(); - - retValue.append("xFruit ( ").append("name = ").append(this.name).append(TAB).append(" )"); - - return retValue.toString(); - } - - public void addText(String s) { - textList.add(s); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurationTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurationTest.java deleted file mode 100755 index 8b0aae7f20..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurationTest.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import java.util.HashMap; -import java.util.List; - -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.testUtil.CoreTestConstants; - -import org.junit.Test; - -import ch.qos.logback.core.joran.SimpleConfigurator; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.util.StatusPrinter; - -/** - * The Fruit* code is intended to test Joran's replay capability - * */ -public class FruitConfigurationTest { - - FruitContext fruitContext = new FruitContext(); - - public List doFirstPart(String filename) throws Exception { - - try { - HashMap rulesMap = new HashMap(); - rulesMap.put(new ElementSelector("group/fruitShell"), new FruitShellAction()); - rulesMap.put(new ElementSelector("group/fruitShell/fruit"), new FruitFactoryAction()); - rulesMap.put(new ElementSelector("group/fruitShell/fruit/*"), new NOPAction()); - SimpleConfigurator simpleConfigurator = new SimpleConfigurator(rulesMap); - - simpleConfigurator.setContext(fruitContext); - - simpleConfigurator.doConfigure(CoreTestConstants.TEST_SRC_PREFIX + "input/joran/replay/" + filename); - - return fruitContext.getFruitShellList(); - } catch (Exception je) { - StatusPrinter.print(fruitContext); - throw je; - } - } - - @Test - public void fruit1() throws Exception { - List fsList = doFirstPart("fruit1.xml"); - assertNotNull(fsList); - assertEquals(1, fsList.size()); - - FruitShell fs0 = fsList.get(0); - assertNotNull(fs0); - assertEquals("fs0", fs0.getName()); - Fruit fruit0 = fs0.fruitFactory.buildFruit(); - assertTrue(fruit0 instanceof Fruit); - assertEquals("blue", fruit0.getName()); - } - - @Test - public void fruit2() throws Exception { - List fsList = doFirstPart("fruit2.xml"); - assertNotNull(fsList); - assertEquals(2, fsList.size()); - - FruitShell fs0 = fsList.get(0); - assertNotNull(fs0); - assertEquals("fs0", fs0.getName()); - Fruit fruit0 = fs0.fruitFactory.buildFruit(); - assertTrue(fruit0 instanceof Fruit); - assertEquals("blue", fruit0.getName()); - - FruitShell fs1 = fsList.get(1); - assertNotNull(fs1); - assertEquals("fs1", fs1.getName()); - Fruit fruit1 = fs1.fruitFactory.buildFruit(); - assertTrue(fruit1 instanceof WeightytFruit); - assertEquals("orange", fruit1.getName()); - assertEquals(1.2, ((WeightytFruit) fruit1).getWeight(), 0.01); - } - - @Test - public void withSubst() throws Exception { - List fsList = doFirstPart("fruitWithSubst.xml"); - assertNotNull(fsList); - assertEquals(1, fsList.size()); - - FruitShell fs0 = fsList.get(0); - assertNotNull(fs0); - assertEquals("fs0", fs0.getName()); - int oldCount = FruitFactory.count; - Fruit fruit0 = fs0.fruitFactory.buildFruit(); - assertTrue(fruit0 instanceof WeightytFruit); - assertEquals("orange-" + oldCount, fruit0.getName()); - assertEquals(1.2, ((WeightytFruit) fruit0).getWeight(), 0.01); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurator.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurator.java deleted file mode 100644 index cda638f8e8..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitConfigurator.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import java.util.List; - -import ch.qos.logback.core.joran.GenericConfigurator; -import ch.qos.logback.core.joran.action.NOPAction; -import ch.qos.logback.core.joran.action.NestedBasicPropertyIA; -import ch.qos.logback.core.joran.action.NestedComplexPropertyIA; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.joran.spi.EventPlayer; -import ch.qos.logback.core.joran.spi.Interpreter; -import ch.qos.logback.core.joran.spi.JoranException; -import ch.qos.logback.core.joran.spi.RuleStore; - -public class FruitConfigurator extends GenericConfigurator { - - FruitFactory ff; - - public FruitConfigurator(FruitFactory ff) { - this.ff = ff; - } - - @Override - final public void doConfigure(final List eventList) throws JoranException { - buildInterpreter(); - interpreter.getInterpretationContext().pushObject(ff); - EventPlayer player = new EventPlayer(interpreter); - player.play(eventList); - } - - @Override - protected void addImplicitRules(Interpreter interpreter) { - NestedComplexPropertyIA nestedIA = new NestedComplexPropertyIA(getBeanDescriptionCache()); - nestedIA.setContext(context); - interpreter.addImplicitAction(nestedIA); - - NestedBasicPropertyIA nestedSimpleIA = new NestedBasicPropertyIA(getBeanDescriptionCache()); - nestedIA.setContext(context); - interpreter.addImplicitAction(nestedSimpleIA); - } - - @Override - protected void addInstanceRules(RuleStore rs) { - rs.addRule(new ElementSelector("fruitShell"), new NOPAction()); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitContext.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitContext.java deleted file mode 100644 index ba2af6c5a9..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitContext.java +++ /dev/null @@ -1,36 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import java.util.ArrayList; -import java.util.List; - -import ch.qos.logback.core.ContextBase; - -public class FruitContext extends ContextBase { - - List fruitShellList = new ArrayList(); - - public void addFruitShell(FruitShell fs) { - fruitShellList.add(fs); - } - - public List getFruitShellList() { - return fruitShellList; - } - - public void setFruitShellList(List fruitShellList) { - this.fruitShellList = fruitShellList; - } -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactory.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactory.java deleted file mode 100644 index e3ad7e5884..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactory.java +++ /dev/null @@ -1,69 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import java.util.List; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.JoranException; - -public class FruitFactory { - - static int count = 0; - - private List eventList; - Fruit fruit; - - public void setFruit(Fruit fruit) { - this.fruit = fruit; - } - - public Fruit buildFruit() { - - Context context = new ContextBase(); - this.fruit = null; - context.putProperty("fruitKey", "orange-" + count); - // for next round - count++; - FruitConfigurator fruitConfigurator = new FruitConfigurator(this); - fruitConfigurator.setContext(context); - try { - fruitConfigurator.doConfigure(eventList); - } catch (JoranException je) { - je.printStackTrace(); - } - return fruit; - } - - public String toString() { - final String TAB = " "; - - StringBuilder retValue = new StringBuilder(); - - retValue.append("FruitFactory ( "); - if (eventList != null && eventList.size() > 0) { - retValue.append("event1 = ").append(eventList.get(0)).append(TAB); - } - retValue.append(" )"); - - return retValue.toString(); - } - - public void setEventList(List eventList) { - this.eventList = eventList; - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactoryAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactoryAction.java deleted file mode 100644 index 1680aa0f93..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitFactoryAction.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import java.util.ArrayList; -import java.util.List; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.event.InPlayListener; -import ch.qos.logback.core.joran.event.SaxEvent; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -public class FruitFactoryAction extends Action implements InPlayListener { - - List seList = new ArrayList(); - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - ec.addInPlayListener(this); - } - - @Override - public void end(InterpretationContext ec, String name) throws ActionException { - ec.removeInPlayListener(this); - - Object o = ec.peekObject(); - if (o instanceof FruitShell) { - FruitShell fs = (FruitShell) o; - FruitFactory fruitFactory = new FruitFactory(); - fruitFactory.setEventList(new ArrayList(seList)); - fs.setFruitFactory(fruitFactory); - } - } - - public void inPlay(SaxEvent event) { - seList.add(event); - } - - public List getSeList() { - return seList; - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShell.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShell.java deleted file mode 100644 index ec2a2dcbc3..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShell.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import ch.qos.logback.core.spi.ContextAwareBase; - -public class FruitShell extends ContextAwareBase { - - FruitFactory fruitFactory; - String name; - - public void setFruitFactory(FruitFactory fruitFactory) { - this.fruitFactory = fruitFactory; - } - - void testFruit() { - - Fruit fruit = fruitFactory.buildFruit(); - System.out.println(fruit); - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - /** - * Constructs a String with all attributes - * in name = value format. - * - * @return a String representation - * of this object. - */ - public String toString() { - final String TAB = " "; - - String retValue = ""; - - retValue = "FruitShell ( " + "fruitFactory = " + this.fruitFactory + TAB + "name = " + this.name + TAB + " )"; - - return retValue; - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShellAction.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShellAction.java deleted file mode 100644 index 33d2eed9ec..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/FruitShellAction.java +++ /dev/null @@ -1,77 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ActionException; -import ch.qos.logback.core.joran.spi.InterpretationContext; -import ch.qos.logback.core.util.OptionHelper; - -/** - * The Fruit* code is intended to test Joran's replay capability - * */ -public class FruitShellAction extends Action { - - FruitShell fruitShell; - private boolean inError = false; - - @Override - public void begin(InterpretationContext ec, String name, Attributes attributes) throws ActionException { - - // We are just beginning, reset variables - fruitShell = new FruitShell(); - inError = false; - - try { - - fruitShell.setContext(context); - - String shellName = attributes.getValue(NAME_ATTRIBUTE); - - if (OptionHelper.isEmpty(shellName)) { - addWarn("No appender name given for fruitShell]."); - } else { - fruitShell.setName(shellName); - addInfo("FruitShell named as [" + shellName + "]"); - } - - ec.pushObject(fruitShell); - } catch (Exception oops) { - inError = true; - addError("Could not create an FruitShell", oops); - throw new ActionException(oops); - } - } - - @Override - public void end(InterpretationContext ec, String name) throws ActionException { - if (inError) { - return; - } - - Object o = ec.peekObject(); - - if (o != fruitShell) { - addWarn("The object at the of the stack is not the fruitShell named [" + fruitShell.getName() + "] pushed earlier."); - } else { - addInfo("Popping fruitSHell named [" + fruitShell.getName() + "] from the object stack"); - ec.popObject(); - FruitContext fruitContext = (FruitContext) ec.getContext(); - fruitContext.addFruitShell(fruitShell); - } - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/PackageTest.java deleted file mode 100644 index a9def49e09..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ FruitConfigurationTest.class }) -public class PackageTest { - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/WeightytFruit.java b/logback-core/src/test/java/ch/qos/logback/core/joran/replay/WeightytFruit.java deleted file mode 100644 index da5a967773..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/replay/WeightytFruit.java +++ /dev/null @@ -1,28 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.replay; - -public class WeightytFruit extends Fruit { - - double weight; - - public double getWeight() { - return weight; - } - - public void setWeight(double weight) { - this.weight = weight; - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityCheckerTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityCheckerTest.java new file mode 100644 index 0000000000..aad6c3683c --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/sanity/AppenderWithinAppenderSanityCheckerTest.java @@ -0,0 +1,76 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.sanity; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.model.AppenderModel; +import ch.qos.logback.core.model.TopModel; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static ch.qos.logback.core.joran.sanity.AppenderWithinAppenderSanityChecker.NESTED_APPENDERS_WARNING; + +public class AppenderWithinAppenderSanityCheckerTest { + + + Context context = new ContextBase(); + AppenderWithinAppenderSanityChecker awasc = new AppenderWithinAppenderSanityChecker(); + StatusChecker statusChecker = new StatusChecker(context); + + @BeforeEach + public void setUp() throws Exception { + awasc.setContext(context); + } + + @Test + public void smoke() { + + TopModel topModel = new TopModel(); + awasc.check(topModel); + statusChecker.assertIsWarningOrErrorFree(); + } + + + @Test + public void singleAppender() { + TopModel topModel = new TopModel(); + AppenderModel appenderModel0 = new AppenderModel(); + appenderModel0.setLineNumber(1); + topModel.addSubModel(appenderModel0); + awasc.check(topModel); + statusChecker.assertIsWarningOrErrorFree(); + } + + @Test + public void nestedAppender() { + TopModel topModel = new TopModel(); + AppenderModel appenderModel0 = new AppenderModel(); + appenderModel0.setLineNumber(1); + topModel.addSubModel(appenderModel0); + + AppenderModel appenderModel1 = new AppenderModel(); + appenderModel1.setLineNumber(2); + appenderModel0.addSubModel(appenderModel1); + + awasc.check(topModel); + + statusChecker.assertContainsMatch(Status.WARN, NESTED_APPENDERS_WARNING); + statusChecker.assertContainsMatch(Status.WARN,"Appender at line 1"); + } + +} \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinator.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinator.java index f3c1687ae6..7bc62d49a4 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinator.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinator.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinatorTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinatorTest.java index b548d902ea..e51ea70a66 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinatorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/CaseCombinatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,13 @@ */ package ch.qos.logback.core.joran.spi; -import static org.junit.Assert.assertEquals; - import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; public class CaseCombinatorTest { @@ -37,15 +36,15 @@ public void smoke() { witness.add("A-b="); witness.add("a-B="); witness.add("A-B="); - assertEquals(witness, result); + Assertions.assertEquals(witness, result); } @Test public void other() { List result = p.combinations("aBCd"); - assertEquals(16, result.size()); + Assertions.assertEquals(16, result.size()); Set witness = new HashSet(result); // check that there are no duplicates - assertEquals(16, witness.size()); + Assertions.assertEquals(16, witness.size()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ConfigurationWatchListTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ConfigurationWatchListTest.java index be509ef9d2..7ee23aa117 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ConfigurationWatchListTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ConfigurationWatchListTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,13 @@ */ package ch.qos.logback.core.joran.spi; -import org.junit.Test; +import org.junit.jupiter.api.Test; import java.io.File; import java.net.MalformedURLException; import java.net.URL; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; /** * @author Ceki Gülcü diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java index a3cf6ce9be..a9fe1cd5c9 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DefaultNestedComponentRegistryTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,27 +13,26 @@ */ package ch.qos.logback.core.joran.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.joran.util.House; import ch.qos.logback.core.joran.util.Window; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + public class DefaultNestedComponentRegistryTest { DefaultNestedComponentRegistry registry = new DefaultNestedComponentRegistry(); - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DoNotAutoStart.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DoNotAutoStart.java index b0bf681267..ced98acd35 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DoNotAutoStart.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/DoNotAutoStart.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ElementSelectorTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ElementSelectorTest.java index 1004dff924..329ab39685 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ElementSelectorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/ElementSelectorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,10 @@ */ package ch.qos.logback.core.joran.spi; -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; -import org.junit.Test; /** * Test pattern manipulation code. diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/NoAutoStartUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/NoAutoStartUtilTest.java index 72dbeb362a..408fe9c513 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/NoAutoStartUtilTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/NoAutoStartUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,15 @@ */ package ch.qos.logback.core.joran.spi; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class NoAutoStartUtilTest { @@ -31,4 +36,51 @@ public void markedWithNoAutoStart() { DoNotAutoStart o = new DoNotAutoStart(); assertFalse(NoAutoStartUtil.notMarkedWithNoAutoStart(o)); } + + + + /* + * Annotation declared on implemented interface + */ + @Test + public void noAutoStartOnInterface() { + ComponentWithNoAutoStartOnInterface o = new ComponentWithNoAutoStartOnInterface(); + assertFalse(NoAutoStartUtil.notMarkedWithNoAutoStart(o)); + } + + @NoAutoStart + public interface NoAutoStartInterface { + } + + private static class ComponentWithNoAutoStartOnInterface implements NoAutoStartInterface { + } + + + + /* + * Annotation declared on ancestor + */ + @Test + public void noAutoStartOnAncestor() { + ComponentWithNoAutoStartOnAncestor o = new ComponentWithNoAutoStartOnAncestor(); + assertFalse(NoAutoStartUtil.notMarkedWithNoAutoStart(o)); + } + + private static class ComponentWithNoAutoStartOnAncestor extends DoNotAutoStart { + } + + + + /* + * Annotation declared on interface implemented by an ancestor + */ + @Test + public void noAutoStartOnInterfaceImplementedByAncestor() { + ComponentWithAncestorImplementingInterfaceWithNoAutoStart o = new ComponentWithAncestorImplementingInterfaceWithNoAutoStart(); + assertFalse(NoAutoStartUtil.notMarkedWithNoAutoStart(o)); + } + + private static class ComponentWithAncestorImplementingInterfaceWithNoAutoStart extends ComponentWithNoAutoStartOnInterface { + } + } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java deleted file mode 100644 index 6815e6d288..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.spi; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ ElementSelectorTest.class, SimpleRuleStoreTest.class, NoAutoStartUtilTest.class, ConfigurationWatchListTest.class, - DefaultNestedComponentRegistryTest.class, CaseCombinatorTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/SimpleRuleStoreTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/SimpleRuleStoreTest.java index ab834d091a..3afc726e41 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/spi/SimpleRuleStoreTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/spi/SimpleRuleStoreTest.java @@ -1,37 +1,36 @@ /** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Logback: the reliable, generic, fast and flexible logging framework. Copyright (C) 1999-2015, QOS.ch. All rights + * reserved. * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation + * This program and the accompanying materials are dual-licensed under either the terms of the Eclipse Public License + * v1.0 as published by the Eclipse Foundation * - * or (per the licensee's choosing) + * or (per the licensee's choosing) * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. + * under the terms of the GNU Lesser General Public License version 2.1 as published by the Free Software Foundation. */ package ch.qos.logback.core.joran.spi; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; +import java.util.function.Supplier; -import java.util.List; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import org.xml.sax.Attributes; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.joran.action.Action; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + /** * Test SimpleRuleStore for various explicit rule combinations. - * - * We also test that explicit patterns are case sensitive. - * + * + * We also test that explicit patterns are case-sensitive. + * * @author Ceki Gülcü */ public class SimpleRuleStoreTest { @@ -41,16 +40,15 @@ public class SimpleRuleStoreTest { @Test public void smoke() throws Exception { - srs.addRule(new ElementSelector("a/b"), new XAction()); + srs.addRule(new ElementSelector("a/b"), () -> new XAction()); // test for all possible case combinations of "a/b" for (String s : cc.combinations("a/b")) { System.out.println("s=" + s); - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - if (!(r.get(0) instanceof XAction)) { + if (!(r.get() instanceof XAction)) { fail("Wrong type"); } } @@ -58,35 +56,24 @@ public void smoke() throws Exception { @Test public void smokeII() throws Exception { - srs.addRule(new ElementSelector("a/b"), new XAction()); - srs.addRule(new ElementSelector("a/b"), new YAction()); - - for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); - assertNotNull(r); - assertEquals(2, r.size()); - - if (!(r.get(0) instanceof XAction)) { - fail("Wrong type"); - } + srs.addRule(new ElementSelector("a/b"), () -> new XAction()); - if (!(r.get(1) instanceof YAction)) { - fail("Wrong type"); - } - } + Exception e = assertThrows(IllegalStateException.class, () -> { + srs.addRule(new ElementSelector("a/b"), () -> new YAction()); + }); + assertEquals("[a][b] already has an associated action supplier", e.getMessage()); } @Test public void testSlashSuffix() throws Exception { ElementSelector pa = new ElementSelector("a/"); - srs.addRule(pa, new XAction()); + srs.addRule(pa, () -> new XAction()); for (String s : cc.combinations("a")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - if (!(r.get(0) instanceof XAction)) { + if (!(r.get() instanceof XAction)) { fail("Wrong type"); } } @@ -95,15 +82,13 @@ public void testSlashSuffix() throws Exception { @Test public void testTail1() throws Exception { - srs.addRule(new ElementSelector("*/b"), new XAction()); + srs.addRule(new ElementSelector("*/b"), () -> new XAction()); for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - - if (!(r.get(0) instanceof XAction)) { + if (!(r.get() instanceof XAction)) { fail("Wrong type"); } } @@ -112,15 +97,12 @@ public void testTail1() throws Exception { @Test public void testTail2() throws Exception { SimpleRuleStore srs = new SimpleRuleStore(new ContextBase()); - srs.addRule(new ElementSelector("*/c"), new XAction()); + srs.addRule(new ElementSelector("*/c"), () -> new XAction()); for (String s : cc.combinations("a/b/c")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - - assertEquals(1, r.size()); - - if (!(r.get(0) instanceof XAction)) { + if (!(r.get() instanceof XAction)) { fail("Wrong type"); } } @@ -128,15 +110,14 @@ public void testTail2() throws Exception { @Test public void testTail3() throws Exception { - srs.addRule(new ElementSelector("*/b"), new XAction()); - srs.addRule(new ElementSelector("*/a/b"), new YAction()); + srs.addRule(new ElementSelector("*/b"), () -> new XAction()); + srs.addRule(new ElementSelector("*/a/b"), () -> new YAction()); for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - - if (!(r.get(0) instanceof YAction)) { + Action ya = r.get(); + if (!(ya instanceof YAction)) { fail("Wrong type"); } } @@ -144,16 +125,15 @@ public void testTail3() throws Exception { @Test public void testTail4() throws Exception { - srs.addRule(new ElementSelector("*/b"), new XAction()); - srs.addRule(new ElementSelector("*/a/b"), new YAction()); - srs.addRule(new ElementSelector("a/b"), new ZAction()); + srs.addRule(new ElementSelector("*/b"), () -> new XAction()); + srs.addRule(new ElementSelector("*/a/b"), () -> new YAction()); + srs.addRule(new ElementSelector("a/b"), () -> new ZAction()); for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - if (!(r.get(0) instanceof ZAction)) { + if (!(r.get() instanceof ZAction)) { fail("Wrong type"); } } @@ -161,59 +141,103 @@ public void testTail4() throws Exception { @Test public void testSuffix() throws Exception { - srs.addRule(new ElementSelector("a"), new XAction()); - srs.addRule(new ElementSelector("a/*"), new YAction()); + srs.addRule(new ElementSelector("a"), () -> new XAction()); + srs.addRule(new ElementSelector("a/*"), () -> new YAction()); for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); - assertTrue(r.get(0) instanceof YAction); + assertTrue(r.get() instanceof YAction); } } @Test public void testDeepSuffix() throws Exception { - srs.addRule(new ElementSelector("a"), new XAction(1)); - srs.addRule(new ElementSelector("a/b/*"), new XAction(2)); + srs.addRule(new ElementSelector("a"), () -> new XAction(1)); + srs.addRule(new ElementSelector("a/b/*"), () -> new XAction(2)); for (String s : cc.combinations("a/other")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNull(r); } } @Test public void testPrefixSuffixInteraction1() throws Exception { - srs.addRule(new ElementSelector("a"), new ZAction()); - srs.addRule(new ElementSelector("a/*"), new YAction()); - srs.addRule(new ElementSelector("*/a/b"), new XAction(3)); + srs.addRule(new ElementSelector("a"), () -> new ZAction()); + srs.addRule(new ElementSelector("a/*"), () -> new YAction()); + srs.addRule(new ElementSelector("*/a/b"), () -> new XAction(3)); for (String s : cc.combinations("a/b")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNotNull(r); - assertEquals(1, r.size()); + Action ra = r.get(); - assertTrue(r.get(0) instanceof XAction); - XAction xaction = (XAction) r.get(0); + assertTrue(ra instanceof XAction); + XAction xaction = (XAction) ra; assertEquals(3, xaction.id); } } @Test public void testPrefixSuffixInteraction2() throws Exception { - srs.addRule(new ElementSelector("tG"), new XAction()); - srs.addRule(new ElementSelector("tG/tS"), new YAction()); - srs.addRule(new ElementSelector("tG/tS/test"), new ZAction()); - srs.addRule(new ElementSelector("tG/tS/test/*"), new XAction(9)); + srs.addRule(new ElementSelector("tG"), () -> new XAction()); + srs.addRule(new ElementSelector("tG/tS"), () -> new YAction()); + srs.addRule(new ElementSelector("tG/tS/test"), () -> new ZAction()); + srs.addRule(new ElementSelector("tG/tS/test/*"), () -> new XAction(9)); for (String s : cc.combinations("tG/tS/toto")) { - List r = srs.matchActions(new ElementPath(s)); + Supplier r = srs.matchActions(new ElementPath(s)); assertNull(r); } } + @Test + public void withTransparentParts() throws Exception { + + srs.addTransparentPathPart("if"); + srs.addTransparentPathPart("then"); + srs.addTransparentPathPart("else"); + + { + ElementPath ep = new ElementPath("x/if/then/if"); + ElementPath witness = new ElementPath("x/"); + + ElementPath cleanedEP = srs.removeTransparentPathParts(ep); + assertEquals(witness, cleanedEP); + } + + { + ElementPath ep = new ElementPath("x/if/then/stack"); + ElementPath witness = new ElementPath("x/stack"); + + ElementPath cleanedEP = srs.removeTransparentPathParts(ep); + assertEquals(witness, cleanedEP); + } + + { + ElementPath ep = new ElementPath("x/if/then/if/else/stack"); + ElementPath witness = new ElementPath("x/stack"); + + ElementPath cleanedEP = srs.removeTransparentPathParts(ep); + assertEquals(witness, cleanedEP); + } + } + + @Test + public void withRenamedParts() throws Exception { + srs.addPathPathMapping("included", "configure"); + + { + ElementPath ep = new ElementPath("included/a/b"); + ElementPath witness = new ElementPath("configure/a/b"); + + ElementPath renamedEP = srs.renamePathParts(ep); + assertEquals(witness, renamedEP); + } + } + class XAction extends Action { int id = 0; @@ -224,13 +248,13 @@ class XAction extends Action { this.id = id; } - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { } - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } - public void finish(InterpretationContext ec) { + public void finish(SaxEventInterpretationContext ec) { } public String toString() { @@ -239,24 +263,24 @@ public String toString() { } class YAction extends Action { - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { } - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } - public void finish(InterpretationContext ec) { + public void finish(SaxEventInterpretationContext ec) { } } class ZAction extends Action { - public void begin(InterpretationContext ec, String name, Attributes attributes) { + public void begin(SaxEventInterpretationContext ec, String name, Attributes attributes) { } - public void end(InterpretationContext ec, String name) { + public void end(SaxEventInterpretationContext ec, String name) { } - public void finish(InterpretationContext ec) { + public void finish(SaxEventInterpretationContext ec) { } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/AggregationAssessorTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/AggregationAssessorTest.java new file mode 100644 index 0000000000..e9566bb292 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/AggregationAssessorTest.java @@ -0,0 +1,73 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.joran.util; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; +import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; +import ch.qos.logback.core.util.AggregationType; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +public class AggregationAssessorTest { + Context context = new ContextBase(); + AggregationAssessor aggregationAssessor = new AggregationAssessor(new BeanDescriptionCache(context), House.class); + DefaultNestedComponentRegistry defaultComponentRegistry = new DefaultNestedComponentRegistry(); + + @Test + public void testgetClassNameViaImplicitRules() { + Class compClass = aggregationAssessor.getClassNameViaImplicitRules("door", AggregationType.AS_COMPLEX_PROPERTY, + defaultComponentRegistry); + assertEquals(Door.class, compClass); + } + + @Test + public void testgetComplexPropertyColleClassNameViaImplicitRules() { + Class compClass = aggregationAssessor.getClassNameViaImplicitRules("window", + AggregationType.AS_COMPLEX_PROPERTY_COLLECTION, defaultComponentRegistry); + assertEquals(Window.class, compClass); + } + + @Test + public void testDefaultClassAnnotationForLists() { + Method relevantMethod = aggregationAssessor.getRelevantMethod("LargeSwimmingPool", + AggregationType.AS_COMPLEX_PROPERTY_COLLECTION); + assertNotNull(relevantMethod); + Class spClass = aggregationAssessor.getDefaultClassNameByAnnonation("LargeSwimmingPool", relevantMethod); + assertEquals(LargeSwimmingPoolImpl.class, spClass); + + Class classViaImplicitRules = aggregationAssessor.getClassNameViaImplicitRules("LargeSwimmingPool", + AggregationType.AS_COMPLEX_PROPERTY_COLLECTION, defaultComponentRegistry); + assertEquals(LargeSwimmingPoolImpl.class, classViaImplicitRules); + } + + @Test + public void testDefaultClassAnnonation() { + Method relevantMethod = aggregationAssessor.getRelevantMethod("SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY); + assertNotNull(relevantMethod); + Class spClass = aggregationAssessor.getDefaultClassNameByAnnonation("SwimmingPool", relevantMethod); + assertEquals(SwimmingPoolImpl.class, spClass); + + Class classViaImplicitRules = aggregationAssessor.getClassNameViaImplicitRules("SwimmingPool", + AggregationType.AS_COMPLEX_PROPERTY, defaultComponentRegistry); + assertEquals(SwimmingPoolImpl.class, classViaImplicitRules); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Citrus.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Citrus.java index 57e997051e..9e403d1a14 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Citrus.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Citrus.java @@ -1,25 +1,37 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util; public abstract class Citrus { public static final String PRECARP_PROPERTY_NAME = "pericarp"; public static final String PREFIX_PROPERTY_NAME = "prefix"; - + @SuppressWarnings("unused") private T pericarp; - + String prefix; - + public void setPericarp(T pericarp) { this.pericarp = pericarp; } - + public void setPrefix(String prefix) { this.prefix = prefix; } - + public abstract void foo(); - - - + } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/House.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/House.java index df86f51282..5628b7bef7 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/House.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/House.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -40,6 +40,7 @@ public class House { List adjectiveList = new ArrayList(); List windowList = new ArrayList(); List largePoolList = new ArrayList(); + List fileSizes = new ArrayList(); Orange orange; @@ -109,6 +110,10 @@ public void addWindow(Window w) { windowList.add(w); } + public void addFileSize(FileSize fs) { + fileSizes.add(fs); + } + public void addAdjective(String s) { adjectiveList.add(s); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Orange.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Orange.java index 29017aa0d6..527db13086 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Orange.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Orange.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util; public class Orange extends Citrus { diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/PackageTest.java deleted file mode 100644 index 9fc78577e3..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.joran.util; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ PropertySetterTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/PropertySetterTest.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/PropertySetterTest.java index 42cf1b638b..c8410e030a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/PropertySetterTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/PropertySetterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,44 +13,43 @@ */ package ch.qos.logback.core.joran.util; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.charset.UnsupportedCharsetException; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.util.FileSize; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry; import ch.qos.logback.core.joran.util.beans.BeanDescriptionCache; import ch.qos.logback.core.spi.FilterReply; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.AggregationType; import ch.qos.logback.core.util.StatusPrinter; -public class PropertySetterTest { +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; - DefaultNestedComponentRegistry defaultComponentRegistry = new DefaultNestedComponentRegistry(); +public class PropertySetterTest { Context context = new ContextBase(); StatusChecker checker = new StatusChecker(context); House house = new House(); - + PropertySetter setter = new PropertySetter(new BeanDescriptionCache(context), house); - @Before + @BeforeEach public void setUp() { setter.setContext(context); } - @After + @AfterEach public void tearDown() { } @@ -123,17 +122,7 @@ public void testSetComplexProperty() { assertEquals(door, house.getDoor()); } - @Test - public void testgetClassNameViaImplicitRules() { - Class compClass = setter.getClassNameViaImplicitRules("door", AggregationType.AS_COMPLEX_PROPERTY, defaultComponentRegistry); - assertEquals(Door.class, compClass); - } - @Test - public void testgetComplexPropertyColleClassNameViaImplicitRules() { - Class compClass = setter.getClassNameViaImplicitRules("window", AggregationType.AS_COMPLEX_PROPERTY_COLLECTION, defaultComponentRegistry); - assertEquals(Window.class, compClass); - } @Test public void testPropertyCollection() { @@ -146,6 +135,16 @@ public void testPropertyCollection() { } @Test + public void addValueOfTest() { + setter.addBasicProperty("fileSize", "1GB"); + setter.addBasicProperty("fileSize", "10KB"); + + assertEquals(2, house.fileSizes.size()); + assertEquals(FileSize.valueOf("1GB"), house.fileSizes.get(0)); + assertEquals(FileSize.valueOf("10KB"), house.fileSizes.get(1)); + } + + @Test public void testComplexCollection() { Window w1 = new Window(); w1.handle = 10; @@ -191,28 +190,7 @@ public void testEnum() { assertEquals(HouseColor.BLUE, house.getHouseColor()); } - @Test - public void testDefaultClassAnnonation() { - Method relevantMethod = setter.getRelevantMethod("SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY); - assertNotNull(relevantMethod); - Class spClass = setter.getDefaultClassNameByAnnonation("SwimmingPool", relevantMethod); - assertEquals(SwimmingPoolImpl.class, spClass); - Class classViaImplicitRules = setter.getClassNameViaImplicitRules("SwimmingPool", AggregationType.AS_COMPLEX_PROPERTY, defaultComponentRegistry); - assertEquals(SwimmingPoolImpl.class, classViaImplicitRules); - } - - @Test - public void testDefaultClassAnnotationForLists() { - Method relevantMethod = setter.getRelevantMethod("LargeSwimmingPool", AggregationType.AS_COMPLEX_PROPERTY_COLLECTION); - assertNotNull(relevantMethod); - Class spClass = setter.getDefaultClassNameByAnnonation("LargeSwimmingPool", relevantMethod); - assertEquals(LargeSwimmingPoolImpl.class, spClass); - - Class classViaImplicitRules = setter.getClassNameViaImplicitRules("LargeSwimmingPool", AggregationType.AS_COMPLEX_PROPERTY_COLLECTION, - defaultComponentRegistry); - assertEquals(LargeSwimmingPoolImpl.class, classViaImplicitRules); - } @Test public void charset() { @@ -231,11 +209,13 @@ public void charset() { @Test public void bridgeMethodsShouldBeIgnored() { Orange orange = new Orange(); - + PropertySetter orangeSetter = new PropertySetter(new BeanDescriptionCache(context), orange); - assertEquals(AggregationType.AS_BASIC_PROPERTY, orangeSetter.computeAggregationType(Citrus.PRECARP_PROPERTY_NAME)); - assertEquals(AggregationType.AS_BASIC_PROPERTY, orangeSetter.computeAggregationType(Citrus.PREFIX_PROPERTY_NAME)); - + assertEquals(AggregationType.AS_BASIC_PROPERTY, + orangeSetter.computeAggregationType(Citrus.PRECARP_PROPERTY_NAME)); + assertEquals(AggregationType.AS_BASIC_PROPERTY, + orangeSetter.computeAggregationType(Citrus.PREFIX_PROPERTY_NAME)); + StatusPrinter.print(context); checker.assertIsWarningOrErrorFree(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Window.java b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Window.java index eb078f172e..a0195679f7 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/joran/util/Window.java +++ b/logback-core/src/test/java/ch/qos/logback/core/joran/util/Window.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.joran.util; public class Window { diff --git a/logback-core/src/test/java/ch/qos/logback/core/layout/DummyLayout.java b/logback-core/src/test/java/ch/qos/logback/core/layout/DummyLayout.java index 08040830e9..6cbb0b6138 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/layout/DummyLayout.java +++ b/logback-core/src/test/java/ch/qos/logback/core/layout/DummyLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/layout/NopLayout.java b/logback-core/src/test/java/ch/qos/logback/core/layout/NopLayout.java index 261a75be6d..731b07cdbf 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/layout/NopLayout.java +++ b/logback-core/src/test/java/ch/qos/logback/core/layout/NopLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/FruitShellModel.java b/logback-core/src/test/java/ch/qos/logback/core/model/FruitShellModel.java new file mode 100755 index 0000000000..2fc0fb624b --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/FruitShellModel.java @@ -0,0 +1,64 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +import java.util.Objects; + +public class FruitShellModel extends Model { + + private static final long serialVersionUID = 1644187848970171077L; + String name; + + @Override + protected FruitShellModel makeNewInstance() { + return new FruitShellModel(); + } + + @Override + protected void mirror(Model that) { + FruitShellModel actual = (FruitShellModel) that; + super.mirror(actual); + this.name = actual.name; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + Objects.hash(name); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + FruitShellModel other = (FruitShellModel) obj; + return Objects.equals(name, other.name); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/StackModel.java b/logback-core/src/test/java/ch/qos/logback/core/model/StackModel.java new file mode 100644 index 0000000000..018ed46110 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/StackModel.java @@ -0,0 +1,24 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model; + +public class StackModel extends NamedModel { + + private static final long serialVersionUID = -2623437394373933695L; + + @Override + protected StackModel makeNewInstance() { + return new StackModel(); + } +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/TopModel.java b/logback-core/src/test/java/ch/qos/logback/core/model/TopModel.java new file mode 100755 index 0000000000..7a106e58f7 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/TopModel.java @@ -0,0 +1,26 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model; + +public class TopModel extends Model { + + private static final long serialVersionUID = 6378962040610737208L; + + @Override + protected TopModel makeNewInstance() { + return new TopModel(); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/processor/DefinePropertyActionTest.java b/logback-core/src/test/java/ch/qos/logback/core/model/processor/DefinePropertyActionTest.java new file mode 100644 index 0000000000..1db223f9e9 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/processor/DefinePropertyActionTest.java @@ -0,0 +1,135 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.model.processor; + +import java.util.HashMap; +import java.util.function.Supplier; + +import ch.qos.logback.core.joran.action.Action; +import ch.qos.logback.core.joran.action.DefinePropertyAction; +import ch.qos.logback.core.joran.action.TopElementAction; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.joran.SimpleConfigurator; +import ch.qos.logback.core.joran.spi.ElementSelector; +import ch.qos.logback.core.joran.spi.JoranException; +import ch.qos.logback.core.model.DefineModel; +import ch.qos.logback.core.model.ImplicitModel; +import ch.qos.logback.core.model.TopModel; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.testUtil.CoreTestConstants; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.StatusPrinter; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +/** + * Test {@link DefinePropertyAction}. + * + * @author Aleksey Didik + */ +public class DefinePropertyActionTest { + + private static final String DEFINE_INPUT_DIR = CoreTestConstants.JORAN_INPUT_PREFIX + "define/"; + private static final String GOOD_XML = "good.xml"; + private static final String NONAME_XML = "noname.xml"; + private static final String NOCLASS_XML = "noclass.xml"; + private static final String BADCLASS_XML = "badclass.xml"; + + SimpleConfigurator simpleConfigurator; + Context context = new ContextBase(); + StatusChecker checker = new StatusChecker(context); + + @BeforeEach + public void setUp() throws Exception { + + HashMap> rulesMap = new HashMap<>(); + rulesMap.put(new ElementSelector("top"), TopElementAction::new); + rulesMap.put(new ElementSelector("top/define"), DefinePropertyAction::new); + + simpleConfigurator = new SimpleConfigurator(rulesMap) { + + @Override + protected void addModelHandlerAssociations(DefaultProcessor defaultProcessor) { + defaultProcessor.addHandler(TopModel.class, NOPModelHandler::makeInstance); + defaultProcessor.addHandler(DefineModel.class, DefineModelHandler::makeInstance); + defaultProcessor.addHandler(ImplicitModel.class, ImplicitModelHandler::makeInstance); + } + }; + simpleConfigurator.setContext(context); + } + + @AfterEach + public void tearDown() throws Exception { + } + + @Test + public void good() throws JoranException { + simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + GOOD_XML); + ModelInterpretationContext mic = simpleConfigurator.getModelInterpretationContext(); + String inContextFoo = mic.getProperty("foo"); + assertEquals("monster", inContextFoo); + } + + @Test + public void noName() throws JoranException { + try { + simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + NONAME_XML); + } finally { + StatusPrinter.print(context); + } + // get from context + String inContextFoo = context.getProperty("foo"); + assertNull(inContextFoo); + // check context errors + + checker.assertContainsMatch(Status.ERROR, "Missing attribute \\[name\\]. See element \\[define\\]"); + } + + @Test + public void noClass() throws JoranException { + simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + NOCLASS_XML); + String inContextFoo = context.getProperty("foo"); + + StatusPrinter.print(context); + assertNull(inContextFoo); + checker.assertContainsMatch(Status.ERROR, "Missing attribute \\[class\\]. See element \\[define\\]"); + } + + @Test + public void testBadClass() throws JoranException { + simpleConfigurator.doConfigure(DEFINE_INPUT_DIR + BADCLASS_XML); + // get from context + String inContextFoo = context.getProperty("foo"); + assertNull(inContextFoo); + // check context errors + checker.assertContainsMatch(Status.ERROR, "Could not create an PropertyDefiner of type"); + } + + @Disabled // on certain hosts this test takes 5 seconds to complete + @Test + public void canonicalHostNameProperty() throws JoranException { + String configFileAsStr = DEFINE_INPUT_DIR + "canonicalHostname.xml"; + simpleConfigurator.doConfigure(configFileAsStr); + assertNotNull(context.getProperty("CANONICAL_HOST_NAME")); + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/processor/ImportModelHandlerTest.java b/logback-core/src/test/java/ch/qos/logback/core/model/processor/ImportModelHandlerTest.java new file mode 100644 index 0000000000..973f921d45 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/processor/ImportModelHandlerTest.java @@ -0,0 +1,40 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import org.junit.jupiter.api.Test; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class ImportModelHandlerTest { + + Context context = new ContextBase(); + ImportModelHandler imh = new ImportModelHandler(context); + + @Test + public void testStemExtraction() { + assertNull(imh.extractStem(null)); + assertNull(imh.extractStem("")); + assertNull(imh.extractStem("bla.")); + assertEquals("Foo", imh.extractStem("bla.Foo")); + assertEquals("Foo", imh.extractStem("com.titi.bla.Foo")); + + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/model/processor/StackModelHandler.java b/logback-core/src/test/java/ch/qos/logback/core/model/processor/StackModelHandler.java new file mode 100644 index 0000000000..f6a88f67ce --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/model/processor/StackModelHandler.java @@ -0,0 +1,63 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.model.processor; + +import java.util.Stack; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.model.Model; +import ch.qos.logback.core.model.StackModel; + +public class StackModelHandler extends ModelHandlerBase { + + static public final String STACK_TEST = "STACK_TEST"; + + public StackModelHandler(Context context) { + super(context); + } + + static public ModelHandlerBase makeInstance(Context context, ModelInterpretationContext ic) { + return new StackModelHandler(context); + } + + @Override + protected Class getSupportedModelClass() { + return StackModel.class; + } + + @Override + public void handle(ModelInterpretationContext mic, Model model) throws ModelHandlerException { + + StackModel stackModel = (StackModel) model; + + String name = stackModel.getName(); + + ContextBase contextBase = (ContextBase) context; + + @SuppressWarnings("unchecked") + Stack aStack = (Stack) context.getObject(STACK_TEST); + if(aStack == null) { + aStack = new Stack<>(); + contextBase.putObject(STACK_TEST, aStack); + } + aStack.push(name); + } + + @Override + public void postHandle(ModelInterpretationContext intercon, Model model) throws ModelHandlerException { + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSSLSocketAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSSLSocketAppenderTest.java index e82585607c..b449fc0d6a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSSLSocketAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSSLSocketAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,9 @@ */ package ch.qos.logback.core.net; -import static org.junit.Assert.assertNotNull; - -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.spi.PreSerializationTransformer; @@ -32,7 +31,7 @@ public class AbstractSSLSocketAppenderTest { private InstrumentedSSLSocketAppenderBase appender = new InstrumentedSSLSocketAppenderBase(); - @Before + @BeforeEach public void setUp() throws Exception { appender.setContext(context); } @@ -42,7 +41,7 @@ public void testUsingDefaultConfig() throws Exception { // should be able to start and stop successfully with no SSL // configuration at all appender.start(); - assertNotNull(appender.getSocketFactory()); + Assertions.assertNotNull(appender.getSocketFactory()); appender.stop(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSocketAppenderIntegrationTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSocketAppenderIntegrationTest.java index 91056a41cc..ef73b9a884 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSocketAppenderIntegrationTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/AbstractSocketAppenderIntegrationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,7 @@ */ package ch.qos.logback.core.net; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.Matchers.anyInt; +import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.timeout; @@ -31,11 +28,14 @@ import java.net.Socket; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.net.server.test.ServerSocketUtil; @@ -52,28 +52,30 @@ public class AbstractSocketAppenderIntegrationTest { private static final int TIMEOUT = 2000; - private ScheduledExecutorService executorService = ExecutorServiceUtil.newScheduledExecutorService(); - private MockContext mockContext = new MockContext(executorService); + private ThreadPoolExecutor threadPoolExecutor = ExecutorServiceUtil.newThreadPoolExecutor(); + private MockContext mockContext = new MockContext(threadPoolExecutor); private AutoFlushingObjectWriter objectWriter; private ObjectWriterFactory objectWriterFactory = new SpyProducingObjectWriterFactory(); private LinkedBlockingDeque deque = spy(new LinkedBlockingDeque(1)); private QueueFactory queueFactory = mock(QueueFactory.class); - private InstrumentedSocketAppender instrumentedAppender = new InstrumentedSocketAppender(queueFactory, objectWriterFactory); + private InstrumentedSocketAppender instrumentedAppender = new InstrumentedSocketAppender(queueFactory, + objectWriterFactory); - @Before + @BeforeEach public void setUp() throws Exception { - when(queueFactory. newLinkedBlockingDeque(anyInt())).thenReturn(deque); + when(queueFactory.newLinkedBlockingDeque(anyInt())).thenReturn(deque); instrumentedAppender.setContext(mockContext); } - @After + @AfterEach public void tearDown() throws Exception { instrumentedAppender.stop(); - assertFalse(instrumentedAppender.isStarted()); - executorService.shutdownNow(); - assertTrue(executorService.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)); + Assertions.assertFalse(instrumentedAppender.isStarted()); + threadPoolExecutor.shutdownNow(); + Assertions.assertTrue(threadPoolExecutor.awaitTermination(TIMEOUT, TimeUnit.MILLISECONDS)); } + @Disabled // JDK 16 @Test public void dispatchesEvents() throws Exception { @@ -95,7 +97,7 @@ public void dispatchesEvents() throws Exception { // then ObjectInputStream ois = new ObjectInputStream(appenderSocket.getInputStream()); - assertEquals("some event", ois.readObject()); + Assertions.assertEquals( ois.readObject(), "some event"); appenderSocket.close(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/AutoFlushingObjectWriterTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/AutoFlushingObjectWriterTest.java index 010ce42454..f07e8ebe2b 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/AutoFlushingObjectWriterTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/AutoFlushingObjectWriterTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,8 +16,9 @@ import java.io.IOException; import java.io.ObjectOutputStream; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import org.mockito.InOrder; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.spy; @@ -28,11 +29,12 @@ * * @author Sebastian Gröbler */ +@Disabled public class AutoFlushingObjectWriterTest { private InstrumentedObjectOutputStream objectOutputStream; - @Before + @BeforeEach public void beforeEachTest() throws IOException { objectOutputStream = spy(new InstrumentedObjectOutputStream()); } @@ -93,6 +95,7 @@ public void resetsObjectOutputStreamAccordingToGivenResetFrequency() throws IOEx private static class InstrumentedObjectOutputStream extends ObjectOutputStream { protected InstrumentedObjectOutputStream() throws IOException, SecurityException { + super(); } @Override diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/DefaultSocketConnectorTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/DefaultSocketConnectorTest.java index d7133a4ec5..a66edbee23 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/DefaultSocketConnectorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/DefaultSocketConnectorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,27 +17,24 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; - +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import ch.qos.logback.core.net.SocketConnector.ExceptionHandler; import ch.qos.logback.core.net.server.test.ServerSocketUtil; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import ch.qos.logback.core.testUtil.EnvUtilForTests; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.Timeout; /** * Unit tests for {@link DefaultSocketConnector}. @@ -46,7 +43,7 @@ */ public class DefaultSocketConnectorTest { - private static final int DELAY = 1000; + private static final int DELAY = 2000; private static final int SHORT_DELAY = 10; private static final int RETRY_DELAY = 10; @@ -57,15 +54,22 @@ public class DefaultSocketConnectorTest { ExecutorService executor = Executors.newSingleThreadExecutor(); - @Before + @BeforeEach public void setUp() throws Exception { + if(EnvUtilForTests.isGithubAction()) + return; + serverSocket = ServerSocketUtil.createServerSocket(); - connector = new DefaultSocketConnector(serverSocket.getInetAddress(), serverSocket.getLocalPort(), 0, RETRY_DELAY); + connector = new DefaultSocketConnector(serverSocket.getInetAddress(), serverSocket.getLocalPort(), 0, + RETRY_DELAY); connector.setExceptionHandler(exceptionHandler); } - @After + @AfterEach public void tearDown() throws Exception { + if(EnvUtilForTests.isGithubAction()) + return; + if (serverSocket != null) { serverSocket.close(); } @@ -73,52 +77,63 @@ public void tearDown() throws Exception { @Test public void testConnect() throws Exception { + if(EnvUtilForTests.isGithubAction()) + return; + Future connectorTask = executor.submit(connector); - Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS); - assertNotNull(socket); + Socket socket = connectorTask.get(DELAY, TimeUnit.MILLISECONDS); + Assertions.assertNotNull(socket); connectorTask.cancel(true); - assertTrue(connectorTask.isDone()); + Assertions.assertTrue(connectorTask.isDone()); socket.close(); } @Test public void testConnectionFails() throws Exception { + if(EnvUtilForTests.isGithubAction()) + return; + serverSocket.close(); Future connectorTask = executor.submit(connector); - // this connection attempt will always timeout + // this connection attempt will always time out try { connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS); - fail(); + Assertions.fail(); } catch (TimeoutException e) { } - Exception lastException = exceptionHandler.awaitConnectionFailed(DELAY); - assertTrue(lastException instanceof ConnectException); - assertFalse(connectorTask.isDone()); + Exception lastException = exceptionHandler.awaitConnectionFailed(); + Assertions.assertTrue(lastException instanceof ConnectException); + Assertions.assertFalse(connectorTask.isDone()); connectorTask.cancel(true); // thread.join(4 * DELAY); - assertTrue(connectorTask.isCancelled()); + Assertions.assertTrue(connectorTask.isCancelled()); } - @Test(timeout = 5000) + @Disabled + @Test + @Timeout(value=5, unit = TimeUnit.SECONDS) public void testConnectEventually() throws Exception { + if(EnvUtilForTests.isGithubAction()) + return; + serverSocket.close(); Future connectorTask = executor.submit(connector); - // this connection attempt will always timeout + // this connection attempt will always time out try { connectorTask.get(SHORT_DELAY, TimeUnit.MILLISECONDS); - fail(); + Assertions.fail(); } catch (TimeoutException e) { } - // on Ceki's machine (Windows 7) this always takes 1second regardless of the value of DELAY - Exception lastException = exceptionHandler.awaitConnectionFailed(DELAY); - assertNotNull(lastException); - assertTrue(lastException instanceof ConnectException); + // the following call requires over 1000 millis + Exception lastException = exceptionHandler.awaitConnectionFailed(); + Assertions.assertNotNull(lastException); + Assertions.assertTrue(lastException instanceof ConnectException); // now rebind to the same local address SocketAddress address = serverSocket.getLocalSocketAddress(); @@ -129,38 +144,38 @@ public void testConnectEventually() throws Exception { // now we should be able to connect Socket socket = connectorTask.get(2 * DELAY, TimeUnit.MILLISECONDS); - assertNotNull(socket); + Assertions.assertNotNull(socket); - assertFalse(connectorTask.isCancelled()); + Assertions.assertFalse(connectorTask.isCancelled()); socket.close(); } private static class MockExceptionHandler implements ExceptionHandler { - private final Lock lock = new ReentrantLock(); - private final Condition failedCondition = lock.newCondition(); + private final CyclicBarrier failureBarrier = new CyclicBarrier(2); private Exception lastException; public void connectionFailed(SocketConnector connector, Exception ex) { lastException = ex; + try { + failureBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + e.printStackTrace(); + } } - public Exception awaitConnectionFailed(long delay) throws InterruptedException { - lock.lock(); - try { - long increment = 10; - while (lastException == null && delay > 0) { - boolean success = failedCondition.await(increment, TimeUnit.MILLISECONDS); - delay -= increment; - if (success) - break; - - } + public Exception awaitConnectionFailed() throws InterruptedException { + if (lastException != null) { return lastException; - } finally { - lock.unlock(); } + + try { + failureBarrier.await(); + } catch (InterruptedException | BrokenBarrierException e) { + e.printStackTrace(); + } + return lastException; } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/HardenedObjectInputStreamTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/HardenedObjectInputStreamTest.java index 36a8ded0c9..0e780823ff 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/net/HardenedObjectInputStreamTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/HardenedObjectInputStreamTest.java @@ -1,15 +1,34 @@ -package ch.qos.logback.core.net; +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ -import static org.junit.Assert.assertEquals; +package ch.qos.logback.core.net; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InvalidClassException; import java.io.ObjectOutputStream; +import java.util.HashSet; +import java.util.Set; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertThrows; public class HardenedObjectInputStreamTest { @@ -18,13 +37,13 @@ public class HardenedObjectInputStreamTest { HardenedObjectInputStream inputStream; String[] whitelist = new String[] { Innocent.class.getName() }; - @Before + @BeforeEach public void setUp() throws Exception { bos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(bos); } - @After + @AfterEach public void tearDown() throws Exception { } @@ -53,39 +72,38 @@ private void writeObject(ObjectOutputStream oos, Object o) throws IOException { oos.close(); } -// @Ignore -// @Test -// public void denialOfService() throws ClassNotFoundException, IOException { -// ByteArrayInputStream bis = new ByteArrayInputStream(payload()); -// inputStream = new HardenedObjectInputStream(bis, whitelist); -// try { -// Set set = (Set) inputStream.readObject(); -// assertNotNull(set); -// } finally { -// inputStream.close(); -// } -// } -// -// private byte[] payload() throws IOException { -// Set root = buildEvilHashset(); -// return serialize(root); -// } -// -// private Set buildEvilHashset() { -// Set root = new HashSet(); -// Set s1 = root; -// Set s2 = new HashSet(); -// for (int i = 0; i < 100; i++) { -// Set t1 = new HashSet(); -// Set t2 = new HashSet(); -// t1.add("foo"); // make it not equal to t2 -// s1.add(t1); -// s1.add(t2); -// s2.add(t1); -// s2.add(t2); -// s1 = t1; -// s2 = t2; -// } -// return root; -// } + @Test + public void denialOfService() throws ClassNotFoundException, IOException { + ByteArrayInputStream bis = new ByteArrayInputStream(payload()); + inputStream = new HardenedObjectInputStream(bis, whitelist); + try { + assertThrows(InvalidClassException.class, () -> inputStream.readObject()); + } finally { + inputStream.close(); + } + } + + private byte[] payload() throws IOException { + Set root = buildEvilHashset(); + writeObject(oos, root); + return bos.toByteArray(); + } + + private Set buildEvilHashset() { + Set root = new HashSet(); + Set s1 = root; + Set s2 = new HashSet(); + for (int i = 0; i < 100; i++) { + Set t1 = new HashSet(); + Set t2 = new HashSet(); + t1.add("foo"); // make it not equal to t2 + s1.add(t1); + s1.add(t2); + s2.add(t1); + s2.add(t2); + s1 = t1; + s2 = t2; + } + return root; + } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/Innocent.java b/logback-core/src/test/java/ch/qos/logback/core/net/Innocent.java index 2cef5a08eb..2185dff424 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/net/Innocent.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/Innocent.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.net; public class Innocent implements java.io.Serializable { diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/PackageTest.java deleted file mode 100644 index 29f146af44..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/net/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.net; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ DefaultSocketConnectorTest.class, AbstractSSLSocketAppenderTest.class, - ch.qos.logback.core.net.server.PackageTest.class, ch.qos.logback.core.net.ssl.PackageTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/SyslogAppenderBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/SyslogAppenderBaseTest.java index 4aee9d8271..adcfbf21b0 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/SyslogAppenderBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/SyslogAppenderBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,9 @@ */ package ch.qos.logback.core.net; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SyslogAppenderBaseTest { @Test diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockContext.java b/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockContext.java index a0bb7f2cc3..6aa95a53a5 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockContext.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockContext.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -14,7 +14,9 @@ package ch.qos.logback.core.net.mock; import java.util.List; +import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ThreadPoolExecutor; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; @@ -29,7 +31,7 @@ */ public class MockContext extends ContextBase { - private final ScheduledExecutorService scheduledExecutorService; + private final ExecutorService executorService; private Status lastStatus; @@ -37,14 +39,14 @@ public MockContext() { this(new MockScheduledExecutorService()); } - public MockContext(ScheduledExecutorService executorService) { + public MockContext(ExecutorService executorService) { this.setStatusManager(new MockStatusManager()); - this.scheduledExecutorService = executorService; + this.executorService = executorService; } @Override - public ScheduledExecutorService getScheduledExecutorService() { - return scheduledExecutorService; + public ExecutorService getExecutorService() { + return executorService; } public Status getLastStatus() { diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockScheduledExecutorService.java b/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockScheduledExecutorService.java index dc2dc06511..04ddd0f983 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockScheduledExecutorService.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/mock/MockScheduledExecutorService.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,11 +15,7 @@ import java.util.Collections; import java.util.List; -import java.util.concurrent.AbstractExecutorService; -import java.util.concurrent.Callable; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * An {@link ScheduledExecutorService} with instrumentation for unit testing. @@ -28,7 +24,8 @@ * * @author Carl Harris */ -public class MockScheduledExecutorService extends AbstractExecutorService implements ScheduledExecutorService { +public class MockScheduledExecutorService extends AbstractExecutorService { + private Runnable lastCommand; @@ -60,24 +57,4 @@ public void execute(Runnable command) { lastCommand = command; } - @Override - public ScheduledFuture schedule(Runnable command, long delay, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public ScheduledFuture schedule(Callable callable, long delay, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public ScheduledFuture scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - - @Override - public ScheduledFuture scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit) { - throw new UnsupportedOperationException(); - } - } \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/AbstractServerSocketAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/AbstractServerSocketAppenderTest.java index a0b00c4e6c..bba7552408 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/AbstractServerSocketAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/AbstractServerSocketAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,14 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.ServerSocket; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.net.server.test.MockServerListener; @@ -49,14 +45,14 @@ public class AbstractServerSocketAppenderTest { private ServerSocket serverSocket; private InstrumentedServerSocketAppenderBase appender; - @Before + @BeforeEach public void setUp() throws Exception { serverSocket = ServerSocketUtil.createServerSocket(); appender = new InstrumentedServerSocketAppenderBase(serverSocket, listener, runner); appender.setContext(context); } - @After + @AfterEach public void tearDown() throws Exception { serverSocket.close(); } @@ -64,40 +60,40 @@ public void tearDown() throws Exception { @Test public void testStartStop() throws Exception { appender.start(); - assertTrue(runner.isContextInjected()); - assertTrue(runner.isRunning()); - assertSame(listener, appender.getLastListener()); + Assertions.assertTrue(runner.isContextInjected()); + Assertions.assertTrue(runner.isRunning()); + Assertions.assertSame(listener, appender.getLastListener()); appender.stop(); - assertFalse(runner.isRunning()); + Assertions.assertFalse(runner.isRunning()); } @Test public void testStartWhenAlreadyStarted() throws Exception { appender.start(); appender.start(); - assertEquals(1, runner.getStartCount()); + Assertions.assertEquals(1, runner.getStartCount()); } @Test public void testStopThrowsException() throws Exception { appender.start(); - assertTrue(appender.isStarted()); + Assertions.assertTrue(appender.isStarted()); IOException ex = new IOException("test exception"); runner.setStopException(ex); appender.stop(); Status status = context.getLastStatus(); - assertNotNull(status); - assertTrue(status instanceof ErrorStatus); - assertTrue(status.getMessage().contains(ex.getMessage())); - assertSame(ex, status.getThrowable()); + Assertions.assertNotNull(status); + Assertions.assertTrue(status instanceof ErrorStatus); + Assertions.assertTrue(status.getMessage().contains(ex.getMessage())); + Assertions.assertSame(ex, status.getThrowable()); } @Test public void testStopWhenNotStarted() throws Exception { appender.stop(); - assertEquals(0, runner.getStartCount()); + Assertions.assertEquals(0, runner.getStartCount()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/ConcurrentServerRunnerTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/ConcurrentServerRunnerTest.java index 2929788e0d..67639c50f5 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/ConcurrentServerRunnerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/ConcurrentServerRunnerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,6 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -26,9 +21,10 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.net.server.test.MockServerListener; @@ -44,32 +40,32 @@ public class ConcurrentServerRunnerTest { private ExecutorService executor = Executors.newCachedThreadPool(); private InstrumentedConcurrentServerRunner runner = new InstrumentedConcurrentServerRunner(listener, executor); - @Before + @BeforeEach public void setUp() throws Exception { runner.setContext(context); } - @After + @AfterEach public void tearDown() throws Exception { executor.shutdownNow(); - assertTrue(executor.awaitTermination(DELAY, TimeUnit.MILLISECONDS)); + Assertions.assertTrue(executor.awaitTermination(DELAY, TimeUnit.MILLISECONDS)); } @Test public void testStartStop() throws Exception { - assertFalse(runner.isRunning()); + Assertions.assertFalse(runner.isRunning()); executor.execute(runner); - assertTrue(runner.awaitRunState(true, DELAY)); + Assertions.assertTrue(runner.awaitRunState(true, DELAY)); int retries = DELAY / SHORT_DELAY; synchronized (listener) { while (retries-- > 0 && listener.getWaiter() == null) { listener.wait(SHORT_DELAY); } } - assertNotNull(listener.getWaiter()); + Assertions.assertNotNull(listener.getWaiter()); runner.stop(); - assertTrue(listener.isClosed()); - assertFalse(runner.awaitRunState(false, DELAY)); + Assertions.assertTrue(listener.isClosed()); + Assertions.assertFalse(runner.awaitRunState(false, DELAY)); } @Test @@ -83,7 +79,7 @@ public void testRunOneClient() throws Exception { client.wait(SHORT_DELAY); } } - assertTrue(runner.awaitRunState(true, DELAY)); + Assertions.assertTrue(runner.awaitRunState(true, DELAY)); client.close(); runner.stop(); } @@ -101,7 +97,7 @@ public void testRunManyClients() throws Exception { client.wait(SHORT_DELAY); } } - assertTrue(runner.awaitRunState(true, DELAY)); + Assertions.assertTrue(runner.awaitRunState(true, DELAY)); } runner.stop(); } @@ -117,10 +113,10 @@ public void testRunClientAndVisit() throws Exception { client.wait(SHORT_DELAY); } } - assertTrue(runner.awaitRunState(true, DELAY)); + Assertions.assertTrue(runner.awaitRunState(true, DELAY)); MockClientVisitor visitor = new MockClientVisitor(); runner.accept(visitor); - assertSame(client, visitor.getLastVisited()); + Assertions.assertSame(client, visitor.getLastVisited()); runner.stop(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/InstrumentedServerSocketAppenderBase.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/InstrumentedServerSocketAppenderBase.java index a221296bcd..04fc729569 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/InstrumentedServerSocketAppenderBase.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/InstrumentedServerSocketAppenderBase.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -41,8 +41,8 @@ public InstrumentedServerSocketAppenderBase(ServerSocket serverSocket) { this(serverSocket, new RemoteReceiverServerListener(serverSocket), null); } - public InstrumentedServerSocketAppenderBase(ServerSocket serverSocket, ServerListener listener, - ServerRunner runner) { + public InstrumentedServerSocketAppenderBase(ServerSocket serverSocket, + ServerListener listener, ServerRunner runner) { this.serverSocket = serverSocket; this.listener = listener; this.runner = runner; @@ -83,7 +83,8 @@ public ServerSocket createServerSocket(int port, int backlog, InetAddress ifAddr } @Override - protected ServerRunner createServerRunner(ServerListener listener, Executor executor) { + protected ServerRunner createServerRunner(ServerListener listener, + Executor executor) { lastListener = listener; return runner != null ? runner : super.createServerRunner(listener, executor); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClient.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClient.java index 8e4c4fd5fc..e632a07833 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClient.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClient.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,12 +13,10 @@ */ package ch.qos.logback.core.net.server; -import ch.qos.logback.core.net.server.Client; - /** * - * A mock {@link Client} that notifies waiting thread when it has started, - * and waits to be interrupted before exiting. + * A mock {@link Client} that notifies waiting thread when it has started, and + * waits to be interrupted before exiting. * * @author Carl Harris */ diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClientVisitor.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClientVisitor.java index 04ad208f17..d4d084241e 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClientVisitor.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockClientVisitor.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockEventQueue.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockEventQueue.java index d24b85317b..bd58a3168d 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/MockEventQueue.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/MockEventQueue.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/PackageTest.java deleted file mode 100644 index ae87f35013..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.net.server; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ ConcurrentServerRunnerTest.class, RemoteReceiverStreamClientTest.class, ServerSocketAppenderBaseFunctionalTest.class, - AbstractServerSocketAppenderTest.class, SSLServerSocketAppenderBaseTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClientTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClientTest.java index 12a3b66ea5..bb52b06213 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClientTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/RemoteReceiverStreamClientTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,13 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; @@ -42,7 +40,7 @@ public class RemoteReceiverStreamClientTest { private RemoteReceiverStreamClient client = new RemoteReceiverStreamClient("someId", outputStream); - @Before + @BeforeEach public void setUp() throws Exception { client.setContext(context); client.setQueue(queue); @@ -57,10 +55,10 @@ public void testOfferEventAndRun() throws Exception { // MockEventQueue will interrupt the thread when the queue is drained thread.join(1000); - assertFalse(thread.isAlive()); + Assertions.assertFalse(thread.isAlive()); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray())); - assertEquals(TEST_EVENT, ois.readObject()); + Assertions.assertEquals(TEST_EVENT, ois.readObject()); } @Test @@ -72,11 +70,11 @@ public void testOfferEventSequenceAndRun() throws Exception { Thread thread = new Thread(client); thread.start(); thread.join(1000); - assertFalse(thread.isAlive()); + Assertions.assertFalse(thread.isAlive()); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(outputStream.toByteArray())); for (int i = 0; i < 10; i++) { - assertEquals(TEST_EVENT + i, ois.readObject()); + Assertions.assertEquals(TEST_EVENT + i, ois.readObject()); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBaseTest.java index afded0495f..0ed399ff03 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/SSLServerSocketAppenderBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,15 +13,15 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertNotNull; - -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.spi.PreSerializationTransformer; import ch.qos.logback.core.util.ExecutorServiceUtil; +import static org.junit.jupiter.api.Assertions.assertNotNull; + /** * Unit tests for {@link SSLServerSocketAppenderBase}. * @@ -29,12 +29,12 @@ */ public class SSLServerSocketAppenderBaseTest { - private MockContext context = new MockContext(ExecutorServiceUtil.newScheduledExecutorService()); + private MockContext context = new MockContext(ExecutorServiceUtil.newThreadPoolExecutor()); @SuppressWarnings("rawtypes") private SSLServerSocketAppenderBase appender = new InstrumentedSSLServerSocketAppenderBase(); - @Before + @BeforeEach public void setUp() throws Exception { appender.setContext(context); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketAppenderBaseFunctionalTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketAppenderBaseFunctionalTest.java index 2ac42034c7..70b9687186 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketAppenderBaseFunctionalTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketAppenderBaseFunctionalTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,6 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.ObjectInputStream; import java.net.InetAddress; import java.net.ServerSocket; @@ -23,10 +20,11 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.mock.MockContext; import ch.qos.logback.core.net.server.test.ServerSocketUtil; @@ -37,7 +35,7 @@ * * @author Carl Harris */ -@Ignore +@Disabled public class ServerSocketAppenderBaseFunctionalTest { private static final String TEST_EVENT = "test event"; @@ -49,7 +47,7 @@ public class ServerSocketAppenderBaseFunctionalTest { private ServerSocket serverSocket; private InstrumentedServerSocketAppenderBase appender; - @Before + @BeforeEach public void setUp() throws Exception { serverSocket = ServerSocketUtil.createServerSocket(); @@ -58,11 +56,11 @@ public void setUp() throws Exception { appender.setContext(context); } - @After + @AfterEach public void tearDown() throws Exception { executor.shutdownNow(); executor.awaitTermination(10000, TimeUnit.MILLISECONDS); - assertTrue(executor.isTerminated()); + Assertions.assertTrue(executor.isTerminated()); } @Test @@ -75,7 +73,7 @@ public void testLogEventClient() throws Exception { for (int i = 0; i < EVENT_COUNT; i++) { appender.append(TEST_EVENT + i); - assertEquals(TEST_EVENT + i, ois.readObject()); + Assertions.assertEquals(TEST_EVENT + i, ois.readObject()); } socket.close(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketListenerTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketListenerTest.java index cc6b648e8e..72a5b80607 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketListenerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/ServerSocketListenerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,19 +13,15 @@ */ package ch.qos.logback.core.net.server; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - import java.io.IOException; import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.net.server.Client; -import ch.qos.logback.core.net.server.ServerSocketListener; import ch.qos.logback.core.net.server.test.ServerSocketUtil; /** @@ -39,10 +35,10 @@ public class ServerSocketListenerTest { @SuppressWarnings("rawtypes") private ServerSocketListener listener; - @Before + @BeforeEach public void setUp() throws Exception { serverSocket = ServerSocketUtil.createServerSocket(); - assertNotNull(serverSocket); + Assertions.assertNotNull(serverSocket); listener = new InstrumentedServerSocketListener(serverSocket); } @@ -57,12 +53,12 @@ public void testAcceptClient() throws Exception { localClient.wait(10); } } - assertTrue(localClient.isConnected()); + Assertions.assertTrue(localClient.isConnected()); localClient.close(); serverSocket.setSoTimeout(5000); Client client = listener.acceptClient(); - assertNotNull(client); + Assertions.assertNotNull(client); client.close(); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerListener.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerListener.java index 7b8a2cd96e..5bc543e86e 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerListener.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerListener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,10 +21,10 @@ import ch.qos.logback.core.net.server.ServerListener; /** - * A mock {@link ServerListener} that has a blocking queue to pass a client - * to a {@link #acceptClient()} caller. If the {@link #close()} method is - * called while a caller is blocked waiting to take from the queue, the - * caller's thread is interrupted. + * A mock {@link ServerListener} that has a blocking queue to pass a client to a + * {@link #acceptClient()} caller. If the {@link #close()} method is called + * while a caller is blocked waiting to take from the queue, the caller's thread + * is interrupted. * * @author Carl Harris */ diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerRunner.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerRunner.java index e7e2aa3865..d8409d2d27 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerRunner.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/MockServerRunner.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/ServerSocketUtil.java b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/ServerSocketUtil.java index 38f8f1e9fd..3a7af66f49 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/server/test/ServerSocketUtil.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/server/test/ServerSocketUtil.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,8 +20,8 @@ import javax.net.ServerSocketFactory; /** - * Static utility methods for obtaining a {@link ServerSocket} bound to - * a random unused port. + * Static utility methods for obtaining a {@link ServerSocket} bound to a random + * unused port. * * @author Carl Harris */ @@ -30,9 +30,10 @@ public class ServerSocketUtil { /** * Creates a new {@link ServerSocket} bound to a random unused port. *

- * This method is a convenience overload for - * {@link #createServerSocket(ServerSocketFactory)} using the platform's - * default {@link ServerSocketFactory}. + * This method is a convenience overload for + * {@link #createServerSocket(ServerSocketFactory)} using the platform's default + * {@link ServerSocketFactory}. + * * @return socket * @throws IOException */ @@ -42,8 +43,8 @@ public static ServerSocket createServerSocket() throws IOException { /** * Creates a new {@link ServerSocket} bound to a random unused port. - * @param socketFactory socket factory that will be used to create the - * socket + * + * @param socketFactory socket factory that will be used to create the socket * @return socket * @throws IOException */ diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBeanTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBeanTest.java index 77b9c02faf..ba851369e2 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBeanTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBeanTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,11 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; - import javax.net.ssl.KeyManagerFactory; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.net.ssl.KeyManagerFactoryFactoryBean; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unit tests for {@link KeyManagerFactoryFactoryBean}. diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBeanTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBeanTest.java index a9f25ee24b..074132bd29 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBeanTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/KeyStoreFactoryBeanTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,14 +13,11 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; - import java.security.KeyStore; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.net.ssl.KeyStoreFactoryBean; -import ch.qos.logback.core.net.ssl.SSL; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unit tests for {@link KeyStoreFactoryBean}. diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/PackageTest.java deleted file mode 100644 index bc7e587b2a..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/PackageTest.java +++ /dev/null @@ -1,25 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ - -package ch.qos.logback.core.net.ssl; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ KeyManagerFactoryFactoryBeanTest.class, KeyStoreFactoryBeanTest.class, SecureRandomFactoryBeanTest.class, SSLConfigurationTest.class, - SSLContextFactoryBeanTest.class, SSLParametersConfigurationTest.class, TrustManagerFactoryFactoryBeanTest.class }) -public class PackageTest { - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLConfigurationTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLConfigurationTest.java index 2bad45540d..f57132f997 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLConfigurationTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLConfigurationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,9 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unit tests for {@link SSLConfiguration}. diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBeanTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBeanTest.java index 7c68c656b4..cfc9c11f11 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBeanTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLContextFactoryBeanTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,11 +13,8 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.net.ssl.mock.MockContextAware; import ch.qos.logback.core.net.ssl.mock.MockKeyManagerFactoryFactoryBean; @@ -25,6 +22,9 @@ import ch.qos.logback.core.net.ssl.mock.MockSecureRandomFactoryBean; import ch.qos.logback.core.net.ssl.mock.MockTrustManagerFactoryFactoryBean; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Unit tests for {@link SSLContextFactoryBean}. * @@ -57,7 +57,7 @@ public class SSLContextFactoryBeanTest { private MockContextAware context = new MockContextAware(); private SSLContextFactoryBean factoryBean = new SSLContextFactoryBean(); - @Before + @BeforeEach public void setUp() throws Exception { keyStore.setLocation(SSLTestConstants.KEYSTORE_JKS_RESOURCE); trustStore.setLocation(SSLTestConstants.KEYSTORE_JKS_RESOURCE); diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLParametersConfigurationTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLParametersConfigurationTest.java index ab5bf8b921..3dcfa84120 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLParametersConfigurationTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLParametersConfigurationTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,16 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertTrue; - import java.util.Arrays; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.net.ssl.mock.MockSSLConfigurable; +import static org.junit.jupiter.api.Assertions.assertTrue; + /** * Unit tests for {@link SSLParametersConfiguration}. * @@ -34,7 +34,7 @@ public class SSLParametersConfigurationTest { private SSLParametersConfiguration configuration = new SSLParametersConfiguration(); - @Before + @BeforeEach public void setUp() throws Exception { configuration.setContext(new ContextBase()); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLTestConstants.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLTestConstants.java index 3e788716a5..841deb505c 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLTestConstants.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SSLTestConstants.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBeanTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBeanTest.java index 223dc716fa..20a82570c5 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBeanTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/SecureRandomFactoryBeanTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,12 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; -import org.junit.Test; - -import ch.qos.logback.core.net.ssl.SSL; -import ch.qos.logback.core.net.ssl.SecureRandomFactoryBean; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; /** * Unit tests for {@link SecureRandomFactoryBean}. @@ -37,14 +31,14 @@ public class SecureRandomFactoryBeanTest { @Test public void testDefaults() throws Exception { - assertNotNull(factoryBean.createSecureRandom()); + Assertions.assertNotNull(factoryBean.createSecureRandom()); } @Test public void testExplicitProvider() throws Exception { SecureRandom secureRandom = SecureRandom.getInstance(SSL.DEFAULT_SECURE_RANDOM_ALGORITHM); factoryBean.setProvider(secureRandom.getProvider().getName()); - assertNotNull(factoryBean.createSecureRandom()); + Assertions.assertNotNull(factoryBean.createSecureRandom()); } @Test @@ -52,9 +46,9 @@ public void testUnknownProvider() throws Exception { factoryBean.setProvider(SSLTestConstants.FAKE_PROVIDER_NAME); try { factoryBean.createSecureRandom(); - fail("expected NoSuchProviderException"); + Assertions.fail("expected NoSuchProviderException"); } catch (NoSuchProviderException ex) { - assertTrue(ex.getMessage().contains(SSLTestConstants.FAKE_PROVIDER_NAME)); + Assertions.assertTrue(ex.getMessage().contains(SSLTestConstants.FAKE_PROVIDER_NAME)); } } @@ -63,9 +57,9 @@ public void testUnknownAlgorithm() throws Exception { factoryBean.setAlgorithm(SSLTestConstants.FAKE_ALGORITHM_NAME); try { factoryBean.createSecureRandom(); - fail("expected NoSuchAlgorithmException"); + Assertions.fail("expected NoSuchAlgorithmException"); } catch (NoSuchAlgorithmException ex) { - assertTrue(ex.getMessage().contains(SSLTestConstants.FAKE_ALGORITHM_NAME)); + Assertions.assertTrue(ex.getMessage().contains(SSLTestConstants.FAKE_ALGORITHM_NAME)); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBeanTest.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBeanTest.java index 408478a2c2..4d252f0670 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBeanTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBeanTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,11 @@ */ package ch.qos.logback.core.net.ssl; -import static org.junit.Assert.assertNotNull; - import javax.net.ssl.TrustManagerFactory; -import org.junit.Test; +import org.junit.jupiter.api.Test; -import ch.qos.logback.core.net.ssl.TrustManagerFactoryFactoryBean; +import static org.junit.jupiter.api.Assertions.assertNotNull; /** * Unit tests for {@link TrustManagerFactoryFactoryBean}. diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockContextAware.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockContextAware.java index 1080deb853..265b1b69cc 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockContextAware.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockContextAware.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyManagerFactoryFactoryBean.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyManagerFactoryFactoryBean.java index 8d6016569e..601401b75f 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyManagerFactoryFactoryBean.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyManagerFactoryFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyStoreFactoryBean.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyStoreFactoryBean.java index 280ad873bb..ac58781265 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyStoreFactoryBean.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockKeyStoreFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -21,7 +21,7 @@ import ch.qos.logback.core.net.ssl.KeyStoreFactoryBean; /** - * A {@link KeyStoreFactoryBean} with test instrumentation. + * A {@link KeyStoreFactoryBean} with test instrumentation. * * @author Carl Harris */ diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSSLConfigurable.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSSLConfigurable.java index 8c47e32dc5..47e8f49434 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSSLConfigurable.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSSLConfigurable.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -92,4 +92,8 @@ public void setWantClientAuth(boolean wantClientAuth) { this.wantClientAuth = wantClientAuth; } + @Override + public void setHostnameVerification(boolean verifyHostname) { + } + } diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSecureRandomFactoryBean.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSecureRandomFactoryBean.java index 5355d84a50..8d37b489d9 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSecureRandomFactoryBean.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockSecureRandomFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockTrustManagerFactoryFactoryBean.java b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockTrustManagerFactoryFactoryBean.java index 21dbc3eb21..63f82f917a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockTrustManagerFactoryFactoryBean.java +++ b/logback-core/src/test/java/ch/qos/logback/core/net/ssl/mock/MockTrustManagerFactoryFactoryBean.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/Converter123.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/Converter123.java index c24861480e..629de61a06 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/Converter123.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/Converter123.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,6 @@ */ package ch.qos.logback.core.pattern; -import ch.qos.logback.core.pattern.DynamicConverter; - public class Converter123 extends DynamicConverter { public String convert(Object event) { diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterHello.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterHello.java index e8171081ff..42debc8807 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterHello.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterHello.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,10 +13,20 @@ */ package ch.qos.logback.core.pattern; -import ch.qos.logback.core.pattern.DynamicConverter; +import java.util.List; public class ConverterHello extends DynamicConverter { + String firstOption; + + @Override + public void start() { + List options = getOptionList(); + if (options != null && !options.isEmpty()) + firstOption = options.get(0); + super.start(); + } + public String convert(Object event) { return "Hello"; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterUtilTest.java new file mode 100644 index 0000000000..bf3888303f --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/ConverterUtilTest.java @@ -0,0 +1,99 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + +package ch.qos.logback.core.pattern; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import ch.qos.logback.core.Context; +import ch.qos.logback.core.ContextBase; +import ch.qos.logback.core.pattern.parser.Node; +import ch.qos.logback.core.pattern.parser.Parser; +import ch.qos.logback.core.spi.ContextAware; +import ch.qos.logback.core.spi.LifeCycle; +import ch.qos.logback.core.spi.ScanException; + +// inspired by ch.qos.logback.core.pattern.parser.CompilerTest +public class ConverterUtilTest { + + Map> converterMap = new HashMap<>(); + Context context = new ContextBase(); + + @BeforeEach + public void setUp() { + converterMap.put("OTT", Converter123::new); + converterMap.put("hello", ConverterHello::new); + converterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); + } + + @Test + public void contextAndStartTest() throws ScanException { + testContextAndStart("hi %hello"); + testContextAndStart("hi %(%hello)"); + testContextAndStart("hi %(abc %(%hello))"); + + } + + private void testContextAndStart(String pattern) throws ScanException { + Parser p = new Parser(pattern); + p.setContext(context); + Node t = p.parse(); + Converter head = p.compile(t, converterMap); + ConverterUtil.setContextForConverters(context, head); + checkContext(head); + + ConverterUtil.startConverters(head); + checkStart(head); + } + + private void checkStart(Converter head) { + Converter c = head; + while (c != null) { + if (c instanceof LifeCycle) { + LifeCycle ca = (LifeCycle) c; + Assertions.assertTrue(ca.isStarted()); + } + if (c instanceof CompositeConverter) { + CompositeConverter cc = (CompositeConverter) c; + Converter childConverter = cc.childConverter; + checkStart(childConverter); + } + c = c.getNext(); + } + + } + + void checkContext(Converter head) { + Converter c = head; + while (c != null) { + if (c instanceof ContextAware) { + ContextAware ca = (ContextAware) c; + Assertions.assertNotNull(ca.getContext()); + } + if (c instanceof CompositeConverter) { + CompositeConverter cc = (CompositeConverter) c; + Converter childConverter = cc.childConverter; + checkContext(childConverter); + } + c = c.getNext(); + } + } + +} diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/ExceptionalConverter.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/ExceptionalConverter.java index 1072946c16..c01a017f15 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/ExceptionalConverter.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/ExceptionalConverter.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,8 +13,9 @@ */ package ch.qos.logback.core.pattern; -import ch.qos.logback.core.pattern.DynamicConverter; - +/** + * Testing purposes only + */ public class ExceptionalConverter extends DynamicConverter { public String convert(Object event) { @@ -23,5 +24,4 @@ public String convert(Object event) { } return ""; } - } diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/PackageTest.java deleted file mode 100644 index 8dd22ef393..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.pattern; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ SpacePadderTest.class, ch.qos.logback.core.pattern.parser.PackageTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/SpacePadderTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/SpacePadderTest.java index 5361c9b87e..c9c17ea697 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/SpacePadderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/SpacePadderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,29 +13,29 @@ */ package ch.qos.logback.core.pattern; -import static org.junit.Assert.assertEquals; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; public class SpacePadderTest { - @BeforeClass + @BeforeAll public static void setUpBeforeClass() throws Exception { } - @AfterClass + @AfterAll public static void tearDownAfterClass() throws Exception { } - @Before + @BeforeEach public void setUp() throws Exception { } - @After + @AfterEach public void tearDown() throws Exception { } diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/CompilerTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/CompilerTest.java index 30fdb29479..f5dd5fbc08 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/CompilerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/CompilerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,25 +18,27 @@ import ch.qos.logback.core.pattern.Converter; import ch.qos.logback.core.pattern.Converter123; import ch.qos.logback.core.pattern.ConverterHello; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.pattern.DynamicConverter; +import ch.qos.logback.core.status.testUtil.StatusChecker; +//import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class CompilerTest { - Map converterMap = new HashMap(); + Map> converterMap = new HashMap<>(); Context context = new ContextBase(); - @Before + @BeforeEach public void setUp() { - converterMap.put("OTT", Converter123.class.getName()); - converterMap.put("hello", ConverterHello.class.getName()); + converterMap.put("OTT", Converter123::new); + converterMap.put("hello", ConverterHello::new); converterMap.putAll(Parser.DEFAULT_COMPOSITE_CONVERTER_MAP); } @@ -79,6 +81,18 @@ public void testBasic() throws Exception { } } + @Test + public void converterStart() throws Exception { + { + Parser p = new Parser("abc %hello"); + p.setContext(context); + Node t = p.parse(); + Converter head = p.compile(t, converterMap); + String result = write(head, new Object()); + assertEquals("abc Hello", result); + } + } + @Test public void testFormat() throws Exception { { @@ -168,7 +182,7 @@ public void testComposite() throws Exception { Node t = p.parse(); Converter head = p.compile(t, converterMap); String result = write(head, new Object()); - StatusPrinter.print(c); + // StatusPrinter.print(c); assertEquals("ABC Hello", result); } { diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/FormatInfoTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/FormatInfoTest.java index 4d9bf2e70a..69b3ae6c8a 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/FormatInfoTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/FormatInfoTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,13 +13,13 @@ */ package ch.qos.logback.core.pattern.parser; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - -import org.junit.Test; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.pattern.FormatInfo; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + public class FormatInfoTest { @Test diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/OptionTokenizerTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/OptionTokenizerTest.java index 75e5e66e8a..22fe1d0d65 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/OptionTokenizerTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/OptionTokenizerTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,7 +13,7 @@ */ package ch.qos.logback.core.pattern.parser; -import org.junit.Test; +import org.junit.jupiter.api.Test; public class OptionTokenizerTest { diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/PackageTest.java deleted file mode 100644 index 23b25310ef..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.pattern.parser; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ TokenStreamTest.class, OptionTokenizerTest.class, ParserTest.class, FormatInfoTest.class, CompilerTest.class, SamplePatternLayoutTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/ParserTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/ParserTest.java index 385aef55d7..a46024c1a6 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/ParserTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/ParserTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,19 +13,17 @@ */ package ch.qos.logback.core.pattern.parser; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import java.util.ArrayList; import java.util.List; -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.pattern.FormatInfo; import ch.qos.logback.core.spi.ScanException; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; public class ParserTest { @@ -36,8 +34,8 @@ public class ParserTest { public void testBasic() throws Exception { Parser p = new Parser<>("hello"); Node t = p.parse(); - assertEquals(Node.LITERAL, t.getType()); - assertEquals("hello", t.getValue()); + Assertions.assertEquals(Node.LITERAL, t.getType()); + Assertions.assertEquals("hello", t.getValue()); } @Test @@ -48,7 +46,7 @@ public void testKeyword() throws Exception { Node t = p.parse(); Node witness = new Node(Node.LITERAL, "hello"); witness.next = new SimpleKeywordNode("xyz"); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -60,7 +58,7 @@ public void testKeyword() throws Exception { optionList.add("x"); n.setOptions(optionList); witness.next = n; - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } } @@ -79,7 +77,7 @@ public void testComposite() throws Exception { // System.out.println("w:" + witness); // System.out.println(t); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } // System.out.println("testRecursive part 2"); @@ -93,7 +91,7 @@ public void testComposite() throws Exception { composite.setChildNode(child); witness.next = composite; child.next = new Node(Node.LITERAL, " "); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -106,7 +104,7 @@ public void testComposite() throws Exception { child.next = new Node(Node.LITERAL, " "); child.next.next = new SimpleKeywordNode("h"); witness.next = composite; - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -121,7 +119,7 @@ public void testComposite() throws Exception { witness.next = composite; composite.next = new Node(Node.LITERAL, " "); composite.next.next = new SimpleKeywordNode("m"); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -139,7 +137,7 @@ public void testComposite() throws Exception { witness.next = composite; composite.next = new Node(Node.LITERAL, " "); composite.next.next = new SimpleKeywordNode("m"); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } } @@ -159,7 +157,7 @@ public void testNested() throws Exception { child.next = composite; composite.setChildNode(new SimpleKeywordNode("h")); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } } @@ -170,14 +168,14 @@ public void testFormattingInfo() throws Exception { Node t = p.parse(); FormattingNode witness = new SimpleKeywordNode("x"); witness.setFormatInfo(new FormatInfo(45, Integer.MAX_VALUE)); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { Parser p = new Parser<>("%4.5x"); Node t = p.parse(); FormattingNode witness = new SimpleKeywordNode("x"); witness.setFormatInfo(new FormatInfo(4, 5)); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -185,14 +183,14 @@ public void testFormattingInfo() throws Exception { Node t = p.parse(); FormattingNode witness = new SimpleKeywordNode("x"); witness.setFormatInfo(new FormatInfo(4, 5, false, true)); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { Parser p = new Parser<>("%-4.-5x"); Node t = p.parse(); FormattingNode witness = new SimpleKeywordNode("x"); witness.setFormatInfo(new FormatInfo(4, 5, false, false)); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } { @@ -203,7 +201,7 @@ public void testFormattingInfo() throws Exception { Node n = witness.next = new Node(Node.LITERAL, " "); n = n.next = new SimpleKeywordNode("y"); ((FormattingNode) n).setFormatInfo(new FormatInfo(12, Integer.MAX_VALUE)); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } } @@ -216,7 +214,7 @@ public void testOptions0() throws Exception { List ol = new ArrayList(); ol.add("test "); witness.setOptions(ol); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } @Test @@ -229,7 +227,7 @@ public void testOptions1() throws Exception { ol.add("a"); ol.add("b"); witness.setOptions(ol); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } // see http://jira.qos.ch/browse/LBCORE-180 @@ -240,7 +238,7 @@ public void keywordGluedToLitteral() throws Exception { SimpleKeywordNode witness = new SimpleKeywordNode("x"); witness.setOptions(new ArrayList()); witness.next = new Node(Node.LITERAL, "a"); - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } @Test @@ -255,7 +253,7 @@ public void testCompositeFormatting() throws Exception { composite.setChildNode(child); witness.next = composite; - assertEquals(witness, t); + Assertions.assertEquals(witness, t); } @@ -264,7 +262,7 @@ public void empty() { try { Parser p = new Parser<>(""); p.parse(); - fail(""); + Assertions.fail(""); } catch (ScanException e) { } @@ -276,9 +274,9 @@ public void lbcore193() throws Exception { Parser p = new Parser<>("hello%(abc"); p.setContext(context); p.parse(); - fail("where the is exception?"); + Assertions.fail("where the is exception?"); } catch (ScanException ise) { - assertEquals("Expecting RIGHT_PARENTHESIS token but got null", ise.getMessage()); + Assertions.assertEquals("Expecting RIGHT_PARENTHESIS token but got null", ise.getMessage()); } StatusChecker sc = new StatusChecker(context); sc.assertContainsMatch("Expecting RIGHT_PARENTHESIS"); diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayout.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayout.java index 7461bc8995..9e09c6123d 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayout.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayout.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,20 +15,25 @@ import java.util.HashMap; import java.util.Map; +import java.util.function.Supplier; -import ch.qos.logback.core.pattern.Converter123; -import ch.qos.logback.core.pattern.ConverterHello; -import ch.qos.logback.core.pattern.PatternLayoutBase; +import ch.qos.logback.core.pattern.*; public class SamplePatternLayout extends PatternLayoutBase { - Map converterMap = new HashMap(); + Map> converterSupplierMap = new HashMap<>(); + Map converterMap = new HashMap<>(); public SamplePatternLayout() { - converterMap.put("OTT", Converter123.class.getName()); - converterMap.put("hello", ConverterHello.class.getName()); + converterSupplierMap.put("OTT", Converter123::new); + converterSupplierMap.put("hello", ConverterHello::new); } + public Map> getDefaultConverterSupplierMap() { + return converterSupplierMap; + } + + @Override public Map getDefaultConverterMap() { return converterMap; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayoutTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayoutTest.java index 106f01f667..2647872766 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayoutTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/SamplePatternLayoutTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,20 @@ */ package ch.qos.logback.core.pattern.parser; -import static org.junit.Assert.*; - -import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.pattern.PatternLayoutBase; import ch.qos.logback.core.pattern.parser.test.AbstractPatternLayoutBaseTest; +import ch.qos.logback.core.status.Status; +import ch.qos.logback.core.status.testUtil.StatusChecker; public class SamplePatternLayoutTest extends AbstractPatternLayoutBaseTest { Context context = new ContextBase(); + StatusChecker checker = new StatusChecker(context); public PatternLayoutBase getPatternLayoutBase() { return new SamplePatternLayout(); @@ -46,7 +48,7 @@ public void testOK() { // StatusManager sm = context.getStatusManager(); // StatusPrinter.print(sm); - assertEquals("x123", s); + Assertions.assertEquals("x123", s); } @Test @@ -57,7 +59,7 @@ public void testEscapeClosingParentheses() { plb.setPattern("x(%OTT\\)y"); plb.start(); String s = plb.doLayout(new Object()); - assertEquals("x(123)y", s); + Assertions.assertEquals("x(123)y", s); } @Test @@ -68,7 +70,7 @@ public void testEscapeBothParentheses() { plb.setPattern("x\\(%OTT\\)y"); plb.start(); String s = plb.doLayout(new Object()); - assertEquals("x(123)y", s); + Assertions.assertEquals("x(123)y", s); } @Test @@ -79,7 +81,17 @@ public void testPercentAsLiteral() { plb.setPattern("hello \\% world"); plb.start(); String s = plb.doLayout(new Object()); - assertEquals("hello % world", s); + Assertions.assertEquals("hello % world", s); + } + + @Test + public void noClosingCurlyBrace() { + PatternLayoutBase plb = getPatternLayoutBase(); + plb.setContext(context); + plb.setPattern("%x %hello{asd"); + plb.start(); + + checker.assertContainsMatch(Status.ERROR, "Failed to parse pattern"); } @Override diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/TokenStreamTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/TokenStreamTest.java index 8350dc7c73..770ad7b0fb 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/TokenStreamTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/TokenStreamTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,17 +13,17 @@ */ package ch.qos.logback.core.pattern.parser; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; - import java.util.ArrayList; import java.util.List; import ch.qos.logback.core.spi.ScanException; -import org.junit.Test; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.pattern.util.AlmostAsIsEscapeUtil; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; + public class TokenStreamTest { @Test diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/test/AbstractPatternLayoutBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/test/AbstractPatternLayoutBaseTest.java index 99984fb96b..c352393161 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/test/AbstractPatternLayoutBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/parser/test/AbstractPatternLayoutBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,18 +13,17 @@ */ package ch.qos.logback.core.pattern.parser.test; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; - -import org.junit.Test; +import ch.qos.logback.core.pattern.color.ConverterSupplierByClassName; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.pattern.ExceptionalConverter; import ch.qos.logback.core.pattern.PatternLayoutBase; -import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.status.testUtil.StatusChecker; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; abstract public class AbstractPatternLayoutBaseTest { @@ -41,25 +40,36 @@ public void testUnStarted() { plb.setContext(context); String s = plb.doLayout(getEventObject()); assertEquals("", s); - StatusManager sm = context.getStatusManager(); - StatusPrinter.print(sm); } + + /** * This test checks that the pattern layout implementation starts its - * converters. ExceptionalConverter throws an exception if it's convert - * method is called before being started. + * converters. ExceptionalConverter throws an exception if it's convert method + * is called before being started. */ @Test public void testConverterStart() { PatternLayoutBase plb = getPatternLayoutBase(); plb.setContext(getContext()); - plb.getInstanceConverterMap().put("EX", ExceptionalConverter.class.getName()); + String exceptionalConverterClassName = getExceptionalConverterClassName(); + ConverterSupplierByClassName converterSupplierByClassName = new ConverterSupplierByClassName("EX", exceptionalConverterClassName); + converterSupplierByClassName.setContext(getContext()); + plb.getInstanceConverterMap().put("EX", converterSupplierByClassName); plb.setPattern("%EX"); plb.start(); String result = plb.doLayout(getEventObject()); assertFalse(result.contains("%PARSER_ERROR_EX")); - // System.out.println("========="+result); + } + + /** + * + * @return the class for the ExceptionalConverter (used in tests) + * @since 1.4.2 + */ + protected String getExceptionalConverterClassName() { + return ExceptionalConverter.class.getName(); } @Test @@ -69,8 +79,6 @@ public void testStarted() { plb.setContext(context); String s = plb.doLayout(getEventObject()); assertEquals("", s); - StatusManager sm = context.getStatusManager(); - StatusPrinter.print(sm); } @Test @@ -84,7 +92,6 @@ public void testNullPattern() { String s = plb.doLayout(getEventObject()); assertEquals("", s); StatusChecker checker = new StatusChecker(context.getStatusManager()); - // StatusPrinter.print(context); checker.assertContainsMatch("Empty or null pattern."); } @@ -99,7 +106,6 @@ public void testEmptyPattern() { String s = plb.doLayout(getEventObject()); assertEquals("", s); StatusChecker checker = new StatusChecker(context.getStatusManager()); - // StatusPrinter.print(context); checker.assertContainsMatch("Empty or null pattern."); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/pattern/util/RegularEscapeUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/pattern/util/RegularEscapeUtilTest.java new file mode 100644 index 0000000000..8a6cbd7539 --- /dev/null +++ b/logback-core/src/test/java/ch/qos/logback/core/pattern/util/RegularEscapeUtilTest.java @@ -0,0 +1,39 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ +package ch.qos.logback.core.pattern.util; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class RegularEscapeUtilTest { + + @BeforeEach + public void setUp() throws Exception { + } + + @Test + public void basicEscape() { + assertEquals("a", RegularEscapeUtil.basicEscape("a")); + assertEquals("a\t", RegularEscapeUtil.basicEscape("a\t")); + assertEquals("a\\", RegularEscapeUtil.basicEscape("a\\")); + assertEquals("a\\", RegularEscapeUtil.basicEscape("a\\\\")); + } + + @Test + public void zbasicEscape() { + + } +} \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/read/CyclicBufferAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/read/CyclicBufferAppenderTest.java index fef9258e68..c793466db1 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/read/CyclicBufferAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/read/CyclicBufferAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,16 +13,16 @@ */ package ch.qos.logback.core.read; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; -import static org.junit.Assert.assertEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; public class CyclicBufferAppenderTest { private CyclicBufferAppender cyclicBufferAppender; - @Before + @BeforeEach public void before() { cyclicBufferAppender = new CyclicBufferAppender(); cyclicBufferAppender.start(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/recovery/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/recovery/PackageTest.java deleted file mode 100644 index f4a1e55fa2..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/recovery/PackageTest.java +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.recovery; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; -import org.junit.runners.Suite.SuiteClasses; - -@RunWith(Suite.class) -@SuiteClasses({ RecoveryCoordinatorTest.class, ResilientOutputStreamTest.class }) -public class PackageTest { -} \ No newline at end of file diff --git a/logback-core/src/test/java/ch/qos/logback/core/recovery/RecoveryCoordinatorTest.java b/logback-core/src/test/java/ch/qos/logback/core/recovery/RecoveryCoordinatorTest.java index 7da610385b..4589aff2ef 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/recovery/RecoveryCoordinatorTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/recovery/RecoveryCoordinatorTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,9 +13,10 @@ */ package ch.qos.logback.core.recovery; -import static org.junit.Assert.*; +import org.junit.jupiter.api.Test; -import org.junit.Test; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; public class RecoveryCoordinatorTest { @@ -64,9 +65,9 @@ public void recoveryConditionDetectedEvenAfterReallyLongTimesBetweenRecovery() { rc.setCurrentTime(now + offset); if (i % 2 == 0) { - assertTrue("recovery should've been needed at " + offset, rc.isTooSoon()); + assertTrue(rc.isTooSoon(), "recovery should've been needed at " + offset); } else { - assertFalse("recovery should NOT have been needed at " + offset, rc.isTooSoon()); + assertFalse(rc.isTooSoon(), "recovery should NOT have been needed at " + offset); } offset *= 2; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/recovery/ResilientOutputStreamTest.java b/logback-core/src/test/java/ch/qos/logback/core/recovery/ResilientOutputStreamTest.java index 4acfb7a102..dba8102e66 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/recovery/ResilientOutputStreamTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/recovery/ResilientOutputStreamTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -18,8 +18,8 @@ import java.io.File; -import org.junit.BeforeClass; -import org.junit.Test; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; @@ -35,7 +35,7 @@ public class ResilientOutputStreamTest { int diff = RandomUtil.getPositiveInt(); Context context = new ContextBase(); - @BeforeClass + @BeforeAll public static void setUp() { File file = new File(CoreTestConstants.OUTPUT_DIR_PREFIX); file.mkdirs(); diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/CollisionDetectionTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/CollisionDetectionTest.java deleted file mode 100755 index 8fbe502c49..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/CollisionDetectionTest.java +++ /dev/null @@ -1,141 +0,0 @@ -package ch.qos.logback.core.rolling; - -import static ch.qos.logback.core.CoreConstants.FA_FILENAME_COLLISION_MAP; -import static ch.qos.logback.core.testUtil.CoreTestConstants.OUTPUT_DIR_PREFIX; - -import java.util.Map; - -import org.junit.After; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Test; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.FileAppender; -import ch.qos.logback.core.encoder.NopEncoder; -import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; - -public class CollisionDetectionTest { - - Context context = new ContextBase(); - StatusChecker statusChecker = new StatusChecker(context); - int diff = RandomUtil.getPositiveInt(); - protected String randomOutputDir = OUTPUT_DIR_PREFIX + diff + "/"; - - @Before - public void setUp() throws Exception { - } - - @After - public void tearDown() throws Exception { - } - - - FileAppender buildFileAppender(String name, String filenameSuffix) { - FileAppender fileAppender = new FileAppender(); - fileAppender.setName(name); - fileAppender.setContext(context); - fileAppender.setFile(randomOutputDir+filenameSuffix); - fileAppender.setEncoder(new NopEncoder()); - return fileAppender; - } - - RollingFileAppender buildRollingFileAppender(String name, String filenameSuffix, String patternSuffix) { - RollingFileAppender rollingFileAppender = new RollingFileAppender(); - rollingFileAppender.setName(name); - rollingFileAppender.setContext(context); - rollingFileAppender.setFile(randomOutputDir+filenameSuffix); - rollingFileAppender.setEncoder(new NopEncoder()); - - TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy(); - tbrp.setContext(context); - tbrp.setFileNamePattern(randomOutputDir+patternSuffix); - tbrp.setParent(rollingFileAppender); - //tbrp.timeBasedFileNamingAndTriggeringPolicy = new DefaultTimeBasedFileNamingAndTriggeringPolicy(); - //tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(givenTime); - rollingFileAppender.setRollingPolicy(tbrp); - tbrp.start(); - - - return rollingFileAppender; - } - - - @Test - public void collisionImpossibleForSingleAppender() { - FileAppender fileAppender = buildFileAppender("FA", "collisionImpossibleForSingleAppender"); - fileAppender.start(); - statusChecker.assertIsErrorFree(); - - } - - @Test - public void appenderStopShouldClearEntryInCollisionMap() { - String key = "FA"; - FileAppender fileAppender = buildFileAppender(key, "collisionImpossibleForSingleAppender"); - fileAppender.start(); - assertCollisionMapHasEntry(FA_FILENAME_COLLISION_MAP, key); - fileAppender.stop(); - assertCollisionMapHasNoEntry(FA_FILENAME_COLLISION_MAP, key); - statusChecker.assertIsErrorFree(); - - - } - - private void assertCollisionMapHasEntry(String mapName, String key) { - @SuppressWarnings("unchecked") - Map map = (Map) context.getObject(mapName); - Assert.assertNotNull(map); - Assert.assertNotNull(map.get(key)); - } - private void assertCollisionMapHasNoEntry(String mapName, String key) { - @SuppressWarnings("unchecked") - Map map = (Map) context.getObject(mapName); - Assert.assertNotNull(map); - Assert.assertNull(map.get(key)); - } - - @Test - public void collisionWithTwoFileAppenders() { - String suffix = "collisionWithToFileAppenders"; - - FileAppender fileAppender1 = buildFileAppender("FA1", suffix); - fileAppender1.start(); - - FileAppender fileAppender2 = buildFileAppender("FA2", suffix); - fileAppender2.start(); - statusChecker.assertContainsMatch(Status.ERROR, "'File' option has the same value"); - //StatusPrinter.print(context); - } - - @Test - public void collisionWith_FA_RFA() { - String suffix = "collisionWith_FA_RFA"; - - FileAppender fileAppender1 = buildFileAppender("FA", suffix); - fileAppender1.start(); - - RollingFileAppender rollingfileAppender = buildRollingFileAppender("RFA", suffix, "bla-%d.log"); - rollingfileAppender.start(); - StatusPrinter.print(context); - statusChecker.assertContainsMatch(Status.ERROR, "'File' option has the same value"); - } - - @Test - public void collisionWith_2RFA() { - String suffix = "collisionWith_2RFA"; - - RollingFileAppender rollingfileAppender1 = buildRollingFileAppender("RFA1", suffix, "bla-%d.log"); - rollingfileAppender1.start(); - RollingFileAppender rollingfileAppender2 = buildRollingFileAppender("RFA1", suffix, "bla-%d.log"); - rollingfileAppender2.start(); - - StatusPrinter.print(context); - statusChecker.assertContainsMatch(Status.ERROR, "'FileNamePattern' option has the same value"); - } - -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/ConfigParameters.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/ConfigParameters.java index 02cac7f333..20ee2eaaf1 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/ConfigParameters.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/ConfigParameters.java @@ -1,3 +1,17 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.rolling; class ConfigParameters { @@ -10,7 +24,7 @@ class ConfigParameters { String fileNamePattern; long periodDurationInMillis = TimeBasedRollingWithArchiveRemoval_Test.MILLIS_IN_DAY; long sizeCap; - + ConfigParameters(long simulatedTime) { this.simulatedTime = simulatedTime; } @@ -44,7 +58,7 @@ ConfigParameters periodDurationInMillis(long periodDurationInMillis) { this.periodDurationInMillis = periodDurationInMillis; return this; } - + ConfigParameters sizeCap(long sizeCap) { this.sizeCap = sizeCap; return this; diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/DefaultRolloverChecker.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/DefaultRolloverChecker.java index 9df027cd2c..79047ee7b5 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/DefaultRolloverChecker.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/DefaultRolloverChecker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -19,7 +19,7 @@ import java.io.IOException; import java.util.List; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertTrue; public class DefaultRolloverChecker implements RolloverChecker { @@ -39,7 +39,8 @@ public void check(List expectedFilenameList) throws IOException { for (String fn : expectedFilenameList) { String suffix = withCompression ? addGZIfNotLast(expectedFilenameList, i, compressionSuffix) : ""; - String witnessFileName = CoreTestConstants.TEST_SRC_PREFIX + "witness/rolling/tbr-" + testId + "." + i + suffix; + String witnessFileName = CoreTestConstants.TEST_SRC_PREFIX + "witness/rolling/tbr-" + testId + "." + i + + suffix; assertTrue(Compare.compare(fn, witnessFileName)); i++; } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/FileMatchFunction.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/FileMatchFunction.java index 314a3d932b..c879647cc3 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/FileMatchFunction.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/FileMatchFunction.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,11 +16,8 @@ import java.io.File; /** - * Created with IntelliJ IDEA. - * User: ceki - * Date: 4/3/13 - * Time: 7:42 PM - * To change this template use File | Settings | File Templates. + * Created with IntelliJ IDEA. User: ceki Date: 4/3/13 Time: 7:42 PM To change + * this template use File | Settings | File Templates. */ public interface FileMatchFunction { diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/FileOpener.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/FileOpener.java index 0fd3a99ba2..af58c603b9 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/FileOpener.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/FileOpener.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -17,8 +17,9 @@ import java.io.InputStream; /** - * Keep the file "output/test.log open for 10 seconds so that we can test + * Keep the file "output/test.log" open for 10 seconds so that we can test * RollingFileAppender's ability to roll file open by another process. + * * @author Ceki Gülcü */ public class FileOpener { diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/JVMExitBeforeCompressionISDoneTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/JVMExitBeforeCompressionISDoneTest.java index e4da426572..6018800005 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/JVMExitBeforeCompressionISDoneTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/JVMExitBeforeCompressionISDoneTest.java @@ -1,11 +1,30 @@ +/* + * Logback: the reliable, generic, fast and flexible logging framework. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. + * + * This program and the accompanying materials are dual-licensed under + * either the terms of the Eclipse Public License v2.0 as published by + * the Eclipse Foundation + * + * or (per the licensee's choosing) + * + * under the terms of the GNU Lesser General Public License version 2.1 + * as published by the Free Software Foundation. + */ + package ch.qos.logback.core.rolling; +import java.net.URL; +import java.net.URLClassLoader; import java.util.Date; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import ch.qos.logback.core.Context; +import ch.qos.logback.core.hook.ShutdownHook; +import ch.qos.logback.core.hook.ShutdownHookBase; +import ch.qos.logback.core.status.Status; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import ch.qos.logback.core.CoreConstants; import ch.qos.logback.core.encoder.EchoEncoder; @@ -15,23 +34,32 @@ import ch.qos.logback.core.testUtil.RandomUtil; import ch.qos.logback.core.util.StatusListenerConfigHelper; import ch.qos.logback.core.util.StatusPrinter; -@Ignore +import org.junit.jupiter.api.Test; + +@Disabled +/** + * This test is disabled because it is intended to be run manually as it is difficult + * to unit test shutdown hooks. + * + * To run this test, enable it and execute it as a JUnit test. Observe the + * console output to see if the compression completes before the JVM exits. + */ public class JVMExitBeforeCompressionISDoneTest extends ScaffoldingForRollingTests { RollingFileAppender rfa = new RollingFileAppender(); TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy(); - DefaultShutdownHook delayingShutdownHook = new DefaultShutdownHook(); - + ShutdownHook shutdownHook = new DefaultShutdownHook(); + static final long FRI_2016_05_13_T_170415_GMT = 1463159055630L; - + EchoEncoder encoder = new EchoEncoder(); - @Before + @BeforeEach @Override public void setUp() { super.setUp(); StatusListenerConfigHelper.addOnConsoleListenerInstance(context, new OnConsoleStatusListener()); - delayingShutdownHook.setContext(context); + shutdownHook.setContext(context); initRFA(rfa); } @@ -40,7 +68,8 @@ void initRFA(RollingFileAppender rfa) { rfa.setEncoder(encoder); } - void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, String filenamePattern, long givenTime) { + void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, String filenamePattern, + long givenTime) { tbrp.setContext(context); tbrp.setFileNamePattern(filenamePattern); tbrp.setParent(rfa); @@ -51,32 +80,33 @@ void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tb rfa.start(); } - @After + @AfterEach public void tearDown() throws Exception { - StatusPrinter.print(context); + //StatusPrinter.print(context); } - @Ignore + @Disabled @Test public void test1() { - Thread shutdownThread = new Thread(delayingShutdownHook); + Thread shutdownThread = new Thread(shutdownHook); Runtime.getRuntime().addShutdownHook(shutdownThread); - + String patternPrefix = "test1"; String compressionSuffix = ".zip"; this.currentTime = FRI_2016_05_13_T_170415_GMT; - - Date d = new Date(FRI_2016_05_13_T_170415_GMT); //WED_2016_03_23_T_230705_CET); + + Date d = new Date(FRI_2016_05_13_T_170415_GMT); // WED_2016_03_23_T_230705_CET); System.out.println(d); System.out.print(d.getTime()); - + int ticksPerHour = 100; int hours = 7; - int totalTicks = ticksPerHour*hours; - long singleTickDuration = CoreConstants.MILLIS_IN_ONE_HOUR/ticksPerHour; - - String fileNamePatternStr = randomOutputDir + patternPrefix + "-%d{" + DATE_PATTERN_BY_DAY + ", GMT}" + compressionSuffix; + int totalTicks = ticksPerHour * hours; + long singleTickDuration = CoreConstants.MILLIS_IN_ONE_HOUR / ticksPerHour; + + String fileNamePatternStr = randomOutputDir + patternPrefix + "-%d{" + DATE_PATTERN_BY_DAY + ", GMT}" + + compressionSuffix; initTRBP(rfa, tbrp, fileNamePatternStr, currentTime); incCurrentTime(singleTickDuration); @@ -85,24 +115,23 @@ public void test1() { for (int i = 0; i < totalTicks; i++) { StringBuilder sb = new StringBuilder(1000); sb.append("Hello"); - for(int j = 0; j < 100; j++) { + for (int j = 0; j < 100; j++) { sb.append(RandomUtil.getPositiveInt()); } sb.append(i); - + rfa.doAppend(sb.toString()); addExpectedFileNamedIfItsTime_ByDate(fileNamePatternStr); incCurrentTime(singleTickDuration); tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime); } - - - - // String nameOfExpectedZipFile = randomOutputDir + patternPrefix+"-2016-05-13.zip";; - + // String nameOfExpectedZipFile = randomOutputDir + + // patternPrefix+"-2016-05-13.zip";; + // File expectedZipFile = new File(nameOfExpectedZipFile); - // assertTrue("expecting file ["+nameOfExpectedZipFile+"] to exist", expectedZipFile.exists()); + // assertTrue("expecting file ["+nameOfExpectedZipFile+"] to exist", + // expectedZipFile.exists()); // File[] files = getFilesInDirectory(randomOutputDir); // assertEquals(2, files.length); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/MultiThreadedRollingTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/MultiThreadedRollingTest.java index 867b88f091..62b5fe0513 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/MultiThreadedRollingTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/MultiThreadedRollingTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -15,8 +15,6 @@ import static ch.qos.logback.core.testUtil.CoreTestConstants.FAILURE_EXIT_CODE; import static ch.qos.logback.core.testUtil.CoreTestConstants.SUCCESSFUL_EXIT_CODE; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; import java.io.File; import java.io.FileOutputStream; @@ -27,18 +25,19 @@ import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.EnvUtilForTests; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.contention.MultiThreadedHarness; -import ch.qos.logback.core.contention.RunnableWithCounterAndDone; +import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.encoder.Encoder; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.FileSize; import ch.qos.logback.core.util.StatusPrinter; @@ -61,12 +60,14 @@ public class MultiThreadedRollingTest { String pathToBash = EnvUtilForTests.getPathToBash(); OutputStream scriptOS; - @Before + @BeforeEach public void setUp() throws Exception { encoder = new EchoEncoder(); File outputDir = new File(outputDirStr); - outputDir.mkdirs(); - + boolean result = outputDir.mkdirs(); + if(!result) { + System.out.println("Failed to create folder "+outputDirStr); + } System.out.println("Output dir [" + outputDirStr + "]"); scriptOS = openScript(); @@ -87,7 +88,7 @@ void close(OutputStream os) { } } - @After + @AfterEach public void tearDown() throws Exception { rfa.stop(); } @@ -170,7 +171,7 @@ void verify() throws IOException, InterruptedException { process.waitFor(); int exitCode = process.exitValue(); - assertEquals(SUCCESSFUL_EXIT_CODE, exitCode); + Assertions.assertEquals(SUCCESSFUL_EXIT_CODE, exitCode); System.out.println("External script based verification returned with exit code " + exitCode); } @@ -251,7 +252,7 @@ private void executeHarness(int duration, boolean withDelay) throws InterruptedE StatusChecker checker = new StatusChecker(context.getStatusManager()); if (!checker.isErrorFree(0)) { StatusPrinter.print(context); - fail("errors reported"); + Assertions.fail("errors reported"); } } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/PackageTest.java deleted file mode 100755 index df9b6a0b2f..0000000000 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/PackageTest.java +++ /dev/null @@ -1,24 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package ch.qos.logback.core.rolling; - -import org.junit.runner.RunWith; -import org.junit.runners.Suite; - -@RunWith(Suite.class) -@Suite.SuiteClasses({ RenameUtilTest.class, SizeBasedRollingTest.class, TimeBasedRollingTest.class, TimeBasedRollingWithArchiveRemoval_Test.class, - MultiThreadedRollingTest.class, SizeAndTimeBasedFNATP_Test.class, RollingFileAppenderTest.class, - CollisionDetectionTest.class, ch.qos.logback.core.rolling.helper.PackageTest.class }) -public class PackageTest { -} diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/RenameUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/RenameUtilTest.java index 6f71c4db25..0e63bf39a3 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/RenameUtilTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/RenameUtilTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -20,37 +20,41 @@ import ch.qos.logback.core.rolling.helper.RenameUtil; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; +import ch.qos.logback.core.util.EnvUtil; import ch.qos.logback.core.util.StatusPrinter; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; +import ch.qos.logback.core.util.StatusPrinter2; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; +import java.nio.channels.FileLock; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; public class RenameUtilTest { Encoder encoder; Context context = new ContextBase(); StatusChecker statusChecker = new StatusChecker(context); + StatusPrinter2 statusPrinter2 = new StatusPrinter2(); long currentTime = System.currentTimeMillis(); int diff = RandomUtil.getPositiveInt(); protected String randomOutputDirAsStr = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; protected File randomOutputDir = new File(randomOutputDirAsStr); - @Before + @BeforeEach public void setUp() throws Exception { encoder = new EchoEncoder(); - // if this this the fist test run after 'build clean up' then the + // if this the fist test run after 'build clean up' then the // OUTPUT_DIR_PREFIX might be not yet created randomOutputDir.mkdirs(); } @@ -66,34 +70,42 @@ public void renameToNonExistingDirectory() throws IOException, RolloverFailure { String randomTARGETDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff2; renameUtil.rename(fromFile.toString(), new File(randomTARGETDir + "/to.test").toString()); - StatusPrinter.printInCaseOfErrorsOrWarnings(context); + statusPrinter2.printInCaseOfErrorsOrWarnings(context); assertTrue(statusChecker.isErrorFree(0)); } - - @Test // LOGBACK-1054 - public void renameLockedAbstractFile_LOGBACK_1054 () throws IOException, RolloverFailure { + @Test // LOGBACK-1054 + public void renameLockedAbstractFile_LOGBACK_1054() throws IOException, RolloverFailure { + + // this tests only works on windows + if(!EnvUtil.isWindows()) { + return; + } + RenameUtil renameUtil = new RenameUtil(); renameUtil.setContext(context); - String abstractFileName = "abstract_pathname-"+diff; - - String src = CoreTestConstants.OUTPUT_DIR_PREFIX+abstractFileName; + String abstractFileName = "abstract_pathname-" + diff; + + String src = CoreTestConstants.OUTPUT_DIR_PREFIX + abstractFileName; String target = abstractFileName + ".target"; - + makeFile(src); - - FileInputStream fisLock = new FileInputStream(src); - renameUtil.rename(src, target); + + // open file in a way preventing simple rename in order to force call to + // areOnDifferentVolumes() method. This only works on Windows + + FileInputStream fis = new FileInputStream(src); + renameUtil.rename(src, target); // release the lock - fisLock.close(); - - StatusPrinter.print(context); - assertEquals(0, statusChecker.matchCount("Parent of target file ."+target+". is null")); + fis.close(); + + statusPrinter2.print(context); + assertEquals(0, statusChecker.matchCount("Parent of target file ." + target + ". is null")); } @Test - @Ignore + @Disabled public void MANUAL_renamingOnDifferentVolumesOnLinux() throws IOException, RolloverFailure { RenameUtil renameUtil = new RenameUtil(); renameUtil.setContext(context); @@ -102,30 +114,28 @@ public void MANUAL_renamingOnDifferentVolumesOnLinux() throws IOException, Rollo makeFile(src); renameUtil.rename(src, "/tmp/foo" + diff + ".txt"); - StatusPrinter.print(context); + statusPrinter2.print(context); } - @Test - @Ignore + @Disabled public void MANUAL_renamingOnDifferentVolumesOnWindows() throws IOException, RolloverFailure { RenameUtil renameUtil = new RenameUtil(); renameUtil.setContext(context); - String src = "c:/tmp/foo.txt"; + String src = "c:/tmp/foo.txt"; makeFile(src); - + renameUtil.rename(src, "d:/tmp/foo" + diff + ".txt"); - StatusPrinter.print(context); + statusPrinter2.print(context); assertTrue(statusChecker.isErrorFree(0)); } private void makeFile(String src) throws FileNotFoundException, IOException { - + FileOutputStream fos = new FileOutputStream(src); fos.write(("hello" + diff).getBytes()); fos.close(); } - } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/RollingFileAppenderTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/RollingFileAppenderTest.java index df4e3dfb37..dd668f5d73 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/RollingFileAppenderTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/RollingFileAppenderTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,25 +13,33 @@ */ package ch.qos.logback.core.rolling; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.FileSize; +import ch.qos.logback.core.util.FileUtil; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Appender; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.appender.AbstractAppenderTest; -import ch.qos.logback.core.encoder.DummyEncoder; +import ch.qos.logback.core.testUtil.DummyEncoder; import ch.qos.logback.core.status.Status; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.testUtil.RandomUtil; -import ch.qos.logback.core.testUtil.StatusChecker; -import ch.qos.logback.core.util.StatusPrinter; +import ch.qos.logback.core.status.testUtil.StatusChecker; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.Collections; +import java.util.List; +//import ch.qos.logback.core.util.StatusPrinter; public class RollingFileAppenderTest extends AbstractAppenderTest { @@ -41,19 +49,20 @@ public class RollingFileAppenderTest extends AbstractAppenderTest { TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy(); int diff = RandomUtil.getPositiveInt(); String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/"; + DummyEncoder encoder; - @Before + @BeforeEach public void setUp() throws Exception { // noStartTest fails if the context is set in setUp // rfa.setContext(context); - - rfa.setEncoder(new DummyEncoder()); + encoder = new DummyEncoder<>(); + rfa.setEncoder(encoder); rfa.setName("test"); tbrp.setContext(context); tbrp.setParent(rfa); } - @After + @AfterEach public void tearDown() throws Exception { } @@ -87,9 +96,9 @@ public void testPrudentModeLogicalImplications() { rfa.start(); - assertTrue(rfa.isAppend()); - assertNull(rfa.rawFileProperty()); - assertTrue(rfa.isStarted()); + Assertions.assertTrue(rfa.isAppend()); + Assertions.assertNull(rfa.rawFileProperty()); + Assertions.assertTrue(rfa.isStarted()); } @Test @@ -105,8 +114,8 @@ public void testPrudentModeLogicalImplicationsOnCompression() { rfa.start(); StatusChecker checker = new StatusChecker(context); - assertFalse(rfa.isStarted()); - assertEquals(Status.ERROR, checker.getHighestLevel(0)); + Assertions.assertFalse(rfa.isStarted()); + Assertions.assertEquals(Status.ERROR, checker.getHighestLevel(0)); } @Test @@ -114,7 +123,7 @@ public void testFilePropertyAfterRollingPolicy() { rfa.setContext(context); rfa.setRollingPolicy(tbrp); rfa.setFile("x"); - StatusPrinter.print(context); + // StatusPrinter.print(context); StatusChecker statusChecker = new StatusChecker(context.getStatusManager()); statusChecker.assertContainsMatch(Status.ERROR, "File property must be set before any triggeringPolicy "); } @@ -131,7 +140,8 @@ public void testFilePropertyAfterTriggeringPolicy() { @Test public void testFileNameWithParenthesis() { // if ')' is not escaped, the test throws - // java.lang.IllegalStateException: FileNamePattern [.../program(x86)/toto-%d.log] does not contain a valid + // java.lang.IllegalStateException: FileNamePattern + // [.../program(x86)/toto-%d.log] does not contain a valid // DateToken rfa.setContext(context); tbrp.setFileNamePattern(randomOutputDir + "program(x86)/toto-%d.log"); @@ -150,12 +160,12 @@ public void stopTimeBasedRollingPolicy() { rfa.setRollingPolicy(tbrp); rfa.start(); - StatusPrinter.print(context); - assertTrue(tbrp.isStarted()); - assertTrue(rfa.isStarted()); + //StatusPrinter.print(context); + Assertions.assertTrue(tbrp.isStarted()); + Assertions.assertTrue(rfa.isStarted()); rfa.stop(); - assertFalse(rfa.isStarted()); - assertFalse(tbrp.isStarted()); + Assertions.assertFalse(rfa.isStarted()); + Assertions.assertFalse(tbrp.isStarted()); } @@ -178,14 +188,14 @@ public void stopFixedWindowRollingPolicy() { rfa.start(); - StatusPrinter.print(context); - assertTrue(fwRollingPolicy.isStarted()); - assertTrue(sbTriggeringPolicy.isStarted()); - assertTrue(rfa.isStarted()); + // StatusPrinter.print(context); + Assertions.assertTrue(fwRollingPolicy.isStarted()); + Assertions.assertTrue(sbTriggeringPolicy.isStarted()); + Assertions.assertTrue(rfa.isStarted()); rfa.stop(); - assertFalse(rfa.isStarted()); - assertFalse(fwRollingPolicy.isStarted()); - assertFalse(sbTriggeringPolicy.isStarted()); + Assertions.assertFalse(rfa.isStarted()); + Assertions.assertFalse(fwRollingPolicy.isStarted()); + Assertions.assertFalse(sbTriggeringPolicy.isStarted()); } @@ -204,7 +214,7 @@ public void testFileShouldNotMatchFileNamePattern() { StatusChecker statusChecker = new StatusChecker(context); final String msg = "File property collides with fileNamePattern. Aborting."; boolean containsMatch = statusChecker.containsMatch(Status.ERROR, msg); - assertTrue("Missing error: " + msg, containsMatch); + Assertions.assertTrue(containsMatch, "Missing error: " + msg); } @Test @@ -220,47 +230,63 @@ public void collidingTimeformat() { rfa.start(); StatusChecker checker = new StatusChecker(context); - assertFalse(rfa.isStarted()); - assertEquals(Status.ERROR, checker.getHighestLevel(0)); - StatusPrinter.print(context); + Assertions.assertFalse(rfa.isStarted()); + Assertions.assertEquals(Status.ERROR, checker.getHighestLevel(0)); + // StatusPrinter.print(context); checker.assertContainsMatch("The date format in FileNamePattern will result"); } @Test - public void collidingFileNamePattern() { - String filenamePattern = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "-collision-%d.log.zip"; - - RollingFileAppender appender0 = new RollingFileAppender(); - appender0.setName("FA0"); - appender0.setContext(context); - appender0.setEncoder(new DummyEncoder()); - TimeBasedRollingPolicy tbrp0 = new TimeBasedRollingPolicy(); - tbrp0.setContext(context); - tbrp0.setFileNamePattern(filenamePattern); - tbrp0.setParent(appender0); - tbrp0.start(); - appender0.setRollingPolicy(tbrp0); - appender0.start(); - assertTrue(appender0.isStarted()); - - RollingFileAppender appender1 = new RollingFileAppender(); - appender1.setName("FA1"); - appender1.setFile("X"); - appender1.setContext(context); - appender1.setEncoder(new DummyEncoder()); - TimeBasedRollingPolicy tbrp1 = new TimeBasedRollingPolicy(); - tbrp1.setContext(context); - tbrp1.setFileNamePattern(filenamePattern); - tbrp1.setParent(appender1); - tbrp1.start(); - appender1.setRollingPolicy(tbrp1); - appender1.start(); + @DisplayName("Checks header and footer are written when the files are rolled") + public void testHeaderFooterWritten() throws IOException, InterruptedException { - // StatusPrinter.print(context); + String folderPrefix = CoreTestConstants.OUTPUT_DIR_PREFIX+diff+"/"; + String namePrefix = folderPrefix+"header-"; + File folderFile = new File(folderPrefix); + FileUtil.createMissingParentDirectories(folderFile); - assertFalse(appender1.isStarted()); - StatusChecker checker = new StatusChecker(context); - checker.assertContainsMatch(Status.ERROR, "'FileNamePattern' option has the same value"); + + encoder.setFileHeader("HEADER"); + encoder.setFileFooter("FOOTER"); + rfa.setContext(context); + FixedWindowRollingPolicy fixedWindowRollingPolicy = new FixedWindowRollingPolicy(); + fixedWindowRollingPolicy.setContext(context); + fixedWindowRollingPolicy.setParent(rfa); + fixedWindowRollingPolicy.setMaxIndex(3); + String fileNamePattern = namePrefix + "%i.log"; + fixedWindowRollingPolicy.setFileNamePattern(fileNamePattern); + rfa.setRollingPolicy(fixedWindowRollingPolicy); + rfa.setFile(namePrefix+"0.log"); + fixedWindowRollingPolicy.start(); + rfa.setImmediateFlush(true); + SizeBasedTriggeringPolicy sbtp = new SizeBasedTriggeringPolicy<>(); + sbtp.setMaxFileSize(new FileSize(10)); + sbtp.setCheckIncrement(Duration.buildByMilliseconds(10)); + + rfa.setTriggeringPolicy(sbtp); + rfa.getTriggeringPolicy().start(); + rfa.start(); + + for (int i = 0; i < 100; i++) { + rfa.doAppend("data" + i); + File file = new File(namePrefix + fixedWindowRollingPolicy.getMaxIndex() + ".log"); + if (file.exists()) { + break; + } + Thread.sleep(5); + } + rfa.stop(); + + for (int i = 0; i < fixedWindowRollingPolicy.getMaxIndex(); i++) { + File file = new File(namePrefix + i + ".log"); + Assertions.assertTrue(file.exists()); + List lines = Files.readAllLines(file.toPath()); + Assertions.assertTrue(lines.size() > 2, "At least 2 lines per file are expected in " + file); + Assertions.assertEquals("HEADER", lines.get(0)); + Assertions.assertEquals("FOOTER", lines.get(lines.size() - 1)); + Assertions.assertEquals(1, Collections.frequency(lines, "HEADER")); + Assertions.assertEquals(1, Collections.frequency(lines, "FOOTER")); + } } - + } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/RolloverChecker.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/RolloverChecker.java index a674cb1a6e..1880f6e3e9 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/RolloverChecker.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/RolloverChecker.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_Test.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_Test.java index 21650d2e54..7c725dd7cf 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_Test.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_Test.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,26 +13,35 @@ */ package ch.qos.logback.core.rolling; -import static org.junit.Assert.assertFalse; - import java.io.File; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; import java.util.Date; import java.util.List; import java.util.concurrent.ExecutionException; +import java.util.function.UnaryOperator; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.CoreConstants; +import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests; +import ch.qos.logback.core.util.CachingDateFormatter; +import ch.qos.logback.core.util.Duration; +import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.encoder.EchoEncoder; import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; import ch.qos.logback.core.status.InfoStatus; import ch.qos.logback.core.status.StatusManager; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; import ch.qos.logback.core.util.FileSize; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + public class SizeAndTimeBasedFNATP_Test extends ScaffoldingForRollingTests { - private SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = null; + private SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFNATP = null; private RollingFileAppender rfa1 = new RollingFileAppender(); private TimeBasedRollingPolicy tbrp1 = new TimeBasedRollingPolicy(); private RollingFileAppender rfa2 = new RollingFileAppender(); @@ -43,7 +52,7 @@ public class SizeAndTimeBasedFNATP_Test extends ScaffoldingForRollingTests { int fileIndexCounter = 0; int sizeThreshold = 0; - @Before + @BeforeEach public void setUp() { super.setUp(); } @@ -56,9 +65,11 @@ private void initRollingFileAppender(RollingFileAppender rfa, String fil } } - private void initPolicies(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, String filenamePattern, int sizeThreshold, long givenTime, - long lastCheck) { - sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP(); + private void initPolicies(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, + String filenamePattern, int sizeThreshold, long givenTime, long lastCheck) { + sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy(); + sizeAndTimeBasedFNATP.setContext(context); + sizeAndTimeBasedFNATP.setCheckIncrement(Duration.buildByMilliseconds(10)); tbrp.setContext(context); sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(sizeThreshold)); tbrp.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP); @@ -70,31 +81,38 @@ private void initPolicies(RollingFileAppender rfa, TimeBasedRollingPolic rfa.start(); } - private void addExpectedFileNamedIfItsTime(String randomOutputDir, String testId, String msg, String compressionSuffix) { + private void addExpectedFileNamedIfItsTime(String randomOutputDir, String testId, String msg, + String compressionSuffix) { fileSize = fileSize + msg.getBytes().length; if (passThresholdTime(nextRolloverThreshold)) { fileIndexCounter = 0; fileSize = 0; - addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), fileIndexCounter, compressionSuffix); + addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), + fileIndexCounter, compressionSuffix); recomputeRolloverThreshold(currentTime); return; } // windows can delay file size changes, so we only allow for fileIndexCounter 0 if ((fileIndexCounter == 0) && fileSize > sizeThreshold) { - addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), fileIndexCounter, compressionSuffix); + addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), + fileIndexCounter, compressionSuffix); fileIndexCounter = fileIndexCounter + 1; fileSize = 0; } } - void generic(String testId, String stem, boolean withSecondPhase, String compressionSuffix) throws IOException, InterruptedException, ExecutionException { - String file = (stem != null) ? randomOutputDir + stem : null; + void generic(String testId, UnaryOperator filenameFunction, boolean withSecondPhase, + String compressionSuffix) throws IOException, InterruptedException, ExecutionException { + String file = filenameFunction.apply(testId); initRollingFileAppender(rfa1, file); sizeThreshold = 300; - initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix, sizeThreshold, currentTime, 0); - addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), fileIndexCounter, compressionSuffix); + initPolicies(rfa1, tbrp1, + randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix, + sizeThreshold, currentTime, 0); + addExpectedFileName_ByFileIndexCounter(randomOutputDir, testId, getMillisOfCurrentPeriodsStart(), + fileIndexCounter, compressionSuffix); incCurrentTime(100); tbrp1.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(currentTime); int runLength = 100; @@ -111,27 +129,29 @@ void generic(String testId, String stem, boolean withSecondPhase, String compres } if (withSecondPhase) { - secondPhase(testId, file, stem, compressionSuffix, runLength, prefix); + secondPhase(testId, filenameFunction, compressionSuffix, runLength, prefix); runLength = runLength * 2; } - if (stem != null) - massageExpectedFilesToCorresponToCurrentTarget(file, true); + if (file != null) + massageExpectedFilesToCorresponToCurrentTarget(testId, this::testId2FileName); Thread.yield(); // wait for compression to finish waitForJobsToComplete(); - // StatusPrinter.print(context); + //StatusPrinter.print(context); existenceCheck(expectedFilenameList); sortedContentCheck(randomOutputDir, runLength, prefix); } - void secondPhase(String testId, String file, String stem, String compressionSuffix, int runLength, String prefix) { + void secondPhase(String testId, UnaryOperator filenameFunction, String compressionSuffix, int runLength, + String prefix) { rfa1.stop(); - if (stem != null) { - File f = new File(file); + String filename = filenameFunction.apply(testId); + if (filename != null) { + File f = new File(filename); f.setLastModified(currentTime); } @@ -139,8 +159,10 @@ void secondPhase(String testId, String file, String stem, String compressionSuff sm.add(new InfoStatus("Time when rfa1 is stopped: " + new Date(currentTime), this)); sm.add(new InfoStatus("currentTime%1000=" + (currentTime % 1000), this)); - initRollingFileAppender(rfa2, file); - initPolicies(rfa2, tbrp2, randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix, sizeThreshold, currentTime, 0); + initRollingFileAppender(rfa2, filename); + initPolicies(rfa2, tbrp2, + randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt" + compressionSuffix, + sizeThreshold, currentTime, 0); for (int i = runLength; i < runLength * 2; i++) { incCurrentTime(100); @@ -157,37 +179,37 @@ void secondPhase(String testId, String file, String stem, String compressionSuff @Test public void noCompression_FileSet_NoRestart_1() throws InterruptedException, ExecutionException, IOException { - generic("test1", "toto.log", FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX); + generic("test1", this::testId2FileName, FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX); } @Test public void noCompression_FileBlank_NoRestart_2() throws Exception { - generic("test2", null, FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX); + generic("test2", this::nullFileName, FIRST_PHASE_ONLY, DEFAULT_COMPRESSION_SUFFIX); } @Test public void noCompression_FileBlank_WithStopStart_3() throws Exception { - generic("test3", null, WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX); + generic("test3", this::nullFileName, WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX); } @Test public void noCompression_FileSet_WithStopStart_4() throws Exception { - generic("test4", "test4.log", WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX); + generic("test4", this::testId2FileName, WITH_SECOND_PHASE, DEFAULT_COMPRESSION_SUFFIX); } @Test public void withGZCompression_FileSet_NoRestart_5() throws Exception { - generic("test5", "toto.log", FIRST_PHASE_ONLY, ".gz"); + generic("test5", this::testId2FileName, FIRST_PHASE_ONLY, ".gz"); } @Test public void withGZCompression_FileBlank_NoRestart_6() throws Exception { - generic("test6", null, FIRST_PHASE_ONLY, ".gz"); + generic("test6", this::nullFileName, FIRST_PHASE_ONLY, ".gz"); } @Test public void withZipCompression_FileSet_NoRestart_7() throws Exception { - generic("test7", "toto.log", FIRST_PHASE_ONLY, ".zip"); + generic("test7", this::testId2FileName, FIRST_PHASE_ONLY, ".zip"); List zipFiles = filterElementsInListBySuffix(".zip"); checkZipEntryMatchesZipFilename(zipFiles); } @@ -201,7 +223,9 @@ public void checkMissingIntToken() { String file = (stem != null) ? randomOutputDir + stem : null; initRollingFileAppender(rfa1, file); sizeThreshold = 300; - initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}.txt" + compressionSuffix, sizeThreshold, currentTime, 0); + initPolicies(rfa1, tbrp1, + randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}.txt" + compressionSuffix, + sizeThreshold, currentTime, 0); // StatusPrinter.print(context); assertFalse(rfa1.isStarted()); @@ -218,7 +242,8 @@ public void checkDateCollision() { String file = (stem != null) ? randomOutputDir + stem : null; initRollingFileAppender(rfa1, file); sizeThreshold = 300; - initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{EE}.txt" + compressionSuffix, sizeThreshold, currentTime, 0); + initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{EE}.txt" + compressionSuffix, sizeThreshold, + currentTime, 0); // StatusPrinter.print(context); assertFalse(rfa1.isStarted()); @@ -226,14 +251,63 @@ public void checkDateCollision() { checker.assertContainsMatch("The date format in FileNamePattern"); } + @Test + public void checkInitialFileSize_withFile() throws IOException { + String stem = "foo.log"; + String testId = "checkDateCollision"; + String fixedContent = "Hello world"; + byte[] fixedContentBytes = fixedContent.getBytes(); + + String fileProperty = randomOutputDir + stem; + Files.createDirectories(Paths.get(randomOutputDir)); + Files.write(Paths.get(fileProperty), fixedContentBytes); + + initRollingFileAppender(rfa1, fileProperty); + sizeThreshold = 300; + initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d-%i.txt", sizeThreshold, + currentTime, 0); + + //StatusPrinter.print(context); + + assertEquals(fixedContentBytes.length, tbrp1.getLengthCounter().getLength()); + } + + + @Test + public void checkInitialFileSize_withoutFile() throws IOException { + String testId = "checkInitialFileSize_withoutFile"; + String fixedContent = "Hello world"; + byte[] fixedContentBytes = fixedContent.getBytes(); + + + CachingDateFormatter cdf = new CachingDateFormatter(CoreConstants.DAILY_DATE_PATTERN); + String nowString = cdf.format(currentTime); + String pathToFirstFile = randomOutputDir + testId + "-"+nowString+"-0.txt"; + + Files.createDirectories(Paths.get(randomOutputDir)); + Files.write(Paths.get(pathToFirstFile), fixedContentBytes); + + + initRollingFileAppender(rfa1, null); + sizeThreshold = 300; + initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d-%i.txt", sizeThreshold, + currentTime, 0); + + StatusPrinter.print(context); + + assertEquals(fixedContentBytes.length, tbrp1.getLengthCounter().getLength()); + } + // @Test // public void testHistoryAsFileCount() throws IOException { // String testId = "testHistoryAsFileCount"; // int maxHistory = 10; // initRollingFileAppender(rfa1, randomOutputDir + "~" + testId); // sizeThreshold = 50; - // System.out.println("testHistoryAsFileCount started on "+new Date(currentTime)); - // initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}-%i.txt", + // System.out.println("testHistoryAsFileCount started on "+new + // Date(currentTime)); + // initPolicies(rfa1, tbrp1, randomOutputDir + testId + "-%d{" + + // DATE_PATTERN_WITH_SECONDS + "}-%i.txt", // sizeThreshold, currentTime, 0, maxHistory, true); // // incCurrentTime(100); diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeBasedRollingTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeBasedRollingTest.java index 4bfa812b33..a2374d24b7 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeBasedRollingTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/SizeBasedRollingTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,23 +16,25 @@ import java.io.IOException; import java.util.List; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests; +import ch.qos.logback.core.util.Duration; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.encoder.EchoEncoder; -import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; import ch.qos.logback.core.testUtil.CoreTestConstants; import ch.qos.logback.core.util.FileSize; import ch.qos.logback.core.util.StatusPrinter; +public class SizeBasedRollingTest extends ParentScaffoldingForRollingTests { -public class SizeBasedRollingTest extends ScaffoldingForRollingTests { - - RollingFileAppender rfa = new RollingFileAppender(); + static public final String DATE_PATTERN_WITH_SECONDS = "yyyy-MM-dd_HH_mm_ss"; + RollingFileAppender rfa = new RollingFileAppender<>(); FixedWindowRollingPolicy fwrp = new FixedWindowRollingPolicy(); - SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy(); - EchoEncoder encoder = new EchoEncoder(); + SizeBasedTriggeringPolicy sizeBasedTriggeringPolicy = new SizeBasedTriggeringPolicy<>(); + EchoEncoder encoder = new EchoEncoder<>(); - @Before + @BeforeEach public void setUp() { super.setUp(); fwrp.setContext(context); @@ -52,21 +54,25 @@ private void initRFA(String filename) { * Test whether FixedWindowRollingPolicy throws an exception when the * ActiveFileName is not set. */ - @Test(expected = IllegalStateException.class) + @Test public void activeFileNameNotSet() { - sizeBasedTriggeringPolicy.setMaxFileSize(new FileSize(100)); - sizeBasedTriggeringPolicy.start(); - - fwrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "sizeBased-test1.%i"); - fwrp.start(); - // The absence of activeFileName option should cause an exception. + Assertions.assertThrows(IllegalStateException.class, () -> { + sizeBasedTriggeringPolicy.setMaxFileSize(new FileSize(100)); + sizeBasedTriggeringPolicy.start(); + + fwrp.setFileNamePattern(CoreTestConstants.OUTPUT_DIR_PREFIX + "sizeBased-test1.%i"); + fwrp.start(); + // The absence of activeFileName option should cause an exception. + }); } - void generic(String testName, String fileName, String filenamePattern, List expectedFilenameList) throws InterruptedException, IOException { + void generic(String testName, String fileName, String filenamePattern, List expectedFilenameList) + throws InterruptedException, IOException { rfa.setName("ROLLING"); initRFA(randomOutputDir + fileName); sizeBasedTriggeringPolicy.setMaxFileSize(new FileSize(100)); + sizeBasedTriggeringPolicy.setCheckIncrement(Duration.buildByMilliseconds(50)); fwrp.setMinIndex(0); fwrp.setFileNamePattern(randomOutputDir + filenamePattern); diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBaseTest.java index 328ba9c07c..b7d66b5fc9 100644 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBaseTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedFileNamingAndTriggeringPolicyBaseTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -13,17 +13,17 @@ */ package ch.qos.logback.core.rolling; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.util.StatusPrinter; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.Context; import ch.qos.logback.core.ContextBase; import ch.qos.logback.core.status.Status; -import ch.qos.logback.core.testUtil.StatusChecker; +import ch.qos.logback.core.status.testUtil.StatusChecker; + +import static org.junit.jupiter.api.Assertions.assertTrue; /** * @author Ceki Gülcü @@ -37,8 +37,8 @@ public class TimeBasedFileNamingAndTriggeringPolicyBaseTest { RollingFileAppender rfa = new RollingFileAppender(); TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy(); DefaultTimeBasedFileNamingAndTriggeringPolicy timeBasedFNATP = new DefaultTimeBasedFileNamingAndTriggeringPolicy(); - - @Before + StatusChecker statusChecker = new StatusChecker(context); + @BeforeEach public void setUp() { rfa.setContext(context); tbrp.setContext(context); @@ -50,23 +50,30 @@ public void setUp() { timeBasedFNATP.setTimeBasedRollingPolicy(tbrp); } + @Test + public void doublePolicySet() { + rfa.setTriggeringPolicy(new SizeBasedTriggeringPolicy<>()); + statusChecker.assertContainsMatch(Status.WARN, "A triggering policy of type " ); + } + @Test public void singleDate() { // Tuesday December 20th 17:59:01 CET 2011 long startTime = 1324400341553L; tbrp.setFileNamePattern("foo-%d{yyyy-MM'T'mm}.log"); - tbrp.start(); - timeBasedFNATP.setCurrentTime(startTime); - timeBasedFNATP.start(); + tbrp.start(); timeBasedFNATP.setCurrentTime(startTime + MILLIS_IN_MINUTE); - timeBasedFNATP.isTriggeringEvent(null, null); + boolean result = timeBasedFNATP.isTriggeringEvent(null, null); + StatusPrinter.print(context); + assertTrue(result); String elapsedPeriodsFileName = timeBasedFNATP.getElapsedPeriodsFileName(); - assertEquals("foo-2011-12T59.log", elapsedPeriodsFileName); + Assertions.assertEquals("foo-2011-12T59.log", elapsedPeriodsFileName); } - // see "log rollover should be configurable using %d multiple times in file name pattern" + // see "log rollover should be configurable using %d multiple times in file name + // pattern" // http://jira.qos.ch/browse/LBCORE-242 @Test @@ -83,7 +90,7 @@ public void multiDate() { boolean triggerred = timeBasedFNATP.isTriggeringEvent(null, null); assertTrue(triggerred); String elapsedPeriodsFileName = timeBasedFNATP.getElapsedPeriodsFileName(); - assertEquals("foo-2011-12/59.log", elapsedPeriodsFileName); + Assertions.assertEquals("foo-2011-12/59.log", elapsedPeriodsFileName); } @Test @@ -100,7 +107,7 @@ public void withTimeZone() { boolean triggerred = timeBasedFNATP.isTriggeringEvent(null, null); assertTrue(triggerred); String elapsedPeriodsFileName = timeBasedFNATP.getElapsedPeriodsFileName(); - assertEquals("foo-2011-12-20.log", elapsedPeriodsFileName); + Assertions.assertEquals("foo-2011-12-20.log", elapsedPeriodsFileName); } @Test @@ -108,8 +115,8 @@ public void extraIntegerTokenInFileNamePatternShouldBeDetected() { String pattern = "test-%d{yyyy-MM-dd'T'HH}-%i.log.zip"; tbrp.setFileNamePattern(pattern); tbrp.start(); - - assertFalse(tbrp.isStarted()); + + Assertions.assertFalse(tbrp.isStarted()); StatusChecker statusChecker = new StatusChecker(context); statusChecker.assertContainsMatch(Status.ERROR, "Filename pattern .{37} contains an integer token converter"); } diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingTest.java index e72ff5e821..cd3eef3c41 100755 --- a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingTest.java +++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingTest.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) @@ -16,13 +16,15 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; +import java.util.function.UnaryOperator; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; +import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests; +import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; import ch.qos.logback.core.encoder.EchoEncoder; -import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests; import ch.qos.logback.core.testUtil.EnvUtilForTests; import ch.qos.logback.core.util.StatusPrinter; @@ -35,6 +37,7 @@ * predict the names of the files which should be generated and compare them * with witness files. *

+ * *

  *                Compression     file option    Stop/Restart
  *     Test1      NO              BLANK           NO
@@ -49,6 +52,7 @@
  */
 public class TimeBasedRollingTest extends ScaffoldingForRollingTests {
 
+    static public final String DATE_PATTERN_WITH_SECONDS = "yyyy-MM-dd_HH_mm_ss";
     static final int NO_RESTART = 0;
     static final int WITH_RESTART = 1;
     static final int WITH_RESTART_AND_LONG_WAIT = 2000;
@@ -66,13 +70,13 @@ public class TimeBasedRollingTest extends ScaffoldingForRollingTests {
 
     RolloverChecker rolloverChecker;
 
-    @Before
+    @BeforeEach
     @Override
     public void setUp() {
         super.setUp();
     }
 
-    @After
+    @AfterEach
     public void tearDown() {
     }
 
@@ -84,7 +88,8 @@ void initRFA(RollingFileAppender rfa, String filename) {
         }
     }
 
-    void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, String filenamePattern, long givenTime) {
+    void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tbrp, String filenamePattern,
+            long givenTime) {
         tbrp.setContext(context);
         tbrp.setFileNamePattern(filenamePattern);
         tbrp.setParent(rfa);
@@ -95,11 +100,16 @@ void initTRBP(RollingFileAppender rfa, TimeBasedRollingPolicy tb
         rfa.start();
     }
 
-    void genericTest(String testId, String patternPrefix, String compressionSuffix, boolean fileOptionIsSet, int waitDuration) throws IOException {
-        String fileName = fileOptionIsSet ? testId2FileName(testId) : null;
+    void genericTest(String testId, String patternPrefix, String compressionSuffix,
+            UnaryOperator filenameFunction, int waitDuration) throws IOException {
+
+        String fileName = filenameFunction.apply(testId);
+        // String fileName = fileOptionIsSet ? testId2FileName(testId) : null;
+
         initRFA(rfa1, fileName);
 
-        String fileNamePatternStr = randomOutputDir + patternPrefix + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}" + compressionSuffix;
+        String fileNamePatternStr = randomOutputDir + patternPrefix + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}"
+                + compressionSuffix;
 
         initTRBP(rfa1, tbrp1, fileNamePatternStr, currentTime);
 
@@ -121,22 +131,23 @@ void genericTest(String testId, String patternPrefix, String compressionSuffix,
         waitForJobsToComplete();
 
         if (waitDuration != NO_RESTART) {
-            doRestart(testId, patternPrefix, fileOptionIsSet, waitDuration);
+            doRestart(testId, patternPrefix, filenameFunction, waitDuration);
         }
         waitForJobsToComplete();
 
-        massageExpectedFilesToCorresponToCurrentTarget(fileName, fileOptionIsSet);
-        StatusPrinter.print(context);
+        massageExpectedFilesToCorresponToCurrentTarget(testId, filenameFunction);
+        // StatusPrinter.print(context);
         rolloverChecker.check(expectedFilenameList);
     }
 
-    void defaultTest(String testId, String patternPrefix, String compressionSuffix, boolean fileOptionIsSet, int waitDuration) throws IOException {
+    void defaultTest(String testId, String patternPrefix, String compressionSuffix,
+            UnaryOperator filenameFunction, int waitDuration) throws IOException {
         boolean withCompression = compressionSuffix.length() > 0;
         rolloverChecker = new DefaultRolloverChecker(testId, withCompression, compressionSuffix);
-        genericTest(testId, patternPrefix, compressionSuffix, fileOptionIsSet, waitDuration);
+        genericTest(testId, patternPrefix, compressionSuffix, filenameFunction, waitDuration);
     }
 
-    void doRestart(String testId, String patternPart, boolean fileOptionIsSet, int waitDuration) {
+    void doRestart(String testId, String patternPart, UnaryOperator filenameFunction, int waitDuration) {
         // change the timestamp of the currently actively file
         File activeFile = new File(rfa1.getFile());
         activeFile.setLastModified(currentTime);
@@ -145,7 +156,7 @@ void doRestart(String testId, String patternPart, boolean fileOptionIsSet, int w
 
         String filePatternStr = randomOutputDir + patternPart + "-%d{" + DATE_PATTERN_WITH_SECONDS + "}";
 
-        String fileName = fileOptionIsSet ? testId2FileName(testId) : null;
+        String fileName = filenameFunction.apply(testId);
         initRFA(rfa2, fileName);
         initTRBP(rfa2, tbrp2, filePatternStr, currentTime);
         for (int i = 0; i < 3; i++) {
@@ -161,48 +172,48 @@ void doRestart(String testId, String patternPart, boolean fileOptionIsSet, int w
 
     @Test
     public void noCompression_FileBlank_NoRestart_1() throws IOException {
-        defaultTest("test1", "test1", "", FILE_OPTION_BLANK, NO_RESTART);
+        defaultTest("test1", "test1", "", this::nullFileName, NO_RESTART);
     }
 
     @Test
     public void withCompression_FileBlank_NoRestart_2() throws IOException {
-        defaultTest("test2", "test2", ".gz", FILE_OPTION_BLANK, NO_RESTART);
+        defaultTest("test2", "test2", ".gz", this::nullFileName, NO_RESTART);
     }
 
     @Test
     public void noCompression_FileBlank_StopRestart_3() throws IOException {
-        defaultTest("test3", "test3", "", FILE_OPTION_BLANK, WITH_RESTART);
+        defaultTest("test3", "test3", "", this::nullFileName, WITH_RESTART);
     }
 
     @Test
     public void noCompression_FileSet_StopRestart_4() throws IOException {
-        defaultTest("test4", "test4", "", FILE_OPTION_SET, WITH_RESTART);
+        defaultTest("test4", "test4", "", this::testId2FileName, WITH_RESTART);
     }
 
     @Test
     public void noCompression_FileSet_StopRestart_WithLongWait_4B() throws IOException {
-        defaultTest("test4B", "test4B", "", FILE_OPTION_SET, WITH_RESTART_AND_LONG_WAIT);
+        defaultTest("test4B", "test4B", "", this::testId2FileName, WITH_RESTART_AND_LONG_WAIT);
     }
 
     @Test
     public void noCompression_FileSet_NoRestart_5() throws IOException {
-        defaultTest("test5", "test5", "", FILE_OPTION_SET, NO_RESTART);
+        defaultTest("test5", "test5", "", this::testId2FileName, NO_RESTART);
     }
 
     @Test
     public void withCompression_FileSet_NoRestart_6() throws IOException {
-        defaultTest("test6", "test6", ".gz", FILE_OPTION_SET, NO_RESTART);
+        defaultTest("test6", "test6", ".gz", this::testId2FileName, NO_RESTART);
     }
 
     // LOGBACK-168
     @Test
     public void withMissingTargetDirWithCompression() throws IOException {
-        defaultTest("test7", "%d{yyyy-MM-dd, aux}/test7", ".gz", FILE_OPTION_SET, NO_RESTART);
+        defaultTest("test7", "%d{yyyy-MM-dd, aux}/test7", ".gz", this::testId2FileName, NO_RESTART);
     }
 
     @Test
     public void withMissingTargetDirWithZipCompression() throws IOException {
-        defaultTest("test8", "%d{yyyy-MM-dd, aux}/test8", ".zip", FILE_OPTION_SET, NO_RESTART);
+        defaultTest("test8", "%d{yyyy-MM-dd, aux}/test8", ".zip", this::testId2FileName, NO_RESTART);
     }
 
     @Test
@@ -210,17 +221,18 @@ public void failed_rename() throws IOException {
         if (!EnvUtilForTests.isWindows())
             return;
 
+        String testId = "failed_rename";
         FileOutputStream fos = null;
         try {
-            String fileName = testId2FileName("failed_rename");
+            String fileName = testId2FileName(testId);
             File file = new File(fileName);
             file.getParentFile().mkdirs();
 
             fos = new FileOutputStream(fileName);
 
-            String testId = "failed_rename";
             rolloverChecker = new ZRolloverChecker(testId);
-            genericTest(testId, "failed_rename", "", FILE_OPTION_SET, NO_RESTART);
+            genericTest(testId, "failed_rename", "", this::testId2FileName, NO_RESTART);
+            rolloverChecker.check(expectedFilenameList);
 
         } finally {
             StatusPrinter.print(context);
@@ -229,6 +241,22 @@ public void failed_rename() throws IOException {
         }
     }
 
-    
-    
+//    @Test
+//    public void failed_rename2() throws IOException {
+//
+//        String testId = "failed_rename";
+//        try {
+//            String fileName = testId2FileName(testId);
+//
+//            
+//            rolloverChecker = new ZRolloverChecker(testId);
+//            genericTest(testId, "test10", ".gz", this::testId2FileName, NO_RESTART);
+//            rolloverChecker.check(expectedFilenameList);
+//
+//        } finally {
+//            StatusPrinter.print(context);
+//
+//        }
+//    }
+
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java
index 7c26081495..843ff815be 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_Test.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -14,11 +14,16 @@
 package ch.qos.logback.core.rolling;
 
 import static ch.qos.logback.core.CoreConstants.DAILY_DATE_PATTERN;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
+import static ch.qos.logback.core.CoreConstants.STRICT_ISO8601_PATTERN;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.File;
 import java.io.FileFilter;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZonedDateTime;
+import java.time.format.DateTimeFormatter;
 import java.util.ArrayList;
 import java.util.Calendar;
 import java.util.Collections;
@@ -27,26 +32,30 @@
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.stream.Stream;
+
+import java.util.concurrent.atomic.LongAdder;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.time.temporal.ChronoUnit;
 
-import org.joda.time.DateTimeZone;
-import org.joda.time.Days;
-import org.joda.time.LocalDate;
-import org.junit.Before;
-import org.junit.Ignore;
-import org.junit.Test;
+import ch.qos.logback.core.rolling.testUtil.ParentScaffoldingForRollingTests;
+import ch.qos.logback.core.status.OnConsoleStatusListener;
+import ch.qos.logback.core.util.StatusPrinter;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.ParameterizedTest;
 
 import ch.qos.logback.core.CoreConstants;
 import ch.qos.logback.core.pattern.SpacePadder;
 import ch.qos.logback.core.rolling.helper.RollingCalendar;
-import ch.qos.logback.core.rolling.testUtil.ScaffoldingForRollingTests;
-import ch.qos.logback.core.testUtil.StatusChecker;
+import ch.qos.logback.core.status.testUtil.StatusChecker;
 import ch.qos.logback.core.util.FileSize;
 import ch.qos.logback.core.util.FixedRateInvocationGate;
-import ch.qos.logback.core.util.StatusPrinter;
 
-public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRollingTests {
+public class TimeBasedRollingWithArchiveRemoval_Test extends ParentScaffoldingForRollingTests {
     String MONTHLY_DATE_PATTERN = "yyyy-MM";
     String MONTHLY_CRONOLOG_DATE_PATTERN = "yyyy/MM";
     final String DAILY_CRONOLOG_DATE_PATTERN = "yyyy/MM/dd";
@@ -54,7 +63,11 @@ public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRolli
     RollingFileAppender rfa = new RollingFileAppender();
     TimeBasedRollingPolicy tbrp = new TimeBasedRollingPolicy();
 
-    // by default tbfnatp is an instance of DefaultTimeBasedFileNamingAndTriggeringPolicy
+    DateTimeFormatter STRICT_DATE_PARSER = DateTimeFormatter.ofPattern(STRICT_ISO8601_PATTERN);
+
+
+       // by default tbfnatp is an instance of
+    // DefaultTimeBasedFileNamingAndTriggeringPolicy
     TimeBasedFileNamingAndTriggeringPolicy tbfnatp = new DefaultTimeBasedFileNamingAndTriggeringPolicy();
 
     StatusChecker checker = new StatusChecker(context);
@@ -65,6 +78,8 @@ public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRolli
     static long MILLIS_IN_MONTH = (long) ((365.242199 / 12) * MILLIS_IN_DAY);
     static int MONTHS_IN_YEAR = 12;
 
+    public static final String DAILY_HOUR_PATTERN = "yyyy-MM-dd-HH";
+
     // Wed Mar 23 23:07:05 CET 2016
     static final long WED_2016_03_23_T_230705_CET = 1458770825333L;
     static final long THU_2016_03_17_T_230330_CET = 1458252210975L;
@@ -75,7 +90,7 @@ public class TimeBasedRollingWithArchiveRemoval_Test extends ScaffoldingForRolli
     ConfigParameters cp; // initialized in setup
     FixedRateInvocationGate fixedRateInvocationGate = new FixedRateInvocationGate(ticksPerPeriod / 2);
 
-    @Before
+    @BeforeEach
     public void setUp() {
         super.setUp();
         this.cp = new ConfigParameters(currentTime);
@@ -95,9 +110,9 @@ private int computeSlashCount(String datePattern) {
         }
     }
 
-    // test that the number of files at the end of the test is same as the expected number taking into account end dates
-    // near the beginning of a new year. This test has been run in a loop with start date varying over a two years
-    // with success.
+    // test that the number of files at the end of the test is same as the expected
+    // number taking into account end dates near the beginning of a new year.
+    // This test has been run in a loop with start date varying over two years with success.
     @Test
     public void monthlyRolloverOverManyPeriods() {
         this.slashCount = computeSlashCount(MONTHLY_CRONOLOG_DATE_PATTERN);
@@ -105,7 +120,8 @@ public void monthlyRolloverOverManyPeriods() {
         int simulatedNumberOfPeriods = 30;
         String fileNamePattern = randomOutputDir + "/%d{" + MONTHLY_CRONOLOG_DATE_PATTERN + "}/clean.txt.zip";
 
-        cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods).periodDurationInMillis(MILLIS_IN_MONTH);
+        cp.maxHistory(maxHistory).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods)
+                .periodDurationInMillis(MILLIS_IN_MONTH);
 
         long startTime = currentTime;
         long endTime = logOverMultiplePeriods(cp);
@@ -130,19 +146,18 @@ long generateDailyRollover(ConfigParameters cp) {
     long generateDailyRolloverAndCheckFileCount(ConfigParameters cp) {
         long millisAtEnd = generateDailyRollover(cp);
         int periodBarriersCrossed = computeCrossedDayBarriers(currentTime, millisAtEnd);
-        System.out.println("**** periodBarriersCrossed=" + periodBarriersCrossed);
-        checkFileCount(expectedCountWithoutFoldersWithInactivity(cp.maxHistory, periodBarriersCrossed, cp.startInactivity + cp.numInactivityPeriods));
+        // StatusPrinter.print(context);
+        checkFileCount(expectedCountWithoutFoldersWithInactivity(cp.maxHistory, periodBarriersCrossed,
+                cp.startInactivity + cp.numInactivityPeriods));
         return millisAtEnd;
     }
 
     @Test
     public void checkCrossedPeriodsWithDSTBarrier() {
         long SAT_2016_03_26_T_230705_CET = WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY;
-        System.out.println("SAT_2016_03_26_T_230705_CET " + new Date(SAT_2016_03_26_T_230705_CET));
         long MON_2016_03_28_T_000705_CET = SAT_2016_03_26_T_230705_CET + CoreConstants.MILLIS_IN_ONE_DAY;
-        System.out.println("MON_2016_03_28_T_000705_CET " + new Date(MON_2016_03_28_T_000705_CET));
 
-        int result = computeCrossedDayBarriers(SAT_2016_03_26_T_230705_CET, MON_2016_03_28_T_000705_CET, "CET");
+        long result = computeCrossedDayBarriers(SAT_2016_03_26_T_230705_CET, MON_2016_03_28_T_000705_CET, "CET");
         assertEquals(2, result);
     }
 
@@ -151,14 +166,24 @@ private int computeCrossedDayBarriers(long currentTime, long millisAtEnd) {
     }
 
     private int computeCrossedDayBarriers(long currentTime, long millisAtEnd, String timeZoneID) {
-        DateTimeZone dateTimeZone = DateTimeZone.getDefault();
+        ZoneId dateTimeZone = ZoneId.systemDefault();
         if (timeZoneID != null) {
-            dateTimeZone = DateTimeZone.forID(timeZoneID);
+            dateTimeZone = ZoneId.of(timeZoneID);
         }
-        LocalDate startInstant = new LocalDate(currentTime, dateTimeZone);
-        LocalDate endInstant = new LocalDate(millisAtEnd, dateTimeZone);
-        Days days = Days.daysBetween(startInstant, endInstant);
-        return days.getDays();
+
+        Instant startInstant = Instant.ofEpochMilli(currentTime);
+        ZonedDateTime startZDT = startInstant.atZone(dateTimeZone);
+        // truncate to beginning of day as DAYS.between computes fully elapsed days
+        ZonedDateTime startZDT0 = startZDT.truncatedTo(ChronoUnit.DAYS);
+
+        Instant endInstant = Instant.ofEpochMilli(millisAtEnd);
+        ZonedDateTime endZDT = endInstant.atZone(dateTimeZone);
+        // truncate to beginning of day as DAYS.between computes fully elapsed days
+        ZonedDateTime endZDT0 = endZDT.truncatedTo(ChronoUnit.DAYS);
+
+        // computes fully elapsed days
+        long dayCount = ChronoUnit.DAYS.between(startZDT0, endZDT0);
+        return (int) dayCount;
     }
 
     @Test
@@ -167,30 +192,45 @@ public void checkCleanupForBasicDailyRollover() {
         generateDailyRolloverAndCheckFileCount(cp);
     }
 
-    @Test
-    public void checkCleanupForBasicDailyRolloverWithSizeCap() {
-        long bytesOutputPerPeriod = 15984;
+    @ParameterizedTest
+    @MethodSource
+    public void checkCleanupForBasicDailyRolloverWithSizeCap(Long injectedCurrentTime) {
+        this.currentTime = injectedCurrentTime;
+        // the size cap is based on observations made during test runs
+        long bytesOutputPerPeriod = 16500;
         int sizeInUnitsOfBytesPerPeriod = 2;
+        // 1000 is to give some leeway
+        long sizeCap = sizeInUnitsOfBytesPerPeriod * bytesOutputPerPeriod + 1000;
+
+
 
-        cp.maxHistory(5).simulatedNumberOfPeriods(10).sizeCap(sizeInUnitsOfBytesPerPeriod * bytesOutputPerPeriod + 1000);
+        cp.maxHistory(5).simulatedNumberOfPeriods(10)
+                .sizeCap(sizeCap);
         generateDailyRollover(cp);
-        StatusPrinter.print(context);
+        // expect two archive for sizeInUnitsOfBytesPerPeriod =2 plus the latest period to remain
         checkFileCount(sizeInUnitsOfBytesPerPeriod + 1);
     }
 
+    static Stream checkCleanupForBasicDailyRolloverWithSizeCap() {
+        // currentTime = 1760822446333
+        // Sat Oct 18 2025 21:20:46.333 UTC
+        return Stream.of(1760822446333L, System.currentTimeMillis());
+    }
     @Test
     public void checkThatSmallTotalSizeCapLeavesAtLeastOneArhcive() {
         long WED_2016_03_23_T_131345_CET = WED_2016_03_23_T_230705_CET - 10 * CoreConstants.MILLIS_IN_ONE_HOUR;
 
         // long bytesOutputPerPeriod = 15984;
 
+
+
         cp = new ConfigParameters(WED_2016_03_23_T_131345_CET);
         final int verySmallCapSize = 1;
         cp.maxHistory(5).simulatedNumberOfPeriods(3).sizeCap(verySmallCapSize);
         generateDailyRollover(cp);
-        StatusPrinter.print(context);
+        // StatusPrinter.print(context);
         checkFileCountAtMost(1);
-       
+
     }
 
     @Test
@@ -199,7 +239,8 @@ public void checkCleanupForBasicDailyRolloverWithMaxSize() {
         generateDailyRolloverAndCheckFileCount(cp);
     }
 
-    // Since the duration of a month (in seconds) varies from month to month, tests with inactivity period must
+    // Since the duration of a month (in seconds) varies from month to month, tests
+    // with inactivity period must
     // be conducted with daily rollover not monthly
     @Test
     public void checkCleanupForDailyRollover_15Periods() {
@@ -228,11 +269,11 @@ public void checkCleanupForDailyRolloverWithSecondPhase() {
         String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
 
         ConfigParameters cp0 = new ConfigParameters(currentTime).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
-                        .simulatedNumberOfPeriods(maxHistory * 2);
+                .simulatedNumberOfPeriods(maxHistory * 2);
         long endTime = logOverMultiplePeriods(cp0);
 
-        ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 10).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
-                        .simulatedNumberOfPeriods(maxHistory);
+        ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 10).maxHistory(maxHistory)
+                .fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(maxHistory);
         logOverMultiplePeriods(cp1);
         checkFileCount(expectedCountWithoutFolders(maxHistory));
     }
@@ -250,8 +291,8 @@ public void dailyRolloverWithCronologPattern() {
 
     @Test
     public void dailySizeBasedRolloverWithoutCap() {
-        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
-        sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
+        SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy();
+        //sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
 
         sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
         tbfnatp = sizeAndTimeBasedFNATP;
@@ -264,10 +305,9 @@ public void dailySizeBasedRolloverWithoutCap() {
 
     @Test
     public void dailySizeBasedRolloverWithSizeCap() {
-        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
-        sizeAndTimeBasedFNATP.invocationGate = new FixedRateInvocationGate(ticksPerPeriod / 8);
-        long bytesPerPeriod = 17000;
-        long fileSize = (bytesPerPeriod) / 5;
+        SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFNATP = new SizeAndTimeBasedFileNamingAndTriggeringPolicy();
+
+        long fileSize = 3400;
         int expectedFileCount = 10;
         long sizeCap = expectedFileCount * fileSize;
         sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(fileSize));
@@ -275,7 +315,9 @@ public void dailySizeBasedRolloverWithSizeCap() {
         this.slashCount = computeSlashCount(DAILY_DATE_PATTERN);
 
         // 2016-03-05 00:14:39 CET
-        long simulatedTime = 1457133279186L;
+        //long simulatedTime = 1457133279186L;
+        long simulatedTime = getSimulatedTimeFromString("2016-03-05T00:14:39,186");
+
         ConfigParameters params = new ConfigParameters(simulatedTime);
         String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i";
         params.maxHistory(60).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(10).sizeCap(sizeCap);
@@ -284,22 +326,41 @@ public void dailySizeBasedRolloverWithSizeCap() {
         List foundFiles = findFilesByPattern("\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)");
         Collections.sort(foundFiles, new Comparator() {
             public int compare(File f0, File f1) {
-                String s0 = f0.getName().toString();
-                String s1 = f1.getName().toString();
+                String s0 = f0.getName();
+                String s1 = f1.getName();
                 return s0.compareTo(s1);
             }
         });
-        System.out.print(foundFiles);
+
         StatusPrinter.print(context);
-        checkFileCount(expectedFileCount - 1);
+        foundFiles.forEach(f -> System.out.println(""+f+ " "+f.length()));
+        LongAdder la = new LongAdder();
+        foundFiles.forEach(f -> la.add(f.length()));
+        System.out.println("Sum: "+la.sum());
+
+        // adding fileSize to sizeCap may make sense
+        assertTrue(la.sum() < sizeCap);
+
+        checkFileCount(expectedFileCount + 1);
+    }
+
+    /**
+     * The returned millis is based on local time
+     * @param dateStr
+     * @return
+     */
+    private long getSimulatedTimeFromString(String dateStr) {
+        LocalDateTime localDateTime = LocalDateTime.parse(dateStr, STRICT_DATE_PARSER);
+        long simulatedTime = localDateTime.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli();
+        return simulatedTime;
     }
 
     @Test
     public void dailyChronologSizeBasedRollover() {
-        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
-        sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
-        sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
-        tbfnatp = sizeAndTimeBasedFNATP;
+        SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFileNamingAndTriggeringPolicy = new SizeAndTimeBasedFileNamingAndTriggeringPolicy();
+        sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize(new FileSize(10000));
+        //sizeAndTimeBasedFileNamingAndTriggeringPolicy.invocationGate = fixedRateInvocationGate;
+        tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy;
         slashCount = 1;
         String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i.zip";
         cp.maxHistory(5).fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(5 * 3);
@@ -309,10 +370,10 @@ public void dailyChronologSizeBasedRollover() {
 
     @Test
     public void dailyChronologSizeBasedRolloverWithSecondPhase() {
-        SizeAndTimeBasedFNATP sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP();
-        sizeAndTimeBasedFNATP.setMaxFileSize(new FileSize(10000));
-        sizeAndTimeBasedFNATP.invocationGate = fixedRateInvocationGate;
-        tbfnatp = sizeAndTimeBasedFNATP;
+        SizeAndTimeBasedFileNamingAndTriggeringPolicy sizeAndTimeBasedFileNamingAndTriggeringPolicy = new SizeAndTimeBasedFileNamingAndTriggeringPolicy();
+        sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize(new FileSize(10000));
+        //sizeAndTimeBasedFileNamingAndTriggeringPolicy.invocationGate = fixedRateInvocationGate;
+        tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy;
         this.slashCount = 1;
         String fileNamePattern = randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i";
         int maxHistory = 5;
@@ -320,17 +381,18 @@ public void dailyChronologSizeBasedRolloverWithSecondPhase() {
         long endTime = logOverMultiplePeriods(cp);
 
         int simulatedNumberOfPeriods = maxHistory * 4;
-        ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 7).maxHistory(maxHistory).fileNamePattern(fileNamePattern)
-                        .simulatedNumberOfPeriods(simulatedNumberOfPeriods);
+        ConfigParameters cp1 = new ConfigParameters(endTime + MILLIS_IN_DAY * 7).maxHistory(maxHistory)
+                .fileNamePattern(fileNamePattern).simulatedNumberOfPeriods(simulatedNumberOfPeriods);
         logOverMultiplePeriods(cp1);
         checkDirPatternCompliance(maxHistory + 1);
     }
 
-    void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory) {
-        ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern).maxHistory(maxHistory);
-        buildRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
+    void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory, long durationInMillis) {
+        ConfigParameters params = new ConfigParameters(currentTime).fileNamePattern(fileNamePattern)
+                .maxHistory(maxHistory);
+        configureRollingFileAppender(params, DO_CLEAN_HISTORY_ON_START);
         rfa.doAppend("Hello ----------------------------------------------------------" + new Date(currentTime));
-        currentTime += MILLIS_IN_DAY / 2;
+        currentTime += durationInMillis / 2;
         add(tbrp.compressionFuture);
         add(tbrp.cleanUpFuture);
         waitForJobsToComplete();
@@ -339,49 +401,58 @@ void logTwiceAndStop(long currentTime, String fileNamePattern, int maxHistory) {
         rfa.stop();
     }
 
+    // LOGBACK-1562
     @Test
-    public void cleanHistoryOnStart() {
+    public void cleanHistoryOnStartWithHourPattern() {
         long simulatedTime = WED_2016_03_23_T_230705_CET;
-        System.out.println(new Date(simulatedTime));
+        String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_HOUR_PATTERN + "}.txt";
+        int maxHistory = 3;
+        for (int i = 0; i <= 5; i++) {
+            logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
+            simulatedTime += MILLIS_IN_HOUR;
+        }
+        checkFileCount(expectedCountWithoutFolders(maxHistory));
+    }
 
-        String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
+    @Disabled
+    @Test
+    // this test assumes a high degree of collisions in the archived files. Every 24
+    // hours, the archive belonging to the previous day will be overwritten. Given that
+    // logback goes 14 days (336 hours) in history to clean files on start up, it is
+    // bound to delete more recent files. It is not logback's responsibility
+    // to cater for such degenerate cases.
+    public void cleanHistoryOnStartWithHourPatternWithCollisions() {
+        long now = this.currentTime;
+        String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
         int maxHistory = 3;
         for (int i = 0; i <= 5; i++) {
-            logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory);
-            simulatedTime += MILLIS_IN_DAY;
+            logTwiceAndStop(now, fileNamePattern, maxHistory, MILLIS_IN_DAY);
+            now = now + MILLIS_IN_HOUR;
         }
-        StatusPrinter.print(context);
         checkFileCount(expectedCountWithoutFolders(maxHistory));
     }
 
     @Test
     public void cleanHistoryOnStartWithDayPattern() {
         long simulatedTime = WED_2016_03_23_T_230705_CET;
-        String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd}.txt";
+        String fileNamePattern = randomOutputDir + "clean-%d{" + DAILY_DATE_PATTERN + "}.txt";
         int maxHistory = 3;
         for (int i = 0; i <= 5; i++) {
-            logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory);
+            logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_DAY);
             simulatedTime += MILLIS_IN_DAY;
         }
-        StatusPrinter.print(context);
         checkFileCount(expectedCountWithoutFolders(maxHistory));
     }
 
-    @Ignore
     @Test
-    // this test assumes a high degree of collisions in the archived files. Every 24 hours, the archive
-    // belonging to the previous day will be overwritten. Given that logback goes 14 days (336 hours) in history
-    // to clean files on start up, it is bound to delete more recent files. It is not logback's responsibility
-    // to cater for such degenerate cases.
-    public void cleanHistoryOnStartWithHourPattern() {
-        long now = this.currentTime;
-        String fileNamePattern = randomOutputDir + "clean-%d{HH}.txt";
+    public void cleanHistoryOnStartWithHourDayPattern() {
+        long simulatedTime = WED_2016_03_23_T_230705_CET;
+        String fileNamePattern = randomOutputDir + "clean-%d{yyyy-MM-dd-HH}.txt";
         int maxHistory = 3;
         for (int i = 0; i <= 5; i++) {
-            logTwiceAndStop(now, fileNamePattern, maxHistory);
-            now = now + MILLIS_IN_HOUR;
+            logTwiceAndStop(simulatedTime, fileNamePattern, maxHistory, MILLIS_IN_HOUR);
+            simulatedTime += MILLIS_IN_HOUR;
         }
-        StatusPrinter.print(context);
         checkFileCount(expectedCountWithoutFolders(maxHistory));
     }
 
@@ -398,7 +469,7 @@ int expectedCountWithFolders(int maxHistory, boolean withExtraFolder) {
         return result;
     }
 
-    void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
+    void configureRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart) {
         rfa.setContext(context);
         rfa.setEncoder(encoder);
         tbrp.setContext(context);
@@ -407,6 +478,7 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)
         tbrp.setTotalSizeCap(new FileSize(cp.sizeCap));
         tbrp.setParent(rfa);
         tbrp.setCleanHistoryOnStart(cleanHistoryOnStart);
+        tbfnatp.setContext(context);
         tbrp.timeBasedFileNamingAndTriggeringPolicy = tbfnatp;
         tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(cp.simulatedTime);
         tbrp.start();
@@ -418,35 +490,44 @@ void buildRollingFileAppender(ConfigParameters cp, boolean cleanHistoryOnStart)
     boolean DO_NOT_CLEAN_HISTORY_ON_START = false;
 
     long logOverMultiplePeriods(ConfigParameters cp) {
+        //addOnConsoleStatusListenerForDebugging();
+
+        configureRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
 
-        buildRollingFileAppender(cp, DO_NOT_CLEAN_HISTORY_ON_START);
 
         int runLength = cp.simulatedNumberOfPeriods * ticksPerPeriod;
         int startInactivityIndex = cp.startInactivity * ticksPerPeriod;
         int endInactivityIndex = startInactivityIndex + cp.numInactivityPeriods * ticksPerPeriod;
         long tickDuration = cp.periodDurationInMillis / ticksPerPeriod;
 
-        System.out.println("cp.periodDurationInMillis=" + cp.periodDurationInMillis + ", tickDuration=:" + tickDuration + ", runLength=" + runLength);
+        System.out.println("ticksPerPeriod=" + ticksPerPeriod);
+        System.out.println("cp.startInactivity="+cp.startInactivity);
+        System.out.println("cp.simulatedNumberOfPeriods="+cp.simulatedNumberOfPeriods);
+        System.out.println("cp.periodDurationInMillis="+cp.periodDurationInMillis);
+
+        System.out.println("runLength=" + runLength);
+
+        System.out.println("startInactivityIndex=" + startInactivityIndex);
+        System.out.println("endInactivityIndex=" + endInactivityIndex);
+        System.out.println("tickDuration=" + tickDuration);
+        System.out.println(" ");
+
         for (int i = 0; i <= runLength; i++) {
-            Date currentDate = new Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime());
+            long timeInMillis = tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime();
+
+            Date currentDate = new Date(timeInMillis);
+            //System.out.println("i=" + i + ", currentDate=" + currentDate);
             if (i < startInactivityIndex || i > endInactivityIndex) {
-                StringBuilder sb = new StringBuilder("Hello");
-                String currentDateStr = currentDate.toString();
-                String iAsString = Integer.toString(i);
-                sb.append(currentDateStr);
-                SpacePadder.spacePad(sb, 66 + (3 - iAsString.length() - currentDateStr.length()));
-                sb.append(iAsString);
-                rfa.doAppend(sb.toString());
-            } 
-
-            tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(addTime(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime(), tickDuration));
-            
+                rfa.doAppend(buildMessageString(currentDate, i));
+            }
+
+            tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(addTime(timeInMillis, tickDuration));
+
             add(tbrp.compressionFuture);
             add(tbrp.cleanUpFuture);
             waitForJobsToComplete();
         }
-        
-        
+
         try {
             Thread.sleep(100);
         } catch (InterruptedException e) {
@@ -455,10 +536,28 @@ long logOverMultiplePeriods(ConfigParameters cp) {
         }
         rfa.stop();
 
-        System.out.println("Current time at end of loop: "+new Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()));
+        // System.out.println("Current time at end of loop: "+new
+        // Date(tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime()));
         return tbrp.timeBasedFileNamingAndTriggeringPolicy.getCurrentTime();
     }
 
+    private void addOnConsoleStatusListenerForDebugging() {
+        OnConsoleStatusListener onConsoleStatusListener = new OnConsoleStatusListener();
+        onConsoleStatusListener.setContext(context);
+        onConsoleStatusListener.start();
+        context.getStatusManager().add(onConsoleStatusListener);
+    }
+
+    private static String buildMessageString(Date currentDate, int i) {
+        StringBuilder sb = new StringBuilder("Hello");
+        String currentDateStr = currentDate.toString();
+        String iAsString = Integer.toString(i);
+        sb.append(currentDateStr);
+        SpacePadder.spacePad(sb, 68 + (3 - iAsString.length() - currentDateStr.length() - CoreConstants.LINE_SEPARATOR_LEN));
+        sb.append(iAsString);
+        return sb.toString();
+    }
+
     void fillWithChar(StringBuffer sb, char c, int count) {
         for (int i = 0; i < count; i++) {
             sb.append(c);
@@ -480,8 +579,9 @@ void expectedFileAndDirCount(int expectedFileAndDirCount, int expectedDirCountMi
         findFilesInFolderRecursivelyByPatterMatch(dir, fileList, "clean");
         List dirList = new ArrayList();
         findAllFoldersInFolderRecursively(dir, dirList);
-        String msg = "expectedDirCountMin=" + expectedDirCountMin + ", expectedDirCountMax=" + expectedDirCountMax + " actual value=" + dirList.size();
-        assertTrue(msg, expectedDirCountMin <= dirList.size() && dirList.size() <= expectedDirCountMax);
+        String msg = "expectedDirCountMin=" + expectedDirCountMin + ", expectedDirCountMax=" + expectedDirCountMax
+                + " actual value=" + dirList.size();
+        assertTrue(expectedDirCountMin <= dirList.size() && dirList.size() <= expectedDirCountMax, msg);
     }
 
     void checkFileCount(int expectedCount) {
@@ -496,17 +596,18 @@ void checkFileCountAtMost(int expectedCount) {
         List fileList = new ArrayList();
         findAllDirsOrStringContainsFilesRecursively(dir, fileList, "clean");
         int fileListSize = fileList.size();
-        
-        assertTrue("file list size "+ fileListSize+", expectedCount="+expectedCount, fileListSize <= expectedCount);
+
+        assertTrue(fileListSize <= expectedCount, "file list size " + fileListSize + ", expectedCount=" + expectedCount);
     }
-    
+
     int expectedCountWithoutFoldersWithInactivity(int maxHistory, int totalPeriods, int endOfInactivity) {
         int availableHistory = (totalPeriods + 1) - endOfInactivity;
         int actualHistory = Math.min(availableHistory, maxHistory + 1);
         return actualHistory;
     }
 
-    void genericFindMatching(final FileMatchFunction matchFunc, File dir, List fileList, final String pattern, boolean includeDirs) {
+    void genericFindMatching(final FileMatchFunction matchFunc, File dir, List fileList, final String pattern,
+                             boolean includeDirs) {
         if (dir.isDirectory()) {
             File[] matchArray = dir.listFiles(new FileFilter() {
                 public boolean accept(File f) {
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/ZRolloverChecker.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/ZRolloverChecker.java
index 9daa494eaa..26c160c7bb 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/ZRolloverChecker.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/ZRolloverChecker.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -19,7 +19,7 @@
 import java.io.IOException;
 import java.util.List;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class ZRolloverChecker implements RolloverChecker {
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/CompressTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/CompressTest.java
deleted file mode 100755
index 2879d1658f..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/CompressTest.java
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.rolling.helper;
-
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import org.junit.Before;
-import org.junit.Test;
-
-import ch.qos.logback.core.Context;
-import ch.qos.logback.core.ContextBase;
-import ch.qos.logback.core.testUtil.CoreTestConstants;
-import ch.qos.logback.core.testUtil.StatusChecker;
-import ch.qos.logback.core.util.Compare;
-
-/**
- * @author Ceki Gulcu
- */
-public class CompressTest {
-
-    Context context = new ContextBase();
-
-    @Before
-    public void setUp() throws IOException {
-        // Copy source files
-        // Delete output files
-        {
-            File source = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress1.copy");
-            File dest = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress1.txt");
-
-            copy(source, dest);
-            File target = new File(CoreTestConstants.OUTPUT_DIR_PREFIX + "compress1.txt.gz");
-            target.mkdirs();
-            target.delete();
-        }
-        {
-            File source = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress2.copy");
-            File dest = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress2.txt");
-            copy(source, dest);
-            File target = new File(CoreTestConstants.OUTPUT_DIR_PREFIX + "compress2.txt.gz");
-            target.mkdirs();
-            target.delete();
-        }
-        {
-            File source = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress3.copy");
-            File dest = new File(CoreTestConstants.TEST_SRC_PREFIX + "input/compress3.txt");
-            copy(source, dest);
-            File target = new File(CoreTestConstants.OUTPUT_DIR_PREFIX + "compress3.txt.zip");
-            target.mkdirs();
-            target.delete();
-        }
-    }
-
-    @Test
-    public void test1() throws Exception {
-        Compressor compressor = new Compressor(CompressionMode.GZ);
-        compressor.setContext(context);
-        compressor.compress(CoreTestConstants.TEST_SRC_PREFIX + "input/compress1.txt", CoreTestConstants.OUTPUT_DIR_PREFIX + "compress1.txt.gz", null);
-
-        StatusChecker checker = new StatusChecker(context);
-        assertTrue(checker.isErrorFree(0));
-        assertTrue(Compare.gzCompare(CoreTestConstants.OUTPUT_DIR_PREFIX + "compress1.txt.gz", CoreTestConstants.TEST_SRC_PREFIX + "witness/compress1.txt.gz"));
-    }
-
-    @Test
-    public void test2() throws Exception {
-        Compressor compressor = new Compressor(CompressionMode.GZ);
-        compressor.setContext(context);
-        compressor.compress(CoreTestConstants.TEST_SRC_PREFIX + "input/compress2.txt", CoreTestConstants.OUTPUT_DIR_PREFIX + "compress2.txt", null);
-
-        StatusChecker checker = new StatusChecker(context);
-        assertTrue(checker.isErrorFree(0));
-
-        assertTrue(Compare.gzCompare(CoreTestConstants.OUTPUT_DIR_PREFIX + "compress2.txt.gz", CoreTestConstants.TEST_SRC_PREFIX + "witness/compress2.txt.gz"));
-    }
-
-    @Test
-    public void test3() throws Exception {
-        Compressor compressor = new Compressor(CompressionMode.ZIP);
-        compressor.setContext(context);
-        compressor.compress(CoreTestConstants.TEST_SRC_PREFIX + "input/compress3.txt", CoreTestConstants.OUTPUT_DIR_PREFIX + "compress3.txt", "compress3.txt");
-        StatusChecker checker = new StatusChecker(context);
-        assertTrue(checker.isErrorFree(0));
-
-        // we don't know how to compare .zip files
-        // assertTrue(Compare.compare(CoreTestConstants.OUTPUT_DIR_PREFIX
-        // + "compress3.txt.zip", CoreTestConstants.TEST_SRC_PREFIX
-        // + "witness/compress3.txt.zip"));
-    }
-
-    private void copy(File src, File dst) throws IOException {
-        InputStream in = new FileInputStream(src);
-        OutputStream out = new FileOutputStream(dst);
-        byte[] buf = new byte[1024];
-        int len;
-        while ((len = in.read(buf)) > 0) {
-            out.write(buf, 0, len);
-        }
-        in.close();
-        out.close();
-    }
-
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileNamePatternTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileNamePatternTest.java
index 93ccd62d14..e502de1c3c 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileNamePatternTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileNamePatternTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,18 +13,19 @@
  */
 package ch.qos.logback.core.rolling.helper;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-
+import java.time.ZoneId;
 import java.util.Calendar;
 import java.util.TimeZone;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+
 /**
  * @author Ceki
  * 
@@ -122,7 +123,9 @@ public void auxAndTimeZoneShouldNotConflict() {
         }
 
         {
-            FileNamePattern fnp = new FileNamePattern("folder/%d{yyyy/MM, aux, Australia/Perth}/test.%d{yyyy-MM-dd'T'HHmm, Australia/Perth}.log", context);
+            FileNamePattern fnp = new FileNamePattern(
+                    "folder/%d{yyyy/MM, aux, Australia/Perth}/test.%d{yyyy-MM-dd'T'HHmm, Australia/Perth}.log",
+                    context);
             assertEquals("folder/2003/05/test.2003-05-20T1855.log", fnp.convert(cal.getTime()));
             assertNotNull(fnp.getPrimaryDateTokenConverter());
         }
@@ -185,14 +188,14 @@ public void convertMultipleDates() {
     @Test
     public void nullTimeZoneByDefault() {
         FileNamePattern fnp = new FileNamePattern("%d{hh}", context);
-        assertNull(fnp.getPrimaryDateTokenConverter().getTimeZone());
+        assertNull(fnp.getPrimaryDateTokenConverter().getZoneId());
     }
 
     @Test
     public void settingTimeZoneOptionHasAnEffect() {
-        TimeZone tz = TimeZone.getTimeZone("Australia/Perth");
+        ZoneId tz = ZoneId.of("Australia/Perth");
 
-        FileNamePattern fnp = new FileNamePattern("%d{hh, " + tz.getID() + "}", context);
-        assertEquals(tz, fnp.getPrimaryDateTokenConverter().getTimeZone());
+        FileNamePattern fnp = new FileNamePattern("%d{hh, " + tz.getId() + "}", context);
+        assertEquals(tz, fnp.getPrimaryDateTokenConverter().getZoneId());
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileStoreUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileStoreUtilTest.java
index 84e3990910..11ff78b23e 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileStoreUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/FileStoreUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -18,14 +18,14 @@
 import ch.qos.logback.core.testUtil.RandomUtil;
 import ch.qos.logback.core.util.EnvUtil;
 import ch.qos.logback.core.util.FileUtil;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 
 import java.io.File;
 import java.io.IOException;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class FileStoreUtilTest {
 
@@ -45,7 +45,7 @@ public void filesOnSameFolderShouldBeOnTheSameFileStore() throws RolloverFailure
     }
 
     // test should be run manually
-    @Ignore
+    @Disabled
     @Test
     public void manual_filesOnDifferentVolumesShouldBeDetectedAsSuch() throws RolloverFailure {
         if (!EnvUtil.isJDK7OrHigher())
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/JDKOnlyCompressTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/JDKOnlyCompressTest.java
new file mode 100644
index 0000000000..7728df371e
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/JDKOnlyCompressTest.java
@@ -0,0 +1,140 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+package ch.qos.logback.core.rolling.helper;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.ContextBase;
+import ch.qos.logback.core.rolling.helper.CompressionMode;
+import ch.qos.logback.core.rolling.helper.Compressor;
+//import ch.qos.logback.core.status.testUtil.StatusChecker;
+import ch.qos.logback.core.status.Status;
+import ch.qos.logback.core.status.testUtil.StatusChecker;
+import ch.qos.logback.core.util.Compare;
+import ch.qos.logback.core.util.StatusPrinter2;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.*;
+
+import static ch.qos.logback.core.rolling.helper.Compressor.COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE;
+import static ch.qos.logback.core.rolling.helper.Compressor.XZ_COMPRESSION_STRATEGY_CLASS_NAME;
+import static ch.qos.logback.core.testUtil.CoreTestConstants.OUTPUT_DIR_PREFIX;
+import static ch.qos.logback.core.testUtil.CoreTestConstants.TEST_SRC_PREFIX;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * @author Ceki Gulcu
+ */
+public class JDKOnlyCompressTest {
+    Context context = new ContextBase();
+    StatusPrinter2 statusPrinter2 = new StatusPrinter2();
+
+    final String original1 = TEST_SRC_PREFIX + "input/compress1.original";
+    final String copy1 = TEST_SRC_PREFIX + "input/compress1.txt";
+    final String compressed1 = OUTPUT_DIR_PREFIX + "compress1.txt.gz";
+
+    final String original2 = TEST_SRC_PREFIX + "input/compress2.original";
+    final String copy2 = TEST_SRC_PREFIX + "input/compress2.txt";
+    final String compressed2 = OUTPUT_DIR_PREFIX + "compress2.txt.gz";
+
+    final String original3 = TEST_SRC_PREFIX + "input/compress3.original";
+    final String copy3 = TEST_SRC_PREFIX + "input/compress3.txt";
+    final String compressed3 = OUTPUT_DIR_PREFIX + "compress3.txt.zip";
+
+    final String original4 = TEST_SRC_PREFIX + "input/compress4.original";
+    final String copy4 = TEST_SRC_PREFIX + "input/compress4.txt";
+    final String compressed4 = OUTPUT_DIR_PREFIX + "compress4.txt.xz";
+
+    @BeforeEach
+    public void setUp() throws IOException {
+
+    }
+
+    protected void copySourceFilesAndDeleteCompressedOutputFiles(String originalPathStr, String copyPathStr, String compressedStr) throws IOException {
+        // Copy source files
+        // Delete output files
+
+        File originalFile = new File(originalPathStr);
+        File copyFile = new File(copyPathStr);
+        copy(originalFile, copyFile);
+        File compressedFile = new File(compressedStr);
+        compressedFile.mkdirs();
+        compressedFile.delete();
+    }
+
+    protected void copy(File src, File dst) throws IOException {
+        try (InputStream in = new FileInputStream(src); OutputStream out = new FileOutputStream(dst);) {
+            byte[] buf = new byte[1024];
+            int len;
+            while ((len = in.read(buf)) > 0) {
+                out.write(buf, 0, len);
+            }
+        }
+    }
+
+    @Test
+    public void gzTest1() throws Exception {
+        copySourceFilesAndDeleteCompressedOutputFiles(original1, copy1, compressed1);
+        Compressor compressor = new Compressor(CompressionMode.GZ);
+        compressor.setContext(context);
+        compressor.compress(copy1, compressed1, null);
+
+
+        StatusChecker checker = new StatusChecker(context);
+        Assertions.assertTrue(checker.isErrorFree(0));
+        Assertions.assertTrue(Compare.gzCompare(compressed1, TEST_SRC_PREFIX + "witness/compress1.txt.gz"));
+    }
+
+    @Test
+    public void gzTest2() throws Exception {
+        copySourceFilesAndDeleteCompressedOutputFiles(original2, copy2, compressed2);
+        Compressor compressor = new Compressor(CompressionMode.GZ);
+        compressor.setContext(context);
+        compressor.compress(copy2, compressed2, null);
+
+        StatusChecker checker = new StatusChecker(context);
+        Assertions.assertTrue(checker.isErrorFree(0));
+
+        Assertions.assertTrue(Compare.gzCompare(compressed2, TEST_SRC_PREFIX + "witness/compress2.txt.gz"));
+    }
+
+    @Test
+    public void zipTest() throws Exception {
+        copySourceFilesAndDeleteCompressedOutputFiles(original3, copy3, compressed3);
+        Compressor compressor = new Compressor(CompressionMode.ZIP);
+        compressor.setContext(context);
+        compressor.compress(copy3,  compressed3, "compress3.txt");
+        StatusChecker checker = new StatusChecker(context);
+        Assertions.assertTrue(checker.isErrorFree(0));
+
+        // we don't know how to compare .zip files
+        // Assertions.assertTrue(Compare.compare(CoreTestConstants.OUTPUT_DIR_PREFIX
+        // + "compress3.txt.zip", CoreTestConstants.TEST_SRC_PREFIX
+        // + "witness/compress3.txt.zip"));
+    }
+
+    @Test
+    public void xzTest() throws Exception {
+        copySourceFilesAndDeleteCompressedOutputFiles(original4, copy4, compressed4);
+        Compressor compressor = new Compressor(CompressionMode.XZ);
+        compressor.setContext(context);
+        compressor.compress(copy4, compressed4, null);
+        StatusChecker checker = new StatusChecker(context);
+        //statusPrinter2.print(context);
+        checker.assertContainsMatch(Status.ERROR, "Could not instantiate "+XZ_COMPRESSION_STRATEGY_CLASS_NAME);
+        checker.assertContainsMatch(Status.WARN, COULD_NOT_OBTAIN_COMPRESSION_STRATEGY_MESSAGE);
+    }
+
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/PackageTest.java
deleted file mode 100644
index 553fab438a..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/PackageTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.rolling.helper;
-
-import ch.qos.logback.core.util.DatePatternToRegexTest;
-import junit.framework.TestCase;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({ CompressTest.class, FileNamePatternTest.class, RollingCalendarTest.class, DatePatternToRegexTest.class })
-public class PackageTest extends TestCase {
-
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java
index 7d1144c8e0..1ab14d036a 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/RollingCalendarTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,50 +13,51 @@
  */
 package ch.qos.logback.core.rolling.helper;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
+import java.time.Instant;
 import java.util.Date;
 import java.util.Locale;
 import java.util.TimeZone;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.*;
 
 import ch.qos.logback.core.CoreConstants;
 import ch.qos.logback.core.util.EnvUtil;
 
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
 public class RollingCalendarTest {
 
-  String dailyPattern = "yyyy-MM-dd";
-
-   @Before
-   public void setUp() {
-       
-       // Most surprisingly, in certain environments (e.g. Windows 7), setting the default locale
-       // allows certain tests to pass which otherwise fail.
-       //
-       // These tests are:
-       //
-       //  checkCollisionFreeness("yyyy-WW", false);
-       //  checkCollisionFreeness("yyyy-ww", true);
-       //  checkCollisionFreeness("ww", false);
-       //  {
-       //    RollingCalendar rc = new RollingCalendar("yyyy-ww");
-       //    assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
-       //  }
-       // 
-       
-       Locale oldLocale = Locale.getDefault();
-       Locale.setDefault(oldLocale);
-   }
-
-   @After
-   public void tearDown() {
-   }
-   
+    String dailyPattern = "yyyy-MM-dd";
+
+
+    @BeforeEach
+    public void setUp() {
+
+        // Due to fist day of week differences, tests may fail
+        // certain locales, namely GB.
+        //
+        // These tests are:
+        //
+        // checkCollisionFreeness("yyyy-WW", false);
+        // checkCollisionFreeness("yyyy-ww", true);
+        // checkCollisionFreeness("ww", false);
+        // {
+        // RollingCalendar rc = new RollingCalendar("yyyy-ww");
+        // assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
+        // }
+        //
+    }
+
+    void set_EN_US_Locale() {
+        Locale usEn_Locale = Locale.forLanguageTag("en-US");
+        Locale.setDefault(usEn_Locale);
+    }
+
+    @AfterEach
+    public void tearDown() {
+
+    }
+
     @Test
     public void testPeriodicity() {
         {
@@ -95,7 +96,7 @@ public void testPeriodicity() {
         }
 
         {
-            RollingCalendar rc = new RollingCalendar("yyyy-WW");
+            RollingCalendar rc = new RollingCalendar("yyyy-W");
             assertEquals(PeriodicityType.TOP_OF_WEEK, rc.getPeriodicityType());
         }
     }
@@ -108,9 +109,9 @@ public void testVaryingNumberOfHourlyPeriods() {
 
         for (int p = 100; p > -100; p--) {
             long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008
-            Date result = rc.getEndOfNextNthPeriod(new Date(now), p);
+            Instant result = rc.getEndOfNextNthPeriod(Instant.ofEpochMilli(now), p);
             long expected = now - (now % (MILLIS_IN_HOUR)) + p * MILLIS_IN_HOUR;
-            assertEquals(expected, result.getTime());
+            assertEquals(expected, result.toEpochMilli());
         }
     }
 
@@ -121,13 +122,13 @@ public void testVaryingNumberOfDailyPeriods() {
 
         for (int p = 20; p > -100; p--) {
             long now = 1223325293589L; // Mon Oct 06 22:34:53 CEST 2008
-            Date nowDate = new Date(now);
-            Date result = rc.getEndOfNextNthPeriod(nowDate, p);
+            Instant nowInstant = Instant.ofEpochMilli(now);
+            Instant result = rc.getEndOfNextNthPeriod(nowInstant, p);
             long offset = rc.getTimeZone().getRawOffset() + rc.getTimeZone().getDSTSavings();
 
             long origin = now - ((now + offset) % (MILLIS_IN_DAY));
             long expected = origin + p * MILLIS_IN_DAY;
-            assertEquals("p=" + p, expected, result.getTime());
+            assertEquals(expected, result.toEpochMilli(), "p=" + p);
         }
     }
 
@@ -136,10 +137,14 @@ public void testVaryingNumberOfDailyPeriods() {
 
     @Test
     public void testBarrierCrossingComputation() {
-        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmmss", WED_2016_03_23_T_230705_CET, WED_2016_03_23_T_230705_CET + 3*CoreConstants.MILLIS_IN_ONE_SECOND, 3);
-        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmm", WED_2016_03_23_T_230705_CET, WED_2016_03_23_T_230705_CET + 3*CoreConstants.MILLIS_IN_ONE_MINUTE, 3);
-        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HH", WED_2016_03_23_T_230705_CET, WED_2016_03_23_T_230705_CET + 3*CoreConstants.MILLIS_IN_ONE_HOUR, 3);
-        checkPeriodBarriersCrossed("yyyy-MM-dd", WED_2016_03_23_T_230705_CET, WED_2016_03_23_T_230705_CET + 3*CoreConstants.MILLIS_IN_ONE_DAY, 3);
+        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmmss", WED_2016_03_23_T_230705_CET,
+                WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_SECOND, 3);
+        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HHmm", WED_2016_03_23_T_230705_CET,
+                WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_MINUTE, 3);
+        checkPeriodBarriersCrossed("yyyy-MM-dd'T'HH", WED_2016_03_23_T_230705_CET,
+                WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_HOUR, 3);
+        checkPeriodBarriersCrossed("yyyy-MM-dd", WED_2016_03_23_T_230705_CET,
+                WED_2016_03_23_T_230705_CET + 3 * CoreConstants.MILLIS_IN_ONE_DAY, 3);
     }
 
     private void checkPeriodBarriersCrossed(String pattern, long start, long end, int count) {
@@ -169,31 +174,35 @@ public void testCollisionFreenes() {
         checkCollisionFreeness("DDD", false);
 
         // 'u' is new to JDK 7
-        if (EnvUtil.isJDK7OrHigher()) {
-            checkCollisionFreeness("yyyy-MM-dd-uu", true);
-            checkCollisionFreeness("yyyy-MM-uu", false);
+//        if (EnvUtil.isJDK7OrHigher()) {
+//            checkCollisionFreeness("yyyy-MM-dd-uu", true);
+//            checkCollisionFreeness("yyyy-MM-uu", false);
+//        }
+
+
+        Locale oldLocale = Locale.getDefault();
+        try {
+            set_EN_US_Locale();
+            // weekly
+            checkCollisionFreeness("yyyy-MM-W", true);
+            dumpCurrentLocale(Locale.getDefault());
+            checkCollisionFreeness("yyyy-W", false);
+            checkCollisionFreeness("yyyy-ww", true);
+            checkCollisionFreeness("ww", false);
+        } finally {
+            if(oldLocale != null)
+                Locale.setDefault(oldLocale);
         }
-
-        // weekly
-        checkCollisionFreeness("yyyy-MM-WW", true);
-        dumpCurrentLocale(Locale.getDefault());
-        checkCollisionFreeness("yyyy-WW", false);
-        checkCollisionFreeness("yyyy-ww", true);
-        checkCollisionFreeness("ww", false);
     }
 
     private void dumpCurrentLocale(Locale locale) {
-       System.out.println("***Current default locale is "+locale);
-        
+        System.out.println("***Current default locale is " + locale);
+
     }
 
     private void checkCollisionFreeness(String pattern, boolean expected) {
         RollingCalendar rc = new RollingCalendar(pattern);
-        if (expected) {
-            assertTrue(rc.isCollisionFree());
-        } else {
-            assertFalse(rc.isCollisionFree());
-        }
+        assertEquals(expected, rc.isCollisionFree());
     }
 
     @Test
@@ -201,19 +210,19 @@ public void basicPeriodBarriersCrossed() {
         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
         // Thu Jan 26 19:46:58 CET 2017, GMT offset = -1h
         long start = 1485456418969L;
-        // Fri Jan 27 19:46:58 CET 2017,  GMT offset = -1h
-        long end = start+CoreConstants.MILLIS_IN_ONE_DAY;
+        // Fri Jan 27 19:46:58 CET 2017, GMT offset = -1h
+        long end = start + CoreConstants.MILLIS_IN_ONE_DAY;
         assertEquals(1, rc.periodBarriersCrossed(start, end));
     }
-    
+
     @Test
     public void testPeriodBarriersCrossedWhenGoingIntoDaylightSaving() {
         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
-        // Sun Mar 26 00:02:03 CET  2017, GMT offset = -1h
+        // Sun Mar 26 00:02:03 CET 2017, GMT offset = -1h
         long start = 1490482923333L;
-        // Mon Mar 27 00:02:03 CEST 2017,  GMT offset = -2h
+        // Mon Mar 27 00:02:03 CEST 2017, GMT offset = -2h
         long end = 1490565723333L;
-        
+
         assertEquals(1, rc.periodBarriersCrossed(start, end));
     }
 
@@ -221,24 +230,23 @@ public void testPeriodBarriersCrossedWhenGoingIntoDaylightSaving() {
     public void testPeriodBarriersCrossedWhenLeavingDaylightSaving() {
         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
         // Sun Oct 29 00:02:03 CEST 2017, GMT offset = -2h
-        long start = 1509228123333L;//1490482923333L+217*CoreConstants.MILLIS_IN_ONE_DAY-CoreConstants.MILLIS_IN_ONE_HOUR;
-        // Mon Oct 30 00:02:03 CET  2017,  GMT offset = -1h
-        long end = 1509228123333L+25*CoreConstants.MILLIS_IN_ONE_HOUR;
+        long start = 1509228123333L;// 1490482923333L+217*CoreConstants.MILLIS_IN_ONE_DAY-CoreConstants.MILLIS_IN_ONE_HOUR;
+        // Mon Oct 30 00:02:03 CET 2017, GMT offset = -1h
+        long end = 1509228123333L + 25 * CoreConstants.MILLIS_IN_ONE_HOUR;
         assertEquals(1, rc.periodBarriersCrossed(start, end));
     }
-    
+
     @Test
     public void testPeriodBarriersCrossedJustBeforeEnteringDaylightSaving() {
         RollingCalendar rc = new RollingCalendar(dailyPattern, TimeZone.getTimeZone("CET"), Locale.US);
         // Sun Mar 26 22:18:38 CEST 2017, GMT offset = +2h
         long start = 1490559518333L;
         System.out.println(new Date(start));
-        
+
         // Mon Mar 27 00:05:18 CEST 2017, GMT offset = +2h
         long end = 1490565918333L;
         System.out.println(new Date(end));
         assertEquals(1, rc.periodBarriersCrossed(start, end));
-        
-        
+
     }
 }
\ No newline at end of file
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemoverTest.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemoverTest.java
index eb5ac20064..61e7baa762 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemoverTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/helper/SizeAndTimeBasedArchiveRemoverTest.java
@@ -1,15 +1,31 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
 package ch.qos.logback.core.rolling.helper;
 
-import static org.junit.Assert.assertArrayEquals;
 
 import java.io.File;
+import java.time.Instant;
 import java.util.Date;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
 
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+
 public class SizeAndTimeBasedArchiveRemoverTest {
 
     Context context = new ContextBase();
@@ -24,11 +40,11 @@ public void smoke() {
         fileArray[0] = expected[1] = new File("/tmp/smoke-1970-01-01-0.gz");
         fileArray[1] = expected[0] = new File("/tmp/smoke-1970-01-01-1.gz");
 
-        remover.descendingSort(fileArray, new Date(0));
+        remover.descendingSort(fileArray, Instant.ofEpochMilli(0));
 
         assertArrayEquals(expected, fileArray);
     }
-    
+
     @Test
     public void badFilenames() {
         FileNamePattern fileNamePattern = new FileNamePattern("smoke-%d-%i.gz", context);
@@ -39,7 +55,7 @@ public void badFilenames() {
         fileArray[0] = expected[0] = new File("/tmp/smoke-1970-01-01-b.gz");
         fileArray[1] = expected[1] = new File("/tmp/smoke-1970-01-01-c.gz");
 
-        remover.descendingSort(fileArray, new Date(0));
+        remover.descendingSort(fileArray, Instant.ofEpochMilli(0));
 
         assertArrayEquals(expected, fileArray);
     }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ParentScaffoldingForRollingTests.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ParentScaffoldingForRollingTests.java
new file mode 100644
index 0000000000..81acb3c296
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ParentScaffoldingForRollingTests.java
@@ -0,0 +1,133 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.rolling.testUtil;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.ContextBase;
+import ch.qos.logback.core.encoder.EchoEncoder;
+import ch.qos.logback.core.rolling.helper.FileFilterUtil;
+import ch.qos.logback.core.rolling.helper.FileNamePattern;
+import ch.qos.logback.core.testUtil.CoreTestConstants;
+import ch.qos.logback.core.testUtil.RandomUtil;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Date;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class ParentScaffoldingForRollingTests {
+
+    protected EchoEncoder encoder = new EchoEncoder();
+    protected int diff = RandomUtil.getPositiveInt();
+    protected String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/";
+    protected List expectedFilenameList = new ArrayList();
+
+    Calendar calendar = Calendar.getInstance();
+    protected Context context = new ContextBase();
+
+    protected long currentTime; // initialized in setUp()
+    protected List> futureList = new ArrayList>();
+
+    public static void existenceCheck(List filenameList) {
+        for (String filename : filenameList) {
+            assertTrue(new File(filename).exists(), "File " + filename + " does not exist");
+        }
+    }
+
+    public static void reverseSortedContentCheck(String outputDirStr, int runLength, String prefix) throws IOException {
+        File[] fileArray = ScaffoldingForRollingTests.getFilesInDirectory(outputDirStr);
+        FileFilterUtil.reverseSortFileArrayByName(fileArray);
+        ScaffoldingForRollingTests.fileContentCheck(fileArray, runLength, prefix);
+    }
+
+    static protected void checkZipEntryName(String filepath, String pattern) throws IOException {
+        ZipFile zf = new ZipFile(filepath);
+
+        try {
+            Enumeration entries = zf.entries();
+            assert ((entries.hasMoreElements()));
+            ZipEntry firstZipEntry = entries.nextElement();
+            assert ((!entries.hasMoreElements()));
+            assertTrue(firstZipEntry.getName().matches(pattern));
+        } finally {
+            if (zf != null)
+                zf.close();
+        }
+    }
+
+    static protected void zipEntryNameCheck(List expectedFilenameList, String pattern) throws IOException {
+        for (String filepath : expectedFilenameList) {
+            checkZipEntryName(filepath, pattern);
+        }
+    }
+
+    public void setUp() {
+        context.setName("test");
+        calendar.set(Calendar.MILLISECOND, 333);
+        currentTime = 1760822446333L; //calendar.getTimeInMillis();
+
+    }
+
+    protected void add(Future future) {
+        if (future == null)
+            return;
+        if (!futureList.contains(future)) {
+            futureList.add(future);
+        }
+    }
+
+    protected void waitForJobsToComplete() {
+        for (Future future : futureList) {
+            try {
+                future.get(10, TimeUnit.SECONDS);
+            } catch (Exception e) {
+                new RuntimeException("unexpected exception while testing", e);
+            }
+        }
+        futureList.clear();
+    }
+
+    protected List filterElementsInListBySuffix(String suffix) {
+        List zipFiles = new ArrayList();
+        for (String filename : expectedFilenameList) {
+            if (filename.endsWith(suffix))
+                zipFiles.add(filename);
+        }
+        return zipFiles;
+    }
+
+    protected void addExpectedFileName_ByDate(String patternStr, long millis) {
+        FileNamePattern fileNamePattern = new FileNamePattern(patternStr, context);
+        String fn = fileNamePattern.convert(new Date(millis));
+        expectedFilenameList.add(fn);
+    }
+
+    protected String testId2FileName(String testId) {
+        return randomOutputDir + testId + ".log";
+    }
+
+    protected void incCurrentTime(long increment) {
+        currentTime += increment;
+    }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ScaffoldingForRollingTests.java b/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ScaffoldingForRollingTests.java
index e812e43e36..8667a2a4c8 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ScaffoldingForRollingTests.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/rolling/testUtil/ScaffoldingForRollingTests.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,30 +13,19 @@
  */
 package ch.qos.logback.core.rolling.testUtil;
 
-import ch.qos.logback.core.Context;
-import ch.qos.logback.core.ContextBase;
-import ch.qos.logback.core.encoder.EchoEncoder;
 import ch.qos.logback.core.rolling.helper.FileFilterUtil;
-import ch.qos.logback.core.rolling.helper.FileNamePattern;
-import ch.qos.logback.core.testUtil.CoreTestConstants;
 import ch.qos.logback.core.testUtil.FileToBufferUtil;
-import ch.qos.logback.core.testUtil.RandomUtil;
 
 import java.io.File;
 import java.io.IOException;
 import java.sql.Date;
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.Enumeration;
 import java.util.List;
-import java.util.concurrent.Future;
-import java.util.concurrent.TimeUnit;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
+import java.util.function.UnaryOperator;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Scaffolding for various rolling tests. Some assumptions are made: - rollover
@@ -44,32 +33,23 @@
  *
  * @author Ceki Gülcü
  */
-public class ScaffoldingForRollingTests {
+public class ScaffoldingForRollingTests extends ParentScaffoldingForRollingTests {
 
     static public final String DATE_PATTERN_WITH_SECONDS = "yyyy-MM-dd_HH_mm_ss";
     static public final String DATE_PATTERN_BY_DAY = "yyyy-MM-dd";
     static public final SimpleDateFormat SDF = new SimpleDateFormat(DATE_PATTERN_WITH_SECONDS);
+    public static final int MILLIS_IN_ONE_SECOND = 1000;
 
-    int diff = RandomUtil.getPositiveInt();
-    protected String randomOutputDir = CoreTestConstants.OUTPUT_DIR_PREFIX + diff + "/";
-    protected EchoEncoder encoder = new EchoEncoder();
-    protected Context context = new ContextBase();
-    protected List expectedFilenameList = new ArrayList();
-    protected long nextRolloverThreshold; // initialized in setUp()
-    protected long currentTime; // initialized in setUp()
-    protected List> futureList = new ArrayList>();
 
-    Calendar calendar = Calendar.getInstance();
+    protected long nextRolloverThreshold; // initialized in setUp()
 
     public void setUp() {
-        context.setName("test");
-        calendar.set(Calendar.MILLISECOND, 333);
-        currentTime = calendar.getTimeInMillis();
+        super.setUp();
         recomputeRolloverThreshold(currentTime);
     }
 
     public static void existenceCheck(String filename) {
-        assertTrue("File " + filename + " does not exist", new File(filename).exists());
+        assertTrue(new File(filename).exists(), "File " + filename + " does not exist");
     }
 
     public static File[] getFilesInDirectory(String outputDirStr) {
@@ -81,7 +61,8 @@ public static void fileContentCheck(File[] fileArray, int runLength, String pref
         fileContentCheck(fileArray, runLength, prefix, 0);
     }
 
-    public static void fileContentCheck(File[] fileArray, int runLength, String prefix, int runStart) throws IOException {
+    public static void fileContentCheck(File[] fileArray, int runLength, String prefix, int runStart)
+            throws IOException {
         List stringList = new ArrayList();
         for (File file : fileArray) {
             FileToBufferUtil.readIntoList(file, stringList);
@@ -99,24 +80,13 @@ public static void sortedContentCheck(String outputDirStr, int runLength, String
         sortedContentCheck(outputDirStr, runLength, prefix, 0);
     }
 
-    public static void sortedContentCheck(String outputDirStr, int runLength, String prefix, int runStart) throws IOException {
+    public static void sortedContentCheck(String outputDirStr, int runLength, String prefix, int runStart)
+            throws IOException {
         File[] fileArray = getFilesInDirectory(outputDirStr);
         FileFilterUtil.sortFileArrayByName(fileArray);
         fileContentCheck(fileArray, runLength, prefix, runStart);
     }
 
-    public static void reverseSortedContentCheck(String outputDirStr, int runLength, String prefix) throws IOException {
-        File[] fileArray = getFilesInDirectory(outputDirStr);
-        FileFilterUtil.reverseSortFileArrayByName(fileArray);
-        fileContentCheck(fileArray, runLength, prefix);
-    }
-
-    public static void existenceCheck(List filenameList) {
-        for (String filename : filenameList) {
-            assertTrue("File " + filename + " does not exist", new File(filename).exists());
-        }
-    }
-
     public static int existenceCount(List filenameList) {
         int existenceCounter = 0;
         for (String filename : filenameList) {
@@ -127,45 +97,39 @@ public static int existenceCount(List filenameList) {
         return existenceCounter;
     }
 
-    protected String testId2FileName(String testId) {
-        return randomOutputDir + testId + ".log";
+    protected String nullFileName(String testId) {
+        return null;
+    }
+
+    protected String impossibleFileName(String testId) {
+        throw new RuntimeException("implement");
     }
 
     // assuming rollover every second
     protected void recomputeRolloverThreshold(long ct) {
-        long delta = ct % 1000;
-        nextRolloverThreshold = (ct - delta) + 1000;
+        long delta = ct % MILLIS_IN_ONE_SECOND;
+        nextRolloverThreshold = (ct - delta) + MILLIS_IN_ONE_SECOND;
     }
 
     protected boolean passThresholdTime(long nextRolloverThreshold) {
         return currentTime >= nextRolloverThreshold;
     }
 
-    protected void incCurrentTime(long increment) {
-        currentTime += increment;
-    }
-
     protected Date getDateOfCurrentPeriodsStart() {
-        long delta = currentTime % 1000;
+        long delta = currentTime % MILLIS_IN_ONE_SECOND;
         return new Date(currentTime - delta);
     }
 
     protected Date getDateOfPreviousPeriodsStart() {
-        long delta = currentTime % 1000;
-        return new Date(currentTime - delta - 1000);
+        long delta = currentTime % MILLIS_IN_ONE_SECOND;
+        return new Date(currentTime - delta - MILLIS_IN_ONE_SECOND);
     }
 
     protected long getMillisOfCurrentPeriodsStart() {
-        long delta = currentTime % 1000;
+        long delta = currentTime % MILLIS_IN_ONE_SECOND;
         return (currentTime - delta);
     }
 
-    protected void addExpectedFileName_ByDate(String patternStr, long millis) {
-        FileNamePattern fileNamePattern = new FileNamePattern(patternStr, context);
-        String fn = fileNamePattern.convert(new Date(millis));
-        expectedFilenameList.add(fn);
-    }
-
     protected void addExpectedFileNamedIfItsTime_ByDate(String fileNamePatternStr) {
         if (passThresholdTime(nextRolloverThreshold)) {
             addExpectedFileName_ByDate(fileNamePatternStr, getMillisOfCurrentPeriodsStart());
@@ -182,20 +146,13 @@ protected void addExpectedFileName_ByDate(String outputDir, String testId, Date
         expectedFilenameList.add(fn);
     }
 
-    protected void addExpectedFileName_ByFileIndexCounter(String randomOutputDir, String testId, long millis, int fileIndexCounter, String compressionSuffix) {
-        String fn = randomOutputDir + testId + "-" + SDF.format(millis) + "-" + fileIndexCounter + ".txt" + compressionSuffix;
+    protected void addExpectedFileName_ByFileIndexCounter(String randomOutputDir, String testId, long millis,
+            int fileIndexCounter, String compressionSuffix) {
+        String fn = randomOutputDir + testId + "-" + SDF.format(millis) + "-" + fileIndexCounter + ".txt"
+                + compressionSuffix;
         expectedFilenameList.add(fn);
     }
 
-    protected List filterElementsInListBySuffix(String suffix) {
-        List zipFiles = new ArrayList();
-        for (String filename : expectedFilenameList) {
-            if (filename.endsWith(suffix))
-                zipFiles.add(filename);
-        }
-        return zipFiles;
-    }
-
     protected void addExpectedFileNamedIfItsTime_ByDate(String outputDir, String testId, boolean gzExtension) {
         if (passThresholdTime(nextRolloverThreshold)) {
             addExpectedFileName_ByDate(outputDir, testId, getDateOfCurrentPeriodsStart(), gzExtension);
@@ -203,12 +160,14 @@ protected void addExpectedFileNamedIfItsTime_ByDate(String outputDir, String tes
         }
     }
 
-    protected void massageExpectedFilesToCorresponToCurrentTarget(String fileName, boolean fileOptionIsSet) {
+    protected void massageExpectedFilesToCorresponToCurrentTarget(String testId,
+            UnaryOperator filenameFunction) {
         int lastIndex = expectedFilenameList.size() - 1;
         String last = expectedFilenameList.remove(lastIndex);
 
-        if (fileOptionIsSet) {
-            expectedFilenameList.add(fileName);
+        String filename = filenameFunction.apply(testId);
+        if (filename != null) {
+            expectedFilenameList.add(filename);
         } else if (last.endsWith(".gz")) {
             int lastLen = last.length();
             String stem = last.substring(0, lastLen - 3);
@@ -225,12 +184,6 @@ String addGZIfNotLast(int i) {
         }
     }
 
-    protected void zipEntryNameCheck(List expectedFilenameList, String pattern) throws IOException {
-        for (String filepath : expectedFilenameList) {
-            checkZipEntryName(filepath, pattern);
-        }
-    }
-
     protected void checkZipEntryMatchesZipFilename(List expectedFilenameList) throws IOException {
         for (String filepath : expectedFilenameList) {
             String stripped = stripStemFromZipFilename(filepath);
@@ -246,39 +199,5 @@ String stripStemFromZipFilename(String filepath) {
 
     }
 
-    void checkZipEntryName(String filepath, String pattern) throws IOException {
-        System.out.println("Checking [" + filepath + "]");
-        ZipFile zf = new ZipFile(filepath);
-
-        try {
-            Enumeration entries = zf.entries();
-            assert ((entries.hasMoreElements()));
-            ZipEntry firstZipEntry = entries.nextElement();
-            assert ((!entries.hasMoreElements()));
-            System.out.println("Testing zip entry [" + firstZipEntry.getName() + "]");
-            assertTrue(firstZipEntry.getName().matches(pattern));
-        } finally {
-            if (zf != null)
-                zf.close();
-        }
-    }
-
-    protected void add(Future future) {
-        if (future == null)
-            return;
-        if (!futureList.contains(future)) {
-            futureList.add(future);
-        }
-    }
 
-    protected void waitForJobsToComplete() {
-        for (Future future : futureList) {
-            try {
-                future.get(10, TimeUnit.SECONDS);
-            } catch (Exception e) {
-                new RuntimeException("unexpected exception while testing", e);
-            }
-        }
-        futureList.clear();
-    }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/sift/AppenderTrackerTest.java b/logback-core/src/test/java/ch/qos/logback/core/sift/AppenderTrackerTest.java
index ae123922d5..d596928cc5 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/sift/AppenderTrackerTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/sift/AppenderTrackerTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -19,14 +19,13 @@
 import ch.qos.logback.core.joran.spi.JoranException;
 import ch.qos.logback.core.read.ListAppender;
 import ch.qos.logback.core.testUtil.RandomUtil;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.junit.Assert.*;
-
 /**
  * Relatively straightforward unit tests for AppenderTracker.
  */
@@ -39,32 +38,32 @@ public class AppenderTrackerTest {
     String key = "k-" + diff;
     long now = 3000;
 
-    @Before
+    @BeforeEach
     public void setUp() {
     }
 
     @Test
     public void removeStaleComponentsShouldNotBomb() {
         appenderTracker.removeStaleComponents(now);
-        assertEquals(0, appenderTracker.getComponentCount());
+        Assertions.assertEquals(0, appenderTracker.getComponentCount());
     }
 
     @Test
     public void findingTheInexistentShouldNotBomb() {
-        assertNull(appenderTracker.find(key));
+        Assertions.assertNull(appenderTracker.find(key));
         now += AppenderTracker.DEFAULT_TIMEOUT + 1;
         appenderTracker.removeStaleComponents(now);
-        assertNull(appenderTracker.find(key));
+        Assertions.assertNull(appenderTracker.find(key));
     }
 
     @Test
     public void smoke() {
         Appender a = appenderTracker.getOrCreate(key, now);
-        assertTrue(a.isStarted());
+        Assertions.assertTrue(a.isStarted());
         now += AppenderTracker.DEFAULT_TIMEOUT + 1;
         appenderTracker.removeStaleComponents(now);
-        assertFalse(a.isStarted());
-        assertNull(appenderTracker.find(key));
+        Assertions.assertFalse(a.isStarted());
+        Assertions.assertNull(appenderTracker.find(key));
     }
 
     @Test
@@ -73,9 +72,9 @@ public void endOfLivedAppendersShouldBeRemovedAfterLingeringTimeout() {
         appenderTracker.endOfLife(key);
         now += AppenderTracker.LINGERING_TIMEOUT + 1;
         appenderTracker.removeStaleComponents(now);
-        assertFalse(a.isStarted());
+        Assertions.assertFalse(a.isStarted());
         a = appenderTracker.find(key);
-        assertNull(a);
+        Assertions.assertNull(a);
     }
 
     @Test
@@ -85,13 +84,13 @@ public void endOfLivedAppenderShouldBeAvailableDuringLingeringPeriod() {
         // clean
         appenderTracker.removeStaleComponents(now);
         Appender lingering = appenderTracker.getOrCreate(key, now);
-        assertTrue(lingering.isStarted());
-        assertTrue(a == lingering);
+        Assertions.assertTrue(lingering.isStarted());
+        Assertions.assertTrue(a == lingering);
         now += AppenderTracker.LINGERING_TIMEOUT + 1;
         appenderTracker.removeStaleComponents(now);
-        assertFalse(a.isStarted());
+        Assertions.assertFalse(a.isStarted());
         a = appenderTracker.find(key);
-        assertNull(a);
+        Assertions.assertNull(a);
     }
 
     @Test
@@ -105,9 +104,9 @@ public void trackerShouldHonorMaxComponentsParameter() {
         }
         // cleaning only happens in removeStaleComponents
         appenderTracker.removeStaleComponents(now++);
-        assertEquals(max, appenderTracker.allKeys().size());
-        assertNull(appenderTracker.find(key + "-" + 0));
-        assertFalse(appenderList.get(0).isStarted());
+        Assertions.assertEquals(max, appenderTracker.allKeys().size());
+        Assertions.assertNull(appenderTracker.find(key + "-" + 0));
+        Assertions.assertFalse(appenderList.get(0).isStarted());
     }
 
     @Test
@@ -121,20 +120,21 @@ public void trackerShouldHonorTimeoutParameter() {
         }
 
         long numComponentsCreated = timeout + 1;
-        assertEquals(numComponentsCreated, appenderTracker.allKeys().size());
+        Assertions.assertEquals(numComponentsCreated, appenderTracker.allKeys().size());
 
-        // cleaning only happens in removeStaleComponents. The first appender should timeout
+        // cleaning only happens in removeStaleComponents. The first appender should
+        // time out
         appenderTracker.removeStaleComponents(now++);
 
         // the first appender should have been removed
-        assertEquals(numComponentsCreated - 1, appenderTracker.allKeys().size());
-        assertNull(appenderTracker.find(key + "-" + 0));
-        assertFalse(appenderList.get(0).isStarted());
+        Assertions.assertEquals(numComponentsCreated - 1, appenderTracker.allKeys().size());
+        Assertions.assertNull(appenderTracker.find(key + "-" + 0));
+        Assertions.assertFalse(appenderList.get(0).isStarted());
 
         // the other appenders should be in the tracker
         for (int i = 1; i <= timeout; i++) {
-            assertNotNull(appenderTracker.find(key + "-" + i));
-            assertTrue(appenderList.get(i).isStarted());
+            Assertions.assertNotNull(appenderTracker.find(key + "-" + i));
+            Assertions.assertTrue(appenderList.get(i).isStarted());
         }
     }
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/sift/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/sift/PackageTest.java
deleted file mode 100644
index e641974dda..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/sift/PackageTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.sift;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-@RunWith(Suite.class)
-@SuiteClasses({ AppenderTrackerTest.class })
-public class PackageTest {
-}
\ No newline at end of file
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplLockTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplLockTest.java
index 2031848faf..ec0b87a313 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplLockTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplLockTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -14,14 +14,19 @@
 package ch.qos.logback.core.spi;
 
 import ch.qos.logback.core.Appender;
-import org.junit.Test;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+
+import java.util.concurrent.TimeUnit;
+
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
 /**
- * This test shows the general problem I described in LBCORE-67.
+ * This test shows the general problem I described in LOGBACK-102.
  *
- * In the two test cases below, an appender that throws an OutOfMemoryError
+ * In the two test cases below, an appender that throws an RuntimeException
  * while getName is called - but this is just an example to show the general
  * problem.
  *
@@ -45,21 +50,23 @@
  *
  * @author Joern Huxhorn
  */
+@Disabled
 public class AppenderAttachableImplLockTest {
 
     private AppenderAttachableImpl aai = new AppenderAttachableImpl();
 
     @SuppressWarnings("unchecked")
-    @Test(timeout = 5000)
+    @Test
+    @Timeout(value=1, unit = TimeUnit.SECONDS)
     public void getAppenderBoom() {
         Appender mockAppender1 = mock(Appender.class);
 
-        when(mockAppender1.getName()).thenThrow(new OutOfMemoryError("oops"));
+        when(mockAppender1.getName()).thenThrow(new RuntimeException("oops"));
         aai.addAppender(mockAppender1);
         try {
             // appender.getName called as a result of next statement
             aai.getAppender("foo");
-        } catch (OutOfMemoryError e) {
+        } catch (RuntimeException e) {
             // this leaves the read lock locked.
         }
 
@@ -70,10 +77,11 @@ public void getAppenderBoom() {
     }
 
     @SuppressWarnings("unchecked")
-    @Test(timeout = 5000)
+    @Test
+    @Timeout(value=1, unit = TimeUnit.SECONDS)
     public void detachAppenderBoom() throws InterruptedException {
         Appender mockAppender = mock(Appender.class);
-        when(mockAppender.getName()).thenThrow(new OutOfMemoryError("oops"));
+        when(mockAppender.getName()).thenThrow(new RuntimeException("oops"));
         mockAppender.doAppend(17);
 
         aai.addAppender(mockAppender);
@@ -83,7 +91,8 @@ public void run() {
                 try {
                     // appender.getName called as a result of next statement
                     aai.detachAppender("foo");
-                } catch (OutOfMemoryError e) {
+                } catch (RuntimeException e) {
+                    System.out.println("Caught " + e.toString());
                     // this leaves the write lock locked.
                 }
             }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java
index 682ab420c8..135393b368 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/AppenderAttachableImplTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,19 +13,15 @@
  */
 package ch.qos.logback.core.spi;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.util.Iterator;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
 
 import ch.qos.logback.core.Appender;
 import ch.qos.logback.core.helpers.NOPAppender;
+import org.junit.jupiter.api.Test;
 
 /**
  * This test case verifies all the methods of AppenderAttableImpl work properly.
@@ -36,12 +32,12 @@ public class AppenderAttachableImplTest {
 
     private AppenderAttachableImpl aai;
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
         aai = new AppenderAttachableImpl();
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
         aai = null;
     }
@@ -57,7 +53,7 @@ public void testAddAppender() throws Exception {
         ta.start();
         aai.addAppender(ta);
         int size = aai.appendLoopOnAppenders(event);
-        assertTrue("Incorrect number of appenders", size == 2);
+        Assertions.assertTrue(size == 2, "Incorrect number of appenders");
     }
 
     @Test
@@ -74,9 +70,9 @@ public void testIteratorForAppenders() throws Exception {
         while (iter.hasNext()) {
             ++size;
             Appender app = iter.next();
-            assertTrue("Bad Appender", app == ta || app == tab);
+            Assertions.assertTrue(app == ta || app == tab, "Bad Appender");
         }
-        assertTrue("Incorrect number of appenders", size == 2);
+        Assertions.assertTrue(size == 2, "Incorrect number of appenders");
     }
 
     @Test
@@ -92,14 +88,14 @@ public void getGetAppender() throws Exception {
         aai.addAppender(testOther);
 
         Appender a = aai.getAppender("testOther");
-        assertNotNull("Could not find appender", a);
-        assertTrue("Wrong appender", a == testOther);
+        Assertions.assertNotNull(a, "Could not find appender");
+        Assertions.assertTrue(a == testOther, "Wrong appender");
 
         a = aai.getAppender("test");
-        assertNotNull("Could not find appender", a);
-        assertTrue("Wrong appender", a == test);
+        Assertions.assertNotNull(a, "Could not find appender");
+        Assertions.assertTrue(a == test, "Wrong appender");
         a = aai.getAppender("NotThere");
-        assertNull("Appender was returned", a);
+        Assertions.assertNull(a, "Appender was returned");
     }
 
     @Test
@@ -111,8 +107,8 @@ public void testIsAttached() throws Exception {
         tab.setName("test");
         tab.start();
         aai.addAppender(tab);
-        assertTrue("Appender is not attached", aai.isAttached(ta));
-        assertTrue("Appender is not attached", aai.isAttached(tab));
+        Assertions.assertTrue(aai.isAttached(ta), "Appender is not attached");
+        Assertions.assertTrue(aai.isAttached(tab), "Appender is not attached");
     }
 
     @Test
@@ -124,10 +120,10 @@ public void testDetachAndStopAllAppenders() throws Exception {
         tab.setName("test");
         tab.start();
         aai.addAppender(tab);
-        assertTrue("Appender was not started", tab.isStarted());
+        Assertions.assertTrue(tab.isStarted(), "Appender was not started");
         aai.detachAndStopAllAppenders();
-        assertNull("Appender was not removed", aai.getAppender("test"));
-        assertFalse("Appender was not stopped", tab.isStarted());
+        Assertions.assertNull(aai.getAppender("test"), "Appender was not removed");
+        Assertions.assertFalse(tab.isStarted(), "Appender was not stopped");
     }
 
     @Test
@@ -139,9 +135,9 @@ public void testDetachAppender() throws Exception {
         tab.setName("test");
         tab.start();
         aai.addAppender(tab);
-        assertTrue("Appender not detached", aai.detachAppender(tab));
-        assertNull("Appender was not removed", aai.getAppender("test"));
-        assertFalse("Appender detach error", aai.detachAppender(tab));
+        Assertions.assertTrue(aai.detachAppender(tab),"Appender not detached");
+        Assertions.assertNull(aai.getAppender("test"), "Appender was not removed");
+        Assertions.assertFalse(aai.detachAppender(tab), "Appender detach error");
     }
 
     @Test
@@ -155,9 +151,9 @@ public void testDetachAppenderByName() throws Exception {
         tab.start();
         aai.addAppender(tab);
 
-        assertTrue(aai.detachAppender("test"));
-        assertTrue(aai.detachAppender("test1"));
-        assertFalse(aai.detachAppender("test1"));
+        Assertions.assertTrue(aai.detachAppender("test"));
+        Assertions.assertTrue(aai.detachAppender("test1"));
+        Assertions.assertFalse(aai.detachAppender("test1"));
     }
 
     private static class TestEvent {
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
index be1ca5d24c..2707441384 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerSimulator.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -122,7 +122,8 @@ public SimulationEvent(EventType eventType, String key, long timestamp) {
 
         @Override
         public String toString() {
-            return "SimulationEvent{" + "eventType=" + eventType + ", key='" + key + '\'' + ", timestamp=" + timestamp + '}';
+            return "SimulationEvent{" + "eventType=" + eventType + ", key='" + key + '\'' + ", timestamp=" + timestamp
+                    + '}';
         }
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerT.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerT.java
index 9bab1e064b..d858c1d7fa 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerT.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerT.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -19,7 +19,7 @@
 import java.util.*;
 
 /**
- * Another tracker implementtion for testing purposes only.
+ * Another tracker implementation for testing purposes only.
  *
  * @author Ceki Gülcü
  */
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerTest.java
index fe632400b2..2df1396ba5 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/CyclicBufferTrackerTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -14,11 +14,10 @@
 package ch.qos.logback.core.spi;
 
 import ch.qos.logback.core.helpers.CyclicBuffer;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 
 
 /**
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java
deleted file mode 100644
index 4cd173cefc..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/PackageTest.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.spi;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({ AppenderAttachableImplTest.class, AppenderAttachableImplLockTest.class, CyclicBufferTrackerTest.class,
-        ScenarioBasedCyclicBufferTrackerTest.class })
-public class PackageTest {
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java b/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java
index bb18c97352..b57ffe9a8c 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/spi/ScenarioBasedCyclicBufferTrackerTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,10 +13,10 @@
  */
 package ch.qos.logback.core.spi;
 
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
  * A
@@ -35,7 +35,7 @@ void verify() {
         assertEquals(t_at.lingererKeysAsOrderedList(), at.lingererKeysAsOrderedList());
     }
 
-    @Before
+    @BeforeEach
     public void setUp() {
         parameters.keySpaceLen = 128;
         parameters.maxTimestampInc = ComponentTracker.DEFAULT_TIMEOUT / 2;
diff --git a/logback-core/src/test/java/ch/qos/logback/core/status/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/status/PackageTest.java
deleted file mode 100755
index e54d9b09b5..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/status/PackageTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.status;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({ StatusBaseTest.class, StatusUtilTest.class })
-public class PackageTest {
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/status/StatusBaseTest.java b/logback-core/src/test/java/ch/qos/logback/core/status/StatusBaseTest.java
index caa5ea1dc8..24a61ccdcd 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/status/StatusBaseTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/status/StatusBaseTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -15,28 +15,32 @@
 
 import java.util.Iterator;
 
-import junit.framework.TestCase;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-public class StatusBaseTest extends TestCase {
+public class StatusBaseTest {
 
+
+    @Test
     public void testAddStatus() {
         {
             InfoStatus status = new InfoStatus("testing", this);
             status.add(new ErrorStatus("error", this));
             Iterator it = status.iterator();
-            assertTrue("No status was added", it.hasNext());
-            assertTrue("hasChilden method reported wrong result", status.hasChildren());
+            Assertions.assertTrue(it.hasNext(), "No status was added");
+            Assertions.assertTrue(status.hasChildren(), "hasChilden method reported wrong result");
         }
         {
             InfoStatus status = new InfoStatus("testing", this);
             try {
                 status.add(null);
-                fail("method should have thrown an Exception");
+                Assertions.fail("method should have thrown an Exception");
             } catch (NullPointerException ex) {
             }
         }
     }
 
+    @Test
     public void testRemoveStatus() {
         {
             InfoStatus status = new InfoStatus("testing", this);
@@ -44,16 +48,16 @@ public void testRemoveStatus() {
             status.add(error);
             boolean result = status.remove(error);
             Iterator it = status.iterator();
-            assertTrue("Remove failed", result);
-            assertFalse("No status was removed", it.hasNext());
-            assertFalse("hasChilden method reported wrong result", status.hasChildren());
+            Assertions.assertTrue(result, "Remove failed");
+            Assertions.assertFalse(it.hasNext(), "No status was removed");
+            Assertions.assertFalse(status.hasChildren(), "hasChilden method reported wrong result");
         }
         {
             InfoStatus status = new InfoStatus("testing", this);
             ErrorStatus error = new ErrorStatus("error", this);
             status.add(error);
             boolean result = status.remove(null);
-            assertFalse("Remove result was not false", result);
+            Assertions.assertFalse(result, "Remove result was not false");
         }
     }
 
@@ -63,7 +67,7 @@ public void testEffectiveLevel() {
             ErrorStatus status = new ErrorStatus("error", this);
             WarnStatus warn = new WarnStatus("warning", this);
             status.add(warn);
-            assertEquals("effective level misevaluated", status.getEffectiveLevel(), Status.ERROR);
+            Assertions.assertEquals(status.getEffectiveLevel(), Status.ERROR, "effective level misevaluated");
         }
 
         {
@@ -71,7 +75,7 @@ public void testEffectiveLevel() {
             InfoStatus status = new InfoStatus("info", this);
             WarnStatus warn = new WarnStatus("warning", this);
             status.add(warn);
-            assertEquals("effective level misevaluated", status.getEffectiveLevel(), Status.WARN);
+            Assertions.assertEquals(status.getEffectiveLevel(), Status.WARN, "effective level misevaluated");
         }
 
         {
@@ -81,7 +85,7 @@ public void testEffectiveLevel() {
             ErrorStatus error = new ErrorStatus("error", this);
             status.add(warn);
             warn.add(error);
-            assertEquals("effective level misevaluated", status.getEffectiveLevel(), Status.ERROR);
+            Assertions.assertEquals(status.getEffectiveLevel(), Status.ERROR, "effective level misevaluated");
         }
     }
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/status/StatusUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/status/StatusUtilTest.java
index 7dcf46123b..beeef70947 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/status/StatusUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/status/StatusUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -16,10 +16,10 @@
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
 import ch.qos.logback.core.CoreConstants;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * @author Ceki Gülcü
diff --git a/logback-core/src/test/java/ch/qos/logback/core/status/testUtil/StatusChecker.java b/logback-core/src/test/java/ch/qos/logback/core/status/testUtil/StatusChecker.java
new file mode 100644
index 0000000000..b9c38dc64c
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/status/testUtil/StatusChecker.java
@@ -0,0 +1,72 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+package ch.qos.logback.core.status.testUtil;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.status.StatusManager;
+import ch.qos.logback.core.status.StatusUtil;
+import org.junit.jupiter.api.Assertions;
+
+/**
+ * Extend StatusUtil with assertions.
+ */
+public class StatusChecker extends StatusUtil {
+
+    public StatusChecker(StatusManager sm) {
+        super(sm);
+    }
+
+    public StatusChecker(Context context) {
+        super(context);
+    }
+
+    public void assertContainsMatch(int level, String regex) {
+        Assertions.assertTrue(containsMatch(level, regex));
+    }
+
+    public void assertNoMatch(String regex) {
+        Assertions.assertFalse(containsMatch(regex));
+    }
+
+    public void assertContainsMatch(String regex) {
+        Assertions.assertTrue(containsMatch(regex));
+    }
+
+    public void assertContainsException(Class scanExceptionClass) {
+        Assertions.assertTrue(containsException(scanExceptionClass));
+    }
+
+    public void assertContainsException(Class scanExceptionClass, String msg) {
+        Assertions.assertTrue(containsException(scanExceptionClass, msg));
+    }
+    
+    public void assertIsErrorFree() {
+        Assertions.assertTrue(isErrorFree(0));
+    }
+
+    public void assertIsErrorFree(long treshhold) {
+        Assertions.assertTrue(isErrorFree(treshhold));
+    }
+
+    public void assertIsWarningOrErrorFree() {
+        Assertions.assertTrue(isWarningOrErrorFree(0));
+    }
+
+    public void assertErrorCount(int i) {
+    }
+
+    public void assertMatchCount(String regex, int expectedCount) {
+        Assertions.assertEquals(expectedCount, matchCount(regex));
+    }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/subst/NodeToStringTransformerTest.java b/logback-core/src/test/java/ch/qos/logback/core/subst/NodeToStringTransformerTest.java
index 9b293097b9..a509fd4058 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/subst/NodeToStringTransformerTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/subst/NodeToStringTransformerTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -15,10 +15,9 @@
 
 import ch.qos.logback.core.ContextBase;
 import ch.qos.logback.core.spi.ScanException;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 /**
  * @author Ceki Gülcü
@@ -27,7 +26,7 @@ public class NodeToStringTransformerTest {
 
     ContextBase propertyContainer0 = new ContextBase();
 
-    @Before
+    @BeforeEach
     public void setUp() {
         propertyContainer0.putProperty("k0", "v0");
         propertyContainer0.putProperty("zero", "0");
@@ -47,13 +46,13 @@ public void literal() throws ScanException {
         String input = "abv";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals(input, nodeToStringTransformer.transform());
+        Assertions.assertEquals(input, nodeToStringTransformer.transform());
     }
 
     void checkInputEqualsOutput(String input) throws ScanException {
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals(input, nodeToStringTransformer.transform());
+        Assertions.assertEquals(input, nodeToStringTransformer.transform());
     }
 
     @Test
@@ -69,7 +68,47 @@ public void variable() throws ScanException {
         String input = "${k0}";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("v0", nodeToStringTransformer.transform());
+        Assertions.assertEquals("v0", nodeToStringTransformer.transform());
+    }
+
+    @Test
+    public void recursion0() throws ScanException {
+        assumeCycle("${nested:-${nested}}");
+        assumeCycle("${:-${}}");
+        assumeCycle("${$a:-${a:-${a:-b}}");
+    }
+
+    @Test
+    public void recursion1() throws ScanException {
+        propertyContainer0.putProperty("k", "${a}");
+        propertyContainer0.putProperty("a", "${k}");
+        assumeCycle("${k}");
+    }
+
+    // Is this a feature or a bug?
+    @Test
+    public void cascadedTransformation() throws ScanException {
+        propertyContainer0.putProperty("x", "${a}");
+        propertyContainer0.putProperty("a", "b");
+        propertyContainer0.putProperty("b", "c");
+        String result = transform("${${x}}");
+        Assertions.assertEquals("c", result);
+    }
+
+    public void assumeCycle(String input) throws ScanException {
+
+        try {
+            transform(input);
+        } catch (IllegalArgumentException e) {
+            return;
+        }
+        Assertions.fail("circular reference should have been caught input=" + input);
+    }
+
+    private String transform(String input) throws ScanException {
+        Node node = makeNode(input);
+        NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
+        return nodeToStringTransformer.transform();
     }
 
     @Test
@@ -77,7 +116,7 @@ public void literalVariableLiteral() throws ScanException {
         String input = "a${k0}c";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("av0c", nodeToStringTransformer.transform());
+        Assertions.assertEquals("av0c", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -85,7 +124,7 @@ public void nestedVariable() throws ScanException {
         String input = "a${k${zero}}b";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("av0b", nodeToStringTransformer.transform());
+        Assertions.assertEquals("av0b", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -93,7 +132,7 @@ public void LOGBACK729() throws ScanException {
         String input = "${${k0}.jdbc.url}";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("http://..", nodeToStringTransformer.transform());
+        Assertions.assertEquals("http://..", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -102,7 +141,7 @@ public void LOGBACK744_withColon() throws ScanException {
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
         System.out.println(nodeToStringTransformer.transform());
-        assertEquals("%d{HH:mm:ss.SSS} host:local %logger{36} - %msg%n", nodeToStringTransformer.transform());
+        Assertions.assertEquals("%d{HH:mm:ss.SSS} host:local %logger{36} - %msg%n", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -110,7 +149,7 @@ public void loneColonShouldReadLikeAnyOtherCharacter() throws ScanException {
         String input = "java:comp/env/jdbc/datasource";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals(input, nodeToStringTransformer.transform());
+        Assertions.assertEquals(input, nodeToStringTransformer.transform());
     }
 
     @Test
@@ -118,7 +157,7 @@ public void withDefaultValue() throws ScanException {
         String input = "${k67:-b}c";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("bc", nodeToStringTransformer.transform());
+        Assertions.assertEquals("bc", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -126,7 +165,7 @@ public void defaultValueNestedAsVar() throws ScanException {
         String input = "a${k67:-x${k0}}c";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("axv0c", nodeToStringTransformer.transform());
+        Assertions.assertEquals("axv0c", nodeToStringTransformer.transform());
     }
 
     @Test
@@ -134,6 +173,24 @@ public void LOGBACK_1101() throws ScanException {
         String input = "a: {y}";
         Node node = makeNode(input);
         NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
-        assertEquals("a: {y}", nodeToStringTransformer.transform());
+        Assertions.assertEquals("a: {y}", nodeToStringTransformer.transform());
+    }
+
+    @Test
+    public void definedAsEmpty() throws ScanException {
+        propertyContainer0.putProperty("empty", "");
+        String input = "a=${empty}";
+        Node node = makeNode(input);
+        NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
+        Assertions.assertEquals("a=", nodeToStringTransformer.transform());
+    }
+
+    @Test
+    public void emptyDefault() throws ScanException {
+        propertyContainer0.putProperty("empty", "");
+        String input = "a=${undef:-${empty}}";
+        Node node = makeNode(input);
+        NodeToStringTransformer nodeToStringTransformer = new NodeToStringTransformer(node, propertyContainer0);
+        Assertions.assertEquals("a=", nodeToStringTransformer.transform());
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/subst/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/subst/PackageTest.java
deleted file mode 100644
index f50df44a39..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/subst/PackageTest.java
+++ /dev/null
@@ -1,22 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.subst;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-
-@RunWith(Suite.class)
-@Suite.SuiteClasses({ TokenizerTest.class, ParserTest.class, NodeToStringTransformerTest.class })
-public class PackageTest {
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/subst/ParserTest.java b/logback-core/src/test/java/ch/qos/logback/core/subst/ParserTest.java
index cbfbf1f9d7..7d684fb07d 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/subst/ParserTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/subst/ParserTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,20 +13,16 @@
  */
 package ch.qos.logback.core.subst;
 
-import static org.junit.Assert.assertEquals;
+import ch.qos.logback.core.spi.ScanException;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 
-import org.junit.Test;
-
-import ch.qos.logback.core.spi.ScanException;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
 
 /**
- * Created with IntelliJ IDEA.
- * User: ceki
- * Date: 05.08.12
- * Time: 00:15
- * To change this template use File | Settings | File Templates.
+ *
  */
 public class ParserTest {
 
@@ -128,6 +124,19 @@ public void withColon() throws ScanException {
         assertEquals(witness, node);
     }
 
+    @Test
+    public void withNoClosingBraces() throws ScanException {
+        Tokenizer tokenizer = new Tokenizer("a${b");
+        Parser parser = new Parser(tokenizer.tokenize());
+        try {
+            parser.parse();
+        } catch (IllegalArgumentException e) {
+            assertEquals("All tokens consumed but was expecting \"}\"", e.getMessage());
+            return;
+        }
+        fail();
+    }
+
     @Test
     public void nested() throws ScanException {
         Tokenizer tokenizer = new Tokenizer("a${b${c}}d");
@@ -155,6 +164,17 @@ public void withDefault() throws ScanException {
         assertEquals(witness, node);
     }
 
+    @Test
+    public void withEmptryDefault() throws ScanException {
+        Tokenizer tokenizer = new Tokenizer("${b:-}");
+        Parser parser = new Parser(tokenizer.tokenize());
+        Node node = parser.parse();
+        Node witness = new Node(Node.Type.VARIABLE, new Node(Node.Type.LITERAL, "b"));
+        witness.defaultPart = new Node(Node.Type.LITERAL, "");
+        assertEquals(witness, node);
+    }
+
+
     @Test
     public void defaultSeparatorOutsideOfAVariable() throws ScanException {
         Tokenizer tokenizer = new Tokenizer("{a:-b}");
diff --git a/logback-core/src/test/java/ch/qos/logback/core/subst/TokenizerTest.java b/logback-core/src/test/java/ch/qos/logback/core/subst/TokenizerTest.java
index dcabacd652..a2cbe114b3 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/subst/TokenizerTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/subst/TokenizerTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -14,12 +14,12 @@
 package ch.qos.logback.core.subst;
 
 import ch.qos.logback.core.spi.ScanException;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 import java.util.ArrayList;
 import java.util.List;
 
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class TokenizerTest {
 
@@ -125,7 +125,7 @@ public void colonFollowedByDollar() throws ScanException {
         witnessList.add(new Token(Token.Type.LITERAL, "b"));
         witnessList.add(Token.CURLY_RIGHT_TOKEN);
         assertEquals(witnessList, tokenList);
-        
+
     }
 
     @Test
@@ -161,7 +161,7 @@ public void literalEndingWithColon_LOGBACK_1140() throws ScanException {
         witnessList.add(new Token(Token.Type.LITERAL, ":"));
         assertEquals(witnessList, tokenList);
     }
-    
+
     @Test
     public void literalEndingWithDollar_LOGBACK_1149() throws ScanException {
         String input = "a$";
@@ -171,18 +171,18 @@ public void literalEndingWithDollar_LOGBACK_1149() throws ScanException {
         witnessList.add(new Token(Token.Type.LITERAL, "$"));
         assertEquals(witnessList, tokenList);
     }
-    
+
     @Test
     public void LOGBACK_1101() throws ScanException {
         String input = "a:{y}";
         Tokenizer tokenizer = new Tokenizer(input);
         List tokenList = tokenizer.tokenize();
         witnessList.add(new Token(Token.Type.LITERAL, "a"));
-        
+
         witnessList.add(new Token(Token.Type.LITERAL, ":"));
         witnessList.add(Token.CURLY_LEFT_TOKEN);
         witnessList.add(new Token(Token.Type.LITERAL, "y"));
-        
+
         witnessList.add(Token.CURLY_RIGHT_TOKEN);
         assertEquals(witnessList, tokenList);
     }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/FileTestUtil.java b/logback-core/src/test/java/ch/qos/logback/core/testUtil/FileTestUtil.java
deleted file mode 100644
index 2a9cbf0aef..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/FileTestUtil.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.testUtil;
-
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-
-
-/**
- * @author Ceki Gülcü
- */
-public class FileTestUtil {
-
-    public static void makeTestOutputDir() {
-        File target = new File(CoreTestConstants.TARGET_DIR);
-        if (target.exists() && target.isDirectory()) {
-            File testoutput = new File(CoreTestConstants.OUTPUT_DIR_PREFIX);
-            if (!testoutput.exists())
-                assertTrue(testoutput.mkdir());
-        } else {
-            throw new IllegalStateException(CoreTestConstants.TARGET_DIR + " does not exist");
-        }
-    }
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/testUtil/StatusChecker.java b/logback-core/src/test/java/ch/qos/logback/core/testUtil/StatusChecker.java
deleted file mode 100644
index 33fcec2e69..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/testUtil/StatusChecker.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.testUtil;
-
-import ch.qos.logback.core.Context;
-import ch.qos.logback.core.status.StatusManager;
-import ch.qos.logback.core.status.StatusUtil;
-
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertFalse;
-
-/**
- * Extend  StatusUtil with assertions.
- */
-public class StatusChecker extends StatusUtil {
-
-    public StatusChecker(StatusManager sm) {
-        super(sm);
-    }
-
-    public StatusChecker(Context context) {
-        super(context);
-    }
-
-    public void assertContainsMatch(int level, String regex) {
-        assertTrue(containsMatch(level, regex));
-    }
-
-    public void assertNoMatch(String regex) {
-        assertFalse(containsMatch(regex));
-    }
-    
-    public void assertContainsMatch(String regex) {
-        assertTrue(containsMatch(regex));
-    }
-
-    public void asssertContainsException(Class scanExceptionClass) {
-        assertTrue(containsException(scanExceptionClass));
-    }
-
-    public void assertIsErrorFree() {
-        assertTrue(isErrorFree(0));
-    }
-
-    public void assertIsWarningOrErrorFree() {
-        assertTrue(isWarningOrErrorFree(0));
-    }
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/COWArrayListTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/COWArrayListTest.java
index d490c4cbd1..c48eea7c41 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/COWArrayListTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/COWArrayListTest.java
@@ -1,21 +1,35 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 
 public class COWArrayListTest {
 
     Integer[] model = new Integer[0];
     COWArrayList cowaList = new COWArrayList(model);
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
     }
 
@@ -33,7 +47,6 @@ public void basicToArrayWithModel() {
         assertArrayEquals(new Integer[] { 1 }, result);
     }
 
-    
     @Test
     public void basicToArrayTyped() {
         cowaList.add(1);
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/CachingDateFotmatterTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/CachingDateFotmatterTest.java
new file mode 100755
index 0000000000..bad8e19af9
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/CachingDateFotmatterTest.java
@@ -0,0 +1,55 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.ZoneId;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class CachingDateFotmatterTest {
+
+    private final static String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm";
+
+    SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);
+    TimeZone perthTZ = TimeZone.getTimeZone("Australia/Perth");
+    TimeZone utcTZ = TimeZone.getTimeZone("UTC");
+
+    @BeforeEach
+    public void setUp() {
+        sdf.setTimeZone(utcTZ);
+    }
+
+    @Test
+    public void timeZoneIsTakenIntoAccount() throws ParseException {
+
+        ZoneId perthZone = ZoneId.of("Australia/Perth");
+        CachingDateFormatter cdf = new CachingDateFormatter(DATE_PATTERN, perthZone);
+
+        Date march26_2015_0949_UTC = sdf.parse("2015-03-26T09:49");
+        System.out.print(march26_2015_0949_UTC);
+
+        String result = cdf.format(march26_2015_0949_UTC.getTime());
+        // AWST (Perth) is 8 hours ahead of UTC
+        assertEquals("2015-03-26T17:49", result);
+    }
+
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/CachingFotmatterTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/CachingFotmatterTest.java
deleted file mode 100755
index 16c62c182d..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/util/CachingFotmatterTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package ch.qos.logback.core.util;
-
-import static org.junit.Assert.assertEquals;
-
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.TimeZone;
-
-import org.junit.Before;
-import org.junit.Test;
-
-public class CachingFotmatterTest {
-
-    final static String DATE_PATTERN = "yyyy-MM-dd'T'HH:mm";
-
-    SimpleDateFormat sdf = new SimpleDateFormat(DATE_PATTERN);
-    TimeZone perthTZ = TimeZone.getTimeZone("Australia/Perth");
-    TimeZone utcTZ = TimeZone.getTimeZone("UTC");
-
-    @Before
-    public void setUp() {
-        sdf.setTimeZone(utcTZ);
-    }
-
-    @Test
-    public void timeZoneIsTakenIntoAccount() throws ParseException {
-
-        CachingDateFormatter cdf = new CachingDateFormatter(DATE_PATTERN);
-        TimeZone perthTZ = TimeZone.getTimeZone("Australia/Perth");
-        cdf.setTimeZone(perthTZ);
-
-        Date march26_2015_0949_UTC = sdf.parse("2015-03-26T09:49");
-        System.out.print(march26_2015_0949_UTC);
-
-        String result = cdf.format(march26_2015_0949_UTC.getTime());
-        // AWST (Perth) is 8 hours ahead of UTC
-        assertEquals("2015-03-26T17:49", result);
-    }
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/CharSequenceToRegexMapperTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/CharSequenceToRegexMapperTest.java
index 4c93c0801a..cb21118122 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/CharSequenceToRegexMapperTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/CharSequenceToRegexMapperTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,21 +13,21 @@
  */
 package ch.qos.logback.core.util;
 
-import org.junit.After;
-import org.junit.Ignore;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 
 import java.text.DateFormatSymbols;
 import java.util.Locale;
 
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class CharSequenceToRegexMapperTest {
     static Locale KO_LOCALE = new Locale("ko", "KR");
     Locale oldLocale = Locale.getDefault();
 
-    @After
+    @AfterEach
     public void tearDown() {
         Locale.setDefault(oldLocale);
     }
@@ -49,14 +49,14 @@ public void emptyStringValuesShouldBeIgnoredByFindMinMaxLengthsInSymbols() {
     }
 
     @Test
-    @Ignore
+    @Disabled
     public void noneOfTheSymbolsAreOfZeroLengthForKorean() {
         Locale.setDefault(KO_LOCALE);
         noneOfTheSymbolsAreOfZeroLength();
     }
 
     @Test
-    @Ignore
+    @Disabled
     public void noneOfTheSymbolsAreOfZeroLengthForSwiss() {
         Locale.setDefault(new Locale("fr", "CH"));
         noneOfTheSymbolsAreOfZeroLength();
@@ -75,7 +75,7 @@ private void noneOfTheSymbolsAreOfZeroLength() {
     private void checkEmptyString(String[] symbolArray, String category) {
         for (String s : symbolArray) {
             System.out.println(category + " [" + s + "]");
-            assertTrue(category + " contains empty strings", s.length() > 0);
+            Assertions.assertTrue(s.length() > 0, category + " contains empty strings");
         }
     }
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/Compare.java b/logback-core/src/test/java/ch/qos/logback/core/util/Compare.java
index b76b8e1390..90324ce013 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/Compare.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/Compare.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -68,7 +68,8 @@ public static boolean regularFileCompare(String file1, String file2) throws File
         return bufferCompare(in1, in2, file1, file2);
     }
 
-    public static boolean bufferCompare(BufferedReader in1, BufferedReader in2, String file1, String file2) throws FileNotFoundException, IOException {
+    public static boolean bufferCompare(BufferedReader in1, BufferedReader in2, String file1, String file2)
+            throws FileNotFoundException, IOException {
 
         String s1;
         int lineCounter = 0;
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/ContentTypeUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/ContentTypeUtilTest.java
index 2750bab82a..ce64b54c81 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/ContentTypeUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/ContentTypeUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,30 +13,29 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.*;
-
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
 public class ContentTypeUtilTest {
 
     @Test
     public void smoke() {
         String contextType = "text/html";
-        assertTrue(ContentTypeUtil.isTextual(contextType));
-        assertEquals("html", ContentTypeUtil.getSubType(contextType));
+        Assertions.assertTrue(ContentTypeUtil.isTextual(contextType));
+        Assertions.assertEquals("html", ContentTypeUtil.getSubType(contextType));
     }
 
     @Test
     public void nullContext() {
         String contextType = null;
-        assertFalse(ContentTypeUtil.isTextual(contextType));
-        assertNull(ContentTypeUtil.getSubType(contextType));
+        Assertions.assertFalse(ContentTypeUtil.isTextual(contextType));
+        Assertions.assertNull(ContentTypeUtil.getSubType(contextType));
     }
 
     @Test
     public void emptySubtype() {
         String contextType = "text/";
-        assertTrue(ContentTypeUtil.isTextual(contextType));
-        assertNull(ContentTypeUtil.getSubType(contextType));
+        Assertions.assertTrue(ContentTypeUtil.isTextual(contextType));
+        Assertions.assertNull(ContentTypeUtil.getSubType(contextType));
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/ContextUtilAddOrReplaceShutdownHookTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/ContextUtilAddOrReplaceShutdownHookTest.java
new file mode 100644
index 0000000000..3322f92df3
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/ContextUtilAddOrReplaceShutdownHookTest.java
@@ -0,0 +1,67 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.ContextBase;
+import ch.qos.logback.core.hook.ShutdownHookBase;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Given the code executed in a shutdown hook is the last bit of code that is executed
+ * before the JVM exits, there is no way to test that a shutdown hook has been removed or
+ * is active.
+ *
+ * As such, this test cannot be automated.
+ */
+@Disabled
+class ContextUtilAddOrReplaceShutdownHookTest {
+
+    Context context = new ContextBase();
+    ContextUtil contextUtil = new ContextUtil(context);
+
+    @Test
+    public void smoke() {
+
+        contextUtil.addOrReplaceShutdownHook(new HelloShutdownHookHook(2));
+        contextUtil.addOrReplaceShutdownHook(new HelloShutdownHookHook(3));
+        contextUtil.addOrReplaceShutdownHook(new HelloShutdownHookHook(5));
+        // expect to see
+        // HelloShutdownHookHook{number=5}
+    }
+
+    static class HelloShutdownHookHook extends ShutdownHookBase {
+
+        int number;
+
+
+        public HelloShutdownHookHook(int number) {
+            this.number = number;
+
+        }
+
+        @Override
+        public void run() {
+            System.out.println(this);
+        }
+
+        @Override
+        public String toString() {
+            return "HelloShutdownHookHook{" + "number=" + number + '}';
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/DatePatternToRegexTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/DatePatternToRegexTest.java
index cd69d7a543..cb5b80c221 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/DatePatternToRegexTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/DatePatternToRegexTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,8 +13,6 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertTrue;
-
 import java.text.SimpleDateFormat;
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -22,18 +20,20 @@
 import java.util.Locale;
 
 import ch.qos.logback.core.rolling.helper.DateTokenConverter;
-import org.junit.BeforeClass;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
 
 import ch.qos.logback.core.CoreConstants;
 
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 public class DatePatternToRegexTest {
     static Calendar CAL_2009_08_3_NIGHT = Calendar.getInstance();
     static Calendar CAL_2009_08_3_MORNING = Calendar.getInstance();
     static Locale CZ_LOCALE = new Locale("cs", "CZ");
     static Locale KO_LOCALE = new Locale("ko", "KR");
 
-    @BeforeClass
+    @BeforeAll
     public static void setUpCalendars() {
         CAL_2009_08_3_NIGHT.set(2009, 8, 3, 21, 57, 16);
         CAL_2009_08_3_NIGHT.set(Calendar.MILLISECOND, 333);
@@ -139,7 +139,7 @@ void verify(SimpleDateFormat sdf, Calendar calendar, DateTokenConverter dtc)
         String regex = dtc.toRegex();
         // System.out.println("expected="+expected);
         // System.out.println(regex);
-        assertTrue("[" + expected + "] does not match regex [" + regex + "]", expected.matches(regex));
+        assertTrue(expected.matches(regex), "[" + expected + "] does not match regex [" + regex + "]");
     }
 
     private DateTokenConverter makeDTC(String datePattern) {
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/DefaultInvocationGateTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/DefaultInvocationGateTest.java
index 66dad5924d..a66142f6a5 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/DefaultInvocationGateTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/DefaultInvocationGateTest.java
@@ -1,10 +1,26 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
 package ch.qos.logback.core.util;
 
 import static ch.qos.logback.core.util.DefaultInvocationGate.DEFAULT_MASK;
 import static ch.qos.logback.core.util.DefaultInvocationGate.MASK_DECREASE_RIGHT_SHIFT_COUNT;
-import static org.junit.Assert.*;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 
 public class DefaultInvocationGateTest {
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/DurationTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/DurationTest.java
index 316675e94d..9c657ee526 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/DurationTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/DurationTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,9 +13,10 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class DurationTest {
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/EnvUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/EnvUtilTest.java
index 516ad26ef2..d23073c0e9 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/EnvUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/EnvUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 2017, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,12 +13,9 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
-import org.junit.After;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 import org.mockito.Mock;
 
 /**
@@ -28,100 +25,103 @@ public class EnvUtilTest {
     @Mock
     private String savedVersion = System.getProperty("java.version");
 
-    @After
+    @AfterEach
     public void tearDown() {
         System.setProperty("java.version", savedVersion);
     }
 
     @Test
     public void jdkVersion() {
-        assertEquals(4, EnvUtil.getJDKVersion("1.4.xx"));
-        assertEquals(5, EnvUtil.getJDKVersion("1.5"));
-        assertEquals(5, EnvUtil.getJDKVersion("1.5.xx"));
-        assertEquals(5, EnvUtil.getJDKVersion("1.5AA"));
-        assertEquals(9, EnvUtil.getJDKVersion("9EA"));
-        assertEquals(9, EnvUtil.getJDKVersion("9.0.1"));
-        assertEquals(18, EnvUtil.getJDKVersion("18.3+xx"));
+        Assertions.assertEquals(4, EnvUtil.getJDKVersion("1.4.xx"));
+        Assertions.assertEquals(5, EnvUtil.getJDKVersion("1.5"));
+        Assertions.assertEquals(5, EnvUtil.getJDKVersion("1.5.xx"));
+        Assertions.assertEquals(5, EnvUtil.getJDKVersion("1.5AA"));
+        Assertions.assertEquals(9, EnvUtil.getJDKVersion("9EA"));
+        Assertions.assertEquals(9, EnvUtil.getJDKVersion("9.0.1"));
+        Assertions.assertEquals(18, EnvUtil.getJDKVersion("18.3+xx"));
+        Assertions.assertEquals(21, EnvUtil.getJDKVersion("21.0.1"));
     }
 
+
+
     @Test
     public void testJava1_4() {
         System.setProperty("java.version", "1.4.xx");
 
-        assertFalse(EnvUtil.isJDK5());
-        assertFalse(EnvUtil.isJDK6OrHigher());
-        assertFalse(EnvUtil.isJDK7OrHigher());
+        Assertions.assertFalse(EnvUtil.isJDK5());
+        Assertions.assertFalse(EnvUtil.isJDK6OrHigher());
+        Assertions.assertFalse(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava1_5() {
         System.setProperty("java.version", "1.5");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertFalse(EnvUtil.isJDK6OrHigher());
-        assertFalse(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertFalse(EnvUtil.isJDK6OrHigher());
+        Assertions.assertFalse(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava1_5_x() {
         System.setProperty("java.version", "1.5.xx");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertFalse(EnvUtil.isJDK6OrHigher());
-        assertFalse(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertFalse(EnvUtil.isJDK6OrHigher());
+        Assertions.assertFalse(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava1_6() {
         System.setProperty("java.version", "1.6.xx");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertFalse(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertFalse(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava1_7() {
         System.setProperty("java.version", "1.7.xx");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertTrue(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava1_8() {
         System.setProperty("java.version", "1.8.xx");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertTrue(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava9() {
         System.setProperty("java.version", "9");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertTrue(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava9_1() {
         System.setProperty("java.version", "9.xx");
 
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertTrue(EnvUtil.isJDK7OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK7OrHigher());
     }
 
     @Test
     public void testJava18_3() {
         System.setProperty("java.version", "18.3+xx");
-        assertEquals(18, EnvUtil.getJDKVersion("18.3+xx"));
-        assertTrue(EnvUtil.isJDK5());
-        assertTrue(EnvUtil.isJDK6OrHigher());
-        assertTrue(EnvUtil.isJDK7OrHigher());
+        Assertions.assertEquals(18, EnvUtil.getJDKVersion("18.3+xx"));
+        Assertions.assertTrue(EnvUtil.isJDK5());
+        Assertions.assertTrue(EnvUtil.isJDK6OrHigher());
+        Assertions.assertTrue(EnvUtil.isJDK7OrHigher());
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/FileSizeTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/FileSizeTest.java
index a14619a7aa..58090eebb4 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/FileSizeTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/FileSizeTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,9 +13,9 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
+import org.junit.jupiter.api.Test;
 
-import org.junit.Test;
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
 public class FileSizeTest {
 
@@ -50,27 +50,26 @@ public void testValueOf() {
             assertEquals(5 * GB_CO, fs.getSize());
         }
     }
-    
-    
-    @Test 
+
+    @Test
     public void testToString() {
         {
             FileSize fs = new FileSize(8);
             assertEquals("8 Bytes", fs.toString());
         }
-        
+
         {
-            FileSize fs = new FileSize(8*1024+3);
+            FileSize fs = new FileSize(8 * 1024 + 3);
             assertEquals("8 KB", fs.toString());
         }
-        
+
         {
-            FileSize fs = new FileSize(8*1024*1024+3*1024);
+            FileSize fs = new FileSize(8 * 1024 * 1024 + 3 * 1024);
             assertEquals("8 MB", fs.toString());
         }
-        
+
         {
-            FileSize fs = new FileSize(8*1024*1024*1024L);
+            FileSize fs = new FileSize(8 * 1024 * 1024 * 1024L);
             assertEquals("8 GB", fs.toString());
         }
     }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/FileUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/FileUtilTest.java
index 691b5f4135..51165c1e0c 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/FileUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/FileUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,9 +13,6 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.ArrayList;
@@ -26,9 +23,12 @@
 import ch.qos.logback.core.ContextBase;
 import ch.qos.logback.core.testUtil.CoreTestConstants;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class FileUtilTest {
 
@@ -38,12 +38,12 @@ public class FileUtilTest {
     // test-output folder is not always clean
     int diff = new Random().nextInt(10000);
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
 
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
         for (File f : cleanupList) {
             f.delete();
@@ -83,7 +83,7 @@ public void basicCopyingWorks() throws IOException {
         File dirFile = new File(dir);
         dirFile.mkdir();
 
-        String src = CoreTestConstants.TEST_INPUT_PREFIX + "compress1.copy";
+        String src = CoreTestConstants.TEST_INPUT_PREFIX + "compress1.original";
         String target = CoreTestConstants.OUTPUT_DIR_PREFIX + "/fu" + diff + "/copyingWorks.txt";
 
         fileUtil.copy(src, target);
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/FixedRateInvocationGate.java b/logback-core/src/test/java/ch/qos/logback/core/util/FixedRateInvocationGate.java
index 279c605b12..c2b69a7d87 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/FixedRateInvocationGate.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/FixedRateInvocationGate.java
@@ -1,3 +1,17 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
 package ch.qos.logback.core.util;
 
 public class FixedRateInvocationGate implements InvocationGate {
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/InvocationGateTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/InvocationGateTest.java
new file mode 100644
index 0000000000..d96cbe9a0c
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/InvocationGateTest.java
@@ -0,0 +1,157 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import ch.qos.logback.core.testUtil.AbstractMultiThreadedHarness;
+import ch.qos.logback.core.testUtil.RunnableWithCounterAndDone;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
+
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+public class InvocationGateTest {
+
+    private static final int ONCE_EVERY = 100;
+    private static final int MAX_TRAVERSAL_COUNT = 10_000;
+    private static final int THREAD_COUNT = 16;
+
+
+    static final int MASK = 0xAFF;
+
+    AtomicLong currentTime = new AtomicLong(1);
+
+
+
+    @Test
+    public void smoke() {
+        InvocationGate sig = new SimpleInvocationGate();
+        long currentTime = SimpleInvocationGate.DEFAULT_INCREMENT.getMilliseconds() + 1;
+        assertFalse(sig.isTooSoon(currentTime));
+        currentTime++;
+        assertTrue(sig.isTooSoon(currentTime));
+    }
+
+    @Disabled
+    @Test
+    void checkThreadSafety() throws InterruptedException {
+        InvocationGate sig = new SimpleInvocationGate(Duration.buildByMilliseconds(1));
+
+        long initialTime = currentTime.get();
+        sig.isTooSoon(initialTime); // sync invocation gate with current time
+
+        AtomicInteger traversalCount = new AtomicInteger(0);
+        RunnableWithCounterAndDone[] runnables = buildRunnables(sig, traversalCount);
+        SimpleInvocationGateHarness harness = new SimpleInvocationGateHarness(traversalCount);
+        harness.execute(runnables);
+
+        int tc = traversalCount.get();
+        long ct = currentTime.get();
+        long diff = ct - initialTime - MAX_TRAVERSAL_COUNT;
+        int traversalCountMismatch = tc - MAX_TRAVERSAL_COUNT;
+        assertTrue(traversalCountMismatch >=0, "traversalCountMismatch must be a positive number");
+        int tolerance = 6;
+        assertTrue(traversalCountMismatch < tolerance, "traversalCountMismatch must be less than "+tolerance+ " actual value "+traversalCountMismatch);
+
+        assertTrue(diff >=0, "time difference must be a positive number");
+        assertTrue(diff < tolerance, "time difference must be less than "+tolerance+" actual value "+diff);
+
+
+
+    }
+
+    private RunnableWithCounterAndDone[] buildRunnables(InvocationGate invocationGate, AtomicInteger traversalCount ) {
+
+        RunnableWithCounterAndDone[] runnables = new RunnableWithCounterAndDone[THREAD_COUNT + 1];
+        runnables[0] = new TimeUpdater(currentTime);
+        for(int i = 1; i < runnables.length; i++) {
+            runnables[i] = new InvocationGateChecker(invocationGate, traversalCount);
+        }
+        return runnables;
+    }
+
+    class SimpleInvocationGateHarness extends AbstractMultiThreadedHarness {
+
+        AtomicInteger traversalCount;
+
+        public SimpleInvocationGateHarness(AtomicInteger traversalCount) {
+            this.traversalCount = traversalCount;
+        }
+
+        public void waitUntilEndCondition() throws InterruptedException {
+            while(traversalCount.get() < MAX_TRAVERSAL_COUNT) {
+                Thread.yield();
+            }
+        }
+    }
+
+    private class TimeUpdater extends RunnableWithCounterAndDone {
+
+        Random random = new Random(69923259L);
+        AtomicLong currentTime;
+        public TimeUpdater(AtomicLong currentTime) {
+            this.currentTime = currentTime;
+        }
+        @Override
+        public void run() {
+            sleep(10);
+            while(!isDone()) {
+                if (0 == random.nextInt(ONCE_EVERY)) {
+                    long ct = currentTime.incrementAndGet();
+                    if((ct & MASK) == MASK) {
+                        System.out.println("Time increment ct="+ct);
+                    }
+                }
+               Thread.yield();
+            }
+        }
+
+        private void sleep(int duration) {
+            try {
+                Thread.sleep(duration);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private class InvocationGateChecker extends RunnableWithCounterAndDone {
+
+        InvocationGate invocationGate;
+        AtomicInteger traversalCount;
+        public InvocationGateChecker(InvocationGate invocationGate, AtomicInteger traversalCount) {
+            this.invocationGate = invocationGate;
+            this.traversalCount = traversalCount;
+        }
+
+        @Override
+        public void run() {
+            while(!isDone()) {
+                if (!invocationGate.isTooSoon(currentTime.get())) {
+                    int tc = traversalCount.incrementAndGet();
+                    if((tc & MASK) == MASK) {
+                        System.out.println("traversalCount="+tc);
+                    }
+                }
+                Thread.yield();
+            }
+        }
+    }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/JNDIUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/JNDIUtilTest.java
new file mode 100644
index 0000000000..786c0ba1b8
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/JNDIUtilTest.java
@@ -0,0 +1,64 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.NamingException;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import ch.qos.logback.core.testUtil.CoreTestConstants;
+import ch.qos.logback.core.testUtil.MockInitialContextFactory;
+
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class JNDIUtilTest {
+
+    @Test
+    public void ensureJavaNameSpace() throws NamingException {
+
+        try {
+            Context ctxt = JNDIUtil.getInitialContext();
+            JNDIUtil.lookupString(ctxt, "ldap:...");
+        } catch (NamingException e) {
+            String excaptionMsg = e.getMessage();
+            if (excaptionMsg.startsWith(JNDIUtil.RESTRICTION_MSG))
+                return;
+            else {
+                fail("unexpected exception " + e);
+            }
+        }
+
+        fail("Should aNot yet implemented");
+    }
+
+    @Test
+    public void testToStringCast() throws NamingException {
+        Hashtable props = new Hashtable();
+        props.put(CoreTestConstants.JAVA_NAMING_FACTORY_INITIAL, MockInitialContextFactory.class.getCanonicalName());
+        Context ctxt = JNDIUtil.getInitialContext(props);
+        String x = JNDIUtil.lookupString(ctxt, "java:comp:/inexistent");
+        Assertions.assertNull(x);
+    }
+
+    public String castToString(Object input) {
+        String a = (String) input;
+        return a;
+    }
+
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/LocationUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/LocationUtilTest.java
index a3adc86062..f2993b8d92 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/LocationUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/LocationUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,8 +13,6 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.BufferedReader;
 import java.io.File;
 import java.io.IOException;
@@ -24,9 +22,8 @@
 import java.net.MalformedURLException;
 import java.net.URL;
 
-import org.junit.Test;
-
-import ch.qos.logback.core.util.LocationUtil;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
 /**
  * Unit tests for {@link LocationUtil}.
@@ -56,14 +53,18 @@ public void testExplicitClasspathUrlWithLeadingSlash() throws Exception {
         validateResource(url);
     }
 
-    @Test(expected = MalformedURLException.class)
+    @Test
     public void testExplicitClasspathUrlEmptyPath() throws Exception {
-        LocationUtil.urlForResource(LocationUtil.CLASSPATH_SCHEME);
+        Assertions.assertThrows(MalformedURLException.class, () -> {
+            LocationUtil.urlForResource(LocationUtil.CLASSPATH_SCHEME);
+        });
     }
 
-    @Test(expected = MalformedURLException.class)
+    @Test
     public void testExplicitClasspathUrlWithRootPath() throws Exception {
-        LocationUtil.urlForResource(LocationUtil.CLASSPATH_SCHEME + "/");
+        Assertions.assertThrows(MalformedURLException.class, () -> {
+            LocationUtil.urlForResource(LocationUtil.CLASSPATH_SCHEME + "/");
+        });
     }
 
     @Test
@@ -82,7 +83,7 @@ private void validateResource(URL url) throws IOException {
         try {
             BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
             String line = reader.readLine();
-            assertEquals(TEST_PATTERN, line);
+            Assertions.assertEquals(TEST_PATTERN, line);
         } finally {
             try {
                 inputStream.close();
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/MD5Test.java b/logback-core/src/test/java/ch/qos/logback/core/util/MD5Test.java
new file mode 100644
index 0000000000..ef95e59392
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/MD5Test.java
@@ -0,0 +1,34 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import org.junit.jupiter.api.Test;
+
+import java.security.NoSuchAlgorithmException;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class MD5Test {
+
+    @Test
+    void smoke() throws NoSuchAlgorithmException {
+        MD5Util md5Util = new MD5Util();
+        byte[] hash = md5Util.md5Hash("toto");
+        String asHexStr = md5Util.asHexString(hash);
+        assertEquals("f71dbe52628a3f83a77ab494817525c6", asHexStr);
+        System.out.println(asHexStr);
+
+    }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/OptionHelperTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/OptionHelperTest.java
index 7a87e97af5..08fed4fbcf 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/OptionHelperTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/OptionHelperTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,45 +13,50 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
+import static ch.qos.logback.core.subst.NodeToStringTransformer.CIRCULAR_VARIABLE_REFERENCE_DETECTED;
+import static ch.qos.logback.core.subst.Parser.EXPECTING_DATA_AFTER_LEFT_ACCOLADE;
+import static java.util.concurrent.TimeUnit.SECONDS;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import java.util.HashMap;
 import java.util.Map;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
+import ch.qos.logback.core.testUtil.RandomUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Test;
 
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
 import ch.qos.logback.core.joran.spi.JoranException;
+import ch.qos.logback.core.spi.ScanException;
+import org.junit.jupiter.api.Timeout;
 
 public class OptionHelperTest {
 
-    @Rule
-    public ExpectedException expectedException = ExpectedException.none();
-
     String text = "Testing ${v1} variable substitution ${v2}";
     String expected = "Testing if variable substitution works";
     Context context = new ContextBase();
     Map secondaryMap;
 
-    @Before
+    int diff = RandomUtil.getPositiveInt();
+
+    @BeforeEach
     public void setUp() throws Exception {
         secondaryMap = new HashMap();
     }
 
     @Test
-    public void testLiteral() {
+    public void testLiteral() throws ScanException {
         String noSubst = "hello world";
         String result = OptionHelper.substVars(noSubst, context);
         assertEquals(noSubst, result);
     }
 
     @Test
-    public void testUndefinedValues() {
+    public void testUndefinedValues() throws ScanException {
         String withUndefinedValues = "${axyz}";
 
         String result = OptionHelper.substVars(withUndefinedValues, context);
@@ -59,7 +64,7 @@ public void testUndefinedValues() {
     }
 
     @Test
-    public void testSubstVarsVariableNotClosed() {
+    public void testSubstVarsVariableNotClosed() throws ScanException {
         String noSubst = "testing if ${v1 works";
 
         try {
@@ -72,7 +77,7 @@ public void testSubstVarsVariableNotClosed() {
     }
 
     @Test
-    public void testSubstVarsContextOnly() {
+    public void testSubstVarsContextOnly() throws ScanException {
         context.putProperty("v1", "if");
         context.putProperty("v2", "works");
 
@@ -81,7 +86,7 @@ public void testSubstVarsContextOnly() {
     }
 
     @Test
-    public void testSubstVarsSystemProperties() {
+    public void testSubstVarsSystemProperties() throws ScanException {
         System.setProperty("v1", "if");
         System.setProperty("v2", "works");
 
@@ -93,7 +98,7 @@ public void testSubstVarsSystemProperties() {
     }
 
     @Test
-    public void testSubstVarsWithDefault() {
+    public void testSubstVarsWithDefault() throws ScanException {
         context.putProperty("v1", "if");
         String textWithDefault = "Testing ${v1} variable substitution ${v2:-toto}";
         String resultWithDefault = "Testing if variable substitution toto";
@@ -103,7 +108,7 @@ public void testSubstVarsWithDefault() {
     }
 
     @Test
-    public void testSubstVarsRecursive() {
+    public void testSubstVarsRecursive() throws ScanException {
         context.putProperty("v1", "if");
         context.putProperty("v2", "${v3}");
         context.putProperty("v3", "works");
@@ -113,7 +118,7 @@ public void testSubstVarsRecursive() {
     }
 
     @Test
-    public void testSubstVarsTwoLevelsDeep() {
+    public void testSubstVarsTwoLevelsDeep() throws ScanException {
         context.putProperty("v1", "if");
         context.putProperty("v2", "${v3}");
         context.putProperty("v3", "${v4}");
@@ -124,7 +129,7 @@ public void testSubstVarsTwoLevelsDeep() {
     }
 
     @Test
-    public void testSubstVarsTwoLevelsWithDefault() {
+    public void testSubstVarsTwoLevelsWithDefault() throws ScanException {
         // Example input taken from LOGBCK-943 bug report
         context.putProperty("APP_NAME", "LOGBACK");
         context.putProperty("ARCHIVE_SUFFIX", "archive.log");
@@ -135,19 +140,23 @@ public void testSubstVarsTwoLevelsWithDefault() {
         assertEquals("logs/archive/LOGBACK_trace_archive.log", result);
     }
 
-    @Test(timeout = 1000)
-    public void stubstVarsShouldNotGoIntoInfiniteLoop() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void stubstVarsShouldNotGoIntoInfiniteLoop() throws ScanException {
         context.putProperty("v1", "if");
         context.putProperty("v2", "${v3}");
         context.putProperty("v3", "${v4}");
         context.putProperty("v4", "${v2}c");
 
-        expectedException.expect(Exception.class);
-        OptionHelper.substVars(text, context);
+        Exception e = assertThrows(Exception.class, () -> {
+            OptionHelper.substVars(text, context);
+        });
+        String expectedMessage =  CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${v2} --> ${v3} --> ${v4} --> ${v2}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
-
+    
     @Test
-    public void nonCircularGraphShouldWork() {
+    public void nonCircularGraphShouldWork() throws ScanException {
         context.putProperty("A", "${B} and ${C}");
         context.putProperty("B", "${B1}");
         context.putProperty("B1", "B1-value");
@@ -158,100 +167,188 @@ public void nonCircularGraphShouldWork() {
         assertEquals("B1-value and C1-value and B1-value", result);
     }
 
-    @Test(timeout = 1000)
-    public void detectCircularReferences0() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void detectCircularReferences0() throws ScanException {
         context.putProperty("A", "${A}");
-
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${A}]");
-        OptionHelper.substVars("${A}", context);
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+           OptionHelper.substVars("${A}", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${A}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
-    @Test(timeout = 1000)
-    public void detectCircularReferences1() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void detectCircularReferences1() throws ScanException {
         context.putProperty("A", "${A}a");
 
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${A}]");
-        OptionHelper.substVars("${A}", context);
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("${A}", context);
+        });
+        
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${A}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
-    @Test(timeout = 1000)
-    public void detectCircularReferences2() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void detectCircularReferences2() throws ScanException {
         context.putProperty("A", "${B}");
         context.putProperty("B", "${C}");
         context.putProperty("C", "${A}");
 
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${B} --> ${C} --> ${A}]");
-        OptionHelper.substVars("${A}", context);
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+           OptionHelper.substVars("${A}", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${B} --> ${C} --> ${A}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
+    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=46755
     @Test
-    public void detectCircularReferencesInDefault() {
+    public void recursionErrorWithNullLiteralPayload() throws ScanException {
+
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+           OptionHelper.substVars("abc${AA$AA${}}}xyz", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${AA} --> ${}]";
+        assertEquals(expectedMessage, e.getMessage());
+    }
+
+    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=46892
+    @Test
+    public void leftAccoladeFollowedByDefaultStateWithNoLiteral() throws ScanException {
+        Exception e = assertThrows(ScanException.class, () -> {
+            OptionHelper.substVars("x{:-a}", context);
+        });
+        String expectedMessage = EXPECTING_DATA_AFTER_LEFT_ACCOLADE;
+        assertEquals(expectedMessage, e.getMessage());
+    }
+
+    // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=46966
+    @Test
+    public void nestedEmptyVariables() throws ScanException {
+
+        Exception e = assertThrows(Exception.class, () -> {
+            OptionHelper.substVars("${${${}}}", context);
+        });
+        String expectedMessage =  CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${ ?  ? } --> ${ ? } --> ${}]";
+        assertEquals(expectedMessage, e.getMessage());
+    }
+    
+    
+    
+    @Test
+    public void detectCircularReferencesInDefault() throws ScanException {
         context.putProperty("A", "${B:-${A}}");
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${B} --> ${A}]");
-        OptionHelper.substVars("${A}", context);
+     
+
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("${A}", context);
+        });
+
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${B} --> ${A}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
-    @Test(timeout = 1000)
-    public void detectCircularReferences3() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void detectCircularReferences3() throws ScanException {
         context.putProperty("A", "${B}");
         context.putProperty("B", "${C}");
         context.putProperty("C", "${A}");
 
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${B} --> ${C} --> ${A} --> ${B}]");
-        OptionHelper.substVars("${B} ", context);
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("${B} ", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED + "${B} --> ${C} --> ${A} --> ${B}]";
+        assertEquals(expectedMessage, e.getMessage());
+
     }
 
-    @Test(timeout = 1000)
-    public void detectCircularReferences4() {
+    @Test
+    @Timeout(value = 1, unit = SECONDS)
+    public void detectCircularReferences4() throws ScanException {
         context.putProperty("A", "${B}");
         context.putProperty("B", "${C}");
         context.putProperty("C", "${A}");
 
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${C} --> ${A} --> ${B} --> ${C}]");
-        OptionHelper.substVars("${C} and ${A}", context);
+        
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("${C} and ${A}", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${C} --> ${A} --> ${B} --> ${C}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
     @Test
-    public void detectCircularReferences5() {
+    public void detectCircularReferences5() throws ScanException {
         context.putProperty("A", "${B} and ${C}");
         context.putProperty("B", "${B1}");
         context.putProperty("B1", "B1-value");
         context.putProperty("C", "${C1}");
         context.putProperty("C1", "here's the loop: ${A}");
 
-        expectedException.expect(IllegalArgumentException.class);
-        expectedException.expectMessage("Circular variable reference detected while parsing input [${A} --> ${C} --> ${C1} --> ${A}]");
-        String result = OptionHelper.substVars("${A}", context);
-        System.err.println(result);
+        
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("${A}", context);
+        });
+        String expectedMessage = CIRCULAR_VARIABLE_REFERENCE_DETECTED+"${A} --> ${C} --> ${C1} --> ${A}]";
+        assertEquals(expectedMessage, e.getMessage());
     }
 
     @Test
-    public void defaultValueReferencingAVariable() {
+    public void defaultValueReferencingAVariable() throws ScanException {
         context.putProperty("v1", "k1");
         String result = OptionHelper.substVars("${undef:-${v1}}", context);
         assertEquals("k1", result);
     }
 
     @Test
-    public void jackrabbit_standalone() {
+    public void jackrabbit_standalone() throws ScanException {
         String r = OptionHelper.substVars("${jackrabbit.log:-${repo:-jackrabbit}/log/jackrabbit.log}", context);
         assertEquals("jackrabbit/log/jackrabbit.log", r);
     }
 
     @Test
-    public void doesNotThrowNullPointerExceptionForEmptyVariable() throws JoranException {
-        context.putProperty("var", "");
-        OptionHelper.substVars("${var}", context);
+    public void emptyVariableIsAccepted() throws JoranException, ScanException {
+        String varName = "var"+diff;
+        context.putProperty(varName, "");
+        String r = OptionHelper.substVars("x ${"+varName+"} b", context);
+        assertEquals("x  b", r);
+    }
+
+    // https://jira.qos.ch/browse/LOGBACK-1012
+    // conflicts with the idea that variables assigned the empty string are valid
+    @Disabled
+    @Test
+    public void defaultExpansionForEmptyVariables() throws JoranException, ScanException {
+        String varName = "var"+diff;
+        context.putProperty(varName, "");
+
+        String r = OptionHelper.substVars("x ${"+varName+":-def} b", context);
+        assertEquals("x def b", r);
+    }
+
+    @Test
+    public void emptyDefault() throws ScanException {
+        String r = OptionHelper.substVars("a${undefinedX:-}b", context);
+        assertEquals("ab", r);
     }
 
     @Test
-    public void trailingColon_LOGBACK_1140() {
+    public void openBraceAsLastCharacter() throws JoranException, ScanException {
+        Exception e = assertThrows(IllegalArgumentException.class, () -> {
+            OptionHelper.substVars("a{a{", context);
+        });
+        String expectedMessage = "All tokens consumed but was expecting \"}\"";
+        assertEquals(expectedMessage, e.getMessage());
+    }
+
+    
+    @Test
+    public void trailingColon_LOGBACK_1140() throws ScanException {
         String prefix = "c:";
         String suffix = "/tmp";
         context.putProperty("var", prefix);
@@ -259,8 +356,11 @@ public void trailingColon_LOGBACK_1140() {
         assertEquals(prefix + suffix, r);
     }
 
+
+
+
     @Test
-    public void curlyBraces_LOGBACK_1101() {
+    public void curlyBraces_LOGBACK_1101() throws ScanException {
         {
             String input = "foo{bar}";
             String r = OptionHelper.substVars(input, context);
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/PackageTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/PackageTest.java
deleted file mode 100755
index 6176f390e9..0000000000
--- a/logback-core/src/test/java/ch/qos/logback/core/util/PackageTest.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package ch.qos.logback.core.util;
-
-import org.junit.runner.RunWith;
-import org.junit.runners.Suite;
-import org.junit.runners.Suite.SuiteClasses;
-
-@RunWith(Suite.class)
-@SuiteClasses({ DurationTest.class, FileSizeTest.class, FileUtilTest.class, OptionHelperTest.class, StatusPrinterTest.class, TimeUtilTest.class,
-        ContentTypeUtilTest.class, CharSequenceToRegexMapperTest.class })
-public class PackageTest {
-}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/PrudentModeTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/PrudentModeTest.java
new file mode 100644
index 0000000000..d56096e7b6
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/PrudentModeTest.java
@@ -0,0 +1,133 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import ch.qos.logback.core.Context;
+import ch.qos.logback.core.ContextBase;
+import ch.qos.logback.core.FileAppender;
+import ch.qos.logback.core.encoder.EchoEncoder;
+import ch.qos.logback.core.status.testUtil.StatusChecker;
+import ch.qos.logback.core.testUtil.CoreTestConstants;
+import ch.qos.logback.core.testUtil.RandomUtil;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.fail;
+
+public class PrudentModeTest {
+
+    FileAppender fa = new FileAppender();
+    Context context = new ContextBase();
+
+    StatusChecker statusChecker = new StatusChecker(context);
+    int diff = RandomUtil.getPositiveInt();
+
+    String outputDirStr = CoreTestConstants.OUTPUT_DIR_PREFIX + "prudentLockTest-" + diff + "/";
+    String logfileStr = outputDirStr + "output.log";
+
+    private static final int THREAD_COUNT = 8;
+    private static final int LOOP_COUNT = 100/ THREAD_COUNT;
+
+    @BeforeEach
+    public void beforeEach() {
+        File outputDir = new File(outputDirStr);
+        if (!outputDir.mkdirs()) {
+            fail("failed to create folder " + outputDir);
+        }
+
+        fa.setContext(context);
+        fa.setName("FILE");
+        fa.setPrudent(true);
+        fa.setEncoder(new EchoEncoder());
+        fa.setFile(logfileStr);
+        fa.start();
+    }
+
+    // see https://jira.qos.ch/browse/LOGBACK-1754
+    @Test
+    public void assertNoOverlappingFileLockException () throws IOException {
+        CountDownLatch latch = new CountDownLatch(1);
+        List threads = new ArrayList<>(THREAD_COUNT);
+        for (int i = 0; i < THREAD_COUNT; i++) {
+            LoggerThread thread = new LoggerThread(latch, "message from thread " + i);
+            thread.start();
+            threads.add(thread);
+        }
+        latch.countDown();
+        int i = 0;
+        for (Thread thread : threads) {
+            try {
+                thread.join();
+            } catch (InterruptedException e) {
+                e.printStackTrace();
+            }
+        }
+        StatusPrinter.print(context);
+        statusChecker.assertIsWarningOrErrorFree();
+
+        fa.stop();
+
+        File file = new File(logfileStr);
+        List allLines = Files.readAllLines(file.toPath());
+        int actualLineCount = allLines.size();
+        assertEquals(LOOP_COUNT*THREAD_COUNT, actualLineCount, "unexpected line count "+actualLineCount);
+
+    }
+
+    class LoggerThread extends Thread {
+        private final CountDownLatch latch;
+        private final String message;
+
+        LoggerThread(CountDownLatch latch, String message) {
+            setDaemon(false);
+            this.latch = latch;
+            this.message = message;
+        }
+
+        @Override
+        public void run() {
+            try {
+                latch.await();
+                for (int i = 0; i < LOOP_COUNT; i++) {
+                    if ((i & 0x08) == 0) {
+                        // yield to spice it up
+                        Thread.yield();
+                    }
+                    PrudentModeTest.this.fa.doAppend(message + " i=" + i);
+                }
+            } catch (InterruptedException ex) {
+                Thread.currentThread().interrupt();
+            }
+        }
+
+        void delay(long millis) {
+            try {
+                Thread.sleep(millis);
+            } catch (InterruptedException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+}
+
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/ResilienceUtil.java b/logback-core/src/test/java/ch/qos/logback/core/util/ResilienceUtil.java
index fb6e206a26..909648f4fe 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/ResilienceUtil.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/ResilienceUtil.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,7 +13,6 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertTrue;
 
 import java.io.BufferedReader;
 import java.io.FileReader;
@@ -23,7 +22,8 @@
 
 public class ResilienceUtil {
 
-    static public void verify(String logfile, String regexp, long totalSteps, double successRatioLowerBound) throws NumberFormatException, IOException {
+    static public int countLines(String logfile, String regexp)
+            throws NumberFormatException, IOException {
         FileReader fr = new FileReader(logfile);
         BufferedReader br = new BufferedReader(fr);
         Pattern p = Pattern.compile(regexp);
@@ -47,11 +47,13 @@ static public void verify(String logfile, String regexp, long totalSteps, double
         fr.close();
         br.close();
 
-        int lowerLimit = (int) (totalSteps * successRatioLowerBound);
-        assertTrue("totalLines=" + totalLines + " less than " + lowerLimit, totalLines > lowerLimit);
-
-        // we want at least one gap indicating recuperation
-        assertTrue("gaps=" + gaps + " less than 1", gaps >= 1);
+        return totalLines;
+        
+//        int lowerLimit = (int) (totalSteps * successRatioLowerBound);
+//        assertTrue("totalLines=" + totalLines + " less than " + lowerLimit, totalLines > lowerLimit);
+//
+//        // we want at least one gap indicating recuperation
+//        assertTrue("gaps=" + gaps + " less than 1", gaps >= 1);
 
     }
 }
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/SimpleTimeBasedGuardTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/SimpleTimeBasedGuardTest.java
new file mode 100644
index 0000000000..c53fe605b4
--- /dev/null
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/SimpleTimeBasedGuardTest.java
@@ -0,0 +1,104 @@
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
+
+package ch.qos.logback.core.util;
+
+import org.junit.jupiter.api.*;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+
+class SimpleTimeBasedGuardTest {
+    //  Nov 24 20:10:52 UTC 2025
+    long UTC_2025_11_24H201052 = 1764015052000L;
+
+
+    private SimpleTimeBasedGuard guard;
+
+    @BeforeEach
+    void setUp() {
+        guard = new SimpleTimeBasedGuard();
+    }
+
+    @AfterEach
+    void tearDown() {
+        guard.clearCurrentTime();
+    }
+
+    @Test
+    void allowsTwoPer30Minutes() {
+        guard.setCurrentTimeMillis(UTC_2025_11_24H201052);
+
+        assertTrue(guard.allow());
+        assertTrue(guard.allow());
+        assertFalse(guard.allow());
+        assertFalse(guard.allow());
+
+        assertEquals(0, guard.getAllowsRemaining());
+    }
+
+    @Test
+    void resetsCompletelyAfter30Minutes() {
+        guard.setCurrentTimeMillis(UTC_2025_11_24H201052);
+        guard.allow();
+        guard.allow(); // use both
+
+        // Move exactly 30 minutes forward
+        guard.incCurrentTimeMillis(30 * 60 * 1000);
+
+        assertTrue(guard.allow());  // new window!
+        assertTrue(guard.allow());
+        assertFalse(guard.allow());
+    }
+
+    @Test
+    void windowsAreFloorAlignedTo30MinuteBoundaries() {
+        guard.setCurrentTimeMillis(10_000); // t=10s
+        guard.allow();
+        guard.allow();
+
+        guard.incCurrentTimeMillis(29 * 60 * 1000L + 48_000L); // still same window
+        assertFalse(guard.allow());
+
+        guard.setCurrentTimeMillis(30 * 60 * 1000L+10_001); // exactly 30 min → new window
+        assertTrue(guard.allow());
+    }
+
+    @Test
+    void worksWithRealTimeWhenNotInjected() throws Exception {
+        guard.clearCurrentTime();
+
+        assertTrue(guard.allow());
+        assertTrue(guard.allow());
+        assertFalse(guard.allow());
+
+        // Fast-forward real time by 30+ minutes (for CI, use fake time instead)
+        // In real app: just wait 30 min
+    }
+
+    @Test
+    void configurableLimitsWork() {
+        int fiveMinutesMs = 5 * 60_000;
+        SimpleTimeBasedGuard guard = new SimpleTimeBasedGuard(fiveMinutesMs, 3); // 3 allows every 5 minutes
+
+        guard.setCurrentTimeMillis(10);
+        assertTrue(guard.allow());
+        assertTrue(guard.allow());
+        assertTrue(guard.allow());
+        assertFalse(guard.allow());
+
+        guard.setCurrentTimeMillis(fiveMinutesMs + 10); // exactly 5 minutes later
+        assertTrue(guard.allow()); // fresh window!
+    }
+}
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/StatusListenerConfigHelperTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/StatusListenerConfigHelperTest.java
index 868f77ec5e..6529e714c0 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/StatusListenerConfigHelperTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/StatusListenerConfigHelperTest.java
@@ -1,29 +1,45 @@
-package ch.qos.logback.core.util;
+/*
+ * Logback: the reliable, generic, fast and flexible logging framework.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
+ *
+ * This program and the accompanying materials are dual-licensed under
+ * either the terms of the Eclipse Public License v2.0 as published by
+ * the Eclipse Foundation
+ *
+ *   or (per the licensee's choosing)
+ *
+ * under the terms of the GNU Lesser General Public License version 2.1
+ * as published by the Free Software Foundation.
+ */
 
-import static org.junit.Assert.*;
+package ch.qos.logback.core.util;
 
 import java.util.List;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
 
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
 import ch.qos.logback.core.status.OnConsoleStatusListener;
 import ch.qos.logback.core.status.StatusListener;
 import ch.qos.logback.core.status.StatusManager;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 public class StatusListenerConfigHelperTest {
 
     Context context = new ContextBase();
     StatusManager sm = context.getStatusManager();
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
     }
 
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/StatusPrinterTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/StatusPrinterTest.java
index 7afd6f16f6..bb191fd2d0 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/StatusPrinterTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/StatusPrinterTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,14 +13,12 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.*;
-
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 
 import ch.qos.logback.core.Context;
 import ch.qos.logback.core.ContextBase;
@@ -29,19 +27,21 @@
 import ch.qos.logback.core.status.Status;
 import ch.qos.logback.core.status.WarnStatus;
 
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
 public class StatusPrinterTest {
 
     ByteArrayOutputStream outputStream;
     PrintStream ps;
 
-    @Before
+    @BeforeEach
     public void setUp() throws Exception {
         outputStream = new ByteArrayOutputStream();
         ps = new PrintStream(outputStream);
         StatusPrinter.setPrintStream(ps);
     }
 
-    @After
+    @AfterEach
     public void tearDown() throws Exception {
         StatusPrinter.setPrintStream(System.out);
         ps = null;
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/StringCollectionUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/StringCollectionUtilTest.java
index a33420aa69..ab0f7fb38c 100755
--- a/logback-core/src/test/java/ch/qos/logback/core/util/StringCollectionUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/StringCollectionUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,14 +13,14 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertTrue;
-
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 /**
  * Unit tests for {@link StringCollectionUtil}.
diff --git a/logback-core/src/test/java/ch/qos/logback/core/util/TimeUtilTest.java b/logback-core/src/test/java/ch/qos/logback/core/util/TimeUtilTest.java
index 47763a2768..d019885916 100644
--- a/logback-core/src/test/java/ch/qos/logback/core/util/TimeUtilTest.java
+++ b/logback-core/src/test/java/ch/qos/logback/core/util/TimeUtilTest.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,13 +13,12 @@
  */
 package ch.qos.logback.core.util;
 
-import static org.junit.Assert.assertEquals;
-
 import java.util.Calendar;
 import java.util.Date;
 import java.util.TimeZone;
 
-import org.junit.Test;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
 public class TimeUtilTest {
 
@@ -30,8 +29,8 @@ public void testSecond() {
         // Mon Nov 20 18:05:18,000 CET 2006
         long expected = 1164042318000L;
         long computed = TimeUtil.computeStartOfNextSecond(now);
-        assertEquals(expected - now, 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     @Test
@@ -42,8 +41,8 @@ public void testMinute() {
         long expected = 1164042360000L;
 
         long computed = TimeUtil.computeStartOfNextMinute(now);
-        assertEquals(expected - now, 1000 * 42 + 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 1000 * 42 + 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     @Test
@@ -56,8 +55,8 @@ public void testHour() {
         expected = correctBasedOnTimeZone(expected);
 
         long computed = TimeUtil.computeStartOfNextHour(now);
-        assertEquals(expected - now, 1000 * (42 + 60 * 54) + 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 1000 * (42 + 60 * 54) + 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     @Test
@@ -70,8 +69,8 @@ public void testDay() {
         expected = correctBasedOnTimeZone(expected);
         long computed = TimeUtil.computeStartOfNextDay(now);
 
-        assertEquals(expected - now, 1000 * (3600 * 5 + 60 * 54 + 42) + 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 1000 * (3600 * 5 + 60 * 54 + 42) + 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     @Test
@@ -95,8 +94,8 @@ public void testWeek() {
         // System.out.println("now "+new Date(now));
         // System.out.println("computed "+new Date(computed));
         // System.out.println("expected "+new Date(expected));
-        assertEquals(expected - now, 1000 * (3600 * (5 + 24 * (5 + dayOffset)) + 60 * 54 + 42) + 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 1000 * (3600 * (5 + 24 * (5 + dayOffset)) + 60 * 54 + 42) + 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     @Test
@@ -109,8 +108,8 @@ public void testMonth() {
         expected = correctBasedOnTimeZone(expected);
 
         long computed = TimeUtil.computeStartOfNextMonth(now);
-        assertEquals(expected - now, 1000 * (3600 * (5 + 24 * 10) + 60 * 54 + 42) + 478);
-        assertEquals(expected, computed);
+        Assertions.assertEquals(expected - now, 1000 * (3600 * (5 + 24 * 10) + 60 * 54 + 42) + 478);
+        Assertions.assertEquals(expected, computed);
     }
 
     private long correctBasedOnTimeZone(long gmtLong) {
diff --git a/logback-core/src/test/scala/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_STest.scala b/logback-core/src/test/scala/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_STest.scala
index 6eb3da6980..4f82141957 100644
--- a/logback-core/src/test/scala/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_STest.scala
+++ b/logback-core/src/test/scala/ch/qos/logback/core/rolling/SizeAndTimeBasedFNATP_STest.scala
@@ -26,7 +26,7 @@ import ch.qos.logback.core.util.StatusPrinter
  * @author Ceki Gücü
  */
 class SizeAndTimeBasedFNATP_STest extends RollingScaffolding {
-  private var sizeAndTimeBasedFNATP: SizeAndTimeBasedFNATP[AnyRef] = null
+  private var sizeAndTimeBasedFileNamingAndTriggeringPolicy: SizeAndTimeBasedFNATP[AnyRef] = null
   private val rfa1: RollingFileAppender[AnyRef] = new RollingFileAppender[AnyRef]
   private val tbrp1: TimeBasedRollingPolicy[AnyRef] = new TimeBasedRollingPolicy[AnyRef]
   private val rfa2: RollingFileAppender[AnyRef] = new RollingFileAppender[AnyRef]
@@ -51,10 +51,10 @@ class SizeAndTimeBasedFNATP_STest extends RollingScaffolding {
   }
 
   private def initPolicies(rfa: RollingFileAppender[AnyRef], tbrp: TimeBasedRollingPolicy[AnyRef], filenamePattern: String, sizeThreshold: Int, givenTime: Long, lastCheck: Long): Unit = {
-    sizeAndTimeBasedFNATP = new SizeAndTimeBasedFNATP[AnyRef]
+    sizeAndTimeBasedFileNamingAndTriggeringPolicy = new SizeAndTimeBasedFNATP[AnyRef]
     tbrp.setContext(context)
-    sizeAndTimeBasedFNATP.setMaxFileSize("" + sizeThreshold)
-    tbrp.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFNATP)
+    sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize("" + sizeThreshold)
+    tbrp.setTimeBasedFileNamingAndTriggeringPolicy(sizeAndTimeBasedFileNamingAndTriggeringPolicy)
     tbrp.setFileNamePattern(filenamePattern)
     tbrp.setParent(rfa)
     tbrp.timeBasedFileNamingAndTriggeringPolicy.setCurrentTime(givenTime)
diff --git a/logback-core/src/test/scala/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_STest.scala b/logback-core/src/test/scala/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_STest.scala
index 8a5d8abca2..6ed9ab79dc 100644
--- a/logback-core/src/test/scala/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_STest.scala
+++ b/logback-core/src/test/scala/ch/qos/logback/core/rolling/TimeBasedRollingWithArchiveRemoval_STest.scala
@@ -125,27 +125,27 @@ class TimeBasedRollingWithArchiveRemoval_STest {
   }
 
   @Test def dailySizeBasedRollover {
-    var sizeAndTimeBasedFNATP: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
-    sizeAndTimeBasedFNATP.setMaxFileSize("10000")
-    tbfnatp = sizeAndTimeBasedFNATP
+    var sizeAndTimeBasedFileNamingAndTriggeringPolicy: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
+    sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize("10000")
+    tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy
     slashCount = computeSlashCount(DAILY_DATE_PATTERN)
     logOverMultiplePeriods(now, randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}-clean.%i.zip", MILLIS_IN_DAY, 5, 5 * 4)
     checkPatternCompliance(5 + 1 + slashCount, "\\d{4}-\\d{2}-\\d{2}-clean(\\.\\d)(.zip)?")
   }
 
   @Test def dailyChronologSizeBasedRollover {
-    var sizeAndTimeBasedFNATP: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
-    sizeAndTimeBasedFNATP.setMaxFileSize("10000")
-    tbfnatp = sizeAndTimeBasedFNATP
+    var sizeAndTimeBasedFileNamingAndTriggeringPolicy: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
+    sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize("10000")
+    tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy
     slashCount = 1
     logOverMultiplePeriods(now, randomOutputDir + "/%d{" + DAILY_DATE_PATTERN + "}/clean.%i.zip", MILLIS_IN_DAY, 5, 5 * 4)
     checkDirPatternCompliance(6)
   }
 
   @Test def dailyChronologSizeBasedRolloverWithSecondPhase {
-    var sizeAndTimeBasedFNATP: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
-    sizeAndTimeBasedFNATP.setMaxFileSize("10000")
-    tbfnatp = sizeAndTimeBasedFNATP
+    var sizeAndTimeBasedFileNamingAndTriggeringPolicy: SizeAndTimeBasedFNATP[AnyRef] = new SizeAndTimeBasedFNATP[AnyRef]
+    sizeAndTimeBasedFileNamingAndTriggeringPolicy.setMaxFileSize("10000")
+    tbfnatp = sizeAndTimeBasedFileNamingAndTriggeringPolicy
     slashCount = 1
     val maxHistory = 5
     val simulatedNumberOfPeriods = maxHistory * 4
diff --git a/logback-examples/pom.xml b/logback-examples/pom.xml
index 62d7ccab93..0249b3610a 100755
--- a/logback-examples/pom.xml
+++ b/logback-examples/pom.xml
@@ -8,7 +8,7 @@
   
     ch.qos.logback
     logback-parent
-    1.3.0-alpha5-SNAPSHOT
+    1.5.28-SNAPSHOT
   
 
   logback-examples
@@ -25,26 +25,22 @@
       ch.qos.logback
       logback-classic
     
-    
-      ch.qos.logback
-      logback-access
-    
     
       org.slf4j
       slf4j-ext
       ${slf4j.version}
     
     
-      log4j
-      log4j
-      1.2.17
+      ch.qos.reload4j
+      reload4j
+      1.2.18.4
     
     
-      javax.servlet
-      javax.servlet-api
+      jakarta.servlet
+      jakarta.servlet-api
       compile
       true
-    
+        
     
       org.fusesource.jansi
       jansi
@@ -84,20 +80,7 @@
           
         
       
-
-      
-        org.codehaus.mojo
-        cobertura-maven-plugin
-        ${cobertura.maven.plugin.version}
-        
-          
-            
-              chapters/**/*.class
-            
-          
-        
-      
-
+       
    
   
-
\ No newline at end of file
+
diff --git a/logback-examples/src/main/java/chapters/appenders/ConfigurationTester.java b/logback-examples/src/main/java/chapters/appenders/ConfigurationTester.java
index 14708be6ec..7bba2d38bf 100644
--- a/logback-examples/src/main/java/chapters/appenders/ConfigurationTester.java
+++ b/logback-examples/src/main/java/chapters/appenders/ConfigurationTester.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java b/logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java
index cae5883589..c22741c364 100755
--- a/logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java
+++ b/logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/IO.java b/logback-examples/src/main/java/chapters/appenders/IO.java
index 0b24547ddf..108d967f38 100644
--- a/logback-examples/src/main/java/chapters/appenders/IO.java
+++ b/logback-examples/src/main/java/chapters/appenders/IO.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/IOPerformance.java b/logback-examples/src/main/java/chapters/appenders/IOPerformance.java
index f813400a0e..ae3512ece8 100644
--- a/logback-examples/src/main/java/chapters/appenders/IOPerformance.java
+++ b/logback-examples/src/main/java/chapters/appenders/IOPerformance.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java b/logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java
index b097e5f37d..007e84539e 100644
--- a/logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java
+++ b/logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/mail/EMail.java b/logback-examples/src/main/java/chapters/appenders/mail/EMail.java
index f3f57eac18..ec046ec295 100644
--- a/logback-examples/src/main/java/chapters/appenders/mail/EMail.java
+++ b/logback-examples/src/main/java/chapters/appenders/mail/EMail.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/mail/Marked_EMail.java b/logback-examples/src/main/java/chapters/appenders/mail/Marked_EMail.java
index 41bc75f0e3..302afe774f 100644
--- a/logback-examples/src/main/java/chapters/appenders/mail/Marked_EMail.java
+++ b/logback-examples/src/main/java/chapters/appenders/mail/Marked_EMail.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/sift/SiftExample.java b/logback-examples/src/main/java/chapters/appenders/sift/SiftExample.java
index 74b9a2cc0a..18b314b821 100644
--- a/logback-examples/src/main/java/chapters/appenders/sift/SiftExample.java
+++ b/logback-examples/src/main/java/chapters/appenders/sift/SiftExample.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/socket/ConsolePluginClient.java b/logback-examples/src/main/java/chapters/appenders/socket/ConsolePluginClient.java
index 12d6ebb0f8..35bf048aaf 100755
--- a/logback-examples/src/main/java/chapters/appenders/socket/ConsolePluginClient.java
+++ b/logback-examples/src/main/java/chapters/appenders/socket/ConsolePluginClient.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/socket/SocketClient1.java b/logback-examples/src/main/java/chapters/appenders/socket/SocketClient1.java
index 2ca8f1e045..e136eb5748 100755
--- a/logback-examples/src/main/java/chapters/appenders/socket/SocketClient1.java
+++ b/logback-examples/src/main/java/chapters/appenders/socket/SocketClient1.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/socket/SocketClient2.java b/logback-examples/src/main/java/chapters/appenders/socket/SocketClient2.java
index 7681f8909d..8259d88276 100644
--- a/logback-examples/src/main/java/chapters/appenders/socket/SocketClient2.java
+++ b/logback-examples/src/main/java/chapters/appenders/socket/SocketClient2.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/appenders/sub/sample/Bar.java b/logback-examples/src/main/java/chapters/appenders/sub/sample/Bar.java
index ee50aefe47..bf647faf08 100644
--- a/logback-examples/src/main/java/chapters/appenders/sub/sample/Bar.java
+++ b/logback-examples/src/main/java/chapters/appenders/sub/sample/Bar.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/architecture/Bar.java b/logback-examples/src/main/java/chapters/architecture/Bar.java
index 5496083adb..017c540168 100644
--- a/logback-examples/src/main/java/chapters/architecture/Bar.java
+++ b/logback-examples/src/main/java/chapters/architecture/Bar.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/architecture/MyAppWithConfigFile.java b/logback-examples/src/main/java/chapters/architecture/MyAppWithConfigFile.java
index 0a90c842f6..c4a6751844 100644
--- a/logback-examples/src/main/java/chapters/architecture/MyAppWithConfigFile.java
+++ b/logback-examples/src/main/java/chapters/architecture/MyAppWithConfigFile.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/architecture/SelectionRule.java b/logback-examples/src/main/java/chapters/architecture/SelectionRule.java
index b1973cb944..ac40381d08 100644
--- a/logback-examples/src/main/java/chapters/architecture/SelectionRule.java
+++ b/logback-examples/src/main/java/chapters/architecture/SelectionRule.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/configuration/AddStatusListenerApp.java b/logback-examples/src/main/java/chapters/configuration/AddStatusListenerApp.java
index 85ef81e1e3..4b72130df3 100644
--- a/logback-examples/src/main/java/chapters/configuration/AddStatusListenerApp.java
+++ b/logback-examples/src/main/java/chapters/configuration/AddStatusListenerApp.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/configuration/Foo.java b/logback-examples/src/main/java/chapters/configuration/Foo.java
index 9eb16c9726..ead3ff69c6 100644
--- a/logback-examples/src/main/java/chapters/configuration/Foo.java
+++ b/logback-examples/src/main/java/chapters/configuration/Foo.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/configuration/MyApp1.java b/logback-examples/src/main/java/chapters/configuration/MyApp1.java
index 04cd399f2f..712ce2fd0a 100644
--- a/logback-examples/src/main/java/chapters/configuration/MyApp1.java
+++ b/logback-examples/src/main/java/chapters/configuration/MyApp1.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/configuration/MyApp2.java b/logback-examples/src/main/java/chapters/configuration/MyApp2.java
index 9ad01fb97f..da46002df1 100644
--- a/logback-examples/src/main/java/chapters/configuration/MyApp2.java
+++ b/logback-examples/src/main/java/chapters/configuration/MyApp2.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/configuration/MyApp3.java b/logback-examples/src/main/java/chapters/configuration/MyApp3.java
index 8c87b418e6..862cc9a5bc 100644
--- a/logback-examples/src/main/java/chapters/configuration/MyApp3.java
+++ b/logback-examples/src/main/java/chapters/configuration/MyApp3.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/filters/FilterEvents.java b/logback-examples/src/main/java/chapters/filters/FilterEvents.java
index 43d91d44ff..cac8512e21 100644
--- a/logback-examples/src/main/java/chapters/filters/FilterEvents.java
+++ b/logback-examples/src/main/java/chapters/filters/FilterEvents.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/filters/GoMDC.java b/logback-examples/src/main/java/chapters/filters/GoMDC.java
index 4e6d137593..6d7171dcae 100644
--- a/logback-examples/src/main/java/chapters/filters/GoMDC.java
+++ b/logback-examples/src/main/java/chapters/filters/GoMDC.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/filters/SampleFilter.java b/logback-examples/src/main/java/chapters/filters/SampleFilter.java
index a08fcb3687..f910ff0980 100644
--- a/logback-examples/src/main/java/chapters/filters/SampleFilter.java
+++ b/logback-examples/src/main/java/chapters/filters/SampleFilter.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java b/logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java
index 804efb6904..4f47e22c2d 100644
--- a/logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java
+++ b/logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/introduction/HelloWorld1.java b/logback-examples/src/main/java/chapters/introduction/HelloWorld1.java
index d5c4bcf7c4..a459ecb48a 100644
--- a/logback-examples/src/main/java/chapters/introduction/HelloWorld1.java
+++ b/logback-examples/src/main/java/chapters/introduction/HelloWorld1.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/introduction/HelloWorld2.java b/logback-examples/src/main/java/chapters/introduction/HelloWorld2.java
index 72a359d79e..0d2b8602a6 100644
--- a/logback-examples/src/main/java/chapters/introduction/HelloWorld2.java
+++ b/logback-examples/src/main/java/chapters/introduction/HelloWorld2.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java b/logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java
index 5f1dec606a..ea934dd786 100644
--- a/logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java
+++ b/logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java b/logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java
index 45ae4a0814..dab8237d23 100644
--- a/logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java
+++ b/logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/MySampleConverter.java b/logback-examples/src/main/java/chapters/layouts/MySampleConverter.java
index d0724ccd4b..576d10bfa2 100644
--- a/logback-examples/src/main/java/chapters/layouts/MySampleConverter.java
+++ b/logback-examples/src/main/java/chapters/layouts/MySampleConverter.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -13,7 +13,6 @@
  */
 package chapters.layouts;
 
-import ch.qos.logback.classic.Level;
 import ch.qos.logback.classic.pattern.ClassicConverter;
 import ch.qos.logback.classic.spi.ILoggingEvent;
 
diff --git a/logback-examples/src/main/java/chapters/layouts/MySampleLayout.java b/logback-examples/src/main/java/chapters/layouts/MySampleLayout.java
index 2b7cc2436f..987ae1beaa 100644
--- a/logback-examples/src/main/java/chapters/layouts/MySampleLayout.java
+++ b/logback-examples/src/main/java/chapters/layouts/MySampleLayout.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/MySampleLayout2.java b/logback-examples/src/main/java/chapters/layouts/MySampleLayout2.java
index 44e9c96789..06340dd221 100644
--- a/logback-examples/src/main/java/chapters/layouts/MySampleLayout2.java
+++ b/logback-examples/src/main/java/chapters/layouts/MySampleLayout2.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/PatternSample.java b/logback-examples/src/main/java/chapters/layouts/PatternSample.java
index 1ae02774ff..6db1d01f0b 100644
--- a/logback-examples/src/main/java/chapters/layouts/PatternSample.java
+++ b/logback-examples/src/main/java/chapters/layouts/PatternSample.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/SampleLogging.java b/logback-examples/src/main/java/chapters/layouts/SampleLogging.java
index fcda49d880..00ac1f6d45 100644
--- a/logback-examples/src/main/java/chapters/layouts/SampleLogging.java
+++ b/logback-examples/src/main/java/chapters/layouts/SampleLogging.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/TestException.java b/logback-examples/src/main/java/chapters/layouts/TestException.java
index bcc49f80bd..191b2eca90 100644
--- a/logback-examples/src/main/java/chapters/layouts/TestException.java
+++ b/logback-examples/src/main/java/chapters/layouts/TestException.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/layouts/TrivialMain.java b/logback-examples/src/main/java/chapters/layouts/TrivialMain.java
index 88a3410d0c..adc9ea39bd 100644
--- a/logback-examples/src/main/java/chapters/layouts/TrivialMain.java
+++ b/logback-examples/src/main/java/chapters/layouts/TrivialMain.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/mdc/NumberCruncher.java b/logback-examples/src/main/java/chapters/mdc/NumberCruncher.java
index ff72b65db4..178d04027c 100644
--- a/logback-examples/src/main/java/chapters/mdc/NumberCruncher.java
+++ b/logback-examples/src/main/java/chapters/mdc/NumberCruncher.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/mdc/NumberCruncherClient.java b/logback-examples/src/main/java/chapters/mdc/NumberCruncherClient.java
index a7167deaeb..815d1f2e0f 100644
--- a/logback-examples/src/main/java/chapters/mdc/NumberCruncherClient.java
+++ b/logback-examples/src/main/java/chapters/mdc/NumberCruncherClient.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java b/logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java
index be58bc9930..909a51a0e3 100644
--- a/logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java
+++ b/logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -84,7 +84,7 @@ public int[] factor(int number) throws RemoteException {
             }
 
             // Placing artificial delays in tight loops will also lead to
-            // sub-optimal resuts. :-)
+            // sub-optimal results. :-)
             delay(100);
         }
 
diff --git a/logback-examples/src/main/java/chapters/mdc/SimpleMDC.java b/logback-examples/src/main/java/chapters/mdc/SimpleMDC.java
index 8f3734e5a4..1fc6ee192d 100644
--- a/logback-examples/src/main/java/chapters/mdc/SimpleMDC.java
+++ b/logback-examples/src/main/java/chapters/mdc/SimpleMDC.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/mdc/UserServletFilter.java b/logback-examples/src/main/java/chapters/mdc/UserServletFilter.java
index 8d96661b8c..dff35a8022 100644
--- a/logback-examples/src/main/java/chapters/mdc/UserServletFilter.java
+++ b/logback-examples/src/main/java/chapters/mdc/UserServletFilter.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
@@ -16,13 +16,13 @@
 import java.io.IOException;
 import java.security.Principal;
 
-import javax.servlet.Filter;
-import javax.servlet.FilterChain;
-import javax.servlet.FilterConfig;
-import javax.servlet.ServletException;
-import javax.servlet.ServletRequest;
-import javax.servlet.ServletResponse;
-import javax.servlet.http.HttpServletRequest;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
 
 import org.slf4j.MDC;
 
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/Log4jMain.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/Log4jMain.java
index cf031e4dc2..772f28b2e3 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/Log4jMain.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/Log4jMain.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/LogbackMain.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/LogbackMain.java
index 3d3d2aa37a..8ba5dd70b0 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/LogbackMain.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/LogbackMain.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jAppender.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jAppender.java
index 6fb2ae0389..b295dd81c2 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jAppender.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jAppender.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jLayout.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jLayout.java
index a6546cf5f9..d1992aa6b8 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jLayout.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLog4jLayout.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackAppender.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackAppender.java
index 0f8b88b164..3747341cb5 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackAppender.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackAppender.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackLayout.java b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackLayout.java
index ef04d481c6..ef26bd9bd4 100644
--- a/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackLayout.java
+++ b/logback-examples/src/main/java/chapters/migrationFromLog4j/TrivialLogbackLayout.java
@@ -1,9 +1,9 @@
-/**
+/*
  * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
+ * Copyright (C) 1999-2026, QOS.ch. All rights reserved.
  *
  * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
+ * either the terms of the Eclipse Public License v2.0 as published by
  * the Eclipse Foundation
  *
  *   or (per the licensee's choosing)
diff --git a/logback-examples/src/main/java/chapters/onJoran/SimpleConfigurator.java b/logback-examples/src/main/java/chapters/onJoran/SimpleConfigurator.java
deleted file mode 100644
index 8413d3ae9a..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/SimpleConfigurator.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran;
-
-import java.util.List;
-import java.util.Map;
-
-import ch.qos.logback.core.joran.GenericConfigurator;
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.action.ImplicitAction;
-import ch.qos.logback.core.joran.spi.ElementPath;
-import ch.qos.logback.core.joran.spi.Interpreter;
-import ch.qos.logback.core.joran.spi.ElementSelector;
-import ch.qos.logback.core.joran.spi.RuleStore;
-
-/**
- * A minimal configurator extending GenericConfigurator.
- * 
- * @author Ceki Gülcü
- *
- */
-public class SimpleConfigurator extends GenericConfigurator {
-
-    final Map ruleMap;
-    final List iaList;
-
-    public SimpleConfigurator(Map ruleMap) {
-        this(ruleMap, null);
-    }
-
-    public SimpleConfigurator(Map ruleMap, List iaList) {
-        this.ruleMap = ruleMap;
-        this.iaList = iaList;
-    }
-
-    @Override
-    protected void addInstanceRules(RuleStore rs) {
-        for (ElementSelector elementSelector : ruleMap.keySet()) {
-            Action action = ruleMap.get(elementSelector);
-            rs.addRule(elementSelector, action);
-        }
-    }
-
-    @Override
-    protected void addImplicitRules(Interpreter interpreter) {
-        if (iaList == null) {
-            return;
-        }
-        for (ImplicitAction ia : iaList) {
-            interpreter.addImplicitAction(ia);
-        }
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/AddAction.java b/logback-examples/src/main/java/chapters/onJoran/calculator/AddAction.java
deleted file mode 100644
index 60e253ab8a..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/AddAction.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import java.util.EmptyStackException;
-
-import org.xml.sax.Attributes;
-
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.InterpretationContext;
-
-/**
- * This action adds the two integers at the top of the stack (they are removed)
- * and pushes the result to the top the stack.  
- * 
- * @author Ceki Gülcü
- */
-public class AddAction extends Action {
-
-    public void begin(InterpretationContext ic, String name, Attributes attributes) {
-        int first = fetchInteger(ic);
-        int second = fetchInteger(ic);
-        // Push the result of the addition for the following actions.
-        ic.pushObject(first + second);
-    }
-
-    /**
-     * Pop the Integer object at the top of the stack.
-     * This code also  illustrates usage of Joran's error handling paradigm. 
-     */
-    int fetchInteger(InterpretationContext ic) {
-        int result = 0;
-
-        try {
-            // Pop the object at the top of the interpretation context's stack.
-            Object o1 = ic.popObject();
-
-            if (o1 instanceof Integer) {
-                result = ((Integer) o1).intValue();
-            } else {
-                String errMsg = "Object [" + o1 + "] currently at the top of the stack is not an integer.";
-                ic.addError(errMsg);
-                throw new IllegalArgumentException(errMsg);
-            }
-        } catch (EmptyStackException ese) {
-            ic.addError(("Expecting an integer on the execution stack."));
-            throw ese;
-        }
-        return result;
-    }
-
-    public void end(InterpretationContext ic, String name) {
-        // Nothing to do here.
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator1.java b/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator1.java
deleted file mode 100644
index cdfc717b83..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator1.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import ch.qos.logback.core.Context;
-import ch.qos.logback.core.ContextBase;
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.ElementSelector;
-import ch.qos.logback.core.util.StatusPrinter;
-import chapters.onJoran.SimpleConfigurator;
-
-/**
- * This examples illustrates collaboration between multiple actions through the
- * common execution context stack.
- * 
- * @author Ceki Gülcü
- */
-public class Calculator1 {
-
-    public static void main(String[] args) throws Exception {
-        Context context = new ContextBase();
-
-        Map ruleMap = new HashMap();
-
-        // Associate "/computation" pattern with ComputationAction1
-        ruleMap.put(new ElementSelector("/computation"), new ComputationAction1());
-
-        // Other associations
-        ruleMap.put(new ElementSelector("/computation/literal"), new LiteralAction());
-        ruleMap.put(new ElementSelector("/computation/add"), new AddAction());
-        ruleMap.put(new ElementSelector("/computation/multiply"), new MultiplyAction());
-
-        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
-        // link the configurator with its context
-        simpleConfigurator.setContext(context);
-
-        simpleConfigurator.doConfigure(args[0]);
-        // Print any errors that might have occured.
-        StatusPrinter.print(context);
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator2.java b/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator2.java
deleted file mode 100644
index 765973da55..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/Calculator2.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import ch.qos.logback.core.Context;
-import ch.qos.logback.core.ContextBase;
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.ElementSelector;
-import ch.qos.logback.core.joran.spi.JoranException;
-import ch.qos.logback.core.util.StatusPrinter;
-import chapters.onJoran.SimpleConfigurator;
-
-/**
- * This examples illustrates collaboration between multiple actions through the
- * common execution context stack.
- * 
- * It differs from Calculator1 in that it supports arbitrary nesting of 
- * computation elements.
- * 
- * You can test this application with the sample XML file calculator3.xml.
- * 
- * @author Ceki Gülcü
- */
-public class Calculator2 {
-    public static void main(String[] args) throws Exception {
-        Map ruleMap = new HashMap();
-
-        // Note the wild card character '*', in the paterns, signifying any level
-        // of nesting.
-        ruleMap.put(new ElementSelector("*/computation"), new ComputationAction2());
-
-        ruleMap.put(new ElementSelector("*/computation/literal"), new LiteralAction());
-        ruleMap.put(new ElementSelector("*/computation/add"), new AddAction());
-        ruleMap.put(new ElementSelector("*/computation/multiply"), new MultiplyAction());
-
-        Context context = new ContextBase();
-        SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap);
-        // link the configurator with its context
-        simpleConfigurator.setContext(context);
-
-        try {
-            simpleConfigurator.doConfigure(args[0]);
-        } catch (JoranException e) {
-            // Print any errors that might have occured.
-            StatusPrinter.print(context);
-        }
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction1.java b/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction1.java
deleted file mode 100644
index 56193ae069..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction1.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import org.xml.sax.Attributes;
-
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.InterpretationContext;
-import ch.qos.logback.core.util.OptionHelper;
-
-/**
- * ComputationAction1 will print the result of the compuration made by 
- * children elements but only if the compuration itself is named, that is if the
- * name attribute of the associated computation element is not null. In other
- * words, anonymous computations will not print their result.
- * 
- * @author Ceki Gülcü
- */
-public class ComputationAction1 extends Action {
-    public static final String NAME_ATR = "name";
-
-    String nameStr;
-
-    /**
-     * Store the value of the name attribute for future use.
-     */
-    public void begin(InterpretationContext ec, String name, Attributes attributes) {
-        nameStr = attributes.getValue(NAME_ATR);
-    }
-
-    /**
-     * Children elements have been processed. The sesults should be an integer 
-     * placed at the top of the execution stack.
-     * 
-     * This value will be printed on the console but only if the action is 
-     * named. Anonymous computation will not print their result.
-     */
-    public void end(InterpretationContext ec, String name) {
-        if (OptionHelper.isEmpty(nameStr)) {
-            // nothing to do
-        } else {
-            Integer i = (Integer) ec.peekObject();
-            System.out.println("The computation named [" + nameStr + "] resulted in the value " + i);
-        }
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction2.java b/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction2.java
deleted file mode 100644
index e7a8eb69a7..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/ComputationAction2.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import java.util.Stack;
-
-import org.xml.sax.Attributes;
-
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.InterpretationContext;
-import ch.qos.logback.core.util.OptionHelper;
-
-/**
- * ComputationAction2 will print the result of the compuration made by 
- * children elements but only if the computation itself is named, that is if the
- * name attribute of the associated computation element is not null. In other
- * words, anonymous computations will not print their result.
- * 
- * ComputationAction2 differs from ComputationAction1 in its handling of
- * instance variables. ComputationAction1 has a simple nameStr
- * instance variable. This variable is set when the begin() method is called 
- * and then later used within the end() method. 
- * 
- * This simple approach works properly if the begin() and end()
- * method of a given action are expected to be called in sequence. However,
- * there are situations where the begin() method of the same action instance is 
- * invoked multiple times before the matching end() method is invoked. 
- * 
- * When this happens, the second call to begin() overwrites values set by
- * the first invocation to begin(). The solution is to save parameter values 
- * into a separate stack. The well-formedness of XML will guarantee that a value
- * saved by one begin() will be consumed only by the matching end() method.
- * 
- * Note that in the vast majority of cases there is no need to resort to a 
- * separate stack for each variable. The situation of successive begin() 
- * invocations can only occur if: 
- * 
- * 1) the associated pattern contains a wildcard, i.e. the * character
- * 
- * and
- * 
- * 2) the associated element tag can contain itself as a child 
- *  
- * For example, "*/computation" pattern means that computations can contain
- * other computation elements as children. 
- * 
- * @author Ceki Gülcü
- */
-public class ComputationAction2 extends Action {
-    public static final String NAME_ATR = "name";
-
-    Stack nameStrStack = new Stack();
-
-    public void begin(InterpretationContext ec, String name, Attributes attributes) {
-        String nameStr = attributes.getValue(NAME_ATR);
-        // save nameStr value in a special stack. Note that the value is saved
-        // even if it is empty or null.
-        nameStrStack.push(nameStr);
-    }
-
-    public void end(InterpretationContext ec, String name) {
-        // pop nameStr value from the special stack
-        String nameStr = (String) nameStrStack.pop();
-
-        if (OptionHelper.isEmpty(nameStr)) {
-            // nothing to do
-        } else {
-            Integer i = (Integer) ec.peekObject();
-            System.out.println("The computation named [" + nameStr + "] resulted in the value " + i);
-        }
-    }
-}
diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/LiteralAction.java b/logback-examples/src/main/java/chapters/onJoran/calculator/LiteralAction.java
deleted file mode 100644
index bc230f5191..0000000000
--- a/logback-examples/src/main/java/chapters/onJoran/calculator/LiteralAction.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * Logback: the reliable, generic, fast and flexible logging framework.
- * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
- *
- * This program and the accompanying materials are dual-licensed under
- * either the terms of the Eclipse Public License v1.0 as published by
- * the Eclipse Foundation
- *
- *   or (per the licensee's choosing)
- *
- * under the terms of the GNU Lesser General Public License version 2.1
- * as published by the Free Software Foundation.
- */
-package chapters.onJoran.calculator;
-
-import org.xml.sax.Attributes;
-
-import ch.qos.logback.core.joran.action.Action;
-import ch.qos.logback.core.joran.spi.InterpretationContext;
-import ch.qos.logback.core.util.OptionHelper;
-
-/**
- * This action converts the value attribute of the associated element to an
- * integer and pushes the resulting Integer object on top of the execution
- * context stack.
- * 
- * 

It also illustrates usage of Joran's error reporting/handling paradigm. - * - * @author Ceki Gülcü - */ -public class LiteralAction extends Action { - public static final String VALUE_ATR = "value"; - - public void begin(InterpretationContext ic, String name, Attributes attributes) { - String valueStr = attributes.getValue(VALUE_ATR); - - if (OptionHelper.isEmpty(valueStr)) { - ic.addError("The literal action requires a value attribute"); - return; - } - - try { - Integer i = Integer.valueOf(valueStr); - ic.pushObject(i); - } catch (NumberFormatException nfe) { - ic.addError("The value [" + valueStr + "] could not be converted to an Integer", nfe); - throw nfe; - } - } - - public void end(InterpretationContext ic, String name) { - // Nothing to do here. - // In general, the end() method of actions associated with elements - // having no children do not need to perform any processing in their - // end() method. - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/MultiplyAction.java b/logback-examples/src/main/java/chapters/onJoran/calculator/MultiplyAction.java deleted file mode 100644 index bd13f4117d..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/calculator/MultiplyAction.java +++ /dev/null @@ -1,65 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.calculator; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -import java.util.EmptyStackException; - -/** - * - * This action multiplies the two integers at the top of the stack (they are - * removed) and pushes the result on top the stack. - * - * @author Ceki Gülcü - */ -public class MultiplyAction extends Action { - - public void begin(InterpretationContext ic, String name, Attributes attributes) { - int first = fetchInteger(ic); - int second = fetchInteger(ic); - ic.pushObject(first * second); - } - - /** - * Pop the Integer object at the top of the stack. This code illustrates usage - * of Joran's error handling paradigm. - */ - int fetchInteger(InterpretationContext ic) { - int result = 0; - - try { - Object o1 = ic.popObject(); - - if (o1 instanceof Integer) { - result = ((Integer) o1).intValue(); - } else { - String errMsg = "Object [" + o1 + "] currently at the top of the stack is not an integer."; - ic.addError(errMsg); - throw new IllegalArgumentException(errMsg); - } - } catch (EmptyStackException ese) { - ic.addError("Expecting an integer on the execution stack."); - throw ese; - } - return result; - } - - public void end(InterpretationContext ic, String name) { - // Nothing to do here. - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/calculator/readme.txt b/logback-examples/src/main/java/chapters/onJoran/calculator/readme.txt deleted file mode 100644 index a0d9621a9b..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/calculator/readme.txt +++ /dev/null @@ -1,7 +0,0 @@ -This directory contains the the calculator example. It shows how Actions can -collaborate in order to accomplish a simple computation. - -For further information, please refer to - - http://logback.qos.ch/manual/onJoran.html#calculator - diff --git a/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorld.java b/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorld.java deleted file mode 100644 index 249d7c862a..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorld.java +++ /dev/null @@ -1,48 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.helloWorld; - -import java.util.HashMap; -import java.util.Map; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.util.StatusPrinter; -import chapters.onJoran.SimpleConfigurator; - -/** - * - * A hello world example using Joran. - * - * @author Ceki Gulcu - */ -public class HelloWorld { - public static void main(String[] args) throws Exception { - Map ruleMap = new HashMap(); - - // Associate "hello-world" pattern with HelloWorldAction - ruleMap.put(new ElementSelector("hello-world"), new HelloWorldAction()); - - // Joran needs to work within a context. - Context context = new ContextBase(); - SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap); - // link the configurator with its context - simpleConfigurator.setContext(context); - - simpleConfigurator.doConfigure(args[0]); - StatusPrinter.print(context); - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorldAction.java b/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorldAction.java deleted file mode 100644 index 1cb4f70696..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/helloWorld/HelloWorldAction.java +++ /dev/null @@ -1,35 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.helloWorld; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -/** - * A trivial action that writes "Hello world" on the console. - * - * See the {@link HelloWorld} class for integration with Joran. - * - * @author Ceki Gülcü - */ -public class HelloWorldAction extends Action { - public void begin(InterpretationContext ec, String name, Attributes attributes) { - System.out.println("Hello World"); - } - - public void end(InterpretationContext ec, String name) { - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/helloWorld/readme.txt b/logback-examples/src/main/java/chapters/onJoran/helloWorld/readme.txt deleted file mode 100644 index c96bb14398..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/helloWorld/readme.txt +++ /dev/null @@ -1,6 +0,0 @@ -The example illustrates the minimal plumbing required for using Joran. - -For further explanations, please refer to - - http://logback.qos.ch/manual/onJoran.html#helloWorld - diff --git a/logback-examples/src/main/java/chapters/onJoran/implicit/NOPAction.java b/logback-examples/src/main/java/chapters/onJoran/implicit/NOPAction.java deleted file mode 100644 index b9a9c53eda..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/implicit/NOPAction.java +++ /dev/null @@ -1,33 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.implicit; - -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -/** - * No operation (NOP) action that does strictly nothing. - * - * @author Ceki Gülcü - */ -public class NOPAction extends Action { - - public void begin(InterpretationContext ec, String name, Attributes attributes) { - } - - public void end(InterpretationContext ec, String name) { - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMe.java b/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMe.java deleted file mode 100644 index 34158b4da8..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMe.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.implicit; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.ImplicitAction; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.util.StatusPrinter; -import chapters.onJoran.SimpleConfigurator; - -/** - * This example illustrates the usage of implicit actions. - * - *

Keep in mind that implicit actions are not associated with any specific - * pattern. Moreover, they are added directly to a Joran Interpreter instead of - * a rule store. - * - * @author Ceki Gülcü - */ -public class PrintMe { - - public static void main(String[] args) throws Exception { - Context context = new ContextBase(); - - Map ruleMap = new HashMap(); - - // we start with the rule for the top-most (root) element - ruleMap.put(new ElementSelector("*/foo"), new NOPAction()); - - // Add an implicit action. - List iaList = new ArrayList(); - iaList.add(new PrintMeImplicitAction()); - SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap, iaList); - - // link the configurator with its context - simpleConfigurator.setContext(context); - - simpleConfigurator.doConfigure(args[0]); - StatusPrinter.printInCaseOfErrorsOrWarnings(context); - - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMeImplicitAction.java b/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMeImplicitAction.java deleted file mode 100644 index b7e68c351a..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/implicit/PrintMeImplicitAction.java +++ /dev/null @@ -1,43 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.implicit; - -import ch.qos.logback.core.joran.spi.ElementPath; -import org.xml.sax.Attributes; - -import ch.qos.logback.core.joran.action.ImplicitAction; -import ch.qos.logback.core.joran.spi.InterpretationContext; - -/** - * - * A rather trivial implicit action which is applicable if an element has a - * printme attribute set to true. - * - * @author Ceki Gülcü - */ -public class PrintMeImplicitAction extends ImplicitAction { - - public boolean isApplicable(ElementPath elementPath, Attributes attributes, InterpretationContext ec) { - String printmeStr = attributes.getValue("printme"); - - return Boolean.valueOf(printmeStr).booleanValue(); - } - - public void begin(InterpretationContext ec, String name, Attributes attributes) { - System.out.println("Element [" + name + "] asked to be printed."); - } - - public void end(InterpretationContext ec, String name) { - } -} diff --git a/logback-examples/src/main/java/chapters/onJoran/implicit/readme.txt b/logback-examples/src/main/java/chapters/onJoran/implicit/readme.txt deleted file mode 100644 index ad5fc946db..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/implicit/readme.txt +++ /dev/null @@ -1,7 +0,0 @@ - -This directory contains an example illustrating implicit actions. - -For further information, please refer to - - http://logback.qos.ch/manual/onJoran.html#implicit - \ No newline at end of file diff --git a/logback-examples/src/main/java/chapters/onJoran/newRule/NewRuleCalculator.java b/logback-examples/src/main/java/chapters/onJoran/newRule/NewRuleCalculator.java deleted file mode 100644 index e8e68ede16..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/newRule/NewRuleCalculator.java +++ /dev/null @@ -1,63 +0,0 @@ -/** - * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. - * - * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by - * the Eclipse Foundation - * - * or (per the licensee's choosing) - * - * under the terms of the GNU Lesser General Public License version 2.1 - * as published by the Free Software Foundation. - */ -package chapters.onJoran.newRule; - -import java.util.HashMap; -import java.util.Map; - -import ch.qos.logback.core.Context; -import ch.qos.logback.core.ContextBase; -import ch.qos.logback.core.joran.action.Action; -import ch.qos.logback.core.joran.action.NewRuleAction; -import ch.qos.logback.core.joran.spi.ElementSelector; -import ch.qos.logback.core.util.StatusPrinter; -import chapters.onJoran.SimpleConfigurator; -import chapters.onJoran.calculator.ComputationAction1; - -/** - * This example illustrates the usage of NewRuleAction which allows the Joran - * interpreter to learn new rules on the fly. - * - *

This example relies heavily on the code from the joran.calculator - * package. - * - * @author Ceki Gülcü - */ -public class NewRuleCalculator { - public static void main(String[] args) throws Exception { - - Context context = new ContextBase(); - - Map ruleMap = new HashMap(); - - // we start with the rule for the top-most (root) element - ruleMap.put(new ElementSelector("*/computation"), new ComputationAction1()); - - // Associate "/new-rule" pattern with NewRuleAction from the - // org.apache.joran.action package. - // - // We will let the XML file to teach the Joran interpreter about new rules - ruleMap.put(new ElementSelector("/computation/newRule"), new NewRuleAction()); - - SimpleConfigurator simpleConfigurator = new SimpleConfigurator(ruleMap); - // link the configurator with its context - simpleConfigurator.setContext(context); - - simpleConfigurator.doConfigure(args[0]); - - // Print any errors that might have occured. - StatusPrinter.printInCaseOfErrorsOrWarnings(context); - } - -} diff --git a/logback-examples/src/main/java/chapters/onJoran/newRule/readme.txt b/logback-examples/src/main/java/chapters/onJoran/newRule/readme.txt deleted file mode 100644 index fb7a5caf77..0000000000 --- a/logback-examples/src/main/java/chapters/onJoran/newRule/readme.txt +++ /dev/null @@ -1,8 +0,0 @@ - -This directory contains an example showing how Joran can -learn new parsing rules on the fly. - -For further documentation please see - - http://logback.qos.ch/manual/onJoran.html#newRule - diff --git a/logback-examples/src/main/java/chapters/receivers/socket/AppenderExample.java b/logback-examples/src/main/java/chapters/receivers/socket/AppenderExample.java index 14fcad9221..ad66e96fb9 100755 --- a/logback-examples/src/main/java/chapters/receivers/socket/AppenderExample.java +++ b/logback-examples/src/main/java/chapters/receivers/socket/AppenderExample.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-examples/src/main/java/chapters/receivers/socket/ReceiverExample.java b/logback-examples/src/main/java/chapters/receivers/socket/ReceiverExample.java index ea51e7e111..58185bb476 100755 --- a/logback-examples/src/main/java/chapters/receivers/socket/ReceiverExample.java +++ b/logback-examples/src/main/java/chapters/receivers/socket/ReceiverExample.java @@ -1,9 +1,9 @@ -/** +/* * Logback: the reliable, generic, fast and flexible logging framework. - * Copyright (C) 1999-2015, QOS.ch. All rights reserved. + * Copyright (C) 1999-2026, QOS.ch. All rights reserved. * * This program and the accompanying materials are dual-licensed under - * either the terms of the Eclipse Public License v1.0 as published by + * either the terms of the Eclipse Public License v2.0 as published by * the Eclipse Foundation * * or (per the licensee's choosing) diff --git a/logback-examples/src/main/java9/module-info.java b/logback-examples/src/main/java9/module-info.java new file mode 100644 index 0000000000..397093de42 --- /dev/null +++ b/logback-examples/src/main/java9/module-info.java @@ -0,0 +1,10 @@ +module logback.examples { + requires org.slf4j; + requires ch.qos.logback.core; + requires ch.qos.logback.classic; + + + // exports chapters.configuration; + +} + diff --git a/logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml b/logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml index c5bd7879d5..a31c738324 100755 --- a/logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml +++ b/logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml @@ -1,26 +1,25 @@ - + foo.log - - %date %level [%thread] %logger{10} [%file : %line] %msg%n - + + %date %level [%thread] %logger{10} [%file : %line] -%kvp- %msg%n + - + - - - %msg%n - - - - - - - - - - - \ No newline at end of file + + + %kvp %msg%n + + + + + + + + + + + diff --git a/logback-examples/src/main/resources/chapters/configuration/contextName.xml b/logback-examples/src/main/resources/chapters/configuration/contextName.xml index a8c7ba8df8..86fc0effbc 100755 --- a/logback-examples/src/main/resources/chapters/configuration/contextName.xml +++ b/logback-examples/src/main/resources/chapters/configuration/contextName.xml @@ -4,7 +4,7 @@ - %d %contextName [%t] %level %logger{36} - %msg%n + %d %contextName [%t] %level %logger{36} -%kvp- %msg%n @@ -12,4 +12,4 @@ - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/includedConfig.xml b/logback-examples/src/main/resources/chapters/configuration/includedConfig.xml index 70962504c4..2232611696 100755 --- a/logback-examples/src/main/resources/chapters/configuration/includedConfig.xml +++ b/logback-examples/src/main/resources/chapters/configuration/includedConfig.xml @@ -6,8 +6,8 @@ - + %d -%kvp- %m%n - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml b/logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml index 1a7f64229e..5f11ed3134 100755 --- a/logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml +++ b/logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml @@ -8,7 +8,7 @@ - %d %contextName %level %msg %logger{50}%n + %d %contextName %level -%kvp- %msg %logger{50}%n diff --git a/logback-examples/src/main/resources/chapters/configuration/restricted.xml b/logback-examples/src/main/resources/chapters/configuration/restricted.xml index 19b06c3573..dd5ef18c71 100755 --- a/logback-examples/src/main/resources/chapters/configuration/restricted.xml +++ b/logback-examples/src/main/resources/chapters/configuration/restricted.xml @@ -1,25 +1,25 @@ - - myApp.log + + myApp.log - %date %level [%thread] %logger{10} [%file:%line] %msg%n + %date %level [%thread] %logger{10} [%file:%line] -%kvp- %msg%n - - - + + + - %msg%n + %kvp %msg%n - - - - - - - - - - \ No newline at end of file + + + + + + + + + + diff --git a/logback-examples/src/main/resources/chapters/configuration/sample0.xml b/logback-examples/src/main/resources/chapters/configuration/sample0.xml index b1c84c34e6..cb65fcb508 100755 --- a/logback-examples/src/main/resources/chapters/configuration/sample0.xml +++ b/logback-examples/src/main/resources/chapters/configuration/sample0.xml @@ -6,7 +6,7 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n diff --git a/logback-examples/src/main/resources/chapters/configuration/sample1.xml b/logback-examples/src/main/resources/chapters/configuration/sample1.xml index cbf6197086..919a576755 100755 --- a/logback-examples/src/main/resources/chapters/configuration/sample1.xml +++ b/logback-examples/src/main/resources/chapters/configuration/sample1.xml @@ -6,7 +6,7 @@ - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n diff --git a/logback-examples/src/main/resources/chapters/configuration/sample2.xml b/logback-examples/src/main/resources/chapters/configuration/sample2.xml index 2a00f36de5..2aa24de8dc 100755 --- a/logback-examples/src/main/resources/chapters/configuration/sample2.xml +++ b/logback-examples/src/main/resources/chapters/configuration/sample2.xml @@ -1,23 +1,20 @@ - + - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n - + - + + + + + + + - - - - - - - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/sample3.xml b/logback-examples/src/main/resources/chapters/configuration/sample3.xml index e19bfb456f..7af0bd6770 100755 --- a/logback-examples/src/main/resources/chapters/configuration/sample3.xml +++ b/logback-examples/src/main/resources/chapters/configuration/sample3.xml @@ -1,24 +1,21 @@ - + - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n - + + + + + + + - - - - - - - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/sample4.xml b/logback-examples/src/main/resources/chapters/configuration/sample4.xml index a6886aa8b9..e20db78934 100755 --- a/logback-examples/src/main/resources/chapters/configuration/sample4.xml +++ b/logback-examples/src/main/resources/chapters/configuration/sample4.xml @@ -1,21 +1,18 @@ - + - - %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n - + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} -%kvp- %msg%n - + + + + + - - - - - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml index 79543eec60..f7889b87a8 100755 --- a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml +++ b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml @@ -5,11 +5,11 @@ ${USER_HOME}/myApp.log - %msg%n + %kvp %msg%n - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml index c2bc57a324..1b6d66212c 100755 --- a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml +++ b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml @@ -3,11 +3,11 @@ ${USER_HOME}/myApp.log - %msg%n + %kvp %msg%n - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml index 3a5e0a4e2c..bc1bab65b3 100755 --- a/logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml +++ b/logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml @@ -5,11 +5,11 @@ ${USER_HOME}/myApp.log - %msg%n + %kvp %msg%n - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml b/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml index df0d56f446..ad8ea6ee13 100755 --- a/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml +++ b/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml @@ -1,17 +1,27 @@ + - + + + + + + + + + + + + 404 + + NEUTRAL + DENY + - - - - event.getStatusCode() == 404 - - DENY - ACCEPT - + + %h %l %u %t %r %s %b + + - %h %l %u %t %r %s %b - + - - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml b/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml index 617e94036f..de754f2664 100755 --- a/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml +++ b/logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml @@ -1,21 +1,35 @@ + - + + + + + + + + + + + + + 404 + + NEUTRAL + DENY + + + + \.css$ + + DENY + NEUTRAL + - - - - - (event.getStatusCode() == 404) - && - !(event.getRequestURI().contains(".css")) - - - DENY - ACCEPT - + + %h %l %u %t %r %s %b + + - %h %l %u %t %r %s %b - + - diff --git a/logback-examples/src/main/resources/chapters/onJoran/calculator/readme.txt b/logback-examples/src/main/resources/chapters/onJoran/calculator/readme.txt index a0d9621a9b..205b5b9eb9 100755 --- a/logback-examples/src/main/resources/chapters/onJoran/calculator/readme.txt +++ b/logback-examples/src/main/resources/chapters/onJoran/calculator/readme.txt @@ -1,4 +1,4 @@ -This directory contains the the calculator example. It shows how Actions can +This directory contains the calculator example. It shows how Actions can collaborate in order to accomplish a simple computation. For further information, please refer to diff --git a/logback-examples/src/main/resources/chapters/onJoran/newRule/newRule.xml b/logback-examples/src/main/resources/chapters/onJoran/newRule/newRule.xml index 8416bc46b8..c416ec07f4 100755 --- a/logback-examples/src/main/resources/chapters/onJoran/newRule/newRule.xml +++ b/logback-examples/src/main/resources/chapters/onJoran/newRule/newRule.xml @@ -1,7 +1,7 @@ @@ -20,4 +20,4 @@ - \ No newline at end of file + diff --git a/logback-examples/src/main/resources/chapters/receivers/socket/appender-1574.xml b/logback-examples/src/main/resources/chapters/receivers/socket/appender-1574.xml new file mode 100755 index 0000000000..99177a600f --- /dev/null +++ b/logback-examples/src/main/resources/chapters/receivers/socket/appender-1574.xml @@ -0,0 +1,32 @@ + + + + + + + + + + + ${host} + ${port} + 10000 + + + true + + + ${truststore} + ${password} + + + + + + + + + + + + diff --git a/logback-site/pom.xml b/logback-site/pom.xml deleted file mode 100755 index 49ec9687b9..0000000000 --- a/logback-site/pom.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - 4.0.0 - - - ch.qos.logback - logback-parent - 1.3.0-alpha5-SNAPSHOT - - - logback-site - jar - Logback Site - logback-site module - - 1999 - - - - - src/site/pages - - - ../../../target/site - true - - - - - - org.apache.maven.plugins - maven-site-plugin - - ${project.parent.basedir}/target/site - - - - - org.apache.maven.plugins - maven-resources-plugin - - - js - - - - - - - \ No newline at end of file diff --git a/logback-site/src/site/images.src/serverSocketReceiver.odg b/logback-site/src/site/images.src/serverSocketReceiver.odg deleted file mode 100644 index bb7fcd3e35..0000000000 Binary files a/logback-site/src/site/images.src/serverSocketReceiver.odg and /dev/null differ diff --git a/logback-site/src/site/images.src/socketReceiver.odg b/logback-site/src/site/images.src/socketReceiver.odg deleted file mode 100644 index 9b7dffd40f..0000000000 Binary files a/logback-site/src/site/images.src/socketReceiver.odg and /dev/null differ diff --git a/logback-site/src/site/pages/.htaccess b/logback-site/src/site/pages/.htaccess deleted file mode 100644 index d9a616651a..0000000000 --- a/logback-site/src/site/pages/.htaccess +++ /dev/null @@ -1,3 +0,0 @@ -Redirect permanent /jmxConfig.html http://logback.qos.ch/manual/jmxConfig.html -Redirect permanent /joran.html http://logback.qos.ch/manual/onJoran.html -Redirect permanent /consolePlugin.html http://logback.qos.ch/beagle/ \ No newline at end of file diff --git a/logback-site/src/site/pages/access.html b/logback-site/src/site/pages/access.html deleted file mode 100755 index ed8035600d..0000000000 --- a/logback-site/src/site/pages/access.html +++ /dev/null @@ -1,618 +0,0 @@ - - - - - - Logback-access - - - - - - - - - -

- -
- - -
- -

HTTP-access logs with logback-access, Jetty and Tomcat

- -
- Authors: Ceki Gülcü, Sébastien Pennec -
- - - - -

Introduction

- -

The logback-access module, part of the standard logback - distribution, integrates with Servlet containers such as Jetty or - Tomcat to provide rich and powerful HTTP-access log functionality. -

- -

Logback was designed as a modular framework from the - start. Making logback-core reusable under different circumstances - without much recoding was one of our main goals. In accordance - with this strategy, logback-access builds on top of logback-core - and can thus provide much of the functionality of logback-classic - but in the scope of HTTP-access logging.

- -

It should be noted that while logback-access requires - logback-core, it is independent of logback-classic as well as - slf4j. Just as importantly, logback-access artifact must be - installed at the container level, not at web-application level. It - does not make sense to bundle logback-access.jar at the - level of a web-application. -

- -

Logback-access under Tomcat

- - - - - -

To use logback-access with Tomcat, after downloading the - logback distribution, place the files - logback-core-${project.version}.jar and - logback-access-${project.version}.jar under - $TOMCAT_HOME/lib/ directory, where $TOMCAT_HOME is the folder - where you have installed Tomcat. -

- -

Deploying recent versions of logback-access - on Tomcat 6.x (instead of Tomcat 7.x) is likely to cause the - server to crash.

- -

Tomcat 7.x This version of - logback-access has been tested with Tomcat version 7.0.62. It will - not work with Tomcat 6.x. -

- -

Tomcat 6.x Logback-access - version 0.9.30 will deploy on Tomcat 6.x.

- -

LogbackValve

- -

The - ch.qos.logback.access.tomcat.LogbackValve class - extends Tomcat's - ValveBase class. Valves are usually associated together - to form a processing pipeline. -

- -

To configure Tomcat in order to use LogbackValve, - add the following lines to the tomcat server configuration file, - namely $TOMCAT_HOME/conf/server.xml: -

-
<Valve className="ch.qos.logback.access.tomcat.LogbackValve"/>
- -

This line is usually nested within an <Engine> - or <Host> element. -

- -

By default, LogbackValve looks for a configuration - file called logback-access.xml, in the same folder where - server.xml is located, that is in - $TOMCAT_HOME/conf/. This configuration file contains - directives for configuring logback-access components. It is used - to specify appenders where the logging requests will be - sent. Please refer to the logback-access - configuration section further below. -

- -

If the LogbackValve is not able to read a configuration file - from the filesystem, it will attempt to load it as a resource (i.e. - getClassLoader().getResourceAsStream()). This might be helpful in scenarios - where your application is embedding Tomcat (e.g. using Spring Boot.) As - mentioned above, if no filename is set, it defaults to looking for a - resource named logback-access.xml. -

- -

In order to help with troubleshooting, by default, the - LogbackValve will print its internal status at its - initialization. Typical output would look as: -

- -

21:56:09,921 |-INFO in c.q.lb.access.j.a.ConfigurationAction - Ignoring debug attribute. -21:56:09,921 |-INFO in c.q.lb.core.j.a.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender] -21:56:09,921 |-INFO in c.q.lb.core.j.a.AppenderAction - Naming appender as [STDOUT] -21:56:10,015 |-INFO in c.q.lb.core.j.a.AppenderAction - Popping appender named [STDOUT] from the object stack -21:56:10,015 |-INFO in c.q.lb.core.j.a.AppenderRefAction - Attaching appender named [STDOUT] to ch.qos.logback.access.tomcat.LogbackValve[Catalina] -21:56:10,015 |-INFO in c.q.lb.access.j.a.ConfigurationAction - End of configuration.

- -

It is possible to override default status printing by specifying - the "quiet" attribute in the Valve - element. Similarly, it is also possible to set the filename for - the logback-access configuration file. Here is an example. -

- -
<Valve className="ch.qos.logback.access.tomcat.LogbackValve"
-       quiet="true" filename="c:/my-logback-access.xml"/>
- -

Viewing status messages

- -

Logback-access ships with a servlet called - ViewStatusMessagesServlet. This servlet prints the - internal status messages of the LogbackValve as an - HTML table. Here is sample output. -

- - - - click to enlarge - - -

To add this servlet to your web-application, add the following - lines to its WEB-INF/web.xml file.

- -
  <servlet>
-    <servlet-name>AccessViewStatusMessages</servlet-name>
-    <servlet-class>ch.qos.logback.access.ViewStatusMessagesServlet</servlet-class>
-  </servlet>
-
-  <servlet-mapping>
-    <servlet-name>AccessViewStatusMessages</servlet-name>
-    <url-pattern>/lbAccessStatus</url-pattern>
-  </servlet-mapping>
- -

The ViewStatusMessages servlet will available - under the URL http://host/yourWebapp/lbAccessStatus -

- - -

Logback-access under - Jetty

- -

After downloading the logback distribution, place the files - logback-core-VERSION.jar and - logback-access-VERSION.jar under $JETTY_HOME/lib - directory, where $JETTY_HOME is the folder where you have - installed Jetty. Versions of logback-access 0.9.31 and later - target Jetty versions 7.x and 8.x. Logback-access versions 0.9.30 - and earlier target Jetty version 6.x. -

- -

Logback's implementation of - org.eclipse.jetty.server.RequestLog interface

- -

The - ch.qos.logback.access.jetty.RequestLogImpl class - implements Jetty's RequestLog - interface. Jetty delegates the management of access logging - functionality to implementations of this interface. -

- -

In logback, a logging destination is called an "appender" which - can be directly attached to a - ch.qos.logback.access.jetty.RequestLogImpl instance. -

- - -

In order to configure Jetty to use logback-access's - RequestLogImpl, please add the following lines to - Jetty's main configuration file, namely - $JETTY_HOME/etc/jetty.xml: -

-
<Ref id="RequestLogHandler">
-  <Set name="requestLog">
-    <New id="requestLogImpl" class="ch.qos.logback.access.jetty.RequestLogImpl">
-    </New>
-  </Set>
-</Ref>
- -

Please note that the above jetty configuration snippet makes - reference to "RequestLogHandler". Check your jetty.xml - configuration file and add a RequestLogHandler if it - has not been already added. Here is a configuration snipped to set - you on the right tract.

- -
<Set name="handler">
-  <New id="Handlers" class="org.eclipse.jetty.server.handler.HandlerCollection">
-    <Set name="handlers">
-      <Array type="org.eclipse.jetty.server.Handler">        
-        <Item>
-          <New id="Contexts" 
-               class="org.eclipse.jetty.server.handler.ContextHandlerCollection"/>
-        </Item>
-        <Item>
-          <New id="DefaultHandler" 
-               class="org.eclipse.jetty.server.handler.DefaultHandler"/>
-        </Item>
-        <!-- add a RequestLogHandler -->
-        <Item>
-          <New id="RequestLogHandler"
-               class="org.eclipse.jetty.server.handler.RequestLogHandler"/>
-        </Item>
-      </Array>
-    </Set>
-  </New>
-</Set>
- - -

By default, RequestLogImpl looks - for a logback configuration file called - logback-access.xml, in the same folder where - jetty.xml is located. This configuration file contains - directives for configuring logback components such as appenders - and layouts. -

- -

As long the path is specified, you can place the logback - configuration file in any location. Here is another example of a - Jetty configuration file, including the path to the logback-access - configuration file here named myaccess.xml. -

- -
<Ref id="RequestLogHandler">
-  <Set name="requestLog">
-    <New id="requestLogImpl" class="ch.qos.logback.access.jetty.RequestLogImpl">
-       <Set name="fileName">path/to/myaccess.xml</Set>
-    </New>   
-  </Set>
-</Ref>
- -

For embedded Jetty, it is helpful to lookup for the - logback-access configuration file as a class path resource. This - can be accomplished by setting the resource property to - the file name to look up on the class path. -

- -
<Ref id="RequestLogHandler">
-  <Set name="requestLog">
-    <New id="requestLogImpl" class="ch.qos.logback.access.jetty.RequestLogImpl">
-       <Set name="resource">as/classpath/resource/myaccess.xml</Set>
-    </New>   
-  </Set>
-</Ref>
- -

Logback-access - configuration

- - -

Although similar, the format of logback-access.xml - configuration file is slightly different than the configuration file - format logback-classic. Appenders and Layouts are declared the - exact same way. However, in the access module there is no notion of - loggers and consequently logger elements are disallowed. -

- - -

Example 1: basic logback-access configuration

-

- Here is a small but fully functional logback-access.xml - configuration file: -

-
<configuration>
-  <!-- always a good activate OnConsoleStatusListener -->
-  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
-
-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-    <encoder>
-      <pattern>%h %l %u %user %date "%r" %s %b</pattern>
-    </encoder>
-  </appender>
-
-  <appender-ref ref="STDOUT" />
-</configuration>
-

- It declares a ConsoleAppender which prints its output - on the console. The ConsoleAppender contains an - Encoder object responsible for formatting output. The - log format is specified by the "%h %l %u %user %date "%r" %s %b" - pattern which incidentally corresponds to the Common Log Format - (CLF). This format is recognized by log analyzers such as Analog or AWStats. -

- -

The words "common" or "clf" are interpreted as shorthands for - the said pattern. Thus, the following are all equivalent: -

- -
<pattern>%h %l %u %user %date "%r" %s %b</pattern>
-<pattern>common</pattern>
-<pattern>clf</pattern>
- -

The so called "combined" format is also widely recognized. It is - defined as the '%h %l %u [%t] "%r" %s %b "%i{Referer}" - "%i{User-Agent}"' pattern. As a facilitator, you can use the - "combined" as a shorthand. Thus, the following directive -

- -
<encoder>
-  <pattern>%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"</pattern>
-</encoder>
- -

is equivalent to:

- -
<encoder>
-  <pattern>combined</pattern>
-</encoder>
- - -

Example 2: RollingFileAppender

- -

The configuration file below configures a daily rolling - RollingFileAppender. Note that due to the - .zip suffix included in the value for fileNamePattern option, the log files are not - only rolled daily, but they are also automatically compressed.

- - -
<configuration>
-  <!-- always a good activate OnConsoleStatusListener -->
-  <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
-
-  <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-    <file>access.log</file>
-    <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-      <fileNamePattern>access.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
-    </rollingPolicy>
-
-    <encoder>
-      <pattern>combined</pattern>
-    </encoder>
-  </appender>
- 
-  <appender-ref ref="FILE" />
-</configuration>
- -

These two examples should give you an idea of the possibilities - offered by logback-access. In principle, most if not all of the - features available in logback-classic are also available in - logback-access. -

- -

PatternLayout

- -

Logback-access ships with an http-specific implementation of - PatternLayout. For detailed instructions on how - to use the PatternLayout, please refer to the corresponding - chapter of the logback manual. -

- -

JMX Components

- -

Logback-access integrates with JMX servers to publish - information about its components. -

- -

Both RequestLogImpl and LogbackValve - expose data and can be updated via JMX. A special filter, covered - further down this document, publishes statistical data on access - logs. -

- - -

Configuring Tomcat for JMX

- -

In order to configure Tomcat for JMX, please add the following - lines to the $TOMCAT_HOME/bin/catalina.sh shell script - (or its MS Windows equivalent): -

- -
CATALINA_OPTS="-Dcom.sun.management.jmxremote"
-CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.ssl=false"
-CATALINA_OPTS="$CATALINA_OPTS -Dcom.sun.management.jmxremote.authenticate=false"
- -

After you launch Tomcat, you can access the MBeans exposed by - Tomcat via the JConsole application, which can be started with the - following command: -

-
jconsole
- -

If you prefer MX4J to access your components via a web-based - interface, here is a short summary of the steps to follow. After - downloading MX4J, place - the mx4j-impl.jar file in the $TOMCAT_HOME/bin/ - directory, and the mx4j-tools.jar file in the - $TOMCAT_HOME/common/lib/ directory. Once that is done, - add the following lines to the - $TOMCAT_HOME/bin/catalina.sh shell script: -

- -
<!-- at the beginning of the file -->
-CATALINA_OPTS="-Dcom.sun.management.jmxremote"
-CATALINA_OPTS="$CATALINA_OPTS -Djavax.management.builder.initial=mx4j.server.MX4JMBeanServerBuilder"
-
-<!-- in the "Add on extra jar files to CLASSPATH" section -->
-CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/mx4j-impl.jar
- -

Finally, declare a new Connector in the - $TOMCAT_HOME/conf/server.xml file:

- -
<Connector port="8050" 
-  handler.list="mx"
-  mx.enabled="true" 
-  mx.httpHost="localhost" 
-  mx.httpPort="8082" 
-  protocol="AJP/1.3" />
- -

Once Tomcat is started, you should be able to reach your JMX - components by pointing your browser at the following URL: -

- -
http://localhost:8082/
- -

Configuring Jetty

- -

- Configuring Jetty to publish JMX components requires a few modifications to the - $JETTY_HOME/etc/jetty.xml configuration file. Here are the elements that need to be - added: -

- -
<Call id="MBeanServer" class="java.lang.management.ManagementFactory" name="getPlatformMBeanServer"/>
-<!-- initialize the Jetty MBean container -->
-<Get id="Container" name="container">
-  <Call name="addEventListener">
-    <Arg>
-      <New class="org.mortbay.management.MBeanContainer">
-        <Arg><Ref id="MBeanServer"/></Arg>
-        <Set name="managementPort">8082</Set>
-        <Call name="start" />
-      </New>
-    </Arg>
-  </Call>
-</Get>
- -

Once Jetty is started with this configuration, all available - components can be reviewed at: -

-
http://localhost:8082/
- -

Logback-access' RequestLogImpl should be - available, including its start() and - stop() methods. -

- - -

TeeFilter (a servlet-filter)

- -

In order to diagnose bugs in a web-application, it is often - handy to capture the client's request as well as the server's - response. The TeeFilter was designed precisely for - this purpose. It should be noted that TeeFilter is a - regular servlet - filter. Like other servlet filters, it needs to be declared in - your web-application's web.xml file: -

- -
<filter>
-  <filter-name>TeeFilter</filter-name>
-  <filter-class>ch.qos.logback.access.servlet.TeeFilter</filter-class>
-</filter>
-
-<filter-mapping>
-  <filter-name>TeeFilter</filter-name>
-  <url-pattern>/*</url-pattern>
-</filter-mapping>
- -

We have tested TeeFilter to the best of our - ability. However, since it duplicates the input stream of the - request and the output stream of the response, it may interfere with - your application. Moreover, for large input or output sizes, it will - add measurable latency. Although we have already fixed all currently - known bugs, TeeFilter has broken otherwise correctly - behaving applications in the past. Thus, in case of doubt, do not - hesitate to disable TeeFilter. -

- -

Once TeeFilter is installed, the PatternLayout - converters fullRequest and fullResponse - will output the full contents of the request and respectively the - response. -

- -

Here is a sample logback-access.xml configuration file which will - output the full contents of the request and response on the console. -

- -
<configuration>
-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-    <encoder>      
-      <pattern>%fullRequest%n%n%fullResponse</pattern>
-    </encoder>
-  </appender>
-
-  <appender-ref ref="STDOUT" />
-</configuration>
- -

Here is the output generated when accessing the logback-demo application configured as shown - above:

- -

GET /logback-demo/index.jsp HTTP/1.1 -Host: localhost:8080 -User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20070312 Firefox/1.5.0 -Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8 -Accept-Language: en-us,en;q=0.5 -Accept-Encoding: gzip,deflate -Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 -Keep-Alive: 300 -Connection: keep-alive -Referer: http://localhost:8080/logback-demo/login.jsp -Cookie: JSESSIONID=15c7tqi9ehlwk; OID324nkzcmr=null; OID32862zgoa=null; - - - -HTTP/1.1 200 OK -Content-Type: text/html; charset=iso-8859-1 -Expires: Thu, 01 Jan 1970 00:00:00 GMT -Set-Cookie: JSESSIONID=bgebt99ce9om;path=/logback-demo - - -<html> -<head> - <LINK REL=StyleSheet HREF="css/pk.css" /> -</head> -<body> - -<h2>Logback demo center</h2> - -[snip, so that text is reasonably sized]

- -

 

- - -

As mentioned previously, while extremely useful during problem - hunting, TeeFilter can introduce new problems. - Consequently, we do not recommend having TeeFilter - active in production systems. In order to avoid shipping different - code for test and production environments, TeeFilter - supports includes and excludes parameters. - TeeFilter will be active if the current host is listed - in the includes list and absent in the excludes list. By special - convention, an empty includes list is interpreted as to contain all - possible host names in the universe. -

- -

To enable TeeFilter only on the hosts named "orion" - and "gemini" and disabled elsewhere you would write:

- -
<filter>
-  <filter-name>TeeFilter</filter-name>
-  <filter-class>ch.qos.logback.access.servlet.TeeFilter</filter-class>
-  <init-param>
-    <param-name>includes</param-name>
-    <param-value>orion, gemini</param-value>
-  </init-param>
-</filter>  
- -

To enable TeeFilter on all hosts except - hosts named "orion" and "gemini" you would write:

- -
<filter>
-  <filter-name>TeeFilter</filter-name>
-  <filter-class>ch.qos.logback.access.servlet.TeeFilter</filter-class>
-  <init-param>
-    <param-name>excludes</param-name>
-    <param-value>orion, gemini</param-value>
-  </init-param>
-</filter>  
- - - -
- - - diff --git a/logback-site/src/site/pages/beagle/index.html b/logback-site/src/site/pages/beagle/index.html deleted file mode 100755 index d5e4dba0a5..0000000000 --- a/logback-site/src/site/pages/beagle/index.html +++ /dev/null @@ -1,196 +0,0 @@ - - - - - - Logback-beagle - - - - - - - - - - - - - - -
- - -
- - -
- - -

Logback-beagle

- - - - - - - - -

Logback-beagle: an Eclipse plug-in for - viewing logs

- - -

During the development process, it is common for developers to - print logging events on the console. Typically, the developer will - also customize the format of the log output by setting an - appropriate layout pattern. Logback-beagle is intended as a - replacement for viewing logs via the console. It offers several - advantages over the plain-old console: -

- -
    - -
  • Events of level WARN and ERROR are marked by an orange flag - and respectively a red flag.
  • - -
  • Quickly jump to the class and line where a given logging - request originated -
  • - -
  • Easly view and jump to the callers of any log statement upto - eight levels deep
  • - -
  • Change the output format on-the-fly
  • - -
  • quickly filter-out events by setting the level of existing - loggers
  • - -
  • Measure the time elapsed between any two lines of log
  • -
- - -

Plug-in Installation

- -

The logback-beagle plug-in requires the Nebula Grid plug-in - which must be installed first.

- -
-
Step 1: Nebula Grid
- -
-

The Nebula Grid plug-in which can be installed from - http://download.eclipse.org/technology/nebula/snapshot/. Only - the "Nebula Grid Feature" needs to be installed.

- -

- -
- -
Step 2: Logbak-beagle
- -

The logback-beagle plug-in can be installed from - http://logback.qos.ch/p2/. You need to select "Logback", - "Logback Beagle" and "SLF4J" bundles for installation as shown - below.

- -

-
-
- - -

Restarting Eclipse should load the plugin. To access the Beagle - View, either type A"lt-Shift-Q, Q" or select: Window > Show - view > Other... . You should see the Beagle view - nested in the Logback category. Selecting this view will - add it to your workspace. It shouls looks as follows:

- -

- -

Configuring the client (logback.xml)

- -

A single additional configuration line in logback.xml - is sufficient to send log events from your application to - logback-beagle. -

- -
<configuration debug="true">
-  <!-- sends logs to logback-beagle -->
-  <consolePlugin />
-</configuration>
- - -

Under the hood, <consolePlugin> element creates - a SocketAppender which will send logging events to a - TCP socket on localhost port - 4321. Logback-beagle launches the corresponding server - listening on this port on localhost. -

- - -

Using logback-beagle

- -

Logback-beagle displays the events it receives in a table - according to the layout format chosen by the user. Here is a screen - shot of the Beagle View containing logging events. -

- - - -

Clicking on any log line will scroll-lock (freeze) the - view. Double clicking on that same line will release the scroll - lock (unfreeze) the view. You can also release the scroll lock by - clicking on . This icon becomes active (clickable) when the view is - frozen. -

- -

Jump to caller/Expand caller data

- -

Right-clicking on any line will reveal the following menu:

- -

- -

Selecting "Jump to caller" will move the edit cursor to the file - and line number whence the logging statement issued.

- -

Selecting "Expand caller data" will reveal the stack trace of - method calls upto eight levels deep. Here is a sample - screen-shot:

- -

- -

Not only are the callers of the log statement revealed, it - becomes possible to jump to any of the listed callers. Right-click - on a caller line. A menu will appear. Select "Jump to caller" in - the menu to jump to the caller of your choice. -

- -

- -

Caller data can be collapsed by selecting "Collapse caller - data" from the said menu.

- -

Preferences

- -

Logback-beagle preferences can be found under the "Beagle" - label nested under "Run/Debug" category. You can customize the - output pattern to best suit your preferences. Changes take effect - immediately. -

- -

- - -

You may also change the font type and size used for displaying - the log events via the Eclipse's preferences dialog as shown - below:

- -

- - -
- - diff --git a/logback-site/src/site/pages/bridge.html b/logback-site/src/site/pages/bridge.html deleted file mode 100644 index 104b850060..0000000000 --- a/logback-site/src/site/pages/bridge.html +++ /dev/null @@ -1,38 +0,0 @@ - - - - -Log4j Bridge - - - - - - - - - - - -
- -
- -
- -

Log4j bridge

- -

This module has migrated into the SLF4J project, its original - home. It has also been renamed as log4j-over-slf4j. -

- - - -
- - diff --git a/logback-site/src/site/pages/bugreport.html b/logback-site/src/site/pages/bugreport.html deleted file mode 100755 index 4856f05d18..0000000000 --- a/logback-site/src/site/pages/bugreport.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - Bug report - - - - - - - - -
- -
- -x -
- - - - -

Before you report a bug

- -

The logback community consists of those who use logback and its - modules, help answer questions on discussions lists, contribute - documentation and patches, and those who develop and maintain its - code. Those who assist on a day to day basis resolving - bug reports do this for a wide variety of reasons, and almost all - of them do this on their own time.

- -

Many bugs reported end up not being a bug in logback, but are - due to misconfiguration, problems caused by installed - applications, the operating system, etc. -

- -

Before reporting a bug please make every effort to resolve the - problem yourself. Just reporting a bug will not fix it. A good - bug report includes a detailed description of the problem and a - succinct test case which can reproduce the problem. -

- -

Review the documentation

- -

Review the documentation for the version of component you are - using. The problem you are having may already be addressed in the - docs. -

- -

Search the mailing list archives

- -

It is very likely you are not the first to run into a problem. - Others may have already found a solution. Our various mailing lists - are likely to have discussed this problem before. -

- -

Search JIRA

- -

Please search the bug database to see if the bug you are seeing - has already been reported. The bug may have already been fixed - and is available in a later version. If someone else has reported - the same bug, you could add supporting information to help - reproduce and resolve the bug. -

- - - -

Reporting a bug

- -

Only after you have exhausted the aforementioned steps, should - you file a formal report in JIRA, our bug tracking system. -

- -

Please make sure you provide as much information as possible. - It is hard to fix a bug if the person looking into the problem - cannot reproduce it. -

- - - - -
- - diff --git a/logback-site/src/site/pages/cla.txt b/logback-site/src/site/pages/cla.txt deleted file mode 100755 index cbe538efbe..0000000000 --- a/logback-site/src/site/pages/cla.txt +++ /dev/null @@ -1,126 +0,0 @@ - QOS.CH Sarl - Individual Contributor License Agreement ("Agreement") - -This agreement is an adaptation of the Apache Software Foundation ICL -agreement where references to ASF have been replaced by QOS.CH. We -thank the ASF for allowing QOS.CH Sarl to make use of their ICL -agreement. - -Thank you for your interest in QOS.CH Sarl, hereafter referred to as -just QOS.CH. In order to clarify the intellectual property license -granted with Contributions from any person or entity, QOS.CH must have -a Contributor License Agreement ("CLA") on file that has been signed -by each Contributor, indicating agreement to the license terms -below. This license is for your protection as a Contributor as well as -the protection of QOS.CH and its users; it does not change your rights -to use your own Contributions for any other purpose. If you have not -already done so, please complete and send an original signed Agreement -to QOS.CH, rue de la Ria 8B, 1462 Yvonand, Switzerland. Please read -this document carefully before signing and keep a copy for your -records. - - Full name: ____________________________ E-Mail: ___________________ - - Mailing Address: ______________________ Telephone: ___________________ - - _______________________________________________________________________ - - _______________________________________ Country: ___________________ - - -You accept and agree to the following terms and conditions for Your -present and future Contributions submitted QOS.CH. In return, QOS.CH -shall not use Your Contributions in a way that is contrary to the -public benefit. Except for the license granted herein to QOS.CH and -recipients of software distributed by QOS.CH, You reserve all right, -title, and interest in and to Your Contributions. - -1. Definitions. - - "You" (or "Your") shall mean the copyright owner or legal entity - authorized by the copyright owner that is making this Agreement - with QOS.CH. For legal entities, the entity making a Contribution - and all other entities that control, are controlled by, or are - under common control with that entity are considered to be a single - Contributor. For the purposes of this definition, "control" means - (i) the power, direct or indirect, to cause the direction or - management of such entity, whether by contract or otherwise, or - (ii) ownership of fifty percent (50%) or more of the outstanding - shares, or (iii) beneficial ownership of such entity. - - "Contribution" shall mean any original work of authorship, - including any modifications or additions to an existing work, that - is intentionally submitted by You to QOS.CH for inclusion in, or - documentation of, any of the products owned or managed by QOS.CH - (the "Work"). For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to QOS.CH or its representatives, including but not limited to - communication on electronic mailing lists, source code control - systems, and issue tracking systems that are managed by, or on - behalf of, QOS.CH for the purpose of discussing and improving the - Work, but excluding communication that is conspicuously marked or - otherwise designated in writing by You as "Not a Contribution." - -2. Grant of Copyright License. Subject to the terms and conditions of - this Agreement, You hereby grant to QOS.CH and to recipients of - software distributed by QOS.CH a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable copyright - license to reproduce, prepare derivative works of, publicly - display, publicly perform, sublicense, and distribute Your - Contributions and such derivative works. - -3. Grant of Patent License. Subject to the terms and conditions of - this Agreement, You hereby grant to QOS.CH and to recipients of - software distributed by QOS.CH a perpetual, worldwide, - non-exclusive, no-charge, royalty-free, irrevocable (except as - stated in this section) patent license to make, have made, use, - offer to sell, sell, import, and otherwise transfer the Work, where - such license applies only to those patent claims licensable by You - that are necessarily infringed by Your Contribution(s) alone or by - combination of Your Contribution(s) with the Work to which such - Contribution(s) was submitted. If any entity institutes patent - litigation against You or any other entity (including a cross-claim - or counterclaim in a lawsuit) alleging that your Contribution, or - the Work to which you have contributed, constitutes direct or - contributory patent infringement, then any patent licenses granted - to that entity under this Agreement for that Contribution or Work - shall terminate as of the date such litigation is filed. - -4. You represent that you are legally entitled to grant the above - license. If your employer(s) has rights to intellectual property - that you create that includes your Contributions, you represent - that you have received permission to make Contributions on behalf - of that employer, that your employer has waived such rights for - your Contributions to QOS.CH. - -5. You represent that each of Your Contributions is Your original - creation (see section 7 for submissions on behalf of others). You - represent that Your Contribution submissions include complete - details of any third-party license or other restriction (including, - but not limited to, related patents and trademarks) of which you - are personally aware and which are associated with any part of Your - Contributions. - -6. You are not expected to provide support for Your Contributions, - except to the extent You desire to provide support. You may provide - support for free, for a fee, or not at all. Unless required by - applicable law or agreed to in writing, You provide Your - Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS - OF ANY KIND, either express or implied, including, without - limitation, any warranties or conditions of TITLE, NON- - INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. - -7. Should You wish to submit work that is not Your original creation, - You may submit it to QOS.CH separately from any Contribution, - identifying the complete details of its source and of any license - or other restriction (including, but not limited to, related - patents, trademarks, and license agreements) of which you are - personally aware, and conspicuously marking the work as "Submitted - on behalf of a third-party: [named here]". - -8. You agree to notify QOS.CH of any facts or circumstances of which - you become aware that would make these representations inaccurate - in any respect. - - -Please sign: __________________________________ Date: ________________ diff --git a/logback-site/src/site/pages/codes.html b/logback-site/src/site/pages/codes.html deleted file mode 100755 index 887e9120e7..0000000000 --- a/logback-site/src/site/pages/codes.html +++ /dev/null @@ -1,721 +0,0 @@ - - - - - - Logback Error Codes - - - - - - - - - - - - - - - - -
- -
- - -
- -

Logback error messages and their meanings

- - -

The contextSelector cannot be - null in StaticLoggerBinder. -

- -

An IllegalStateException is thrown when no - ContextSelector could be set for logback's - StaticLoggerBinder. In principle, this error can only - occur when the context selector is expressly specified by the user, - and when that context selector cannot not be instantiated correctly. -

- -

It should not happen when you are using the default or JNDI - context selectors. -

- - - -

This appender no - longer admits a layout as a sub-component, set an encoder instead. -

- -

As of logback version 0.9.19, the WriterAppender - class has been renamed as OutputStreamAppender, with - FileAppender now sub-classing the - latter. OutputStreamAppender and sub-classes now take - an Encoder as a sub-component instead of a - Layout. -

- -

In practical terms, this means that configuration files need to - be changed

- -

from (DEPRECATED)

- -
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <layout class="ch.qos.logback.classic.PatternLayout">
-    <pattern>%msg%n</pattern>
-  </layout>
-</appender>   
- -

or the shorter equivalent (DEPRECATED)

- -
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <!-- layout are assigned the type ch.qos.logback.classic.PatternLayout by default -->
-  <layout>
-    <pattern>%msg%n</pattern>
-  </layout>
-</appender>   
- - -

to (GOOD)

-
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
-    <pattern>%msg%n</pattern>
-  </encoder>
-</appender>   
- -

or the shorter equivalent (GOOD)

- -
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <!-- encoders are assigned the type 
-       ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
-  <encoder>
-    <pattern>%msg%n</pattern>
-  </encoder>
-</appender>   
- - -

For layout type other than PatternLayout, for - example HTMLLayout, your configuration files need to be - changed -

- -

from (DEPRECATED)

- -
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <layout class="ch.qos.logback.classic.html.HTMLLayout">
-    <pattern>%msg%n</pattern>
-  </layout>
-</appender> 
- - -

to (GOOD)

-
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
-  <file>testFile.log</file>
-  ...
-  <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
-    <layout class="ch.qos.logback.classic.html.HTMLLayout">
-      <pattern>%msg%n</pattern>
-    </layout>
-  </encoder>
-</appender> 
- - - -

We hope to make up for this inconvenience with cool new features - which are only possible using encoders. During a transition - period, layouts passed as parameter will be automatically wrapped by - an encoder so that configuration files in the old format (using a - layout instead of encoder) will continue to work unmodified. -

- - - - - - -

No remote host or address - is set for SocketAppender - -

- -

A remote host or address is mandatory for SocketAppender.

-

You can specify the remote host in the configuration file - as follows. -

- -
<appender name="SOCKET"
-  class="ch.qos.logback.classic.net.SocketAppender">
-  ...
-  <remoteHost>127.0.0.1</remoteHost>
-  ...
-</appender>
- - - - - - -

No remote port is set for - SocketAppender -

- -

A remote port is mandatory for SocketAppender.

- -

You can specify the remote port in the configuration file - like this: -

- -
<appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender">
-  ...
-  <port>4560</port>
-  ...
-</appender>
- - - - - - - -

No Layout is - set for SMTPAppender -

- -

A Layout is mandatory for - SMTPAppender. It allows SMTPAppender to format logging - events before sending an email. -

- -

You can specify the Layout in a configuration file - as follows: -

- -
<appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
-  ...
-  <layout class="ch.qos.logback.classic.PatternLayout">
-    <pattern>%date [%thread] %-5level %logger - %msg%n"></pattern>
-  </layout>
-  ...
-</appender>
- -

SMTPAppender is known to work well with PatternLayout - and HTMLLayout. -

- - - - - - -

Specified number is not - in proper int form, or not expected format. -

- -

When you specify the MaxFileSize to be used by the - SizeBasedRollingPolicy, logback expects a rather precise - format: -

- -
    -
  • The number has to be an integer
  • -
  • You can add 'KB', 'MB' or 'GB' after the number. -
  • -
- -

Here are some correct values: 500KB, 15MB, 2GB.

- - - - - -

No - TriggeringPolicy was set for the - RollingFileAppender. - -

- -

The RollingFileAppender must be set up with a - TriggeringPolicy. It permits the Appender to know when - the rollover must be activated. -

- -

To find more information about TriggeringPolicy - objects, please read the following javadocs: -

- - - -

Please note that the TimeBasedRollingPolicy - is a TriggeringPolicy and and - RollingPolicy at the same time. -

- - - -

Missing - integer token, that is %i, in FileNamePattern [...]. - -

- -

The %i conversion token is mandatory for size and time - based archiving. In case the %i token is missing, - SizeAndTimeBasedFNATP attached to - RollingFileAppender will detect the omission and will - not start. -

- - - - -

No RollingPolicy - was set for the RollingFileAppender. -

- -

The RollingFileAppender must be set up with - a RollingPolicy. It permits the Appender to - know what to do when a rollover is requested. -

- -

To find more information about RollingPolicy - objects, please read the following javadocs: -

- - - -

Please note that the TimeBasedRollingPolicy is a - TriggeringPolicy and and RollingPolicy at - the same time. -

- - - - - -

The FileNamePattern property is mandatory for both - TimeBasedRollingPolicy and - FixedWindowRollingPolicy. -

- - -

The FileNamePattern property for both - TimeBasedRollingPolicy and - FixedWindowRollingPolicy is mandatory. -

- -

Please refer to the documentation of TimeBasedRollingPolicy - and FixedWindowRollingPolicy for - examples. -

- - - - - -

The File property must be set before any rolling - policy or triggering policy. - -

- -

The File property, if present, must - be placed before any rolling policy or triggering policy. Thus, in a - configuration file, the File property, - if present, must be declared declared before any rolling policy or - triggering policy declarations. -

- - - -

File property collides with fileNamePattern. Aborting. -

- -

When the file property matches the - regular expression defined by fileNamePattern, there is a risk of - collision. A collision occurs when the active log file as defined by - the file property has the same path as - an existing log archive. Such a collision will result in the log - archive being overwritten. -

- -

As such, in case file property - matches the regular expression defined by fileNamePattern, in order to avoid data loss, - RollingFileAppender will emit an error message and - refuse to start.

- - - -

The date - format in fileNamePattern will result in - collisions in the names of archived log files. -

- -

This error message is output when the date time pattern in the %d - token within the fileNamePattern is not - collision free. For example, the following - code>fileNamePattern will result in the same log archive - every day of the week after the first week of the month. -

- -
myapp-%d{yyyy-MM-uu}.log.zip
- -

As such collisions will result in log data loss, - RollingFileAppender will check for a variety of such - possible collisions and will not start if any collisions are - detected.

- - - -

File/FileNamePattern option has the same value "..." as - that given for appender [...] defined earlier. -

- - -

If a FileAppender/RollingFileAppender - defined earlier has the same File option - as the current appender, then those two appenders are in collision - as FileAppender instances cannot share the same output - target. To prevent loss of data, the current appender will not - start. Make sure that each appender has a unique File option. -

- -

By analogy the same restriction applies to the FileNamePattern option of - RollingFileAppender. Make sure that each RollingFileAppender has a - unique FileNamePattern option

- - -

Failed to rename file [x] - as [y].

- - On Windows - -

On the Windows platform a log file cannot be renamed if there are - handles referencing it. For example, when the file is read by - another process such as less, tail, - etc. During application hot-redeployment, the old instance of the - application will have open references to log files until the old - instance is completely shutdown or until the stop() - method on its LoggerContext - is invoked. -

- -

Process - Explorer can help you locate the processes which reference a - given file (Find -> Find Handle or DLL). On Linux, you can use - the lsof - pathToFile command to find which process has the - file given by pathToFile open.

- -

Rolling might also fail due to incorrect file permission - settings. On windows, renaming a file requires the "modify" - permission which is different than the permission to "write" to the - file.

- -

File rename operations during rollover can be - avoided altogether by omitting the file - property in RollingFileAppender.

- -

In practice, it can be hard to set the right permissions or to - ensure that there are no file handle references to log files.

- -

Under many circumstances, it can be more robust to avoid file - renaming during rollover altogether. This is as easy as omitting the - file property in - RollingFileAppender, as shown in the next configuration - snippet. -

- -
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
-  <!-- file property left unset/blank -->
-  <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
-    <fileNamePattern>mylog.%d{yyyy-MM-dd}.log</fileNamePattern>
-  </rollingPolicy>
-
-  <encoder>
-    <pattern>%relative [%thread] %level %logger - %msg%n</pattern>
-  </encoder>
-</appender>
- -

Note that for FixedWindowRollingPolicy, the file property is mandatory. -

- -

On Unix-*

- -

On the Unix platform, the basic/quick rename method supplied by - the JDK does not work if the source and target files are located on - different file systems. In order to deal with this contingency, - logback will resort to renaming by copying if all of the following - three conditions are met:

- -
    -
  1. quick renaming fails,
  2. -
  3. source and destination files for the rename are on different - file systems, -
  4. - -
  5. the host JVM platform runs Java 7 or later.
  6. -
- -

The code for determining the file system of a file requires Java - 7. No rename by copying will be performed on Java 6 or earlier.

- -

As explained in the Windows section above, renaming can be - avoided altogether by omitting the file - property. -

- - -

The File property must be set before - FixedWindowRollingPolicy -

- -

The File property is mandatory with - FixedWindowRollingPolicy. Moreover, the File option must be set before the - FixedWindowRollingPolicy element. -

- -

Refer to the logback manual on - - FixedWindowRollingPolicy for more information. -

- - - -

Prudent mode - is not supported with FixedWindowRollingPolicy. -

- -

Given that FixedWindowRollingPolicy performs - multiple file rename operations during roll over, and that these - operations cannot be guaranteed to be safe in a multi-JVM context, - prudent mode is not allowed in conjunction with a - FixedWindowRollingPolicy. -

- - - - -

SyslogAppender does not - admit a layout. -

- - -

Given that the format of a syslog request follows strict rules, - you cannot freely specify the layout to be used with - SyslogAppender. However, you can use SuffixPattern option instead to influence the - contents of the message sent to the syslog daemon. -

- -

For more information on SyslogAppender please refer to the its documentation. -

- - - - -

Only and only one appender can - be nested the <sift> element in - SiftingAppender.

- -

SiftingAppender admits one and only one nested appender. -

- - - -

Could not find Janino library - on the class path. Skipping conditional processing. -

- -

Conditional - processing in configuration files requires the Janino - library. See the setup - instructions for adding Janino to your class path. -

- - -

As of logback version 0.9.28, - JaninoEventEvaluator expects Java blocks. -

- -

As of logback version 0.9.28, JaninoEvaluator expects Java - "block", i.e. the body of a method. In previous versions only - boolean expressions were allowed. For backward compatibility - reasons, whenever logback sees a boolean expression it will attempt - to convert it to a block by prefixing the expression with "return" - and suffixing it with a semicolon. -

- -

Boolean expressions can quickly become hairy. For example, the - following boolean expression is rather difficult to grok. -

- -
 !logger.startsWith("org.apache.http")
-  ||
-  ( logger.equals("org.apache.http.wire")  &&
-       (mdc != null && mdc.get("entity") != null
-         &&
-       ((String) mdc.get("entity")).contains("someSpecialValue"))
-       &&
-     !message.contains("someSecret")
-  )
- -

whereas as its Java block equivalent is considerably easier to - follow.

- -
if(logger.startsWith("org.apache.http"))
-  return true;
-
-if(mdc == null || mdc.get("entity") == null)
-  return false;
-
-String payee = (String) mdc.get("entity");
-
-if(logger.equals("org.apache.http.wire") &&
-  payee.contains("someSpecialValue") &&
-  !message.contains("someSecret")) {
-  return true;
-}
-
-return false;
- -

-

- - -

In a conversion - pattern, opened parenthesis must be closed. -

- -

In conversion patterns, parentheses are special - because they are treated as grouping tokens. If a parenthesis - character needs to be viewed as a literal, it needs to be escaped - by preceding each parenthesis with a backslash. As in, - \(%d{HH:mm:ss.SSS} [%thread]\) . -

- - - -

Appenders must be - definied before they are referenced. -

- -

In a configuration file, at the point where an appender is - referenced by name, it must be defined earlier in the configuration - file. Referencing an appender defined later in the file is not - allowed. Below are examples of correct and incorrect order of - definition and referece. -

- - -

Below is an example of a correct ordering, where appender - definition precedes references made to it. -

- -

CORRECT ORDER

-
<configuration>
-  <!-- definition of appender STDOUT -->
-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-    <encoder>
-      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
-    </encoder>
-  </appender>
-
-  <root level="DEBUG">
-    <!-- appender referenced after it is defined -->
-    <appender-ref ref="STDOUT"/>
-  </root> 
-</configuration>
- -

Below is an example of an incorrect ordering, where appender - definition follows references made to it. -

- -

INCORRECT ORDER

-
<configuration>
-  <root level="DEBUG">
-    <!-- appender INCORRECTLY referenced before it is defined -->
-    <appender-ref ref="STDOUT"/>
-  </root>
-
-  <!-- definition of appender STDOUT -->
-  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
-    <encoder>
-      <pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
-    </encoder>
-  </appender>
-</configuration>
-   
- - - -
- - diff --git a/logback-site/src/site/pages/css/_print.css b/logback-site/src/site/pages/css/_print.css deleted file mode 100644 index 84b45b04ca..0000000000 --- a/logback-site/src/site/pages/css/_print.css +++ /dev/null @@ -1,30 +0,0 @@ - -body { - margin: 0px; - padding: 100px 0px 2px 0px; - line-height: 1.3em; - font-size: 12px; -} - -#left, #right, span.asGroovy { - width: 0px; - height: 0px; - display: none; - visibility:hidden; -} - -#bodyColumn { - margin-right: 1.5em; - margin-left: 0px; /*was: 197*/ -} - - -table.bodyTable td { - vertical-align: text-top; -} - -table.bodyTable th { - vertical-align: text-top; - text-align:center; -} - diff --git a/logback-site/src/site/pages/css/common.css b/logback-site/src/site/pages/css/common.css deleted file mode 100755 index a960d6acc8..0000000000 --- a/logback-site/src/site/pages/css/common.css +++ /dev/null @@ -1,311 +0,0 @@ -html { -/* padding:0px; - margin:0px; */ -} - - -a { - color: #4183c4; - background-color:transparent; - text-decoration: none; -} - -#content a:hover { - text-decoration: underline; -} - -.example { - width: 90%; - font-style: italic; -} - - -div.source { - margin-top: 1em; -} - -div.breaking { - border: 1px solid #F44; - background-color: #FED; - padding-left: 1ex; - padding-right: 1ex; -} - -.longline { - overflow: scroll; -} - -.source, .command, .console { - border-top: 1px solid #DDDDDD; - border-bottom: 1px solid #DDDDDD; - background: #f5f5f5; - font-family: Courier, "MS Courier New", Prestige, monospace; - padding-left: 1ex; - white-space: pre; -} - -.source { - padding-bottom: 0.5ex; - padding-top: 0.5ex; -} - -.command { - padding-bottom: 0ex; - padding-top: 0ex; -} - -.console { -} - -pre { - padding: 0px; - margin: 0px; - background-color:transparent; - font-family: Courier, Monaco, Monospace; -} - -.alignright { - margin-top: 0; - text-align: right; - font-size: 10px; -} - - -h1, h2, h3, h4 { - color: #505050; - padding-top: 0ex; - background-color: transparent; -} - -h2 { - font-weight: 900; - font-size: x-large; -} - -h3 { - font-weight: bold; - font-size: large; -} - -h4 { - font-weight: bold; - font-size: medium; -} - -table.footer { - width: 100%; -} - -.footer { - color: #564b47; - background-color: #fff; - padding:0px; - border-top: 1px solid #CCCCCC; - margin-top: 3ex; - font-size: smaller; -} - - -p.menu { - padding-top: 0px; - padding-bottom: 0px; - margin-top: 0px; - margin-bottom: 0px; -} - -.small { - font-size: smaller; -} - -.strong, .bold { - /*font-size: 13px;*/ - font-weight: bold; -} - -/* ========== body table ============ */ -table.bodyTable { - padding: 0px; - margin-left: -2px; - margin-right: -2px; -} - -table.bodyTable th { - color: white; - background-color: #bbb; - font-weight: bold; -} - - -table.bodyTable td { - vertical-align: text-top; - padding-left: 0.5ex; - padding-bottom: 0.5ex; -} - - -/* apply to tr elements of tables which are both bodytable and dark */ -table[class="bodyTable dark"] tr { - background-color: #ddd; -} - -/* apply to tr elements of tables which are both bodytable and dark */ -table[class="bodyTable properties"] tr { - vertical-align: top; -} - -/* table.bodyTable tr.a { background-color: #ddd; } */ -/* table.bodyTable tr.b { background-color: #eee; } */ -/* table.bodyTable tr.alt { background-color: #eee;} */ - -/* we don't want the first p under td to appear indented */ -table.bodyTable tr td p:first-child { - padding-top: 0px; - margin-top: 0px; -} - -.striped tr:nth-child(odd) td { - background-color: #f9f9f9; -} -.striped td { - background-color: #f0f0f0; -} -.striped tr:last-child td { - border-bottom: 1px solid #ddd; -} - -td.word { - text-align: right; -} - -/* EOF =============== bodyTable =============== */ - -.author { - text-align: left; - font-weight: bold; -} - -.definition { - padding-left: 5px; - padding-right: 5px; - margin: 5px 50px 5px 50px; - text-align: justify; - background-color: #E6E64C; -} - -.deftitle { - font-weight: bold; -} - - -.big { - font-size: 130%; -} - - -.green { - color: green; -} - -.white_bg { - background-color: #FFFFFF; -} - -.lgray_bg { - background-color: #EEE; -} - -.blue { - color: blue; -} - -.red { - color: red; -} - -.redBold { - color: red; - font-weight: bold; -} - -.greenBold { - color: green; - font-weight: bold; -} - -.code { - font-family: Courier, monospace; - color: #666; -} - -.variable { - font-family: Courier, monospace; - font-style:italic; -} - - - -.attr { - font-family: Courier, monospace; - font-style:italic; - -} - -.option, .prop { - border-top: 1px dotted #BBBBBB; - border-bottom: 1px dotted #BBBBBB; - border-left: 1px dotted #AAAAAA; - border-right: 1px dotted #AAAAAA; - padding-left: 2px; - padding-right: 2px; - font-family: Arial, sans-serif; -} - -/* background:#FFCC99; */ -.highlight { - width: 18em; - float: right; - display: inline; - font-size: 110%; - - border: 2px solid #711; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; - background:#FFE0B0; - padding-top: 1ex; - padding-left: 1ex; - padding-right: 1ex; - padding-bottom: 1ex; - margin-left: 1em; - margin-right: 0em; - margin-bottom: 1ex; -} - -.quote { - text-align: right; - padding-left: 12em; -} - -/* --------- numbering --------- */ -.autoEx:before { - counter-increment: example; /* Add 1 to example */ - content: counter(example) -} - -/** ----------- labels -------- */ -.label { - padding: 1px 3px 2px; - font-size: 9.75px; - font-weight: bold; - color: #ffffff; - text-transform: uppercase; - white-space: nowrap; - background-color: #bfbfbf; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -.label.notice { - background-color: #62cffc; -} diff --git a/logback-site/src/site/pages/css/popup.css b/logback-site/src/site/pages/css/popup.css deleted file mode 100644 index 2b20468e33..0000000000 --- a/logback-site/src/site/pages/css/popup.css +++ /dev/null @@ -1,67 +0,0 @@ - -/* =========== popup ================== */ -#backgroundPopup { - display:none; - position:fixed; - _position:absolute; /* hack for internet explorer 6*/ - height:100%; - width:100%; - top:0; - left:0; - background:#000000; - border:1px solid #cecece; - z-index:1; -} - -#popupContents { - display:none; - position:fixed; - _position:absolute; /* hack for internet explorer 6*/ - height: 150px; - width: 408px; - background:#FFFFFF; - border: 2px solid #cecece; - z-index:2; - padding-left: 1ex; - font-size:13px; -} - -#popupContents p { - margin: 0px; -} -#popupContents h3 { - margin: 0px; -} - -#popupContactClose { - font-size:14px; - line-height:14px; - right:6px; - top:4px; - position:absolute; - color:#6fa5fd; - font-weight:700; - display:block; -} - -a.popupLink { - background: #FFF; - color: #0079C5; - font-family: "Comic Sans MS", sans-serif; - white-space: nowrap; - font-size: 14px; - font-weight: bold; - - border-top: 2px solid #DDD; - border-left: 2px solid #DDD; - border-right: 2px solid #888; - border-bottom: 2px solid #888; - padding: 0px 1em 0px 1em; - margin: 0px 0px 3px 0px; -} - -a.popupLink:hover { - background: #E0E0EF; - cursor: pointer; -} - diff --git a/logback-site/src/site/pages/css/prettify.css b/logback-site/src/site/pages/css/prettify.css deleted file mode 100644 index 2e05337195..0000000000 --- a/logback-site/src/site/pages/css/prettify.css +++ /dev/null @@ -1,27 +0,0 @@ -/* Pretty printing styles. Used with prettify.js. */ - -.str { color: #080; } -.kwd { color: #008; } -.com { color: #800; } -.typ { color: #606; } -.lit { color: #066; } -.pun { color: #660; } -.pln { color: #000; } -.tag { color: #008; } -.atn { color: #606; } -.atv { color: #080; } -.dec { color: #606; } -pre.prettyprint { padding: 2px; border-top: 1px solid #888; border-bottom: 1px solid #888;} - -@media print { - .str { color: #060; } - .kwd { color: #006; font-weight: bold; } - .com { color: #600; font-style: italic; } - .typ { color: #404; font-weight: bold; } - .lit { color: #044; } - .pun { color: #440; } - .pln { color: #000; } - .tag { color: #006; font-weight: bold; } - .atn { color: #404; } - .atv { color: #060; } -} diff --git a/logback-site/src/site/pages/css/screen.css b/logback-site/src/site/pages/css/screen.css deleted file mode 100755 index 5f856aed97..0000000000 --- a/logback-site/src/site/pages/css/screen.css +++ /dev/null @@ -1,207 +0,0 @@ -html { -/* padding:0px; - margin:0px; */ -} - -body { - background-color: #fff; - font-family: Verdana, Arial, SunSans-Regular, Sans-Serif; - color: #000; - padding: 0px; - margin: 0px; - counter-reset: example; /* Create an example counter scope */ -} - -.footer { - color: #564b47; - background-color: #fff; - padding:0px; - border-top: 1px solid #CCCCCC; - margin-top: 3ex; - font-size: smaller; -} - -#job img { - border:1px solid #DDDDDD; -} - -#job:hover img { - border:1px solid #8888EE; -} - -p.menu { - padding-top: 0px; - padding-bottom: 0px; - margin-top: 0px; - margin-bottom: 0px; - font-size: smaller; -} - -#left { - position: absolute; - left: 0px; - width: 15em; - margin: 4px 0px 0px 4px; - padding: 0px; - font-size: 80%; - background-color: #ffffff; -} - -.menuGroup { - border: 1px solid #cccccc; - background-color: #fff8e8; - color: #564b47; - border: 1px solid #cccccc; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} - -#left a, #right a { - display: block; - margin: 0px; - padding: 2px; - border: solid 1px #fff8e8; - color: #0066cc; - text-decoration: none; -} - -p.menu_header { - margin: 0px; - padding: 2px; - font-weight: normal; - background-color: #ffd0a0; - border-top: solid 1px #CCCCCC; - border-bottom: solid 1px #CCCCCC; -} - -#left a:hover, #right a:hover { - border: solid 1px #FFFFFF; - background-color: #3333CC; - color: #ffffff; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; -} -.pub { - text-align: center; -} - -#left .pub a:hover { - background-color: transparent; - border: solid 0px #FFFFFF; -} - -#left img { - border: none; -} - -#left div.jobadd { - font-size: 140%; - color: #fff; - margin: 0px; - padding: 0px; - - text-align: center; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - - background-image: -ms-linear-gradient(bottom right, #FFBB55 0%, #FF8822 100%); - background-image: -moz-linear-gradient(bottom right, #FFBB55 0%, #FF8822 100%); - background-image: -o-linear-gradient(bottom right, #FFBB55 0%, #FF8822 100%); - background-image: -webkit-gradient(linear, right bottom, left top, color-stop(0, #FFBB55), color-stop(1, #FF8822)); - background-image: -webkit-linear-gradient(bottom right, #FFBB55 0%, #FF8822 100%); - background-image: linear-gradient(to top left, #FFBB55 0%, #FF8822 100%); -} - -#left div.jobadd a, div.jobadd a:hover { - background-color: transparent; - color: #fff; - border-width: 0px; -} - -/* ------------------------------------------- */ - -#right { - position: absolute; - right: 0px; - width: 12em; - color: #564b47; - margin: 4px 4px 0px 0px; - padding: 0px; - background-color: #ffffff; - border: 1px solid #cccccc; - font-size: 80%; -} - - -#headerLine { - background-color:#FFD0A0; - border:1px solid #CCCCCC; - font-size:small; - margin:0 4px; - padding:3px 10px; -} - -#content { - margin: 0px 15em 0px 15em; - padding: 0px; - line-height: 130%; - background-color: #ffffff; -} - -#content img { - border:none; - margin-left: auto; - margin-right: auto; - display: block; -} - -span.asGroovy { - background: #fff; - color: #0079C5; - font-family: "Comic Sans MS", sans-serif; - white-space: nowrap; - font-size: 80%; - - border-top: 2px solid #DDD; - border-left: 2px solid #DDD; - border-right: 2px solid #888; - border-bottom: 2px solid #888; - padding: 0px 1em 0px 1em; - margin: -4px 4px 0 4px; - float: right; -} - -span.asGroovy:hover { - background: #E0E0EF; - cursor: pointer; -} - -/* ------------------------------------ */ - -.anchor { display:none; } - -h1 .anchor:before {content:url(anchor24.png);} -h2 .anchor:before {content:url(anchor20.png);} -h3 .anchor:before {content:url(anchor16.png);} -h4 .anchor:before {content:url(anchor12.png);} -td .anchor:before {content:url(anchor12.png);} - -h1:hover .anchor { margin-left: -24px; } -h2:hover .anchor { margin-left: -20px; } -h3:hover .anchor { margin-left: -16px; } -h4:hover .anchor { margin-left: -12px; } -td:hover .anchor { margin-left: -12px; } - -h1:hover .anchor, -h2:hover .anchor, -h3:hover .anchor, -h4:hover .anchor, -td:hover .anchor { - display: inline-block; - text-decoration: none; -} - - diff --git a/logback-site/src/site/pages/deadlock.html b/logback-site/src/site/pages/deadlock.html deleted file mode 100644 index c25cd23da5..0000000000 --- a/logback-site/src/site/pages/deadlock.html +++ /dev/null @@ -1,79 +0,0 @@ - - -logback-core/src/main/java/ch/qos/logback/core/AppenderBase.java: - public synchronized void doAppend(E eventObject) - -logback-core/src/main/java/ch/qos/logback/core/BasicStatusManager.java: - locks: - Object statusListLock - Object statusListenerListLock - exposition: to derived classes - -logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java: - locks: this - lock exposition: this - -logback-classic/src/main/java/ch/qos/logback/classic/selector/ContextJNDISelector.java: - locks: synchronizedContextMap - lock exposition: none - - -logback-classic/src/main/java/ch/qos/logback/classic/Logger.java - locks: synchronizedContextMap - lock exposition: none - -logback-classic/src/main/java/ch/qos/logback/classic/LoggerContext.java - locks: logger, - loggerCache should be syncronzied but is not - lock exposition: - -logback-classic/src/main/java/ch/qos/logback/classic/Logger.java: - * synchronized on 'this' (Logger) protecting against simultaneous - * only within addAppender which is synchronized

3) all the other methods - public synchronized void setLevel(Level newLevel) { - private synchronized void handleParentLevelChange(int newParentLevelInt) { - // this method MUST be synchronized. See comments on 'aai' field for further - public synchronized void addAppender(Appender newAppender) { - - -logback-core/src/main/java/ch/qos/logback/core/joran/GenericConfigurator.java: - locks: context - lock exposition: none - - - - - * only within addAppender which is synchronized

3) all the other methods - public synchronized void setLevel(Level newLevel) { - private synchronized void handleParentLevelChange(int newParentLevelInt) { - // this method MUST be synchronized. See comments on 'aai' field for further - public synchronized void addAppender(Appender newAppender) { - - - - -logback-core/src/main/java/ch/qos/logback/core/FileAppender.java: public synchronized void openFile(String file_name) throws IOException { - -logback-core/src/main/java/ch/qos/logback/core/net/SMTPAppenderBase.java: synchronized public void stop() { -logback-core/src/main/java/ch/qos/logback/core/net/SocketAppenderBase.java: synchronized (this) { - -logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java: public synchronized void stop() { -logback-core/src/main/java/ch/qos/logback/core/WriterAppender.java: public synchronized void setWriter(Writer writer) { - -logback-classic/src/main/java/ch/qos/logback/classic/net/JMSQueueAppender.java: public synchronized void stop() { -logback-classic/src/main/java/ch/qos/logback/classic/net/JMSQueueAppender.java: // The synchronized modifier avoids concurrent append and close operations -logback-classic/src/main/java/ch/qos/logback/classic/net/JMSTopicAppender.java: public synchronized void stop() { -logback-classic/src/main/java/ch/qos/logback/classic/net/JMSTopicAppender.java: // The synchronized modifier avoids concurrent append and close operations - - -logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java: public synchronized void add(Status child) { -logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java: public synchronized boolean hasChildren() { -logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java: public synchronized Iterator iterator() { -logback-core/src/main/java/ch/qos/logback/core/status/StatusBase.java: public synchronized boolean remove(Status statusToRemove) { - - -logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java: synchronized (socketNodeList) { -logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java: synchronized(this) { -logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java: synchronized (socketNodeList) { -logback-classic/src/main/java/ch/qos/logback/classic/net/SimpleSocketServer.java: synchronized (socketNodeList) { -logback-classic/src/main/java/ch/qos/logback/classic/pattern/DateConverter.java: // the AppenderBase.doAppend is synchronized, we are should be diff --git a/logback-site/src/site/pages/demo.html b/logback-site/src/site/pages/demo.html deleted file mode 100755 index e8e45faa74..0000000000 --- a/logback-site/src/site/pages/demo.html +++ /dev/null @@ -1,494 +0,0 @@ - - - - - - Logback Demo - - - - - - - - -

- -
- - -
- - -

Logback Demo

- -

Welcome to the logback demo! This document will take you through - a tour of some of logback's major features. -

- -

Installation

- -

First, please download the logback demo. You will need to - install a git - client and issue the following command on a console: -

- -

git clone git://github.com/qos-ch/logback-demo.git logback-demo

- -

This will retrieve a copy of the logback demonstration - web-application to a directory called logback-demo. The - logback demo can be packaged as a war file and deployed - to an application server. We strongly recommend the use of Maven to accomplish this task, - since it takes a single command in order to compile, package and - run this demo. -

- -

Using Maven, let's package the files and run the demo for the first - time. From the logback-demo directory, issue the following - command: -

- -
mvn package jetty:run
- -

Then, visit http://localhost:8080/ - to view the main page of the logback demo. -

- -

Logback-classic

- -

By default (or as packaged), logback-demo configures - logback-classic with two appenders: a ConsoleAppender - and a RollingFileAppender. The - RollingFileAppender sends logging events to a file - called logFile.log and will rollover the active file - every minute. The old file will be renamed and compressed to a - zip file. The ConsoleAppender will output - the logging requests to the console, and shorten the logger names - to gain space on the console window, without loss of - legibility. For example, - ch.qos.logback.demo.prime.NumberCruncherImpl will be - abbreviated as c.q.l.d.prime.NumberCruncherImpl. -

- -

We highly encourage you to study the logback.xml - configuration file located under the src/main/resources/ - folder. You might want to keep this file open in an editor window, - since we will modify its contents throughout the demo. -

- -

Let us now visit the ViewStatii page, via the navigation - menu on the left hand side of your browser's window. This page - contains the content of the Status objects that were - created up until now. Status objects are a part of - logback's internal reporting framework. They allow you to see what - is going on inside logback, and check that a configuration file has - been parsed correctly, or that rollovers occur as expected. -

- -

You should be seeing log messages printed on the console and the - contents of "logFile.log" file rolled over every minute. -

- -

If you visit the View logs page (by clicking on the link - located in the menu on the left), you should see it has no content. Let - us change that by uncommenting two parts in the - config file.

- -

Remove the comments around

- -

<!-- Basic Cyclic buffer -<appender name="CYCLIC" class="ch.qos.logback.core.read.CyclicBufferAppender"> - <MaxSize>512</MaxSize> -</appender> --->

-

and around

- -

<!-- Part I: Basic Cyclic buffer -<appender-ref ref="CYCLIC" /> --->

- -

The <appender-ref> element found at the - end of the configuration file links an appender to a given logger, - in this particular case the root logger. -

- -

A CyclicBuffer keeps track of the incoming logging event - stream in a circular - buffer for later display. After having removed the comments - around the two element/s shown above, reload the logback-demo - web-application by exiting the previous "mvn" command with - CTRL-C and issuing it again: -

- -

mvn package jetty:run

- -

This time the View logs page should have contents.

- - view logs - - -

By virtue of CyclicBufferAppender, this page can - fetch the last events and present them through a servlet. We see - that every ten seconds a line is added to the logs. The formatting - of this page is made with a HTMLLayout. This component - creates a table containing logging events, based on a pattern that - describes the information you wish to see in the table. -

- -

Having the logs cluttered with repetitive - Howdydy-diddly-ho messages is wasteful. We can get rid of - them with an appropriate filter. Uncomment the block entitled - Cyclic buffer with Evaluator. You should then comment the - block entitled "Basic Cyclic buffer" that we uncommented - earlier.

- -

Let's take a look at the filter we've just added:

- -

<filter class="ch.qos.logback.core.filter.EvaluatorFilter"> - <evaluator name="loggingTaskEval"> - <expression> - logger.getName().contains("LoggingTask") &amp;&amp; - message.contains("Howdydy-diddly-ho") &amp;&amp; - (timeStamp - event.getStartTime()) >= 20000 - </expression> - </evaluator> - <OnMatch>DENY</OnMatch> -</filter>

- -

The <expression> element uses the familiar - Java language syntax. It checks that the name of the logger contains - the String LoggingTask, but also that its message contains - the string Howdydy-diddly-ho. Moreover, in order to be - sure that the Howdydy-diddly-ho task actually works, we add - a last condition which checks that that at least 20 seconds have - elapsed after application launch. The variable references in the - expression, namely (logger, message and - event) are implicitly made available by logback. The - <OnMatch> element lets the user specify the filter's - behaviour once the expression matched (evaluated to true). -

- -

After a restart, the View logs page will shows the - Howdydy-diddly-ho logs, but only for the first 20 - seconds. If you wish to see new logging events to be shown on the - "View logs" page, then visit the "Prime number" page. -

- -

Turbo Filters

- -

Logback supports a special category of filters called - TurboFilters. TurboFilter objects are ultra-fast, - context-wide filters. They can be very useful by setting - context-wide (i.e. global) conditions for enabling or disabling - logging events. -

- -

Remove the comments around the block entitled "TurboFilter: MDC - value".

- -

<turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"> - <MDCKey>username</MDCKey> - <Value>sebastien</Value> - <OnMatch>ACCEPT</OnMatch> -</turboFilter>

- - -

This <turboFilter> element adds a - TurboFilter to the logging context which will enable - events if the MDC contains a key named "username" set to the value - "sebastien". The logback-demo application contains a servlet - filter which will set the MDC key "username" with the name of - the currently logged in user.

- - -

For the purpose of this demo, let us disable all logging by - setting the root logger's level to OFF. -

- -

<root> - <level value="OFF"/> - ... -</root>

- -

Now restart the server as before.

- -

Once on the main demo webpage again, perform a number of actions - (e.g. calculate a few prime numbers) and visit the View - logs page. The table should be empty. -

- -

Now login to the logback-demo web-application with the - username sebastien and perform a few prime - computations. The View logs page should show the logging - events that were generated. Moreover, each logging event will have - an MDC field associated with the name of the logged in user, in this - case, sebastien. Please log off before continuing the demo, using - the logout button on the left. -

- - mdc filters - -

Parameterized logging

- -

Parameterized - logging is not a logback feature per se. It is part of - SLF4J. Usually, a logging request is issued as follows: -

- -
logger.debug("Hello, my name is" + username + ", I am " + age + " years old.");
- -

In the above call, the cost of constructing the message of type - String is borne even if the log request is disabled. -

- -

SLF4J offers the following alternative: -

- -
logger.debug("Hello, my name is {}, I am {} years old", username, age);
- -

In this alternative, the final log message will be formatted only - if the log statement is enabled. -

- -

At present, let us see what kind of gain we can expect from this - alternative approach. First, go to the Prime number page - and compute factors for integers of your choice. Check the time it - takes to compute the results. To see a clearer difference between - the two formatting methods, you might want to try the two big - integers that are listed below the prime number textbox. Jot down - the time it takes to compute the result. -

- -

Now let us edit the NumberCruncherImpl class in - order to use parameterized logging. You will find this class in the - src/main/java/ch/qos/logback/demo/prime/ directory. Comment - line 54 (doing unconditional message concatenation) and uncomment - line 55 (parameterized logging). Restart the server with mvn - package jetty:run and re-run the factorization you tried - beforehand. -

- -

The time required to complete the computation should be much lower - this time. Remember that we have turned off all logging in the - previous step of this demo. In the initial version, we were - constructing the message ("Trying "+i+" as a factor.") each - time a factor was tested. With the parameterized logging, the - construction of the message was postponed and, since logging was - turned off, never done. Thus, parameterized logging can - significantly reduce the cost of disabled log statements. -

- -

Markers

- -

You can color log statements with markers. Markers are - part of the SLF4J API. If you look at the LoggingTask class (part of - logback-demo) which includes the Howdydy-diddly-ho log - statement, you should see that it is bound to a marker named - HOWDY marker. If you wish to drop log statements - bearing the HOWDY marker, you can use this - TurboFilter to do so: -

- -

<turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"> - <Name>HOWDY_FILTER</Name> - <Marker>HOWDY</Marker> - <OnMatch>DENY</OnMatch> -</turboFilter>

- -

After you have set the root logger's level back to DEBUG - and uncommented the MarkerFilter block in logback.xml, - restart the server. -

- - -

The logs bearing the Howdydy-diddly-ho message should no - longer appear as they are associated with a HOWDY marker. You can check - that by visiting the View Logs page. -

- -

Logback Access

- -

Access logging is another important feature offered by - logback. By default, the logback-demo web-application is configured - so that each time you access it, an access log is printed on the - console. The details of access logs are configured by the - logback-access.xml file located under the src/etc/ - directory. -

- -

Here is a rather minimal configuration for logback-access:

- -
<configuration>
- 
-  <appender name="STDOUT"
-    class="ch.qos.logback.core.ConsoleAppender">
-    <layout class="ch.qos.logback.access.PatternLayout">
-      <Pattern>%h %l %u %t \"%r\" %s %b</Pattern>
-    </layout>
-  </appender>
-  
-  <appender-ref ref="STDOUT" />
-  
-</configuration>
- -

Note that logback-classic and logback-access are configured via - different files: logback.xml and - logback-access.xml respectively. If you wanted to turn - off logging for logback-classic by setting the level of the root - logger to OFF, logback-access would be unaffected by this change. -

- -

To see the logs produced by logback-access, just visit a few - pages and look at your console. The information contained in each - line has been specified in the configuration file. The - ConsoleAppender named STDOUT contains a - PatternLayout component. This layout component is - used in logback-classic to display either the message, logger name - or level of the request, but in logback-access it is used to display the - request method, requested page, status code and many other fields. -

- -

Here is a sample output for this appender.

- -
127.0.0.1 - - 22/Jan/2007:14:35:40 +0100 GET /logback-demo/ViewStatii.do HTTP/1.1 200 3660
-127.0.0.1 - - 22/Jan/2007:14:35:41 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389
-127.0.0.1 - - 22/Jan/2007:14:35:42 +0100 GET /logback-demo/lastLog/ HTTP/1.1 200 948
-127.0.0.1 - - 22/Jan/2007:14:35:42 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389
-127.0.0.1 - - 22/Jan/2007:14:35:43 +0100 GET /logback-demo/prime.jsp HTTP/1.1 200 1296
-127.0.0.1 - - 22/Jan/2007:14:35:44 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389
-127.0.0.1 - - 22/Jan/2007:14:35:45 +0100 GET /logback-demo/lottery.jsp HTTP/1.1 200 1209
-127.0.0.1 - - 22/Jan/2007:14:35:46 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389
-127.0.0.1 - - 22/Jan/2007:14:35:48 +0100 GET /logback-demo/reload.jsp HTTP/1.1 200 1335
-127.0.0.1 - - 22/Jan/2007:14:35:49 +0100 GET /logback-demo/index.jsp HTTP/1.1 200 2389
-127.0.0.1 - - 22/Jan/2007:14:35:54 +0100 GET /logback-demo/login.jsp HTTP/1.1 200 1214
-127.0.0.1 - - 22/Jan/2007:14:35:55 +0100 GET /logback-demo/Logout.do HTTP/1.1 200 1000
- -

Filtering

- -

In this next part, we are going to add some information to the - console. Let us imagine that we want to log the numbers that are - tried on the Lottery page. We will need a second - ConsoleAppender that will only print a given information - (e.g. the guessed number, along with some hints about the player). The - appender will also have to print that information only when a certain - page is accessed. -

- -

The necessary configuration lines are listed below. -

- -

<appender name="STDOUT_LOTTERY" - class="ch.qos.logback.core.ConsoleAppender"> - <filter class="ch.qos.logback.core.filter.EvaluatorFilter"> - <evaluator name="lotto_eval"> - <Expression> - url.matches(event.getRequestURL().toString()) - </Expression> - <matcher name="url"> - <regex>Lottery.do</regex> - <caseSensitive>false</caseSensitive> - </matcher> - </evaluator> - <OnMatch>ACCEPT</OnMatch> - <OnMismatch>DENY</OnMismatch> - </filter> - <layout class="ch.qos.logback.access.PatternLayout"> - <Pattern> - LOTTERY: %A [%r] Guess=%reqParameter{guessed_number} - </Pattern> - </layout> -</appender>

- -

This appender will use a PatternLayout to format its - output. The %reqParameter conversion word is used to - extract the guessed number from the request, and print it. -

- -

It also uses an EvaluatorFilter that will prevent - the appender to display anything when the access' request url does - not match the given expression. You can see that it is easy to - specify a RegExp, name it and use it in the expression that will be - evaluated. In that case, we only entered the name of the - lottery.do action. -

- -

Let us uncomment the two elements with the Lottery to - Console comments and restart the server. Now, try to play the - lottery. You will see more lines in the Console that you've seen until - now. At every try, logback will produce a log as shown below: -

- -

LOTTERY: 192.168.1.6 [POST /logback-demo/Lottery.do HTTP/1.1] Guess=321

- -

Sending emails

- -

Logback-access provides several components that are usually used - by the classic module. For example, a SMTPAppender can - be used to send an email when a specific event occurs. Here, we will - contact the lottery administrator each time a winner is detected. To - achieve this, we will add a SMTPAppender to the - existing configuration. Please uncomment the part of - logback-access.xml named Lottery to Email. Do not - forget to uncomment the appender-ref element at the end of - the configuration file that references the SMTP appender. - In the appender element, notice the use of a - URLEvaluator. This evaluator allows us to specify - one or more URLs that are to be watched. When one of them is - accessed, an email is sent. -

- -

A reload of the configuration has to be done before we can test - this new component. Once done, try to play the lottery with the - number 99. You should see a "congratulation" message but, - most importantly, the specified recipients should have a new mail in - their mailbox. The content of the email is a nicely formatted HTML - table with information about the accesses that occurred before - the triggering event. -

- -

JMX

- -

Logback publishes several components via JMX. This allows you to - see the status of certain objects, and change several configuration - parameters. Publishing logback's components via JMX is possible - with Jetty and Tomcat. For more information about the JMXConfigurator - please refer to the relevant - chapter in the manual. -

- -

Let us test setting levels using the configurator. - The Prime Number page requests two types of logs. When the - calculation checks if a number is a factor, a DEBUG log is - displayed. When the calculation has found a factor, a INFO - log is displayed. -

- -

Let us first set the level of the logger named - ch.qos.logback.demo.prime to DEBUG. Run a prime - calculation directly, without restarting the server. The View - logs page should show the DEBUG and INFO logs. -

- -

Now, if you set the level of the ch.qos.logback.demo.prime - logger to INFO, and run a prime calculation again, you should - not see the DEBUG level logs anymore. -

- -

This demo of logback is now over. Do not hesitate to play around - with the configuration files. You might want to check the logback documentation - page for more information about any component you'd like to test. -

- - -
- - diff --git a/logback-site/src/site/pages/dependencies.html b/logback-site/src/site/pages/dependencies.html deleted file mode 100755 index 6ba4ff555c..0000000000 --- a/logback-site/src/site/pages/dependencies.html +++ /dev/null @@ -1,217 +0,0 @@ - - - - - Logback Dependencies - - - - - - - - - - -
- -
- - -
- - -

Dependencies per module

- -

As of version 1.3.0, logback requires Java ${jdk.version}.

- -

Each logback module has a different set of dependencies. These - are listed below in a separate table per module.

- -

logback-core

- - - - - - - - - - - - - - - - - - - - - - -
Componentdependencies
Overall
  • JDK ${jdk.version}
JaninoEventEvaluatorBase and derived classes -
    -
  • Janino version ${janino.version}
  • -
-
SMTPAppenderBase and derived classes - -
- -

logback-classic

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Componentdependencies
Overall -
    -
  • JDK ${jdk.version} -
  • -
-
Overall -
    -
  • logback-core, and by transitivity logback-core's - dependencies. -
  • -
-
Overall - -
ch.qos.logback.classic.selector.* -
    -
  • servlet-api version 2.5 -
  • -
-
c.q.l.c.boolex.JaninoEventEvaluator -
    -
  • Depends on JaninoEventEvaluatorBase, and by - transitivity on JaninoEventEvaluatorBase's - dependencies. -
  • -
-
SMTPAppender -
    -
  • Depends on SMTPAppenderBase, and by - transitivity on SMTPAppenderBase's - dependencies. -
  • -
-
- -

logback-access

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Componentdependencies
Overall -
    -
  • JDK ${jdk.version} -
  • -
-
Overall -
    -
  • logback-core, and by transitivity logback-core's - dependencies. -
  • -
-
Overall -
    -
  • servlet-api version 2.5 -
  • -
-
ch.qos.logback.access.jetty.* -
    -
  • Jetty version ${jetty.version} -
  • -
-
ch.qos.logback.access.tomcat.* -
    -
  • Tomcat version ${tomcat.version} -
  • -
-
- - - -
- - - diff --git a/logback-site/src/site/pages/documentation.html b/logback-site/src/site/pages/documentation.html deleted file mode 100755 index 9a9248e527..0000000000 --- a/logback-site/src/site/pages/documentation.html +++ /dev/null @@ -1,175 +0,0 @@ - - - - - - Documentation - - - - - - - - -
- - -
- - -
- -

Logback documentation

- -

Below is a list of logback-related documentation currently - available.

- - - -

Source code related documentation:

- - - -

Articles and Presentations

- - - - -

In french

- - - - -
- - diff --git a/logback-site/src/site/pages/download.html b/logback-site/src/site/pages/download.html deleted file mode 100755 index dc50afbfcb..0000000000 --- a/logback-site/src/site/pages/download.html +++ /dev/null @@ -1,213 +0,0 @@ - - - - - - Download - - - - - - - - - - -
- - -
- - -
-

Would you like to subscribe to the - QOS.CH announcements mailing list?

- - -

The list is reserved for announcements related to QOS.CH - projects such as cal10n, logback, mistletoe and SLF4J.

-
- - - - - -
Yes, I'd like to subscribe.No, thanks.
-
-
- - -
- -

Latest STABLE version

- -

Download logback version 1.2.3 including full source code, - class files and documentation in ZIP or TAR.GZ format:

- - - -

EXPERIMENTAL version compatible with SLF4J 1.8.x (Jigsaw/Java9 modular)

- -

Download logback version ${project.version} including full source code, - class files and documentation in ZIP or TAR.GZ format:

- -

Logback modules are available as downloads including full - source code, class files and documentation. -

- - - - -

If you wish to download an older version of logback, please - refer to the distributions - directory.

- - -

logback-beagle: an Eclipse plug-in for viewing logs

- -

We also offer a console plugin for Eclipse called - logback-beagle. It allows you to receive logging events in a - convenient Eclipse view, and offers various convenient features. A - more precise description for this plug-in along with installation - instructions can be found in logback-beagle user guide.

- -
- -

Third-party tools, extensions or forks (in chronological - order)

- -

If you are the author of a logback-related project and would - like us to add your project to the list below please drop a line - on the logback-dev mailing list.

- - -
- - -
- Lilith by Joern Huxhorn -
- -
- Lilith is a Logging- and AccessEvent viewer for logback. -
- -
- -
- Logback-akka - by Ivan Porto Carrero. -
- -
- Consists of several akka-based logback utilities, including - ActorAppender, HoptoadActorAppender and Logstash redis - appender. - -
- -
- - -
- Logback-android - by Tony Trinh. -
- -
- Logback-Android brings the power of Logback to Android. -
- -
- -
- - Simpledb-appender by Gabriel Nell -
- -
- Logback Appender writing to Amazon SimpleDB. See also Logging - the cloud with SimpleDB. -
- -
- - -
- logback-configuration - by Martin Todorov -
- -
- A service layer (using Spring) and a REST interface which - provides methods to: add or update loggers, resolve a log - file, resolve the logback configuration file, and upload a - logback configuration file and reload it. - -
- -
- - - - -
- Logback-gelf - by Anthony Marcar -
- -
Logback-gelf can log messages to a Graylog2 server via GELF - messages. -
-
- -
- Logback-testng - by Scott Babcock -
- -
Logback appender for TestNG Reporter.
- - - -
- -

- - - - -

- - diff --git a/logback-site/src/site/pages/faq.html b/logback-site/src/site/pages/faq.html deleted file mode 100755 index f806c15c82..0000000000 --- a/logback-site/src/site/pages/faq.html +++ /dev/null @@ -1,363 +0,0 @@ - - - - - - Logback FAQ - - - - - - - - - -
- -
- - -
- -

- Frequently Asked Questions -

- -

Logback project

- -
    -
  1. Why is logback distributed under LGPL - and not the Apache Software License?
  2. - -
  3. What are logback's dependencies, i.e. - JDK version and third-party libraries? -
  4. -
- - -

Logback Classic

- - -
    -
  1. - Are logback loggers - serializable? -
  2. - -
  3. - How does the automatic configuration - work? -
  4. - -
  5. - Where should the configuration - files such as logback.groovy, - logback-test.xml or logback.xml be located - on the classpath?
  6. - -
  7. - Is it possible for multiple JEE - applications to share the same configuration file but without - stepping on each other's toes? - -
  8. - -
  9. - - How can I disable logging from the command line? - -
  10. - -
  11. - How can Jetty be instructed to use - logback-classic as its logging implementation? - -
  12. -
- - - - - - - -
-

Logback project

- -
-
- Why is logback - distributed under LGPL and not the Apache Software License - (ASL)?
- -
-

The logback project is dual licensed under the LGPL and - the EPL for two main reasons. For one, the different - license emphasizes that the fact that logback is a related - but different project than log4j. -

- -

Both the LGPL and EPL are reasonable and widely-accepted - licenses. In contrast to the ASL, both the LGPL and he EPL - require that derivate work be licensed under the same - license. While there might be debate about the exact - definition of derivative work, we find such reciprocity both - justified and morally appealing -- that is the second - reason for our choice of the LGPL & EPL - dual-license. The subtly more liberal approach embodied in - the ASL is not necessarily wrong. It is the expression of a - different balance. -

- -
-
- -
- -
-
What are logback's - dependencies, i.e. JDK version and third-party libraries? -
- -

This question is answered on a separate page dedicated to the - question of dependencies.

-
-
- - -
- -
-

Logback-classic

-
- - - -
- - How does the automatic configuration work? - -
-
-

This question is answered in the relevant - section of the logback manual. -

-
-
- - - - -
- - Where should the configuration - files such as logback.groovy, - logback-test.xml or logback.xml be located - on the classpath? -
- - -
-

Configuration files such as logback.groovy, - logback-test.xml or logback.xml can be - located directly under any folder declared in the - class path. For example, if the class path reads - "c:/java/jdk15/lib/rt.jar;c:/mylibs/" then the - logback.xml file should be located directly under - "c:/mylibs/", that is as "c:/mylibs/logback.xml". Placing it - under a sub-folder of c:/mylibs/, say, c:/mylibs/other/, - will not work.

- -

For web-applications, configuration files can be placed - directly under WEB-INF/classes/.

- -
- -
- - - -
- - Are logback loggers serializable? -
-
-

Yes. A logback logger is an SLF4J logger and SLF4J - loggers are serializable. This means that an object - referencing a logger will be able to log after its - deserialization. -

- -

The deserialized logger instance will be generated by - org.slf4j.LoggerFactory. Thus, it is possible - for a logback logger to be deserialized as a log4j or j.u.l. - logger, depending on the deserialization environment.

- -
-
- - - - -
- - Is it possible for multiple JEE applications to share the - same configuration file but without stepping on each other's - toes? -
-
-

Yes, it is. Using variable - substitution, it is possible to have a single - configuration file to output logs to different destinations - for each JEE application. Here is a sample - configuration file designed for this purpose.

- -

<configuration> - <appender name="FILE" class="ch.qos.logback.core.FileAppender"> - <!-- "application-name" is a variable --> - <File>c:/logs/${application-name}.log</File> - <layout class="ch.qos.logback.classic.PatternLayout"> - <Pattern>%d %p %t %c - %m%n</Pattern> - </layout> - </appender> - <root level="debug"> - <appender-ref ref="FILE"/> - </root> -</configuration>

- -

Assuming each JEE application loads a different copy of - logback classes into memory, if we can somehow inject a - different value for application-name - each time an application starts, logs will be output to - different files. We just need to initialize logback with the - above configuration file while injecting a different value - for application-name variable. Here is - sample code that programmatically configures logback. It - should be invoked during the initialization of your JEE - applications. -

- -

LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); - JoranConfigurator jc = new JoranConfigurator(); - jc.setContext(context); - context.reset(); // override default configuration - // inject the name of the current application as "application-name" - // property of the LoggerContext - context.putProperty("application-name", NAME_OF_CURRENT_APPLICATION); - jc.doConfigure("/path/to/the/above/configuration/file.xml");

- -
-
- - - - -
- - How can I disable logging from the command line? - -
-
-

Logback does not allow logging to be disabled from the command - line. However, if the configuration file allows it, you can - set the level of loggers on the command line via a Java - system property. Here is such a configuration file.

- -

<configuration> - <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> - <layout class="ch.qos.logback.classic.PatternLayout"> - <Pattern>%d [%thread] %level %logger - %m%n</Pattern> - </layout> - </appender> - <root level="${root-level:-INFO}"> - <appender-ref ref="CONSOLE"/> - </root> -</configuration>

- - -

Making use of variable - substitution as well as default - values for variables, if the root-level system property is set to - OFF, then all logging will be turned - off. However, if it is not set, it will assume the default - value of INFO. Note that you can set the root-level system property to any - level value of your choice. The value OFF is - just an example. - -

-
-
- - - - -
- - How can Jetty be instructed to use logback-classic as its - logging implementation? - -
- -
-

The Jetty application server uses SLF4J for its internal - logging. -

- -

Logback jar files must be present on Jetty's class - path. These files are - logback-core-${project.version}.jar and - logback-classic-${project.version}.jar. These files - should be placed under the $JETTY_HOME/lib - directory. -

- - -

Since Jetty uses an older version of SLF4J internally, - we recommend that the old version be replaced by - slf4j-api-${slf4j.version}.jar. This file can be - downloaded from the SLF4J project. -

- - -

For automatically configuring logback-classic, you can - place the file logback.xml under the - $JETTY_HOME/resources directory. You can find - sample configuration files in the - logback-examples/src/main/java/chapters/appenders/conf/ - folder shipping within the logback distribution. -

- -
-
- - - - - -
- - -
- - - -
- - - - - - diff --git a/logback-site/src/site/pages/images/qoslogo.gif b/logback-site/src/site/pages/images/qoslogo.gif deleted file mode 100644 index 2fc95ca4ab..0000000000 Binary files a/logback-site/src/site/pages/images/qoslogo.gif and /dev/null differ diff --git a/logback-site/src/site/pages/index.html b/logback-site/src/site/pages/index.html deleted file mode 100755 index 320313a6d9..0000000000 --- a/logback-site/src/site/pages/index.html +++ /dev/null @@ -1,57 +0,0 @@ - - - - - - Logback Home - - - - - - - - -
- - -
- - -
- -

Logback Project

- -

Logback is intended as a successor to the popular log4j - project, picking up where log4j - leaves off. -

- -

Logback's architecture is sufficiently generic so as to apply - under different circumstances. At present time, logback is divided - into three modules, logback-core, logback-classic and - logback-access. -

- -

The logback-core module lays the groundwork for the other two - modules. The logback-classic module can be assimilated to a - significantly improved version of log4j. Moreover, logback-classic - natively implements the SLF4J - API so that you can readily switch back and forth between - logback and other logging frameworks such as log4j or - java.util.logging (JUL). -

- -

The logback-access module integrates with Servlet containers, - such as Tomcat and Jetty, to provide HTTP-access log - functionality. Note that you could easily build your own module - on top of logback-core. -

- - -
- - diff --git a/logback-site/src/site/pages/job.html b/logback-site/src/site/pages/job.html deleted file mode 100644 index 41f4829600..0000000000 --- a/logback-site/src/site/pages/job.html +++ /dev/null @@ -1,77 +0,0 @@ - - - - - - Careers at QOS.ch - - - - - - - - - - -
- - -
- -
- - -

Software developer

- -

QOS.ch or Quality Open Software is a software development - company based in Lausanne, Switzerland.

- -

We have extensive experience building mission-critical - enterprise software in the Java language. We are also founding - contributors of several open source projects, namely log4j, slf4j, - mistletoe, cal10n and logback. -

- -

We are looking for software engineers with superb software - development skills. -

- -

Required qualifications

- -
    -
  • University graduate
  • - -
  • Strong analytical skills
  • - -
  • Good experience in object-oriented programming, preferably - Java
  • - -
- -

We offer excellent working conditions with many opportunities - to improve your software development skills. -

- - - -
- - \ No newline at end of file diff --git a/logback-site/src/site/pages/js/decorator.js b/logback-site/src/site/pages/js/decorator.js deleted file mode 100644 index 07b5872d1a..0000000000 --- a/logback-site/src/site/pages/js/decorator.js +++ /dev/null @@ -1,100 +0,0 @@ - - -//

Logger context

- -function decorate() { - var anchor = findAnchorInURL(document.URL); - decoratePropertiesInTables(anchor); - decorateDoAnchor(anchor); - decorateConversionWordInTables(anchor); -} - -// ---------------------------------------------- -function findAnchorInURL(url) { - - if(url == null) return null - var index = url.lastIndexOf("#"); - if(index != -1 && (index+1) < url.length) - return url.substr(index+1); - else - return null; -} - -// ---------------------------------------------- -function decoratePropertiesInTables(anchor) { - - //if(1==1) return; - var elems = $('tr td:first-child span.prop'); - - for(var i = 0; i < elems.length; i++) { - var e = elems[i]; - var p = e.parentNode; - if(p == null) continue; - - var tmpHTML = p.innerHTML; - var propName = e.innerHTML; - var nameAttr = $(e).attr('name') - - if(nameAttr == null) { - var containerAttr = $(e).attr('container') - if(containerAttr != null) - nameAttr = containerAttr+capitaliseFirstLetter(propName); - else - nameAttr = propName; - } - - p.innerHTML = "" +tmpHTML +""; - scrollIfMatch(p, nameAttr, anchor); - } // for -} - -function decorateConversionWordInTables(anchor) { - var elems = $('tr td.word'); - for(var i = 0; i < elems.length; i++) { - var e = elems[i]; - var tmpHTML = e.innerHTML; - var nameAttr = $(e).attr('name') - if(nameAttr == null) - continue; - e.innerHTML = "" +tmpHTML; - scrollIfMatch(e, nameAttr, anchor); - } -} - - -function decorateDoAnchor(anchor) { - var elems = $('.doAnchor'); - for(var i = 0; i < elems.length; i++) { - var e = elems[i]; - var tmpHTML = e.innerHTML; - var nameAttr = $(e).attr('name') - if(nameAttr == null) { - nameAttr = camelCase($.trim(tmpHTML)) - } - e.innerHTML = "" +tmpHTML; - scrollIfMatch(e, nameAttr, anchor); - } -} - -function scrollIfMatch(element, nameAttr, anchor) { - if(anchor != null && nameAttr.toString() == anchor) - element.scrollIntoView(true); - - -} - -function capitaliseFirstLetter(str) { - return str.charAt(0).toUpperCase() + str.slice(1); -} - - -function camelCase(str) { - return $.trim(str).replace(/\s\w/g, function(match) { - return $.trim(match).toUpperCase(); - }); -} - diff --git a/logback-site/src/site/pages/js/dsl.js b/logback-site/src/site/pages/js/dsl.js deleted file mode 100755 index 4f5d2a8be5..0000000000 --- a/logback-site/src/site/pages/js/dsl.js +++ /dev/null @@ -1,39 +0,0 @@ - -var asGroovyURL='https://logback.qos.ch/translator/dsl/asGroovy'; - -function asGroovy(id) { - - var form = document.getElementById('aForm'); - if(form == null) { - form = document.createElement("form"); - document.body.appendChild(form); - } - var p = document.getElementById(id); - - var inner = p.innerHTML; - //alert("==="+inner); - inner = inner.replace(/</gi, '<'); - inner = inner.replace(/>/gi, '>'); - - inner = inner.replace(//gi, ''); - inner = inner.replace(/<\/span>/gi, ''); - inner = inner.replace(/
/gi, ''); - inner = inner.replace(/ /gi, ''); - inner = inner.replace(//gi, ''); - inner = inner.replace(/<\/b>/gi, ''); - - form.setAttribute("method", "post"); - form.setAttribute("action", asGroovyURL); - - var hiddenField = document.createElement("input"); - hiddenField.setAttribute("type", "hidden"); - hiddenField.setAttribute("name", "val"); - hiddenField.setAttribute("value", inner); - form.appendChild(hiddenField); - - //alert("==="+inner); - form.submit(); - return false; -} - - diff --git a/logback-site/src/site/pages/js/jquery-min.js b/logback-site/src/site/pages/js/jquery-min.js deleted file mode 100644 index 16ad06c5ac..0000000000 --- a/logback-site/src/site/pages/js/jquery-min.js +++ /dev/null @@ -1,4 +0,0 @@ -/*! jQuery v1.7.2 jquery.com | jquery.org/license */ -(function(a,b){function cy(a){return f.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cu(a){if(!cj[a]){var b=c.body,d=f("<"+a+">").appendTo(b),e=d.css("display");d.remove();if(e==="none"||e===""){ck||(ck=c.createElement("iframe"),ck.frameBorder=ck.width=ck.height=0),b.appendChild(ck);if(!cl||!ck.createElement)cl=(ck.contentWindow||ck.contentDocument).document,cl.write((f.support.boxModel?"":"")+""),cl.close();d=cl.createElement(a),cl.body.appendChild(d),e=f.css(d,"display"),b.removeChild(ck)}cj[a]=e}return cj[a]}function ct(a,b){var c={};f.each(cp.concat.apply([],cp.slice(0,b)),function(){c[this]=a});return c}function cs(){cq=b}function cr(){setTimeout(cs,0);return cq=f.now()}function ci(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function ch(){try{return new a.XMLHttpRequest}catch(b){}}function cb(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var d=a.dataTypes,e={},g,h,i=d.length,j,k=d[0],l,m,n,o,p;for(g=1;g0){if(c!=="border")for(;e=0===c})}function S(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function K(){return!0}function J(){return!1}function n(a,b,c){var d=b+"defer",e=b+"queue",g=b+"mark",h=f._data(a,d);h&&(c==="queue"||!f._data(a,e))&&(c==="mark"||!f._data(a,g))&&setTimeout(function(){!f._data(a,e)&&!f._data(a,g)&&(f.removeData(a,d,!0),h.fire())},0)}function m(a){for(var b in a){if(b==="data"&&f.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function l(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(k,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:f.isNumeric(d)?+d:j.test(d)?f.parseJSON(d):d}catch(g){}f.data(a,c,d)}else d=b}return d}function h(a){var b=g[a]={},c,d;a=a.split(/\s+/);for(c=0,d=a.length;c)[^>]*$|#([\w\-]*)$)/,j=/\S/,k=/^\s+/,l=/\s+$/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=/-([a-z]|[0-9])/ig,w=/^-ms-/,x=function(a,b){return(b+"").toUpperCase()},y=d.userAgent,z,A,B,C=Object.prototype.toString,D=Object.prototype.hasOwnProperty,E=Array.prototype.push,F=Array.prototype.slice,G=String.prototype.trim,H=Array.prototype.indexOf,I={};e.fn=e.prototype={constructor:e,init:function(a,d,f){var g,h,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!d&&c.body){this.context=c,this[0]=c.body,this.selector=a,this.length=1;return this}if(typeof a=="string"){a.charAt(0)!=="<"||a.charAt(a.length-1)!==">"||a.length<3?g=i.exec(a):g=[null,a,null];if(g&&(g[1]||!d)){if(g[1]){d=d instanceof e?d[0]:d,k=d?d.ownerDocument||d:c,j=m.exec(a),j?e.isPlainObject(d)?(a=[c.createElement(j[1])],e.fn.attr.call(a,d,!0)):a=[k.createElement(j[1])]:(j=e.buildFragment([g[1]],[k]),a=(j.cacheable?e.clone(j.fragment):j.fragment).childNodes);return e.merge(this,a)}h=c.getElementById(g[2]);if(h&&h.parentNode){if(h.id!==g[2])return f.find(a);this.length=1,this[0]=h}this.context=c,this.selector=a;return this}return!d||d.jquery?(d||f).find(a):this.constructor(d).find(a)}if(e.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return e.makeArray(a,this)},selector:"",jquery:"1.7.2",length:0,size:function(){return this.length},toArray:function(){return F.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=this.constructor();e.isArray(a)?E.apply(d,a):e.merge(d,a),d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")");return d},each:function(a,b){return e.each(this,a,b)},ready:function(a){e.bindReady(),A.add(a);return this},eq:function(a){a=+a;return a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(F.apply(this,arguments),"slice",F.call(arguments).join(","))},map:function(a){return this.pushStack(e.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:E,sort:[].sort,splice:[].splice},e.fn.init.prototype=e.fn,e.extend=e.fn.extend=function(){var a,c,d,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i=="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!="object"&&!e.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;A.fireWith(c,[e]),e.fn.trigger&&e(c).trigger("ready").off("ready")}},bindReady:function(){if(!A){A=e.Callbacks("once memory");if(c.readyState==="complete")return setTimeout(e.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",B,!1),a.addEventListener("load",e.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",B),a.attachEvent("onload",e.ready);var b=!1;try{b=a.frameElement==null}catch(d){}c.documentElement.doScroll&&b&&J()}}},isFunction:function(a){return e.type(a)==="function"},isArray:Array.isArray||function(a){return e.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):I[C.call(a)]||"object"},isPlainObject:function(a){if(!a||e.type(a)!=="object"||a.nodeType||e.isWindow(a))return!1;try{if(a.constructor&&!D.call(a,"constructor")&&!D.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||D.call(a,d)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw new Error(a)},parseJSON:function(b){if(typeof b!="string"||!b)return null;b=e.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return(new Function("return "+b))();e.error("Invalid JSON: "+b)},parseXML:function(c){if(typeof c!="string"||!c)return null;var d,f;try{a.DOMParser?(f=new DOMParser,d=f.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(g){d=b}(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&e.error("Invalid XML: "+c);return d},noop:function(){},globalEval:function(b){b&&j.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(w,"ms-").replace(v,x)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var f,g=0,h=a.length,i=h===b||e.isFunction(a);if(d){if(i){for(f in a)if(c.apply(a[f],d)===!1)break}else for(;g0&&a[0]&&a[j-1]||j===0||e.isArray(a));if(k)for(;i1?i.call(arguments,0):b,j.notifyWith(k,e)}}function l(a){return function(c){b[a]=arguments.length>1?i.call(arguments,0):c,--g||j.resolveWith(j,b)}}var b=i.call(arguments,0),c=0,d=b.length,e=Array(d),g=d,h=d,j=d<=1&&a&&f.isFunction(a.promise)?a:f.Deferred(),k=j.promise();if(d>1){for(;c
a",d=p.getElementsByTagName("*"),e=p.getElementsByTagName("a")[0];if(!d||!d.length||!e)return{};g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=p.getElementsByTagName("input")[0],b={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(e.getAttribute("style")),hrefNormalized:e.getAttribute("href")==="/a",opacity:/^0.55/.test(e.style.opacity),cssFloat:!!e.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,getSetAttribute:p.className!=="t",enctype:!!c.createElement("form").enctype,html5Clone:c.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,pixelMargin:!0},f.boxModel=b.boxModel=c.compatMode==="CSS1Compat",i.checked=!0,b.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,b.optDisabled=!h.disabled;try{delete p.test}catch(r){b.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",function(){b.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick")),i=c.createElement("input"),i.value="t",i.setAttribute("type","radio"),b.radioValue=i.value==="t",i.setAttribute("checked","checked"),i.setAttribute("name","t"),p.appendChild(i),j=c.createDocumentFragment(),j.appendChild(p.lastChild),b.checkClone=j.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=i.checked,j.removeChild(i),j.appendChild(p);if(p.attachEvent)for(n in{submit:1,change:1,focusin:1})m="on"+n,o=m in p,o||(p.setAttribute(m,"return;"),o=typeof p[m]=="function"),b[n+"Bubbles"]=o;j.removeChild(p),j=g=h=p=i=null,f(function(){var d,e,g,h,i,j,l,m,n,q,r,s,t,u=c.getElementsByTagName("body")[0];!u||(m=1,t="padding:0;margin:0;border:",r="position:absolute;top:0;left:0;width:1px;height:1px;",s=t+"0;visibility:hidden;",n="style='"+r+t+"5px solid #000;",q="
"+""+"
",d=c.createElement("div"),d.style.cssText=s+"width:0;height:0;position:static;top:0;margin-top:"+m+"px",u.insertBefore(d,u.firstChild),p=c.createElement("div"),d.appendChild(p),p.innerHTML="
t
",k=p.getElementsByTagName("td"),o=k[0].offsetHeight===0,k[0].style.display="",k[1].style.display="none",b.reliableHiddenOffsets=o&&k[0].offsetHeight===0,a.getComputedStyle&&(p.innerHTML="",l=c.createElement("div"),l.style.width="0",l.style.marginRight="0",p.style.width="2px",p.appendChild(l),b.reliableMarginRight=(parseInt((a.getComputedStyle(l,null)||{marginRight:0}).marginRight,10)||0)===0),typeof p.style.zoom!="undefined"&&(p.innerHTML="",p.style.width=p.style.padding="1px",p.style.border=0,p.style.overflow="hidden",p.style.display="inline",p.style.zoom=1,b.inlineBlockNeedsLayout=p.offsetWidth===3,p.style.display="block",p.style.overflow="visible",p.innerHTML="
",b.shrinkWrapBlocks=p.offsetWidth!==3),p.style.cssText=r+s,p.innerHTML=q,e=p.firstChild,g=e.firstChild,i=e.nextSibling.firstChild.firstChild,j={doesNotAddBorder:g.offsetTop!==5,doesAddBorderForTableAndCells:i.offsetTop===5},g.style.position="fixed",g.style.top="20px",j.fixedPosition=g.offsetTop===20||g.offsetTop===15,g.style.position=g.style.top="",e.style.overflow="hidden",e.style.position="relative",j.subtractsBorderForOverflowNotVisible=g.offsetTop===-5,j.doesNotIncludeMarginInBodyOffset=u.offsetTop!==m,a.getComputedStyle&&(p.style.marginTop="1%",b.pixelMargin=(a.getComputedStyle(p,null)||{marginTop:0}).marginTop!=="1%"),typeof d.style.zoom!="undefined"&&(d.style.zoom=1),u.removeChild(d),l=p=d=null,f.extend(b,j))});return b}();var j=/^(?:\{.*\}|\[.*\])$/,k=/([A-Z])/g;f.extend({cache:{},uuid:0,expando:"jQuery"+(f.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?f.cache[a[f.expando]]:a[f.expando];return!!a&&!m(a)},data:function(a,c,d,e){if(!!f.acceptData(a)){var g,h,i,j=f.expando,k=typeof c=="string",l=a.nodeType,m=l?f.cache:a,n=l?a[j]:a[j]&&j,o=c==="events";if((!n||!m[n]||!o&&!e&&!m[n].data)&&k&&d===b)return;n||(l?a[j]=n=++f.uuid:n=j),m[n]||(m[n]={},l||(m[n].toJSON=f.noop));if(typeof c=="object"||typeof c=="function")e?m[n]=f.extend(m[n],c):m[n].data=f.extend(m[n].data,c);g=h=m[n],e||(h.data||(h.data={}),h=h.data),d!==b&&(h[f.camelCase(c)]=d);if(o&&!h[c])return g.events;k?(i=h[c],i==null&&(i=h[f.camelCase(c)])):i=h;return i}},removeData:function(a,b,c){if(!!f.acceptData(a)){var d,e,g,h=f.expando,i=a.nodeType,j=i?f.cache:a,k=i?a[h]:h;if(!j[k])return;if(b){d=c?j[k]:j[k].data;if(d){f.isArray(b)||(b in d?b=[b]:(b=f.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,g=b.length;e1,null,!1)},removeData:function(a){return this.each(function(){f.removeData(this,a)})}}),f.extend({_mark:function(a,b){a&&(b=(b||"fx")+"mark",f._data(a,b,(f._data(a,b)||0)+1))},_unmark:function(a,b,c){a!==!0&&(c=b,b=a,a=!1);if(b){c=c||"fx";var d=c+"mark",e=a?0:(f._data(b,d)||1)-1;e?f._data(b,d,e):(f.removeData(b,d,!0),n(b,c,"mark"))}},queue:function(a,b,c){var d;if(a){b=(b||"fx")+"queue",d=f._data(a,b),c&&(!d||f.isArray(c)?d=f._data(a,b,f.makeArray(c)):d.push(c));return d||[]}},dequeue:function(a,b){b=b||"fx";var c=f.queue(a,b),d=c.shift(),e={};d==="inprogress"&&(d=c.shift()),d&&(b==="fx"&&c.unshift("inprogress"),f._data(a,b+".run",e),d.call(a,function(){f.dequeue(a,b)},e)),c.length||(f.removeData(a,b+"queue "+b+".run",!0),n(a,b,"queue"))}}),f.fn.extend({queue:function(a,c){var d=2;typeof a!="string"&&(c=a,a="fx",d--);if(arguments.length1)},removeAttr:function(a){return this.each(function(){f.removeAttr(this,a)})},prop:function(a,b){return f.access(this,f.prop,a,b,arguments.length>1)},removeProp:function(a){a=f.propFix[a]||a;return this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,g,h,i;if(f.isFunction(a))return this.each(function(b){f(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(p);for(c=0,d=this.length;c-1)return!0;return!1},val:function(a){var c,d,e,g=this[0];{if(!!arguments.length){e=f.isFunction(a);return this.each(function(d){var g=f(this),h;if(this.nodeType===1){e?h=a.call(this,d,g.val()):h=a,h==null?h="":typeof h=="number"?h+="":f.isArray(h)&&(h=f.map(h,function(a){return a==null?"":a+""})),c=f.valHooks[this.type]||f.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,h,"value")===b)this.value=h}})}if(g){c=f.valHooks[g.type]||f.valHooks[g.nodeName.toLowerCase()];if(c&&"get"in c&&(d=c.get(g,"value"))!==b)return d;d=g.value;return typeof d=="string"?d.replace(q,""):d==null?"":d}}}}),f.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,g=a.selectedIndex,h=[],i=a.options,j=a.type==="select-one";if(g<0)return null;c=j?g:0,d=j?g+1:i.length;for(;c=0}),c.length||(a.selectedIndex=-1);return c}}},attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,d,e){var g,h,i,j=a.nodeType;if(!!a&&j!==3&&j!==8&&j!==2){if(e&&c in f.attrFn)return f(a)[c](d);if(typeof a.getAttribute=="undefined")return f.prop(a,c,d);i=j!==1||!f.isXMLDoc(a),i&&(c=c.toLowerCase(),h=f.attrHooks[c]||(u.test(c)?x:w));if(d!==b){if(d===null){f.removeAttr(a,c);return}if(h&&"set"in h&&i&&(g=h.set(a,d,c))!==b)return g;a.setAttribute(c,""+d);return d}if(h&&"get"in h&&i&&(g=h.get(a,c))!==null)return g;g=a.getAttribute(c);return g===null?b:g}},removeAttr:function(a,b){var c,d,e,g,h,i=0;if(b&&a.nodeType===1){d=b.toLowerCase().split(p),g=d.length;for(;i=0}})});var z=/^(?:textarea|input|select)$/i,A=/^([^\.]*)?(?:\.(.+))?$/,B=/(?:^|\s)hover(\.\S+)?\b/,C=/^key/,D=/^(?:mouse|contextmenu)|click/,E=/^(?:focusinfocus|focusoutblur)$/,F=/^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,G=function( -a){var b=F.exec(a);b&&(b[1]=(b[1]||"").toLowerCase(),b[3]=b[3]&&new RegExp("(?:^|\\s)"+b[3]+"(?:\\s|$)"));return b},H=function(a,b){var c=a.attributes||{};return(!b[1]||a.nodeName.toLowerCase()===b[1])&&(!b[2]||(c.id||{}).value===b[2])&&(!b[3]||b[3].test((c["class"]||{}).value))},I=function(a){return f.event.special.hover?a:a.replace(B,"mouseenter$1 mouseleave$1")};f.event={add:function(a,c,d,e,g){var h,i,j,k,l,m,n,o,p,q,r,s;if(!(a.nodeType===3||a.nodeType===8||!c||!d||!(h=f._data(a)))){d.handler&&(p=d,d=p.handler,g=p.selector),d.guid||(d.guid=f.guid++),j=h.events,j||(h.events=j={}),i=h.handle,i||(h.handle=i=function(a){return typeof f!="undefined"&&(!a||f.event.triggered!==a.type)?f.event.dispatch.apply(i.elem,arguments):b},i.elem=a),c=f.trim(I(c)).split(" ");for(k=0;k=0&&(h=h.slice(0,-1),k=!0),h.indexOf(".")>=0&&(i=h.split("."),h=i.shift(),i.sort());if((!e||f.event.customEvent[h])&&!f.event.global[h])return;c=typeof c=="object"?c[f.expando]?c:new f.Event(h,c):new f.Event(h),c.type=h,c.isTrigger=!0,c.exclusive=k,c.namespace=i.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+i.join("\\.(?:.*\\.)?")+"(\\.|$)"):null,o=h.indexOf(":")<0?"on"+h:"";if(!e){j=f.cache;for(l in j)j[l].events&&j[l].events[h]&&f.event.trigger(c,d,j[l].handle.elem,!0);return}c.result=b,c.target||(c.target=e),d=d!=null?f.makeArray(d):[],d.unshift(c),p=f.event.special[h]||{};if(p.trigger&&p.trigger.apply(e,d)===!1)return;r=[[e,p.bindType||h]];if(!g&&!p.noBubble&&!f.isWindow(e)){s=p.delegateType||h,m=E.test(s+h)?e:e.parentNode,n=null;for(;m;m=m.parentNode)r.push([m,s]),n=m;n&&n===e.ownerDocument&&r.push([n.defaultView||n.parentWindow||a,s])}for(l=0;le&&j.push({elem:this,matches:d.slice(e)});for(k=0;k0?this.on(b,null,a,c):this.trigger(b)},f.attrFn&&(f.attrFn[b]=!0),C.test(b)&&(f.event.fixHooks[b]=f.event.keyHooks),D.test(b)&&(f.event.fixHooks[b]=f.event.mouseHooks)}),function(){function x(a,b,c,e,f,g){for(var h=0,i=e.length;h0){k=j;break}}j=j[a]}e[h]=k}}}function w(a,b,c,e,f,g){for(var h=0,i=e.length;h+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,d="sizcache"+(Math.random()+"").replace(".",""),e=0,g=Object.prototype.toString,h=!1,i=!0,j=/\\/g,k=/\r\n/g,l=/\W/;[0,0].sort(function(){i=!1;return 0});var m=function(b,d,e,f){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!="string")return e;var i,j,k,l,n,q,r,t,u=!0,v=m.isXML(d),w=[],x=b;do{a.exec(""),i=a.exec(x);if(i){x=i[3],w.push(i[1]);if(i[2]){l=i[3];break}}}while(i);if(w.length>1&&p.exec(b))if(w.length===2&&o.relative[w[0]])j=y(w[0]+w[1],d,f);else{j=o.relative[w[0]]?[d]:m(w.shift(),d);while(w.length)b=w.shift(),o.relative[b]&&(b+=w.shift()),j=y(b,j,f)}else{!f&&w.length>1&&d.nodeType===9&&!v&&o.match.ID.test(w[0])&&!o.match.ID.test(w[w.length-1])&&(n=m.find(w.shift(),d,v),d=n.expr?m.filter(n.expr,n.set)[0]:n.set[0]);if(d){n=f?{expr:w.pop(),set:s(f)}:m.find(w.pop(),w.length===1&&(w[0]==="~"||w[0]==="+")&&d.parentNode?d.parentNode:d,v),j=n.expr?m.filter(n.expr,n.set):n.set,w.length>0?k=s(j):u=!1;while(w.length)q=w.pop(),r=q,o.relative[q]?r=w.pop():q="",r==null&&(r=d),o.relative[q](k,r,v)}else k=w=[]}k||(k=j),k||m.error(q||b);if(g.call(k)==="[object Array]")if(!u)e.push.apply(e,k);else if(d&&d.nodeType===1)for(t=0;k[t]!=null;t++)k[t]&&(k[t]===!0||k[t].nodeType===1&&m.contains(d,k[t]))&&e.push(j[t]);else for(t=0;k[t]!=null;t++)k[t]&&k[t].nodeType===1&&e.push(j[t]);else s(k,e);l&&(m(l,h,e,f),m.uniqueSort(e));return e};m.uniqueSort=function(a){if(u){h=i,a.sort(u);if(h)for(var b=1;b0},m.find=function(a,b,c){var d,e,f,g,h,i;if(!a)return[];for(e=0,f=o.order.length;e":function(a,b){var c,d=typeof b=="string",e=0,f=a.length;if(d&&!l.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(j,"")},TAG:function(a,b){return a[1].replace(j,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||m.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&m.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(j,"");!f&&o.attrMap[g]&&(a[1]=o.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(j,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=m(b[3],null,null,c);else{var g=m.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(o.match.POS.test(b[0])||o.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!m(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return a.nodeName.toLowerCase()==="input"&&"text"===c&&(b===c||b===null)},radio:function(a){return a.nodeName.toLowerCase()==="input"&&"radio"===a.type},checkbox:function(a){return a.nodeName.toLowerCase()==="input"&&"checkbox"===a.type},file:function(a){return a.nodeName.toLowerCase()==="input"&&"file"===a.type},password:function(a){return a.nodeName.toLowerCase()==="input"&&"password"===a.type},submit:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"submit"===a.type},image:function(a){return a.nodeName.toLowerCase()==="input"&&"image"===a.type},reset:function(a){var b=a.nodeName.toLowerCase();return(b==="input"||b==="button")&&"reset"===a.type},button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&"button"===a.type||b==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)},focus:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=o.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||n([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||!!a.nodeName&&a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=m.attr?m.attr(a,c):o.attrHandle[c]?o.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":!f&&m.attr?d!=null:f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=o.setFilters[e];if(f)return f(a,c,b,d)}}},p=o.match.POS,q=function(a,b){return"\\"+(b-0+1)};for(var r in o.match)o.match[r]=new RegExp(o.match[r].source+/(?![^\[]*\])(?![^\(]*\))/.source),o.leftMatch[r]=new RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[r].source.replace(/\\(\d+)/g,q));o.match.globalPOS=p;var s=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(t){s=function(a,b){var c=0,d=b||[];if(g.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length=="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(o.find.ID=function(a,c,d){if(typeof c.getElementById!="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},o.filter.ID=function(a,b){var c=typeof a.getAttributeNode!="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(o.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(o.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=m,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){m=function(b,e,f,g){e=e||c;if(!g&&!m.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return s(e.getElementsByTagName(b),f);if(h[2]&&o.find.CLASS&&e.getElementsByClassName)return s(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return s([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return s([],f);if(i.id===h[3])return s([i],f)}try{return s(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var k=e,l=e.getAttribute("id"),n=l||d,p=e.parentNode,q=/^\s*[+~]/.test(b);l?n=n.replace(/'/g,"\\$&"):e.setAttribute("id",n),q&&p&&(e=e.parentNode);try{if(!q||p)return s(e.querySelectorAll("[id='"+n+"'] "+b),f)}catch(r){}finally{l||k.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)m[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}m.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!m.isXML(a))try{if(e||!o.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return m(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
";if(!!a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;o.order.splice(1,0,"CLASS"),o.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?m.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?m.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:m.contains=function(){return!1},m.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var y=function(a,b,c){var d,e=[],f="",g=b.nodeType?[b]:b;while(d=o.match.PSEUDO.exec(a))f+=d[0],a=a.replace(o.match.PSEUDO,"");a=o.relative[a]?a+"*":a;for(var h=0,i=g.length;h0)for(h=g;h=0:f.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c=[],d,e,g=this[0];if(f.isArray(a)){var h=1;while(g&&g.ownerDocument&&g!==b){for(d=0;d-1:f.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b||g.nodeType===11)break}}c=c.length>1?f.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a)return this[0]&&this[0].parentNode?this.prevAll().length:-1;if(typeof a=="string")return f.inArray(this[0],f(a));return f.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a=="string"?f(a,b):f.makeArray(a&&a.nodeType?[a]:a),d=f.merge(this.get(),c);return this.pushStack(S(c[0])||S(d[0])?d:f.unique(d))},andSelf:function(){return this.add(this.prevObject)}}),f.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return f.dir(a,"parentNode")},parentsUntil:function(a,b,c){return f.dir(a,"parentNode",c)},next:function(a){return f.nth(a,2,"nextSibling")},prev:function(a){return f.nth(a,2,"previousSibling")},nextAll:function(a){return f.dir(a,"nextSibling")},prevAll:function(a){return f.dir(a,"previousSibling")},nextUntil:function(a,b,c){return f.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return f.dir(a,"previousSibling",c)},siblings:function(a){return f.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return f.sibling(a.firstChild)},contents:function(a){return f.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:f.makeArray(a.childNodes)}},function(a,b){f.fn[a]=function(c,d){var e=f.map(this,b,c);L.test(a)||(d=c),d&&typeof d=="string"&&(e=f.filter(d,e)),e=this.length>1&&!R[a]?f.unique(e):e,(this.length>1||N.test(d))&&M.test(a)&&(e=e.reverse());return this.pushStack(e,a,P.call(arguments).join(","))}}),f.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?f.find.matchesSelector(b[0],a)?[b[0]]:[]:f.find.matches(a,b)},dir:function(a,c,d){var e=[],g=a[c];while(g&&g.nodeType!==9&&(d===b||g.nodeType!==1||!f(g).is(d)))g.nodeType===1&&e.push(g),g=g[c];return e},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var V="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",W=/ jQuery\d+="(?:\d+|null)"/g,X=/^\s+/,Y=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Z=/<([\w:]+)/,$=/]","i"),bd=/checked\s*(?:[^=]|=\s*.checked.)/i,be=/\/(java|ecma)script/i,bf=/^\s*",""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bh=U(c);bg.optgroup=bg.option,bg.tbody=bg.tfoot=bg.colgroup=bg.caption=bg.thead,bg.th=bg.td,f.support.htmlSerialize||(bg._default=[1,"div
","
"]),f.fn.extend({text:function(a){return f.access(this,function(a){return a===b?f.text(this):this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapAll(a.call(this,b))});if(this[0]){var b=f(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(f.isFunction(a))return this.each(function(b){f(this).wrapInner(a.call(this,b))});return this.each(function(){var b=f(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=f.isFunction(a);return this.each(function(c){f(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){f.nodeName(this,"body")||f(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=f -.clean(arguments);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,f.clean(arguments));return a}},remove:function(a,b){for(var c=0,d;(d=this[c])!=null;c++)if(!a||f.filter(a,[d]).length)!b&&d.nodeType===1&&(f.cleanData(d.getElementsByTagName("*")),f.cleanData([d])),d.parentNode&&d.parentNode.removeChild(d);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&f.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return f.clone(this,a,b)})},html:function(a){return f.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(W,""):null;if(typeof a=="string"&&!ba.test(a)&&(f.support.leadingWhitespace||!X.test(a))&&!bg[(Z.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Y,"<$1>");try{for(;d1&&l0?this.clone(!0):this).get();f(e[h])[b](j),d=d.concat(j)}return this.pushStack(d,a,e.selector)}}),f.extend({clone:function(a,b,c){var d,e,g,h=f.support.html5Clone||f.isXMLDoc(a)||!bc.test("<"+a.nodeName+">")?a.cloneNode(!0):bo(a);if((!f.support.noCloneEvent||!f.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!f.isXMLDoc(a)){bk(a,h),d=bl(a),e=bl(h);for(g=0;d[g];++g)e[g]&&bk(d[g],e[g])}if(b){bj(a,h);if(c){d=bl(a),e=bl(h);for(g=0;d[g];++g)bj(d[g],e[g])}}d=e=null;return h},clean:function(a,b,d,e){var g,h,i,j=[];b=b||c,typeof b.createElement=="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);for(var k=0,l;(l=a[k])!=null;k++){typeof l=="number"&&(l+="");if(!l)continue;if(typeof l=="string")if(!_.test(l))l=b.createTextNode(l);else{l=l.replace(Y,"<$1>");var m=(Z.exec(l)||["",""])[1].toLowerCase(),n=bg[m]||bg._default,o=n[0],p=b.createElement("div"),q=bh.childNodes,r;b===c?bh.appendChild(p):U(b).appendChild(p),p.innerHTML=n[1]+l+n[2];while(o--)p=p.lastChild;if(!f.support.tbody){var s=$.test(l),t=m==="table"&&!s?p.firstChild&&p.firstChild.childNodes:n[1]===""&&!s?p.childNodes:[];for(i=t.length-1;i>=0;--i)f.nodeName(t[i],"tbody")&&!t[i].childNodes.length&&t[i].parentNode.removeChild(t[i])}!f.support.leadingWhitespace&&X.test(l)&&p.insertBefore(b.createTextNode(X.exec(l)[0]),p.firstChild),l=p.childNodes,p&&(p.parentNode.removeChild(p),q.length>0&&(r=q[q.length-1],r&&r.parentNode&&r.parentNode.removeChild(r)))}var u;if(!f.support.appendChecked)if(l[0]&&typeof (u=l.length)=="number")for(i=0;i1)},f.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=by(a,"opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":f.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!!a&&a.nodeType!==3&&a.nodeType!==8&&!!a.style){var g,h,i=f.camelCase(c),j=a.style,k=f.cssHooks[i];c=f.cssProps[i]||i;if(d===b){if(k&&"get"in k&&(g=k.get(a,!1,e))!==b)return g;return j[c]}h=typeof d,h==="string"&&(g=bu.exec(d))&&(d=+(g[1]+1)*+g[2]+parseFloat(f.css(a,c)),h="number");if(d==null||h==="number"&&isNaN(d))return;h==="number"&&!f.cssNumber[i]&&(d+="px");if(!k||!("set"in k)||(d=k.set(a,d))!==b)try{j[c]=d}catch(l){}}},css:function(a,c,d){var e,g;c=f.camelCase(c),g=f.cssHooks[c],c=f.cssProps[c]||c,c==="cssFloat"&&(c="float");if(g&&"get"in g&&(e=g.get(a,!0,d))!==b)return e;if(by)return by(a,c)},swap:function(a,b,c){var d={},e,f;for(f in b)d[f]=a.style[f],a.style[f]=b[f];e=c.call(a);for(f in b)a.style[f]=d[f];return e}}),f.curCSS=f.css,c.defaultView&&c.defaultView.getComputedStyle&&(bz=function(a,b){var c,d,e,g,h=a.style;b=b.replace(br,"-$1").toLowerCase(),(d=a.ownerDocument.defaultView)&&(e=d.getComputedStyle(a,null))&&(c=e.getPropertyValue(b),c===""&&!f.contains(a.ownerDocument.documentElement,a)&&(c=f.style(a,b))),!f.support.pixelMargin&&e&&bv.test(b)&&bt.test(c)&&(g=h.width,h.width=c,c=e.width,h.width=g);return c}),c.documentElement.currentStyle&&(bA=function(a,b){var c,d,e,f=a.currentStyle&&a.currentStyle[b],g=a.style;f==null&&g&&(e=g[b])&&(f=e),bt.test(f)&&(c=g.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),g.left=b==="fontSize"?"1em":f,f=g.pixelLeft+"px",g.left=c,d&&(a.runtimeStyle.left=d));return f===""?"auto":f}),by=bz||bA,f.each(["height","width"],function(a,b){f.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth!==0?bB(a,b,d):f.swap(a,bw,function(){return bB(a,b,d)})},set:function(a,b){return bs.test(b)?b+"px":b}}}),f.support.opacity||(f.cssHooks.opacity={get:function(a,b){return bq.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=f.isNumeric(b)?"alpha(opacity="+b*100+")":"",g=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&f.trim(g.replace(bp,""))===""){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bp.test(g)?g.replace(bp,e):g+" "+e}}),f(function(){f.support.reliableMarginRight||(f.cssHooks.marginRight={get:function(a,b){return f.swap(a,{display:"inline-block"},function(){return b?by(a,"margin-right"):a.style.marginRight})}})}),f.expr&&f.expr.filters&&(f.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!f.support.reliableHiddenOffsets&&(a.style&&a.style.display||f.css(a,"display"))==="none"},f.expr.filters.visible=function(a){return!f.expr.filters.hidden(a)}),f.each({margin:"",padding:"",border:"Width"},function(a,b){f.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bx[d]+b]=e[d]||e[d-2]||e[0];return f}}});var bC=/%20/g,bD=/\[\]$/,bE=/\r?\n/g,bF=/#.*$/,bG=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bH=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bI=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,bJ=/^(?:GET|HEAD)$/,bK=/^\/\//,bL=/\?/,bM=/)<[^<]*)*<\/script>/gi,bN=/^(?:select|textarea)/i,bO=/\s+/,bP=/([?&])_=[^&]*/,bQ=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bR=f.fn.load,bS={},bT={},bU,bV,bW=["*/"]+["*"];try{bU=e.href}catch(bX){bU=c.createElement("a"),bU.href="",bU=bU.href}bV=bQ.exec(bU.toLowerCase())||[],f.fn.extend({load:function(a,c,d){if(typeof a!="string"&&bR)return bR.apply(this,arguments);if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var g=a.slice(e,a.length);a=a.slice(0,e)}var h="GET";c&&(f.isFunction(c)?(d=c,c=b):typeof c=="object"&&(c=f.param(c,f.ajaxSettings.traditional),h="POST"));var i=this;f.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?f("
").append(c.replace(bM,"")).find(g):c)),d&&i.each(d,[c,b,a])}});return this},serialize:function(){return f.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?f.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bN.test(this.nodeName)||bH.test(this.type))}).map(function(a,b){var c=f(this).val();return c==null?null:f.isArray(c)?f.map(c,function(a,c){return{name:b.name,value:a.replace(bE,"\r\n")}}):{name:b.name,value:c.replace(bE,"\r\n")}}).get()}}),f.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){f.fn[b]=function(a){return this.on(b,a)}}),f.each(["get","post"],function(a,c){f[c]=function(a,d,e,g){f.isFunction(d)&&(g=g||e,e=d,d=b);return f.ajax({type:c,url:a,data:d,success:e,dataType:g})}}),f.extend({getScript:function(a,c){return f.get(a,b,c,"script")},getJSON:function(a,b,c){return f.get(a,b,c,"json")},ajaxSetup:function(a,b){b?b$(a,f.ajaxSettings):(b=a,a=f.ajaxSettings),b$(a,b);return a},ajaxSettings:{url:bU,isLocal:bI.test(bV[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":bW},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":f.parseJSON,"text xml":f.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:bY(bS),ajaxTransport:bY(bT),ajax:function(a,c){function w(a,c,l,m){if(s!==2){s=2,q&&clearTimeout(q),p=b,n=m||"",v.readyState=a>0?4:0;var o,r,u,w=c,x=l?ca(d,v,l):b,y,z;if(a>=200&&a<300||a===304){if(d.ifModified){if(y=v.getResponseHeader("Last-Modified"))f.lastModified[k]=y;if(z=v.getResponseHeader("Etag"))f.etag[k]=z}if(a===304)w="notmodified",o=!0;else try{r=cb(d,x),w="success",o=!0}catch(A){w="parsererror",u=A}}else{u=w;if(!w||a)w="error",a<0&&(a=0)}v.status=a,v.statusText=""+(c||w),o?h.resolveWith(e,[r,w,v]):h.rejectWith(e,[v,w,u]),v.statusCode(j),j=b,t&&g.trigger("ajax"+(o?"Success":"Error"),[v,d,o?r:u]),i.fireWith(e,[v,w]),t&&(g.trigger("ajaxComplete",[v,d]),--f.active||f.event.trigger("ajaxStop"))}}typeof a=="object"&&(c=a,a=b),c=c||{};var d=f.ajaxSetup({},c),e=d.context||d,g=e!==d&&(e.nodeType||e instanceof f)?f(e):f.event,h=f.Deferred(),i=f.Callbacks("once memory"),j=d.statusCode||{},k,l={},m={},n,o,p,q,r,s=0,t,u,v={readyState:0,setRequestHeader:function(a,b){if(!s){var c=a.toLowerCase();a=m[c]=m[c]||a,l[a]=b}return this},getAllResponseHeaders:function(){return s===2?n:null},getResponseHeader:function(a){var c;if(s===2){if(!o){o={};while(c=bG.exec(n))o[c[1].toLowerCase()]=c[2]}c=o[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){s||(d.mimeType=a);return this},abort:function(a){a=a||"abort",p&&p.abort(a),w(0,a);return this}};h.promise(v),v.success=v.done,v.error=v.fail,v.complete=i.add,v.statusCode=function(a){if(a){var b;if(s<2)for(b in a)j[b]=[j[b],a[b]];else b=a[v.status],v.then(b,b)}return this},d.url=((a||d.url)+"").replace(bF,"").replace(bK,bV[1]+"//"),d.dataTypes=f.trim(d.dataType||"*").toLowerCase().split(bO),d.crossDomain==null&&(r=bQ.exec(d.url.toLowerCase()),d.crossDomain=!(!r||r[1]==bV[1]&&r[2]==bV[2]&&(r[3]||(r[1]==="http:"?80:443))==(bV[3]||(bV[1]==="http:"?80:443)))),d.data&&d.processData&&typeof d.data!="string"&&(d.data=f.param(d.data,d.traditional)),bZ(bS,d,c,v);if(s===2)return!1;t=d.global,d.type=d.type.toUpperCase(),d.hasContent=!bJ.test(d.type),t&&f.active++===0&&f.event.trigger("ajaxStart");if(!d.hasContent){d.data&&(d.url+=(bL.test(d.url)?"&":"?")+d.data,delete d.data),k=d.url;if(d.cache===!1){var x=f.now(),y=d.url.replace(bP,"$1_="+x);d.url=y+(y===d.url?(bL.test(d.url)?"&":"?")+"_="+x:"")}}(d.data&&d.hasContent&&d.contentType!==!1||c.contentType)&&v.setRequestHeader("Content-Type",d.contentType),d.ifModified&&(k=k||d.url,f.lastModified[k]&&v.setRequestHeader("If-Modified-Since",f.lastModified[k]),f.etag[k]&&v.setRequestHeader("If-None-Match",f.etag[k])),v.setRequestHeader("Accept",d.dataTypes[0]&&d.accepts[d.dataTypes[0]]?d.accepts[d.dataTypes[0]]+(d.dataTypes[0]!=="*"?", "+bW+"; q=0.01":""):d.accepts["*"]);for(u in d.headers)v.setRequestHeader(u,d.headers[u]);if(d.beforeSend&&(d.beforeSend.call(e,v,d)===!1||s===2)){v.abort();return!1}for(u in{success:1,error:1,complete:1})v[u](d[u]);p=bZ(bT,d,c,v);if(!p)w(-1,"No Transport");else{v.readyState=1,t&&g.trigger("ajaxSend",[v,d]),d.async&&d.timeout>0&&(q=setTimeout(function(){v.abort("timeout")},d.timeout));try{s=1,p.send(l,w)}catch(z){if(s<2)w(-1,z);else throw z}}return v},param:function(a,c){var d=[],e=function(a,b){b=f.isFunction(b)?b():b,d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=f.ajaxSettings.traditional);if(f.isArray(a)||a.jquery&&!f.isPlainObject(a))f.each(a,function(){e(this.name,this.value)});else for(var g in a)b_(g,a[g],c,e);return d.join("&").replace(bC,"+")}}),f.extend({active:0,lastModified:{},etag:{}});var cc=f.now(),cd=/(\=)\?(&|$)|\?\?/i;f.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return f.expando+"_"+cc++}}),f.ajaxPrefilter("json jsonp",function(b,c,d){var e=typeof b.data=="string"&&/^application\/x\-www\-form\-urlencoded/.test(b.contentType);if(b.dataTypes[0]==="jsonp"||b.jsonp!==!1&&(cd.test(b.url)||e&&cd.test(b.data))){var g,h=b.jsonpCallback=f.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2";b.jsonp!==!1&&(j=j.replace(cd,l),b.url===j&&(e&&(k=k.replace(cd,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},d.always(function(){a[h]=i,g&&f.isFunction(i)&&a[h](g[0])}),b.converters["script json"]=function(){g||f.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),f.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){f.globalEval(a);return a}}}),f.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),f.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(c||!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var ce=a.ActiveXObject?function(){for(var a in cg)cg[a](0,1)}:!1,cf=0,cg;f.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&ch()||ci()}:ch,function(a){f.extend(f.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(f.ajaxSettings.xhr()),f.support.ajax&&f.ajaxTransport(function(c){if(!c.crossDomain||f.support.cors){var d;return{send:function(e,g){var h=c.xhr(),i,j;c.username?h.open(c.type,c.url,c.async,c.username,c.password):h.open(c.type,c.url,c.async);if(c.xhrFields)for(j in c.xhrFields)h[j]=c.xhrFields[j];c.mimeType&&h.overrideMimeType&&h.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(j in e)h.setRequestHeader(j,e[j])}catch(k){}h.send(c.hasContent&&c.data||null),d=function(a,e){var j,k,l,m,n;try{if(d&&(e||h.readyState===4)){d=b,i&&(h.onreadystatechange=f.noop,ce&&delete cg[i]);if(e)h.readyState!==4&&h.abort();else{j=h.status,l=h.getAllResponseHeaders(),m={},n=h.responseXML,n&&n.documentElement&&(m.xml=n);try{m.text=h.responseText}catch(a){}try{k=h.statusText}catch(o){k=""}!j&&c.isLocal&&!c.crossDomain?j=m.text?200:404:j===1223&&(j=204)}}}catch(p){e||g(-1,p)}m&&g(j,k,m,l)},!c.async||h.readyState===4?d():(i=++cf,ce&&(cg||(cg={},f(a).unload(ce)),cg[i]=d),h.onreadystatechange=d)},abort:function(){d&&d(0,1)}}}});var cj={},ck,cl,cm=/^(?:toggle|show|hide)$/,cn=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,co,cp=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]],cq;f.fn.extend({show:function(a,b,c){var d,e;if(a||a===0)return this.animate(ct("show",3),a,b,c);for(var g=0,h=this.length;g=i.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),i.animatedProperties[this.prop]=!0;for(b in i.animatedProperties)i.animatedProperties[b]!==!0&&(g=!1);if(g){i.overflow!=null&&!f.support.shrinkWrapBlocks&&f.each(["","X","Y"],function(a,b){h.style["overflow"+b]=i.overflow[a]}),i.hide&&f(h).hide();if(i.hide||i.show)for(b in i.animatedProperties)f.style(h,b,i.orig[b]),f.removeData(h,"fxshow"+b,!0),f.removeData(h,"toggle"+b,!0);d=i.complete,d&&(i.complete=!1,d.call(h))}return!1}i.duration==Infinity?this.now=e:(c=e-this.startTime,this.state=c/i.duration,this.pos=f.easing[i.animatedProperties[this.prop]](this.state,c,0,1,i.duration),this.now=this.start+(this.end-this.start)*this.pos),this.update();return!0}},f.extend(f.fx,{tick:function(){var a,b=f.timers,c=0;for(;c-1,k={},l={},m,n;j?(l=e.position(),m=l.top,n=l.left):(m=parseFloat(h)||0,n=parseFloat(i)||0),f.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):e.css(k)}},f.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),d=cx.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(f.css(a,"marginTop"))||0,c.left-=parseFloat(f.css(a,"marginLeft"))||0,d.top+=parseFloat(f.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(f.css(b[0],"borderLeftWidth"))||0;return{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&!cx.test(a.nodeName)&&f.css(a,"position")==="static")a=a.offsetParent;return a})}}),f.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);f.fn[a]=function(e){return f.access(this,function(a,e,g){var h=cy(a);if(g===b)return h?c in h?h[c]:f.support.boxModel&&h.document.documentElement[e]||h.document.body[e]:a[e];h?h.scrollTo(d?f(h).scrollLeft():g,d?g:f(h).scrollTop()):a[e]=g},a,e,arguments.length,null)}}),f.each({Height:"height",Width:"width"},function(a,c){var d="client"+a,e="scroll"+a,g="offset"+a;f.fn["inner"+a]=function(){var a=this[0];return a?a.style?parseFloat(f.css(a,c,"padding")):this[c]():null},f.fn["outer"+a]=function(a){var b=this[0];return b?b.style?parseFloat(f.css(b,c,a?"margin":"border")):this[c]():null},f.fn[c]=function(a){return f.access(this,function(a,c,h){var i,j,k,l;if(f.isWindow(a)){i=a.document,j=i.documentElement[d];return f.support.boxModel&&j||i.body&&i.body[d]||j}if(a.nodeType===9){i=a.documentElement;if(i[d]>=i[e])return i[d];return Math.max(a.body[e],i[e],a.body[g],i[g])}if(h===b){k=f.css(a,c),l=parseFloat(k);return f.isNumeric(l)?l:k}f(a).css(c,h)},c,a,arguments.length,null)}}),a.jQuery=a.$=f,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return f})})(window); \ No newline at end of file diff --git a/logback-site/src/site/pages/js/prettify.js b/logback-site/src/site/pages/js/prettify.js deleted file mode 100644 index 09d6394fb2..0000000000 --- a/logback-site/src/site/pages/js/prettify.js +++ /dev/null @@ -1,1478 +0,0 @@ -// Copyright (C) 2006 Google Inc. -// -// 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. - - -/** - * @fileoverview - * some functions for browser-side pretty printing of code contained in html. - *

- * - * For a fairly comprehensive set of languages see the - * README - * file that came with this source. At a minimum, the lexer should work on a - * number of languages including C and friends, Java, Python, Bash, SQL, HTML, - * XML, CSS, Javascript, and Makefiles. It works passably on Ruby, PHP and Awk - * and a subset of Perl, but, because of commenting conventions, doesn't work on - * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class. - *

- * Usage:

    - *
  1. include this source file in an html page via - * {@code } - *
  2. define style rules. See the example page for examples. - *
  3. mark the {@code
    } and {@code } tags in your source with
    - *    {@code class=prettyprint.}
    - *    You can also use the (html deprecated) {@code } tag, but the pretty
    - *    printer needs to do more substantial DOM manipulations to support that, so
    - *    some css styles may not be preserved.
    - * </ol>
    - * That's it.  I wanted to keep the API as simple as possible, so there's no
    - * need to specify which language the code is in, but if you wish, you can add
    - * another class to the {@code <pre>} or {@code <code>} element to specify the
    - * language, as in {@code <pre class="prettyprint lang-java">}.  Any class that
    - * starts with "lang-" followed by a file extension, specifies the file type.
    - * See the "lang-*.js" files in this directory for code that implements
    - * per-language file handlers.
    - * <p>
    - * Change log:<br>
    - * cbeust, 2006/08/22
    - * <blockquote>
    - *   Java annotations (start with "@") are now captured as literals ("lit")
    - * </blockquote>
    - * @requires console
    - * @overrides window
    - */
    -
    -// JSLint declarations
    -/*global console, document, navigator, setTimeout, window */
    -
    -/**
    - * Split {@code prettyPrint} into multiple timeouts so as not to interfere with
    - * UI events.
    - * If set to {@code false}, {@code prettyPrint()} is synchronous.
    - */
    -window['PR_SHOULD_USE_CONTINUATION'] = true;
    -
    -/** the number of characters between tab columns */
    -window['PR_TAB_WIDTH'] = 8;
    -
    -/** Walks the DOM returning a properly escaped version of innerHTML.
    -  * @param {Node} node
    -  * @param {Array.<string>} out output buffer that receives chunks of HTML.
    -  */
    -window['PR_normalizedHtml']
    -
    -/** Contains functions for creating and registering new language handlers.
    -  * @type {Object}
    -  */
    -  = window['PR']
    -
    -/** Pretty print a chunk of code.
    -  *
    -  * @param {string} sourceCodeHtml code as html
    -  * @return {string} code as html, but prettier
    -  */
    -  = window['prettyPrintOne']
    -/** Find all the {@code <pre>} and {@code <code>} tags in the DOM with
    -  * {@code class=prettyprint} and prettify them.
    -  * @param {Function?} opt_whenDone if specified, called when the last entry
    -  *     has been finished.
    -  */
    -  = window['prettyPrint'] = void 0;
    -
    -/** browser detection. @extern @returns false if not IE, otherwise the major version. */
    -window['_pr_isIE6'] = function () {
    -  var ieVersion = navigator && navigator.userAgent &&
    -      navigator.userAgent.match(/\bMSIE ([678])\./);
    -  ieVersion = ieVersion ? +ieVersion[1] : false;
    -  window['_pr_isIE6'] = function () { return ieVersion; };
    -  return ieVersion;
    -};
    -
    -
    -(function () {
    -  // Keyword lists for various languages.
    -  var FLOW_CONTROL_KEYWORDS =
    -      "break continue do else for if return while ";
    -  var C_KEYWORDS = FLOW_CONTROL_KEYWORDS + "auto case char const default " +
    -      "double enum extern float goto int long register short signed sizeof " +
    -      "static struct switch typedef union unsigned void volatile ";
    -  var COMMON_KEYWORDS = C_KEYWORDS + "catch class delete false import " +
    -      "new operator private protected public this throw true try typeof ";
    -  var CPP_KEYWORDS = COMMON_KEYWORDS + "alignof align_union asm axiom bool " +
    -      "concept concept_map const_cast constexpr decltype " +
    -      "dynamic_cast explicit export friend inline late_check " +
    -      "mutable namespace nullptr reinterpret_cast static_assert static_cast " +
    -      "template typeid typename using virtual wchar_t where ";
    -  var JAVA_KEYWORDS = COMMON_KEYWORDS +
    -      "abstract boolean byte extends final finally implements import " +
    -      "instanceof null native package strictfp super synchronized throws " +
    -      "transient ";
    -  var CSHARP_KEYWORDS = JAVA_KEYWORDS +
    -      "as base by checked decimal delegate descending event " +
    -      "fixed foreach from group implicit in interface internal into is lock " +
    -      "object out override orderby params partial readonly ref sbyte sealed " +
    -      "stackalloc string select uint ulong unchecked unsafe ushort var ";
    -  var JSCRIPT_KEYWORDS = COMMON_KEYWORDS +
    -      "debugger eval export function get null set undefined var with " +
    -      "Infinity NaN ";
    -  var PERL_KEYWORDS = "caller delete die do dump elsif eval exit foreach for " +
    -      "goto if import last local my next no our print package redo require " +
    -      "sub undef unless until use wantarray while BEGIN END ";
    -  var PYTHON_KEYWORDS = FLOW_CONTROL_KEYWORDS + "and as assert class def del " +
    -      "elif except exec finally from global import in is lambda " +
    -      "nonlocal not or pass print raise try with yield " +
    -      "False True None ";
    -  var RUBY_KEYWORDS = FLOW_CONTROL_KEYWORDS + "alias and begin case class def" +
    -      " defined elsif end ensure false in module next nil not or redo rescue " +
    -      "retry self super then true undef unless until when yield BEGIN END ";
    -  var SH_KEYWORDS = FLOW_CONTROL_KEYWORDS + "case done elif esac eval fi " +
    -      "function in local set then until ";
    -  var ALL_KEYWORDS = (
    -      CPP_KEYWORDS + CSHARP_KEYWORDS + JSCRIPT_KEYWORDS + PERL_KEYWORDS +
    -      PYTHON_KEYWORDS + RUBY_KEYWORDS + SH_KEYWORDS);
    -
    -  // token style names.  correspond to css classes
    -  /** token style for a string literal */
    -  var PR_STRING = 'str';
    -  /** token style for a keyword */
    -  var PR_KEYWORD = 'kwd';
    -  /** token style for a comment */
    -  var PR_COMMENT = 'com';
    -  /** token style for a type */
    -  var PR_TYPE = 'typ';
    -  /** token style for a literal value.  e.g. 1, null, true. */
    -  var PR_LITERAL = 'lit';
    -  /** token style for a punctuation string. */
    -  var PR_PUNCTUATION = 'pun';
    -  /** token style for a punctuation string. */
    -  var PR_PLAIN = 'pln';
    -
    -  /** token style for an sgml tag. */
    -  var PR_TAG = 'tag';
    -  /** token style for a markup declaration such as a DOCTYPE. */
    -  var PR_DECLARATION = 'dec';
    -  /** token style for embedded source. */
    -  var PR_SOURCE = 'src';
    -  /** token style for an sgml attribute name. */
    -  var PR_ATTRIB_NAME = 'atn';
    -  /** token style for an sgml attribute value. */
    -  var PR_ATTRIB_VALUE = 'atv';
    -
    -  /**
    -   * A class that indicates a section of markup that is not code, e.g. to allow
    -   * embedding of line numbers within code listings.
    -   */
    -  var PR_NOCODE = 'nocode';
    -
    -  /** A set of tokens that can precede a regular expression literal in
    -    * javascript.
    -    * http://www.mozilla.org/js/language/js20/rationale/syntax.html has the full
    -    * list, but I've removed ones that might be problematic when seen in
    -    * languages that don't support regular expression literals.
    -    *
    -    * <p>Specifically, I've removed any keywords that can't precede a regexp
    -    * literal in a syntactically legal javascript program, and I've removed the
    -    * "in" keyword since it's not a keyword in many languages, and might be used
    -    * as a count of inches.
    -    *
    -    * <p>The link a above does not accurately describe EcmaScript rules since
    -    * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
    -    * very well in practice.
    -    *
    -    * @private
    -    */
    -  var REGEXP_PRECEDER_PATTERN = function () {
    -      var preceders = [
    -          "!", "!=", "!==", "#", "%", "%=", "&", "&&", "&&=",
    -          "&=", "(", "*", "*=", /* "+", */ "+=", ",", /* "-", */ "-=",
    -          "->", /*".", "..", "...", handled below */ "/", "/=", ":", "::", ";",
    -          "<", "<<", "<<=", "<=", "=", "==", "===", ">",
    -          ">=", ">>", ">>=", ">>>", ">>>=", "?", "@", "[",
    -          "^", "^=", "^^", "^^=", "{", "|", "|=", "||",
    -          "||=", "~" /* handles =~ and !~ */,
    -          "break", "case", "continue", "delete",
    -          "do", "else", "finally", "instanceof",
    -          "return", "throw", "try", "typeof"
    -          ];
    -      var pattern = '(?:^^|[+-]';
    -      for (var i = 0; i < preceders.length; ++i) {
    -        pattern += '|' + preceders[i].replace(/([^=<>:&a-z])/g, '\\$1');
    -      }
    -      pattern += ')\\s*';  // matches at end, and matches empty string
    -      return pattern;
    -      // CAVEAT: this does not properly handle the case where a regular
    -      // expression immediately follows another since a regular expression may
    -      // have flags for case-sensitivity and the like.  Having regexp tokens
    -      // adjacent is not valid in any language I'm aware of, so I'm punting.
    -      // TODO: maybe style special characters inside a regexp as punctuation.
    -    }();
    -
    -  // Define regexps here so that the interpreter doesn't have to create an
    -  // object each time the function containing them is called.
    -  // The language spec requires a new object created even if you don't access
    -  // the $1 members.
    -  var pr_amp = /&/g;
    -  var pr_lt = /</g;
    -  var pr_gt = />/g;
    -  var pr_quot = /\"/g;
    -  /** like textToHtml but escapes double quotes to be attribute safe. */
    -  function attribToHtml(str) {
    -    return str.replace(pr_amp, '&amp;')
    -        .replace(pr_lt, '&lt;')
    -        .replace(pr_gt, '&gt;')
    -        .replace(pr_quot, '&quot;');
    -  }
    -
    -  /** escapest html special characters to html. */
    -  function textToHtml(str) {
    -    return str.replace(pr_amp, '&amp;')
    -        .replace(pr_lt, '&lt;')
    -        .replace(pr_gt, '&gt;');
    -  }
    -
    -
    -  var pr_ltEnt = /&lt;/g;
    -  var pr_gtEnt = /&gt;/g;
    -  var pr_aposEnt = /&apos;/g;
    -  var pr_quotEnt = /&quot;/g;
    -  var pr_ampEnt = /&amp;/g;
    -  var pr_nbspEnt = /&nbsp;/g;
    -  /** unescapes html to plain text. */
    -  function htmlToText(html) {
    -    var pos = html.indexOf('&');
    -    if (pos < 0) { return html; }
    -    // Handle numeric entities specially.  We can't use functional substitution
    -    // since that doesn't work in older versions of Safari.
    -    // These should be rare since most browsers convert them to normal chars.
    -    for (--pos; (pos = html.indexOf('&#', pos + 1)) >= 0;) {
    -      var end = html.indexOf(';', pos);
    -      if (end >= 0) {
    -        var num = html.substring(pos + 3, end);
    -        var radix = 10;
    -        if (num && num.charAt(0) === 'x') {
    -          num = num.substring(1);
    -          radix = 16;
    -        }
    -        var codePoint = parseInt(num, radix);
    -        if (!isNaN(codePoint)) {
    -          html = (html.substring(0, pos) + String.fromCharCode(codePoint) +
    -                  html.substring(end + 1));
    -        }
    -      }
    -    }
    -
    -    return html.replace(pr_ltEnt, '<')
    -        .replace(pr_gtEnt, '>')
    -        .replace(pr_aposEnt, "'")
    -        .replace(pr_quotEnt, '"')
    -        .replace(pr_nbspEnt, ' ')
    -        .replace(pr_ampEnt, '&');
    -  }
    -
    -  /** is the given node's innerHTML normally unescaped? */
    -  function isRawContent(node) {
    -    return 'XMP' === node.tagName;
    -  }
    -
    -  var newlineRe = /[\r\n]/g;
    -  /**
    -   * Are newlines and adjacent spaces significant in the given node's innerHTML?
    -   */
    -  function isPreformatted(node, content) {
    -    // PRE means preformatted, and is a very common case, so don't create
    -    // unnecessary computed style objects.
    -    if ('PRE' === node.tagName) { return true; }
    -    if (!newlineRe.test(content)) { return true; }  // Don't care
    -    var whitespace = '';
    -    // For disconnected nodes, IE has no currentStyle.
    -    if (node.currentStyle) {
    -      whitespace = node.currentStyle.whiteSpace;
    -    } else if (window.getComputedStyle) {
    -      // Firefox makes a best guess if node is disconnected whereas Safari
    -      // returns the empty string.
    -      whitespace = window.getComputedStyle(node, null).whiteSpace;
    -    }
    -    return !whitespace || whitespace === 'pre';
    -  }
    -
    -  function normalizedHtml(node, out) {
    -    switch (node.nodeType) {
    -      case 1:  // an element
    -        var name = node.tagName.toLowerCase();
    -        out.push('<', name);
    -        for (var i = 0; i < node.attributes.length; ++i) {
    -          var attr = node.attributes[i];
    -          if (!attr.specified) { continue; }
    -          out.push(' ');
    -          normalizedHtml(attr, out);
    -        }
    -        out.push('>');
    -        for (var child = node.firstChild; child; child = child.nextSibling) {
    -          normalizedHtml(child, out);
    -        }
    -        if (node.firstChild || !/^(?:br|link|img)$/.test(name)) {
    -          out.push('<\/', name, '>');
    -        }
    -        break;
    -      case 2: // an attribute
    -        out.push(node.name.toLowerCase(), '="', attribToHtml(node.value), '"');
    -        break;
    -      case 3: case 4: // text
    -        out.push(textToHtml(node.nodeValue));
    -        break;
    -    }
    -  }
    -
    -  /**
    -   * Given a group of {@link RegExp}s, returns a {@code RegExp} that globally
    -   * matches the union o the sets o strings matched d by the input RegExp.
    -   * Since it matches globally, if the input strings have a start-of-input
    -   * anchor (/^.../), it is ignored for the purposes of unioning.
    -   * @param {Array.<RegExp>} regexs non multiline, non-global regexs.
    -   * @return {RegExp} a global regex.
    -   */
    -  function combinePrefixPatterns(regexs) {
    -    var capturedGroupIndex = 0;
    -
    -    var needToFoldCase = false;
    -    var ignoreCase = false;
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.ignoreCase) {
    -        ignoreCase = true;
    -      } else if (/[a-z]/i.test(regex.source.replace(
    -                     /\\u[0-9a-f]{4}|\\x[0-9a-f]{2}|\\[^ux]/gi, ''))) {
    -        needToFoldCase = true;
    -        ignoreCase = false;
    -        break;
    -      }
    -    }
    -
    -    function decodeEscape(charsetPart) {
    -      if (charsetPart.charAt(0) !== '\\') { return charsetPart.charCodeAt(0); }
    -      switch (charsetPart.charAt(1)) {
    -        case 'b': return 8;
    -        case 't': return 9;
    -        case 'n': return 0xa;
    -        case 'v': return 0xb;
    -        case 'f': return 0xc;
    -        case 'r': return 0xd;
    -        case 'u': case 'x':
    -          return parseInt(charsetPart.substring(2), 16)
    -              || charsetPart.charCodeAt(1);
    -        case '0': case '1': case '2': case '3': case '4':
    -        case '5': case '6': case '7':
    -          return parseInt(charsetPart.substring(1), 8);
    -        default: return charsetPart.charCodeAt(1);
    -      }
    -    }
    -
    -    function encodeEscape(charCode) {
    -      if (charCode < 0x20) {
    -        return (charCode < 0x10 ? '\\x0' : '\\x') + charCode.toString(16);
    -      }
    -      var ch = String.fromCharCode(charCode);
    -      if (ch === '\\' || ch === '-' || ch === '[' || ch === ']') {
    -        ch = '\\' + ch;
    -      }
    -      return ch;
    -    }
    -
    -    function caseFoldCharset(charSet) {
    -      var charsetParts = charSet.substring(1, charSet.length - 1).match(
    -          new RegExp(
    -              '\\\\u[0-9A-Fa-f]{4}'
    -              + '|\\\\x[0-9A-Fa-f]{2}'
    -              + '|\\\\[0-3][0-7]{0,2}'
    -              + '|\\\\[0-7]{1,2}'
    -              + '|\\\\[\\s\\S]'
    -              + '|-'
    -              + '|[^-\\\\]',
    -              'g'));
    -      var groups = [];
    -      var ranges = [];
    -      var inverse = charsetParts[0] === '^';
    -      for (var i = inverse ? 1 : 0, n = charsetParts.length; i < n; ++i) {
    -        var p = charsetParts[i];
    -        switch (p) {
    -          case '\\B': case '\\b':
    -          case '\\D': case '\\d':
    -          case '\\S': case '\\s':
    -          case '\\W': case '\\w':
    -            groups.push(p);
    -            continue;
    -        }
    -        var start = decodeEscape(p);
    -        var end;
    -        if (i + 2 < n && '-' === charsetParts[i + 1]) {
    -          end = decodeEscape(charsetParts[i + 2]);
    -          i += 2;
    -        } else {
    -          end = start;
    -        }
    -        ranges.push([start, end]);
    -        // If the range might intersect letters, then expand it.
    -        if (!(end < 65 || start > 122)) {
    -          if (!(end < 65 || start > 90)) {
    -            ranges.push([Math.max(65, start) | 32, Math.min(end, 90) | 32]);
    -          }
    -          if (!(end < 97 || start > 122)) {
    -            ranges.push([Math.max(97, start) & ~32, Math.min(end, 122) & ~32]);
    -          }
    -        }
    -      }
    -
    -      // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
    -      // -> [[1, 12], [14, 14], [16, 17]]
    -      ranges.sort(function (a, b) { return (a[0] - b[0]) || (b[1]  - a[1]); });
    -      var consolidatedRanges = [];
    -      var lastRange = [NaN, NaN];
    -      for (var i = 0; i < ranges.length; ++i) {
    -        var range = ranges[i];
    -        if (range[0] <= lastRange[1] + 1) {
    -          lastRange[1] = Math.max(lastRange[1], range[1]);
    -        } else {
    -          consolidatedRanges.push(lastRange = range);
    -        }
    -      }
    -
    -      var out = ['['];
    -      if (inverse) { out.push('^'); }
    -      out.push.apply(out, groups);
    -      for (var i = 0; i < consolidatedRanges.length; ++i) {
    -        var range = consolidatedRanges[i];
    -        out.push(encodeEscape(range[0]));
    -        if (range[1] > range[0]) {
    -          if (range[1] + 1 > range[0]) { out.push('-'); }
    -          out.push(encodeEscape(range[1]));
    -        }
    -      }
    -      out.push(']');
    -      return out.join('');
    -    }
    -
    -    function allowAnywhereFoldCaseAndRenumberGroups(regex) {
    -      // Split into character sets, escape sequences, punctuation strings
    -      // like ('(', '(?:', ')', '^'), and runs of characters that do not
    -      // include any of the above.
    -      var parts = regex.source.match(
    -          new RegExp(
    -              '(?:'
    -              + '\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]'  // a character set
    -              + '|\\\\u[A-Fa-f0-9]{4}'  // a unicode escape
    -              + '|\\\\x[A-Fa-f0-9]{2}'  // a hex escape
    -              + '|\\\\[0-9]+'  // a back-reference or octal escape
    -              + '|\\\\[^ux0-9]'  // other escape sequence
    -              + '|\\(\\?[:!=]'  // start of a non-capturing group
    -              + '|[\\(\\)\\^]'  // start/emd of a group, or line start
    -              + '|[^\\x5B\\x5C\\(\\)\\^]+'  // run of other characters
    -              + ')',
    -              'g'));
    -      var n = parts.length;
    -
    -      // Maps captured group numbers to the number they will occupy in
    -      // the output or to -1 if that has not been determined, or to
    -      // undefined if they need not be capturing in the output.
    -      var capturedGroups = [];
    -
    -      // Walk over and identify back references to build the capturedGroups
    -      // mapping.
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          // groups are 1-indexed, so max group index is count of '('
    -          ++groupIndex;
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue && decimalValue <= groupIndex) {
    -            capturedGroups[decimalValue] = -1;
    -          }
    -        }
    -      }
    -
    -      // Renumber groups and reduce capturing groups to non-capturing groups
    -      // where possible.
    -      for (var i = 1; i < capturedGroups.length; ++i) {
    -        if (-1 === capturedGroups[i]) {
    -          capturedGroups[i] = ++capturedGroupIndex;
    -        }
    -      }
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        var p = parts[i];
    -        if (p === '(') {
    -          ++groupIndex;
    -          if (capturedGroups[groupIndex] === undefined) {
    -            parts[i] = '(?:';
    -          }
    -        } else if ('\\' === p.charAt(0)) {
    -          var decimalValue = +p.substring(1);
    -          if (decimalValue && decimalValue <= groupIndex) {
    -            parts[i] = '\\' + capturedGroups[groupIndex];
    -          }
    -        }
    -      }
    -
    -      // Remove any prefix anchors so that the output will match anywhere.
    -      // ^^ really does mean an anchored match though.
    -      for (var i = 0, groupIndex = 0; i < n; ++i) {
    -        if ('^' === parts[i] && '^' !== parts[i + 1]) { parts[i] = ''; }
    -      }
    -
    -      // Expand letters to groupts to handle mixing of case-sensitive and
    -      // case-insensitive patterns if necessary.
    -      if (regex.ignoreCase && needToFoldCase) {
    -        for (var i = 0; i < n; ++i) {
    -          var p = parts[i];
    -          var ch0 = p.charAt(0);
    -          if (p.length >= 2 && ch0 === '[') {
    -            parts[i] = caseFoldCharset(p);
    -          } else if (ch0 !== '\\') {
    -            // TODO: handle letters in numeric escapes.
    -            parts[i] = p.replace(
    -                /[a-zA-Z]/g,
    -                function (ch) {
    -                  var cc = ch.charCodeAt(0);
    -                  return '[' + String.fromCharCode(cc & ~32, cc | 32) + ']';
    -                });
    -          }
    -        }
    -      }
    -
    -      return parts.join('');
    -    }
    -
    -    var rewritten = [];
    -    for (var i = 0, n = regexs.length; i < n; ++i) {
    -      var regex = regexs[i];
    -      if (regex.global || regex.multiline) { throw new Error('' + regex); }
    -      rewritten.push(
    -          '(?:' + allowAnywhereFoldCaseAndRenumberGroups(regex) + ')');
    -    }
    -
    -    return new RegExp(rewritten.join('|'), ignoreCase ? 'gi' : 'g');
    -  }
    -
    -  var PR_innerHtmlWorks = null;
    -  function getInnerHtml(node) {
    -    // inner html is hopelessly broken in Safari 2.0.4 when the content is
    -    // an html description of well formed XML and the containing tag is a PRE
    -    // tag, so we detect that case and emulate innerHTML.
    -    if (null === PR_innerHtmlWorks) {
    -      var testNode = document.createElement('PRE');
    -      testNode.appendChild(
    -          document.createTextNode('<!DOCTYPE foo PUBLIC "foo bar">\n<foo />'));
    -      PR_innerHtmlWorks = !/</.test(testNode.innerHTML);
    -    }
    -
    -    if (PR_innerHtmlWorks) {
    -      var content = node.innerHTML;
    -      // XMP tags contain unescaped entities so require special handling.
    -      if (isRawContent(node)) {
    -        content = textToHtml(content);
    -      } else if (!isPreformatted(node, content)) {
    -        content = content.replace(/(<br\s*\/?>)[\r\n]+/g, '$1')
    -            .replace(/(?:[\r\n]+[ \t]*)+/g, ' ');
    -      }
    -      return content;
    -    }
    -
    -    var out = [];
    -    for (var child = node.firstChild; child; child = child.nextSibling) {
    -      normalizedHtml(child, out);
    -    }
    -    return out.join('');
    -  }
    -
    -  /** returns a function that expand tabs to spaces.  This function can be fed
    -    * successive chunks of text, and will maintain its own internal state to
    -    * keep track of how tabs are expanded.
    -    * @return {function (string) : string} a function that takes
    -    *   plain text and return the text with tabs expanded.
    -    * @private
    -    */
    -  function makeTabExpander(tabWidth) {
    -    var SPACES = '                ';
    -    var charInLine = 0;
    -
    -    return function (plainText) {
    -      // walk over each character looking for tabs and newlines.
    -      // On tabs, expand them.  On newlines, reset charInLine.
    -      // Otherwise increment charInLine
    -      var out = null;
    -      var pos = 0;
    -      for (var i = 0, n = plainText.length; i < n; ++i) {
    -        var ch = plainText.charAt(i);
    -
    -        switch (ch) {
    -          case '\t':
    -            if (!out) { out = []; }
    -            out.push(plainText.substring(pos, i));
    -            // calculate how much space we need in front of this part
    -            // nSpaces is the amount of padding -- the number of spaces needed
    -            // to move us to the next column, where columns occur at factors of
    -            // tabWidth.
    -            var nSpaces = tabWidth - (charInLine % tabWidth);
    -            charInLine += nSpaces;
    -            for (; nSpaces >= 0; nSpaces -= SPACES.length) {
    -              out.push(SPACES.substring(0, nSpaces));
    -            }
    -            pos = i + 1;
    -            break;
    -          case '\n':
    -            charInLine = 0;
    -            break;
    -          default:
    -            ++charInLine;
    -        }
    -      }
    -      if (!out) { return plainText; }
    -      out.push(plainText.substring(pos));
    -      return out.join('');
    -    };
    -  }
    -
    -  var pr_chunkPattern = new RegExp(
    -      '[^<]+'  // A run of characters other than '<'
    -      + '|<\!--[\\s\\S]*?--\>'  // an HTML comment
    -      + '|<!\\[CDATA\\[[\\s\\S]*?\\]\\]>'  // a CDATA section
    -      // a probable tag that should not be highlighted
    -      + '|<\/?[a-zA-Z](?:[^>\"\']|\'[^\']*\'|\"[^\"]*\")*>'
    -      + '|<',  // A '<' that does not begin a larger chunk
    -      'g');
    -  var pr_commentPrefix = /^<\!--/;
    -  var pr_cdataPrefix = /^<!\[CDATA\[/;
    -  var pr_brPrefix = /^<br\b/i;
    -  var pr_tagNameRe = /^<(\/?)([a-zA-Z][a-zA-Z0-9]*)/;
    -
    -  /** split markup into chunks of html tags (style null) and
    -    * plain text (style {@link #PR_PLAIN}), converting tags which are
    -    * significant for tokenization (<br>) into their textual equivalent.
    -    *
    -    * @param {string} s html where whitespace is considered significant.
    -    * @return {Object} source code and extracted tags.
    -    * @private
    -    */
    -  function extractTags(s) {
    -    // since the pattern has the 'g' modifier and defines no capturing groups,
    -    // this will return a list of all chunks which we then classify and wrap as
    -    // PR_Tokens
    -    var matches = s.match(pr_chunkPattern);
    -    var sourceBuf = [];
    -    var sourceBufLen = 0;
    -    var extractedTags = [];
    -    if (matches) {
    -      for (var i = 0, n = matches.length; i < n; ++i) {
    -        var match = matches[i];
    -        if (match.length > 1 && match.charAt(0) === '<') {
    -          if (pr_commentPrefix.test(match)) { continue; }
    -          if (pr_cdataPrefix.test(match)) {
    -            // strip CDATA prefix and suffix.  Don't unescape since it's CDATA
    -            sourceBuf.push(match.substring(9, match.length - 3));
    -            sourceBufLen += match.length - 12;
    -          } else if (pr_brPrefix.test(match)) {
    -            // <br> tags are lexically significant so convert them to text.
    -            // This is undone later.
    -            sourceBuf.push('\n');
    -            ++sourceBufLen;
    -          } else {
    -            if (match.indexOf(PR_NOCODE) >= 0 && isNoCodeTag(match)) {
    -              // A <span class="nocode"> will start a section that should be
    -              // ignored.  Continue walking the list until we see a matching end
    -              // tag.
    -              var name = match.match(pr_tagNameRe)[2];
    -              var depth = 1;
    -              var j;
    -              end_tag_loop:
    -              for (j = i + 1; j < n; ++j) {
    -                var name2 = matches[j].match(pr_tagNameRe);
    -                if (name2 && name2[2] === name) {
    -                  if (name2[1] === '/') {
    -                    if (--depth === 0) { break end_tag_loop; }
    -                  } else {
    -                    ++depth;
    -                  }
    -                }
    -              }
    -              if (j < n) {
    -                extractedTags.push(
    -                    sourceBufLen, matches.slice(i, j + 1).join(''));
    -                i = j;
    -              } else {  // Ignore unclosed sections.
    -                extractedTags.push(sourceBufLen, match);
    -              }
    -            } else {
    -              extractedTags.push(sourceBufLen, match);
    -            }
    -          }
    -        } else {
    -          var literalText = htmlToText(match);
    -          sourceBuf.push(literalText);
    -          sourceBufLen += literalText.length;
    -        }
    -      }
    -    }
    -    return { source: sourceBuf.join(''), tags: extractedTags };
    -  }
    -
    -  /** True if the given tag contains a class attribute with the nocode class. */
    -  function isNoCodeTag(tag) {
    -    return !!tag
    -        // First canonicalize the representation of attributes
    -        .replace(/\s(\w+)\s*=\s*(?:\"([^\"]*)\"|'([^\']*)'|(\S+))/g,
    -                 ' $1="$2$3$4"')
    -        // Then look for the attribute we want.
    -        .match(/[cC][lL][aA][sS][sS]=\"[^\"]*\bnocode\b/);
    -  }
    -
    -  /**
    -   * Apply the given language handler to sourceCode and add the resulting
    -   * decorations to out.
    -   * @param {number} basePos the index of sourceCode within the chunk of source
    -   *    whose decorations are already present on out.
    -   */
    -  function appendDecorations(basePos, sourceCode, langHandler, out) {
    -    if (!sourceCode) { return; }
    -    var job = {
    -      source: sourceCode,
    -      basePos: basePos
    -    };
    -    langHandler(job);
    -    out.push.apply(out, job.decorations);
    -  }
    -
    -  /** Given triples of [style, pattern, context] returns a lexing function,
    -    * The lexing function interprets the patterns to find token boundaries and
    -    * returns a decoration list of the form
    -    * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
    -    * where index_n is an index into the sourceCode, and style_n is a style
    -    * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
    -    * all characters in sourceCode[index_n-1:index_n].
    -    *
    -    * The stylePatterns is a list whose elements have the form
    -    * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
    -    *
    -    * Style is a style constant like PR_PLAIN, or can be a string of the
    -    * form 'lang-FOO', where FOO is a language extension describing the
    -    * language of the portion of the token in $1 after pattern executes.
    -    * E.g., if style is 'lang-lisp', and group 1 contains the text
    -    * '(hello (world))', then that portion of the token will be passed to the
    -    * registered lisp handler for formatting.
    -    * The text before and after group 1 will be restyled using this decorator
    -    * so decorators should take care that this doesn't result in infinite
    -    * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
    -    * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
    -    * '<script>foo()<\/script>', which would cause the current decorator to
    -    * be called with '<script>' which would not match the same rule since
    -    * group 1 must not be empty, so it would be instead styled as PR_TAG by
    -    * the generic tag rule.  The handler registered for the 'js' extension would
    -    * then be called with 'foo()', and finally, the current decorator would
    -    * be called with '<\/script>' which would not match the original rule and
    -    * so the generic tag rule would identify it as a tag.
    -    *
    -    * Pattern must only match prefixes, and if it matches a prefix, then that
    -    * match is considered a token with the same style.
    -    *
    -    * Context is applied to the last non-whitespace, non-comment token
    -    * recognized.
    -    *
    -    * Shortcut is an optional string of characters, any of which, if the first
    -    * character, gurantee that this pattern and only this pattern matches.
    -    *
    -    * @param {Array} shortcutStylePatterns patterns that always start with
    -    *   a known character.  Must have a shortcut string.
    -    * @param {Array} fallthroughStylePatterns patterns that will be tried in
    -    *   order if the shortcut ones fail.  May have shortcuts.
    -    *
    -    * @return {function (Object)} a
    -    *   function that takes source code and returns a list of decorations.
    -    */
    -  function createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns) {
    -    var shortcuts = {};
    -    var tokenizer;
    -    (function () {
    -      var allPatterns = shortcutStylePatterns.concat(fallthroughStylePatterns);
    -      var allRegexs = [];
    -      var regexKeys = {};
    -      for (var i = 0, n = allPatterns.length; i < n; ++i) {
    -        var patternParts = allPatterns[i];
    -        var shortcutChars = patternParts[3];
    -        if (shortcutChars) {
    -          for (var c = shortcutChars.length; --c >= 0;) {
    -            shortcuts[shortcutChars.charAt(c)] = patternParts;
    -          }
    -        }
    -        var regex = patternParts[1];
    -        var k = '' + regex;
    -        if (!regexKeys.hasOwnProperty(k)) {
    -          allRegexs.push(regex);
    -          regexKeys[k] = null;
    -        }
    -      }
    -      allRegexs.push(/[\0-\uffff]/);
    -      tokenizer = combinePrefixPatterns(allRegexs);
    -    })();
    -
    -    var nPatterns = fallthroughStylePatterns.length;
    -    var notWs = /\S/;
    -
    -    /**
    -     * Lexes job.source and produces an output array job.decorations of style
    -     * classes preceded by the position at which they start in job.source in
    -     * order.
    -     *
    -     * @param {Object} job an object like {@code
    -     *    source: {string} sourceText plain text,
    -     *    basePos: {int} position of job.source in the larger chunk of
    -     *        sourceCode.
    -     * }
    -     */
    -    var decorate = function (job) {
    -      var sourceCode = job.source, basePos = job.basePos;
    -      /** Even entries are positions in source in ascending order.  Odd enties
    -        * are style markers (e.g., PR_COMMENT) that run from that position until
    -        * the end.
    -        * @type {Array.<number|string>}
    -        */
    -      var decorations = [basePos, PR_PLAIN];
    -      var pos = 0;  // index into sourceCode
    -      var tokens = sourceCode.match(tokenizer) || [];
    -      var styleCache = {};
    -
    -      for (var ti = 0, nTokens = tokens.length; ti < nTokens; ++ti) {
    -        var token = tokens[ti];
    -        var style = styleCache[token];
    -        var match = void 0;
    -
    -        var isEmbedded;
    -        if (typeof style === 'string') {
    -          isEmbedded = false;
    -        } else {
    -          var patternParts = shortcuts[token.charAt(0)];
    -          if (patternParts) {
    -            match = token.match(patternParts[1]);
    -            style = patternParts[0];
    -          } else {
    -            for (var i = 0; i < nPatterns; ++i) {
    -              patternParts = fallthroughStylePatterns[i];
    -              match = token.match(patternParts[1]);
    -              if (match) {
    -                style = patternParts[0];
    -                break;
    -              }
    -            }
    -
    -            if (!match) {  // make sure that we make progress
    -              style = PR_PLAIN;
    -            }
    -          }
    -
    -          isEmbedded = style.length >= 5 && 'lang-' === style.substring(0, 5);
    -          if (isEmbedded && !(match && typeof match[1] === 'string')) {
    -            isEmbedded = false;
    -            style = PR_SOURCE;
    -          }
    -
    -          if (!isEmbedded) { styleCache[token] = style; }
    -        }
    -
    -        var tokenStart = pos;
    -        pos += token.length;
    -
    -        if (!isEmbedded) {
    -          decorations.push(basePos + tokenStart, style);
    -        } else {  // Treat group 1 as an embedded block of source code.
    -          var embeddedSource = match[1];
    -          var embeddedSourceStart = token.indexOf(embeddedSource);
    -          var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length;
    -          if (match[2]) {
    -            // If embeddedSource can be blank, then it would match at the
    -            // beginning which would cause us to infinitely recurse on the
    -            // entire token, so we catch the right context in match[2].
    -            embeddedSourceEnd = token.length - match[2].length;
    -            embeddedSourceStart = embeddedSourceEnd - embeddedSource.length;
    -          }
    -          var lang = style.substring(5);
    -          // Decorate the left of the embedded source
    -          appendDecorations(
    -              basePos + tokenStart,
    -              token.substring(0, embeddedSourceStart),
    -              decorate, decorations);
    -          // Decorate the embedded source
    -          appendDecorations(
    -              basePos + tokenStart + embeddedSourceStart,
    -              embeddedSource,
    -              langHandlerForExtension(lang, embeddedSource),
    -              decorations);
    -          // Decorate the right of the embedded section
    -          appendDecorations(
    -              basePos + tokenStart + embeddedSourceEnd,
    -              token.substring(embeddedSourceEnd),
    -              decorate, decorations);
    -        }
    -      }
    -      job.decorations = decorations;
    -    };
    -    return decorate;
    -  }
    -
    -  /** returns a function that produces a list of decorations from source text.
    -    *
    -    * This code treats ", ', and ` as string delimiters, and \ as a string
    -    * escape.  It does not recognize perl's qq() style strings.
    -    * It has no special handling for double delimiter escapes as in basic, or
    -    * the tripled delimiters used in python, but should work on those regardless
    -    * although in those cases a single string literal may be broken up into
    -    * multiple adjacent string literals.
    -    *
    -    * It recognizes C, C++, and shell style comments.
    -    *
    -    * @param {Object} options a set of optional parameters.
    -    * @return {function (Object)} a function that examines the source code
    -    *     in the input job and builds the decoration list.
    -    */
    -  function sourceDecorator(options) {
    -    var shortcutStylePatterns = [], fallthroughStylePatterns = [];
    -    if (options['tripleQuotedStrings']) {
    -      // '''multi-line-string''', 'single-line-string', and double-quoted
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,
    -           null, '\'"']);
    -    } else if (options['multiLineStrings']) {
    -      // 'multi-line-string', "multi-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,  /^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,
    -           null, '\'"`']);
    -    } else {
    -      // 'single-line-string', "single-line-string"
    -      shortcutStylePatterns.push(
    -          [PR_STRING,
    -           /^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,
    -           null, '"\'']);
    -    }
    -    if (options['verbatimStrings']) {
    -      // verbatim-string-literal production from the C# grammar.  See issue 93.
    -      fallthroughStylePatterns.push(
    -          [PR_STRING, /^@\"(?:[^\"]|\"\")*(?:\"|$)/, null]);
    -    }
    -    if (options['hashComments']) {
    -      if (options['cStyleComments']) {
    -        // Stop C preprocessor declarations at an unclosed open comment
    -        shortcutStylePatterns.push(
    -            [PR_COMMENT, /^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,
    -             null, '#']);
    -        fallthroughStylePatterns.push(
    -            [PR_STRING,
    -             /^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,
    -             null]);
    -      } else {
    -        shortcutStylePatterns.push([PR_COMMENT, /^#[^\r\n]*/, null, '#']);
    -      }
    -    }
    -    if (options['cStyleComments']) {
    -      fallthroughStylePatterns.push([PR_COMMENT, /^\/\/[^\r\n]*/, null]);
    -      fallthroughStylePatterns.push(
    -          [PR_COMMENT, /^\/\*[\s\S]*?(?:\*\/|$)/, null]);
    -    }
    -    if (options['regexLiterals']) {
    -      var REGEX_LITERAL = (
    -          // A regular expression literal starts with a slash that is
    -          // not followed by * or / so that it is not confused with
    -          // comments.
    -          '/(?=[^/*])'
    -          // and then contains any number of raw characters,
    -          + '(?:[^/\\x5B\\x5C]'
    -          // escape sequences (\x5C),
    -          +    '|\\x5C[\\s\\S]'
    -          // or non-nesting character sets (\x5B\x5D);
    -          +    '|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+'
    -          // finally closed by a /.
    -          + '/');
    -      fallthroughStylePatterns.push(
    -          ['lang-regex',
    -           new RegExp('^' + REGEXP_PRECEDER_PATTERN + '(' + REGEX_LITERAL + ')')
    -           ]);
    -    }
    -
    -    var keywords = options['keywords'].replace(/^\s+|\s+$/g, '');
    -    if (keywords.length) {
    -      fallthroughStylePatterns.push(
    -          [PR_KEYWORD,
    -           new RegExp('^(?:' + keywords.replace(/\s+/g, '|') + ')\\b'), null]);
    -    }
    -
    -    shortcutStylePatterns.push([PR_PLAIN,       /^\s+/, null, ' \r\n\t\xA0']);
    -    fallthroughStylePatterns.push(
    -        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
    -        [PR_LITERAL,     /^@[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_TYPE,        /^@?[A-Z]+[a-z][A-Za-z_$@0-9]*/, null],
    -        [PR_PLAIN,       /^[a-z_$][a-z_$@0-9]*/i, null],
    -        [PR_LITERAL,
    -         new RegExp(
    -             '^(?:'
    -             // A hex number
    -             + '0x[a-f0-9]+'
    -             // or an octal or decimal number,
    -             + '|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)'
    -             // possibly in scientific notation
    -             + '(?:e[+\\-]?\\d+)?'
    -             + ')'
    -             // with an optional modifier like UL for unsigned long
    -             + '[a-z]*', 'i'),
    -         null, '0123456789'],
    -        [PR_PUNCTUATION, /^.[^\s\w\.$@\'\"\`\/\#]*/, null]);
    -
    -    return createSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns);
    -  }
    -
    -  var decorateSource = sourceDecorator({
    -        'keywords': ALL_KEYWORDS,
    -        'hashComments': true,
    -        'cStyleComments': true,
    -        'multiLineStrings': true,
    -        'regexLiterals': true
    -      });
    -
    -  /** Breaks {@code job.source} around style boundaries in
    -    * {@code job.decorations} while re-interleaving {@code job.extractedTags},
    -    * and leaves the result in {@code job.prettyPrintedHtml}.
    -    * @param {Object} job like {
    -    *    source: {string} source as plain text,
    -    *    extractedTags: {Array.<number|string>} extractedTags chunks of raw
    -    *                   html preceded by their position in {@code job.source}
    -    *                   in order
    -    *    decorations: {Array.<number|string} an array of style classes preceded
    -    *                 by the position at which they start in job.source in order
    -    * }
    -    * @private
    -    */
    -  function recombineTagsAndDecorations(job) {
    -    var sourceText = job.source;
    -    var extractedTags = job.extractedTags;
    -    var decorations = job.decorations;
    -
    -    var html = [];
    -    // index past the last char in sourceText written to html
    -    var outputIdx = 0;
    -
    -    var openDecoration = null;
    -    var currentDecoration = null;
    -    var tagPos = 0;  // index into extractedTags
    -    var decPos = 0;  // index into decorations
    -    var tabExpander = makeTabExpander(window['PR_TAB_WIDTH']);
    -
    -    var adjacentSpaceRe = /([\r\n ]) /g;
    -    var startOrSpaceRe = /(^| ) /gm;
    -    var newlineRe = /\r\n?|\n/g;
    -    var trailingSpaceRe = /[ \r\n]$/;
    -    var lastWasSpace = true;  // the last text chunk emitted ended with a space.
    -
    -    // A helper function that is responsible for opening sections of decoration
    -    // and outputing properly escaped chunks of source
    -    function emitTextUpTo(sourceIdx) {
    -      if (sourceIdx > outputIdx) {
    -        if (openDecoration && openDecoration !== currentDecoration) {
    -          // Close the current decoration
    -          html.push('</span>');
    -          openDecoration = null;
    -        }
    -        if (!openDecoration && currentDecoration) {
    -          openDecoration = currentDecoration;
    -          html.push('<span class="', openDecoration, '">');
    -        }
    -        // This interacts badly with some wikis which introduces paragraph tags
    -        // into pre blocks for some strange reason.
    -        // It's necessary for IE though which seems to lose the preformattedness
    -        // of <pre> tags when their innerHTML is assigned.
    -        // http://stud3.tuwien.ac.at/~e0226430/innerHtmlQuirk.html
    -        // and it serves to undo the conversion of <br>s to newlines done in
    -        // chunkify.
    -        var htmlChunk = textToHtml(
    -            tabExpander(sourceText.substring(outputIdx, sourceIdx)))
    -            .replace(lastWasSpace
    -                     ? startOrSpaceRe
    -                     : adjacentSpaceRe, '$1&nbsp;');
    -        // Keep track of whether we need to escape space at the beginning of the
    -        // next chunk.
    -        lastWasSpace = trailingSpaceRe.test(htmlChunk);
    -        // IE collapses multiple adjacient <br>s into 1 line break.
    -        // Prefix every <br> with '&nbsp;' can prevent such IE's behavior.
    -        var lineBreakHtml = window['_pr_isIE6']() ? '&nbsp;<br />' : '<br />';
    -        html.push(htmlChunk.replace(newlineRe, lineBreakHtml));
    -        outputIdx = sourceIdx;
    -      }
    -    }
    -
    -    while (true) {
    -      // Determine if we're going to consume a tag this time around.  Otherwise
    -      // we consume a decoration or exit.
    -      var outputTag;
    -      if (tagPos < extractedTags.length) {
    -        if (decPos < decorations.length) {
    -          // Pick one giving preference to extractedTags since we shouldn't open
    -          // a new style that we're going to have to immediately close in order
    -          // to output a tag.
    -          outputTag = extractedTags[tagPos] <= decorations[decPos];
    -        } else {
    -          outputTag = true;
    -        }
    -      } else {
    -        outputTag = false;
    -      }
    -      // Consume either a decoration or a tag or exit.
    -      if (outputTag) {
    -        emitTextUpTo(extractedTags[tagPos]);
    -        if (openDecoration) {
    -          // Close the current decoration
    -          html.push('</span>');
    -          openDecoration = null;
    -        }
    -        html.push(extractedTags[tagPos + 1]);
    -        tagPos += 2;
    -      } else if (decPos < decorations.length) {
    -        emitTextUpTo(decorations[decPos]);
    -        currentDecoration = decorations[decPos + 1];
    -        decPos += 2;
    -      } else {
    -        break;
    -      }
    -    }
    -    emitTextUpTo(sourceText.length);
    -    if (openDecoration) {
    -      html.push('</span>');
    -    }
    -    job.prettyPrintedHtml = html.join('');
    -  }
    -
    -  /** Maps language-specific file extensions to handlers. */
    -  var langHandlerRegistry = {};
    -  /** Register a language handler for the given file extensions.
    -    * @param {function (Object)} handler a function from source code to a list
    -    *      of decorations.  Takes a single argument job which describes the
    -    *      state of the computation.   The single parameter has the form
    -    *      {@code {
    -    *        source: {string} as plain text.
    -    *        decorations: {Array.<number|string>} an array of style classes
    -    *                     preceded by the position at which they start in
    -    *                     job.source in order.
    -    *                     The language handler should assigned this field.
    -    *        basePos: {int} the position of source in the larger source chunk.
    -    *                 All positions in the output decorations array are relative
    -    *                 to the larger source chunk.
    -    *      } }
    -    * @param {Array.<string>} fileExtensions
    -    */
    -  function registerLangHandler(handler, fileExtensions) {
    -    for (var i = fileExtensions.length; --i >= 0;) {
    -      var ext = fileExtensions[i];
    -      if (!langHandlerRegistry.hasOwnProperty(ext)) {
    -        langHandlerRegistry[ext] = handler;
    -      } else if ('console' in window) {
    -        console.warn('cannot override language handler %s', ext);
    -      }
    -    }
    -  }
    -  function langHandlerForExtension(extension, source) {
    -    if (!(extension && langHandlerRegistry.hasOwnProperty(extension))) {
    -      // Treat it as markup if the first non whitespace character is a < and
    -      // the last non-whitespace character is a >.
    -      extension = /^\s*</.test(source)
    -          ? 'default-markup'
    -          : 'default-code';
    -    }
    -    return langHandlerRegistry[extension];
    -  }
    -  registerLangHandler(decorateSource, ['default-code']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [],
    -          [
    -           [PR_PLAIN,       /^[^<?]+/],
    -           [PR_DECLARATION, /^<!\w[^>]*(?:>|$)/],
    -           [PR_COMMENT,     /^<\!--[\s\S]*?(?:-\->|$)/],
    -           // Unescaped content in an unknown language
    -           ['lang-',        /^<\?([\s\S]+?)(?:\?>|$)/],
    -           ['lang-',        /^<%([\s\S]+?)(?:%>|$)/],
    -           [PR_PUNCTUATION, /^(?:<[%?]|[%?]>)/],
    -           ['lang-',        /^<xmp\b[^>]*>([\s\S]+?)<\/xmp\b[^>]*>/i],
    -           // Unescaped content in javascript.  (Or possibly vbscript).
    -           ['lang-js',      /^<script\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],
    -           // Contains unescaped stylesheet content
    -           ['lang-css',     /^<style\b[^>]*>([\s\S]*?)(<\/style\b[^>]*>)/i],
    -           ['lang-in.tag',  /^(<\/?[a-z][^<>]*>)/i]
    -          ]),
    -      ['default-markup', 'htm', 'html', 'mxml', 'xhtml', 'xml', 'xsl']);
    -  registerLangHandler(
    -      createSimpleLexer(
    -          [
    -           [PR_PLAIN,        /^[\s]+/, null, ' \t\r\n'],
    -           [PR_ATTRIB_VALUE, /^(?:\"[^\"]*\"?|\'[^\']*\'?)/, null, '\"\'']
    -           ],
    -          [
    -           [PR_TAG,          /^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],
    -           [PR_ATTRIB_NAME,  /^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],
    -           ['lang-uq.val',   /^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],
    -           [PR_PUNCTUATION,  /^[=<>\/]+/],
    -           ['lang-js',       /^on\w+\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-js',       /^on\w+\s*=\s*\'([^\']+)\'/i],
    -           ['lang-js',       /^on\w+\s*=\s*([^\"\'>\s]+)/i],
    -           ['lang-css',      /^style\s*=\s*\"([^\"]+)\"/i],
    -           ['lang-css',      /^style\s*=\s*\'([^\']+)\'/i],
    -           ['lang-css',      /^style\s*=\s*([^\"\'>\s]+)/i]
    -           ]),
    -      ['in.tag']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_ATTRIB_VALUE, /^[\s\S]+/]]), ['uq.val']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CPP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true
    -        }), ['c', 'cc', 'cpp', 'cxx', 'cyc', 'm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': 'null true false'
    -        }), ['json']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': CSHARP_KEYWORDS,
    -          'hashComments': true,
    -          'cStyleComments': true,
    -          'verbatimStrings': true
    -        }), ['cs']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JAVA_KEYWORDS,
    -          'cStyleComments': true
    -        }), ['java']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': SH_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true
    -        }), ['bsh', 'csh', 'sh']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PYTHON_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'tripleQuotedStrings': true
    -        }), ['cv', 'py']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': PERL_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': true
    -        }), ['perl', 'pl', 'pm']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': RUBY_KEYWORDS,
    -          'hashComments': true,
    -          'multiLineStrings': true,
    -          'regexLiterals': true
    -        }), ['rb']);
    -  registerLangHandler(sourceDecorator({
    -          'keywords': JSCRIPT_KEYWORDS,
    -          'cStyleComments': true,
    -          'regexLiterals': true
    -        }), ['js']);
    -  registerLangHandler(
    -      createSimpleLexer([], [[PR_STRING, /^[\s\S]+/]]), ['regex']);
    -
    -  function applyDecorator(job) {
    -    var sourceCodeHtml = job.sourceCodeHtml;
    -    var opt_langExtension = job.langExtension;
    -
    -    // Prepopulate output in case processing fails with an exception.
    -    job.prettyPrintedHtml = sourceCodeHtml;
    -
    -    try {
    -      // Extract tags, and convert the source code to plain text.
    -      var sourceAndExtractedTags = extractTags(sourceCodeHtml);
    -      /** Plain text. @type {string} */
    -      var source = sourceAndExtractedTags.source;
    -      job.source = source;
    -      job.basePos = 0;
    -
    -      /** Even entries are positions in source in ascending order.  Odd entries
    -        * are tags that were extracted at that position.
    -        * @type {Array.<number|string>}
    -        */
    -      job.extractedTags = sourceAndExtractedTags.tags;
    -
    -      // Apply the appropriate language handler
    -      langHandlerForExtension(opt_langExtension, source)(job);
    -      // Integrate the decorations and tags back into the source code to produce
    -      // a decorated html string which is left in job.prettyPrintedHtml.
    -      recombineTagsAndDecorations(job);
    -    } catch (e) {
    -      if ('console' in window) {
    -        console.log(e);
    -        console.trace();
    -      }
    -    }
    -  }
    -
    -  function prettyPrintOne(sourceCodeHtml, opt_langExtension) {
    -    var job = {
    -      sourceCodeHtml: sourceCodeHtml,
    -      langExtension: opt_langExtension
    -    };
    -    applyDecorator(job);
    -    return job.prettyPrintedHtml;
    -  }
    -
    -  function prettyPrint(opt_whenDone) {
    -    var isIE678 = window['_pr_isIE6']();
    -    var ieNewline = isIE678 === 6 ? '\r\n' : '\r';
    -    // See bug 71 and http://stackoverflow.com/questions/136443/why-doesnt-ie7-
    -
    -    // fetch a list of nodes to rewrite
    -    var codeSegments = [
    -        document.getElementsByTagName('pre'),
    -        document.getElementsByTagName('code'),
    -        document.getElementsByTagName('xmp') ];
    -    var elements = [];
    -    for (var i = 0; i < codeSegments.length; ++i) {
    -      for (var j = 0, n = codeSegments[i].length; j < n; ++j) {
    -        elements.push(codeSegments[i][j]);
    -      }
    -    }
    -    codeSegments = null;
    -
    -    var clock = Date;
    -    if (!clock['now']) {
    -      clock = { 'now': function () { return (new Date).getTime(); } };
    -    }
    -
    -    // The loop is broken into a series of continuations to make sure that we
    -    // don't make the browser unresponsive when rewriting a large page.
    -    var k = 0;
    -    var prettyPrintingJob;
    -
    -    function doWork() {
    -      var endTime = (window['PR_SHOULD_USE_CONTINUATION'] ?
    -                     clock.now() + 250 /* ms */ :
    -                     Infinity);
    -      for (; k < elements.length && clock.now() < endTime; k++) {
    -        var cs = elements[k];
    -        if (cs.className && cs.className.indexOf('prettyprint') >= 0) {
    -          // If the classes includes a language extensions, use it.
    -          // Language extensions can be specified like
    -          //     <pre class="prettyprint lang-cpp">
    -          // the language extension "cpp" is used to find a language handler as
    -          // passed to PR_registerLangHandler.
    -          var langExtension = cs.className.match(/\blang-(\w+)\b/);
    -          if (langExtension) { langExtension = langExtension[1]; }
    -
    -          // make sure this is not nested in an already prettified element
    -          var nested = false;
    -          for (var p = cs.parentNode; p; p = p.parentNode) {
    -            if ((p.tagName === 'pre' || p.tagName === 'code' ||
    -                 p.tagName === 'xmp') &&
    -                p.className && p.className.indexOf('prettyprint') >= 0) {
    -              nested = true;
    -              break;
    -            }
    -          }
    -          if (!nested) {
    -            // fetch the content as a snippet of properly escaped HTML.
    -            // Firefox adds newlines at the end.
    -            var content = getInnerHtml(cs);
    -            content = content.replace(/(?:\r\n?|\n)$/, '');
    -
    -            // do the pretty printing
    -            prettyPrintingJob = {
    -              sourceCodeHtml: content,
    -              langExtension: langExtension,
    -              sourceNode: cs
    -            };
    -            applyDecorator(prettyPrintingJob);
    -            replaceWithPrettyPrintedHtml();
    -          }
    -        }
    -      }
    -      if (k < elements.length) {
    -        // finish up in a continuation
    -        setTimeout(doWork, 250);
    -      } else if (opt_whenDone) {
    -        opt_whenDone();
    -      }
    -    }
    -
    -    function replaceWithPrettyPrintedHtml() {
    -      var newContent = prettyPrintingJob.prettyPrintedHtml;
    -      if (!newContent) { return; }
    -      var cs = prettyPrintingJob.sourceNode;
    -
    -      // push the prettified html back into the tag.
    -      if (!isRawContent(cs)) {
    -        // just replace the old html with the new
    -        cs.innerHTML = newContent;
    -      } else {
    -        // we need to change the tag to a <pre> since <xmp>s do not allow
    -        // embedded tags such as the span tags used to attach styles to
    -        // sections of source code.
    -        var pre = document.createElement('PRE');
    -        for (var i = 0; i < cs.attributes.length; ++i) {
    -          var a = cs.attributes[i];
    -          if (a.specified) {
    -            var aname = a.name.toLowerCase();
    -            if (aname === 'class') {
    -              pre.className = a.value;  // For IE 6
    -            } else {
    -              pre.setAttribute(a.name, a.value);
    -            }
    -          }
    -        }
    -        pre.innerHTML = newContent;
    -
    -        // remove the old
    -        cs.parentNode.replaceChild(pre, cs);
    -        cs = pre;
    -      }
    -
    -      // Replace <br>s with line-feeds so that copying and pasting works
    -      // on IE 6.
    -      // Doing this on other browsers breaks lots of stuff since \r\n is
    -      // treated as two newlines on Firefox, and doing this also slows
    -      // down rendering.
    -      if (isIE678 && cs.tagName === 'PRE') {
    -        var lineBreaks = cs.getElementsByTagName('br');
    -        for (var j = lineBreaks.length; --j >= 0;) {
    -          var lineBreak = lineBreaks[j];
    -          lineBreak.parentNode.replaceChild(
    -              document.createTextNode(ieNewline), lineBreak);
    -        }
    -      }
    -    }
    -
    -    doWork();
    -  }
    -
    -  window['PR_normalizedHtml'] = normalizedHtml;
    -  window['prettyPrintOne'] = prettyPrintOne;
    -  window['prettyPrint'] = prettyPrint;
    -  window['PR'] = {
    -        'combinePrefixPatterns': combinePrefixPatterns,
    -        'createSimpleLexer': createSimpleLexer,
    -        'registerLangHandler': registerLangHandler,
    -        'sourceDecorator': sourceDecorator,
    -        'PR_ATTRIB_NAME': PR_ATTRIB_NAME,
    -        'PR_ATTRIB_VALUE': PR_ATTRIB_VALUE,
    -        'PR_COMMENT': PR_COMMENT,
    -        'PR_DECLARATION': PR_DECLARATION,
    -        'PR_KEYWORD': PR_KEYWORD,
    -        'PR_LITERAL': PR_LITERAL,
    -        'PR_NOCODE': PR_NOCODE,
    -        'PR_PLAIN': PR_PLAIN,
    -        'PR_PUNCTUATION': PR_PUNCTUATION,
    -        'PR_SOURCE': PR_SOURCE,
    -        'PR_STRING': PR_STRING,
    -        'PR_TAG': PR_TAG,
    -        'PR_TYPE': PR_TYPE
    -      };
    -})();
    diff --git a/logback-site/src/site/pages/license.html b/logback-site/src/site/pages/license.html
    deleted file mode 100755
    index 5247fd9280..0000000000
    --- a/logback-site/src/site/pages/license.html
    +++ /dev/null
    @@ -1,85 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>License</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -    
    -  </head>
    -  <body>
    -    <script type="text/javascript">prefix='';</script>
    -
    -    <script src="templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <script src="templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script type="text/javascript" src="templates/right.js" ></script>
    -    </div>
    -
    -    <div id="content">
    -      
    -    <div class="section">
    -      <h2>Logback License</h2>
    -    </div>
    -  
    -
    -    <p>As of release 0.9.18, logback source code and binaries are
    -    dual-licensed under the EPL v1.0 and the LGPL 2.1, or more
    -    formally:</p>
    -
    -    <p class="source">Logback: the reliable, generic, fast and flexible logging framework.
    -Copyright (C) 1999-2017, QOS.ch. All rights reserved. 
    -
    -This program and the accompanying materials are dual-licensed under
    -either the terms of the <a href="http://www.eclipse.org/legal/epl-v10.html">Eclipse Public License v1.0</a> as published by
    -the Eclipse Foundation
    - 
    -  or (per the licensee's choosing)
    - 
    -under the terms of the GNU <a href="http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html">Lesser General Public License version 2.1</a>
    -as published by the Free Software Foundation.</p>
    -
    -    <!-- =========================================== -->
    -    
    -    <p>The EPL/LGPL dual-license serves several purposes. The LGPL
    -    license ensures <em>continuity</em> in terms of licensing of the
    -    logback project. Prior to version 0.9.18, logback was licensed
    -    (exclusively) under the LGPL v2.1.  Moreover, since the EPL is
    -    deemed <a
    -    href="http://www.fsf.org/licensing/licenses/index_htm">incompatible</a>
    -    by the Free Software Foundation, the LGPL will allow various
    -    licensees, in particular software distributors who may be already
    -    bound by the terms of the GPL or the LGPL, to distribute our
    -    software.
    -    </p>
    -
    -    <p>On the other hand, the EPL license will placate organizations
    -    which refuse certain restrictions imposed by the LGPL.
    -    </p>
    -    
    -    <p>Please note that logback-classic is intended to be used behind
    -    the SLF4J API, which is licensed under the <a
    -    href="http://www.slf4j.org/license.html">MIT license</a>.
    -    </p>
    -
    -    <p>If you wish to make a significant contribution to the logback
    -    project, you are invited to file a <a href="cla.txt">Contributor
    -    License Agreement</a>. The purpose of this agreement is to
    -    formalize the terms of your contribution and to protect the
    -    project in case of litigation.
    -    </p>
    -
    -    <p>Upon request, we may exempt open-source projects from LGPL and
    -    EPL's reciprocity clauses so that the said projects can develop
    -    logback extensions under the license of their choice. Exemptions
    -    are granted on a case by case basis.</p>
    -    
    -<script src="templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/mailinglist.html b/logback-site/src/site/pages/mailinglist.html
    deleted file mode 100755
    index 0fc75a6e66..0000000000
    --- a/logback-site/src/site/pages/mailinglist.html
    +++ /dev/null
    @@ -1,156 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Mailing lists</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -  </head>
    -  <body>
    -    <script  type="text/javascript">prefix='';</script>
    -
    -    <script src="templates/header.js"  type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="templates/left.js"  type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script type="text/javascript" src="templates/right.js" ></script>
    -    </div>
    -
    -    <div id="content">
    -	
    -    <h2>Project Mailing Lists</h2>
    -
    -		<p>A mailing list is an electronic discussion forum that anyone
    -		can subscribe to. When someone sends an email message to the
    -		mailing list, a copy of that message is broadcast to everyone who
    -		is subscribed to that mailing list. Mailing lists provide a simple
    -		and effective communication mechanism. With potentially thousands
    -		of subscribers, there is a common set of <a
    -		href="http://www.shakthimaan.com/downloads/glv/presentations/mailing-list-etiquette.pdf">etiquette
    -		guidelines</a> that you should observe.
    -		</p>
    -
    -    <h3>Respect the mailing list type</h3>
    -		
    -    <p>The "User" lists are where you can send questions and comments
    -    about configuration, setup, usage and other "user" types of
    -    questions. The "Developer" lists are where you can send questions and
    -    comments about the actual software source code and other issues
    -    related to development.
    -		</p>
    -
    -    <p>Some questions are appropriate for posting on both the "user"
    -    and the "developer" lists. In this case, pick one and only one. Do
    -    not cross post.
    -		</p>
    -
    -    <p><span class="label notice">Note</span> <b>Only subscribers can
    -    post to the logback-user and logback-dev mailing lists.</b> If you
    -    are subscribed and your posts bounce, make sure that the address
    -    you post from matches your subscription address.</p>
    -
    -		<table class="bodyTable">
    -			<tr class="a">
    -				<th>Name</th>
    -				<th>Volume</th>
    -				<th>Subscribe</th>
    -				<th>Unsubscribe</th>
    -				<th>Archives</th>
    -			</tr>
    -			<tr class="b">
    -				<td>QOS.ch announce List</td>
    -				<td>Low</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/listinfo/announce">
    -						Subscribe
    -					</a>
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/options/announce">
    -						Unsubscribe
    -					</a>
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/pipermail/announce/">
    -						qos.ch
    -					</a> 
    -				</td>
    -			</tr>
    -			<tr class="a">
    -				<td>Logback User List</td>
    -				<td>Medium</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/listinfo/logback-user">
    -						Subscribe
    -					</a>
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/options/logback-user">
    -						Unsubscribe
    -					</a> 
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/pipermail/logback-user/">
    -						qos.ch
    -					</a> |
    -					<a
    -						href="http://logback.10977.n7.nabble.com/Users-f3.html">
    -						Nabble
    -					</a> |
    -          <a href="http://markmail.org/search/?q=list%3Ach.qos.logback-user">
    -            MarkLogic</a>
    -				</td>
    -			</tr>
    -			<tr class="b">
    -				<td>Logback Dev List</td>
    -				<td>Medium</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/listinfo/logback-dev">
    -						Subscribe
    -					</a>
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/mailman/options/logback-dev">
    -						Unsubscribe
    -					</a> 
    -				</td>
    -				<td>
    -					<a
    -						href="http://mailman.qos.ch/pipermail/logback-dev/">
    -						qos.ch
    -					</a> |
    -					<a
    -						href="http://logback.10977.n7.nabble.com/Developer-f3683.html">
    -						Nabble
    -					</a> |          
    -					<a
    -						href="http://markmail.org/search/?q=list%3Ach.qos.logback-dev">
    -						MarkLogic
    -					</a>
    -
    -				</td>
    -			</tr>
    -
    -		</table>
    -
    -   <p>&nbsp;</p>
    -
    -	
    -  <script src="templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/.htaccess b/logback-site/src/site/pages/manual/.htaccess
    deleted file mode 100644
    index 493b3bc221..0000000000
    --- a/logback-site/src/site/pages/manual/.htaccess
    +++ /dev/null
    @@ -1,2 +0,0 @@
    -Redirect permanent /contextSelector.html http://logback.qos.ch/manual/loggingSeparation.html
    -Redirect permanent /joran.html http://logback.qos.ch/manual/configuration.html
    diff --git a/logback-site/src/site/pages/manual/appenders.html b/logback-site/src/site/pages/manual/appenders.html
    deleted file mode 100755
    index 3f126ff33c..0000000000
    --- a/logback-site/src/site/pages/manual/appenders.html
    +++ /dev/null
    @@ -1,4582 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 4: Appenders</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"/>    
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">      
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>    
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -
    -    <div id="content">
    -
    -    <h1>Chapter 4: Appenders</h1>
    -
    -    <a href="appenders_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -
    -    <div class="quote">
    -
    -      <p><em>There is so much to tell about the Western country in
    -      that day that it is hard to know where to start. One thing sets
    -      off a hundred others. The problem is to decide which one to tell
    -      first.</em></p>
    -  
    -      <p>&mdash;JOHN STEINBECK, <em>East of Eden</em></p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -    
    -    <h2 class="doAnchor" name="whatIsAnAppender">What is an Appender?</h2>
    -    
    -		<p>Logback delegates the task of writing a logging event to
    -		components called appenders.  Appenders must implement the <a
    -		href="../xref/ch/qos/logback/core/Appender.html"><code>ch.qos.logback.core.Appender</code></a>
    -		interface.  The salient methods of this interface are summarized
    -		below:
    -		</p>
    -		<pre class="prettyprint source">package ch.qos.logback.core;
    -  
    -import ch.qos.logback.core.spi.ContextAware;
    -import ch.qos.logback.core.spi.FilterAttachable;
    -import ch.qos.logback.core.spi.LifeCycle;
    -  
    -
    -public interface Appender&lt;E> extends LifeCycle, ContextAware, FilterAttachable {
    -
    -  public String getName();
    -  public void setName(String name);
    -  <b>void doAppend(E event);</b>
    -  
    -}</pre>
    -
    -	<p>Most of the methods in the <code>Appender</code> interface are
    -	setters and getters. A notable exception is the
    -	<code>doAppend()</code> method taking an object instance of type
    -	<em>E</em> as its only parameter. The actual type of <em>E</em>
    -	will vary depending on the logback module. Within the
    -	logback-classic module <em>E</em> would be of type <a
    -	href="../apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvent</a>
    -	and within the logback-access module it would be of type <a
    -	href="../apidocs/ch/qos/logback/access/spi/AccessEvent.html">AccessEvent</a>.
    -	The <code>doAppend()</code> method is perhaps the most important in
    -	the logback framework.  It is responsible for outputting the logging
    -	events in a suitable format to the appropriate output device.
    -  </p>
    -
    -  <p>Appenders are named entities.  This ensures that they can be
    -  referenced by name, a quality confirmed to be instrumental in
    -  configuration scripts. The <code>Appender</code> interface extends
    -  the <code>FilterAttachable</code> interface. It follows that one or
    -  more filters can be attached to an appender instance. Filters are
    -  discussed in detail in a subsequent chapter.
    -	</p>
    -	
    -	<p>Appenders are ultimately responsible for outputting logging
    -	events.  However, they may delegate the actual formatting of the
    -	event to a <code>Layout</code> or to an <code>Encoder</code> object.
    -	Each layout/encoder is associated with one and only one appender,
    -	referred to as the owning appender. Some appenders have a built-in
    -	or fixed event format. Consequently, they do not require nor have a
    -	layout/encoder. For example, the <code>SocketAppender</code> simply
    -	serializes logging events before transmitting them over the wire.
    -	</p>
    -	
    -	
    -	<h2 class="doAnchor" name="AppenderBase">AppenderBase</h2>
    -	
    -	<p>The <a href="../xref/ch/qos/logback/core/AppenderBase.html">
    -	<code>ch.qos.logback.core.AppenderBase</code></a> class is an
    -	abstract class implementing the <code>Appender</code> interface.  It
    -	provides basic functionality shared by all appenders, such as
    -	methods for getting or setting their name, their activation status,
    -	their layout and their filters.  It is the super-class of all
    -	appenders shipped with logback.  Although an abstract class,
    -	<code>AppenderBase</code> actually implements the
    -	<code>doAppend()</code> method in the <code>Append</code> interface.
    -	Perhaps the clearest way to discuss <code>AppenderBase</code> class
    -	is by presenting an excerpt of actual source code.
    -	</p>
    -	
    -<pre class="prettyprint source">public synchronized void doAppend(E eventObject) {
    -
    -  // prevent re-entry.
    -  if (guard) {
    -    return;
    -  }
    -
    -  try {
    -    guard = true;
    -
    -    if (!this.started) {
    -      if (statusRepeatCount++ &lt; ALLOWED_REPEATS) {
    -        addStatus(new WarnStatus(
    -            "Attempted to append to non started appender [" + name + "].",this));
    -      }
    -      return;
    -    }
    -
    -    if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
    -      return;
    -    }
    -    
    -    // ok, we now invoke the derived class's implementation of append
    -    this.append(eventObject);
    -
    -  } finally {
    -    guard = false;
    -  }
    -}</pre>
    -	
    -	<p>This implementation of the <code>doAppend()</code> method is
    -	synchronized.  It follows that logging to the same appender from
    -	different threads is safe. While a thread, say <em>T</em>, is
    -	executing the <code>doAppend()</code> method, subsequent calls by
    -	other threads are queued until <em>T</em> leaves the
    -	<code>doAppend()</code> method, ensuring <em>T</em>'s exclusive
    -	access to the appender.
    -	</p>
    -
    -  <p>Since such synchronization is not always appropriate, logback
    -  ships with <a
    -  href="../xref/ch/qos/logback/core/UnsynchronizedAppenderBase.html"><code>ch.qos.logback.core.UnsynchronizedAppenderBase</code></a>
    -  which is very similar to the <a
    -  href="../xref/ch/qos/logback/core/AppenderBase.html"><code>AppenderBase</code></a>
    -  class. For the sake of conciseness, we will be discussing
    -  <code>UnsynchronizedAppenderBase</code> in the remainder of this document.
    -  </p>
    -
    -
    -  <p>The first thing the <code>doAppend()</code> method does is to
    -  check whether the guard is set to true. If it is, it immediately
    -  exits. If the guard is not set, it is set to true at the next
    -  statement. The guard ensures that the <code>doAppend()</code> method
    -  will not recursively call itself. Just imagine that a component,
    -  called somewhere beyond the <code>append()</code> method, wants to
    -  log something. Its call could be directed to the very same appender
    -  that just called it resulting in an infinite loop and a stack
    -  overflow.
    -	</p>
    -	
    -	<p>In the following statement we check whether the
    -	<code>started</code> field is true.  If it is not,
    -	<code>doAppend()</code> will send a warning message and return.  In
    -	other words, once an appender is closed, it is impossible to write
    -	to it.  <code>Appender</code> objects implement the
    -	<code>LifeCycle</code> interface, which implies that they implement
    -	<code>start()</code>, <code>stop()</code> and
    -	<code>isStarted()</code> methods.  After setting all the properties of
    -	an appender, Joran, logback's configuration framework, calls the
    -	<code>start()</code> method to signal the appender to activate its
    -	properties.  Depending on its kind, an appender may fail to start if
    -	certain properties are missing or because of interference between
    -	various properties.  For example, given that file creation depends on
    -	truncation mode, <code>FileAppender</code> cannot act on the value
    -	of its <code>File</code> option until the value of the Append option
    -	is also known with certainty. The explicit activation step ensures
    -	that an appender acts on its properties <em>after</em> their values
    -	become known.
    -	</p>
    -	
    -	<p>If the appender could not be started or if it has been stopped, a
    -	warning message will be issued through logback's internal status
    -	management system. After several attempts, in order to avoid
    -	flooding the internal status system with copies of the same warning
    -	message, the <code>doAppend()</code> method will stop issuing these
    -	warnings.
    -  </p>
    -
    -	<p>The next <code>if</code> statement checks the result of the
    -	attached filters.  Depending on the decision resulting from the
    -	filter chain, events can be denied or explicitly accepted.  In
    -	the absence of a decision by the filter chain, events are accepted
    -	by default.
    -	</p>
    -	
    -	<p>The <code>doAppend()</code> method then invokes the derived
    -	classes' implementation of the <code>append()</code> method. This
    -	method does the actual work of appending the event to the
    -	appropriate device.
    -	</p>
    -	
    -  <p>Finally, the guard is released so as to allow a subsequent
    -  invocation of the <code>doAppend()</code> method.
    -  </p>
    -
    -	<p>For the remainder of this manual, we reserve the term "option" or
    -	alternatively "property" for any attribute that is inferred
    -	dynamically using JavaBeans introspection through setter and getter
    -	methods. </p>
    -	
    -	<h1>Logback-core</h1>
    -	
    -	<p>Logback-core lays the foundation upon which the other logback
    -	modules are built. In general, the components in logback-core
    -	require some, albeit minimal, customization. However, in the next
    -	few sections, we describe several appenders which are ready for use
    -	out of the box.
    -  </p>
    -
    -
    -	
    -	<h2 class="doAnchor"
    -	name="OutputStreamAppender">OutputStreamAppender
    -  </h2>
    -	
    -	<p><a
    -	href="../xref/ch/qos/logback/core/OutputStreamAppender.html"><code>OutputStreamAppender</code></a>
    -	appends events to a <code>java.io.OutputStream</code>.  This class
    -	provides basic services that other appenders build upon.  Users do
    -	not usually instantiate <code>OutputStreamAppender</code> objects
    -	directly, since in general the <code>java.io.OutputStream</code>
    -	type cannot be conveniently mapped to a string, as there is no way
    -	to specify the target <code>OutputStream</code> object in a
    -	configuration script.  Simply put, you cannot configure a
    -	<code>OutputStreamAppender</code> from a configuration file.
    -	However, this does not mean that <code>OutputStreamAppender</code>
    -	lacks configurable properties.  These properties are described next.
    -	</p>
    -	
    -  <table class="bodyTable striped">
    -    <tr>
    -      <th>Property Name</th>
    -      <th>Type</th>
    -      <th>Description</th>
    -    </tr>
    -    
    -    <tr>
    -      <td><span class="prop" name="osaEncoder">encoder</span></td>
    -
    -      <td><a
    -      href="../xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a></td>
    -
    -      <td>Determines the manner in which an event is written to the
    -      underlying <code>OutputStreamAppender</code>. Encoders are
    -      described in a <a href="encoders.html">dedicated chapter</a>.
    -			</td>
    -    </tr>
    -   <tr>
    -     <td><span class="prop" name="immediateFlush">immediateFlush</span></td>
    -     <td><code>boolean</code></td>
    -     
    -     <td>The default value for <span
    -     class="option">immediateFlush</span> is 'true'. Immediate
    -     flushing of the output stream ensures that logging events are
    -     immediately written out and will not be lost in case your
    -     application exits without properly closing appenders. On the
    -     other hand, setting this property to 'false' is likely to
    -     quadruple (your mileage may vary) logging throughput. Again, if
    -     <span class="option">immediateFlush</span> is set to 'false' and
    -     if appenders are not closed properly when your application exits,
    -     then logging events not yet written to disk may be lost.
    -     </td>
    -    </tr>
    -	
    -	
    -	</table>
    -    
    -  <p>The <code>OutputStreamAppender</code> is the super-class of three other
    -	appenders, namely <code>ConsoleAppender</code>,
    -	<code>FileAppender</code> which in turn is the super class of
    -	<code>RollingFileAppender</code>. The next figure illustrates the
    -	class diagram for <code>OutputStreamAppender</code> and its subclasses.
    -	</p>
    -	
    -	<img src="images/chapters/appenders/appenderClassDiagram.jpg" alt="A UML diagram showing OutputStreamAppender and sub-classes"/>
    -	
    -
    -	<h2 class="doAnchor" name="ConsoleAppender">ConsoleAppender</h2>
    -	
    -  <p>The <a href="../xref/ch/qos/logback/core/ConsoleAppender.html">
    -  <code>ConsoleAppender</code></a>, as the name indicates, appends on
    -  the console, or more precisely on <em>System.out</em> or
    -  <em>System.err</em>, the former being the default
    -  target. <code>ConsoleAppender</code> formats events with the help of
    -  an encoder specified by the user. Encoders will be discussed in a
    -  subsequent chapter. Both <em>System.out</em> and <em>System.err</em>
    -  are of type <code>java.io.PrintStream</code>.  Consequently, they
    -  are wrapped inside an <code>OutputStreamWriter</code> which buffers
    -  I/O operations.
    -	</p>
    -	
    -	<table class="bodyTable striped">
    -			<tr>
    -			<th>Property Name</th>
    -			<th>Type</th>
    -			<th>Description</th>
    -		</tr>
    -		<tr>
    -			<td><span class="prop" container="conApp">encoder</span></td>
    -      <td>
    -        <a href="../xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -      </td>
    -			<td>See <code>OutputStreamAppender</code> properties.</td>
    -		</tr>
    -		<tr>
    -			<td><span class="prop" container="conApp">target</span></td>
    -			<td><code>String</code></td>
    -			<td>
    -				One of the String values <em>System.out</em> or 
    -				<em>System.err</em>. The default target is <em>System.out</em>.
    -			</td>
    -		</tr>
    -
    -		<tr>
    -			<td><span class="prop" container="conApp">withJansi</span></td>
    -			<td><code>boolean</code></td>
    -			<td>By the default <span class="prop">withJansi</span> property
    -			is set to <code>false</code>.  Setting <span
    -			class="prop">withJansi</span> to <code>true</code> activates the
    -			<a href="http://jansi.fusesource.org/">Jansi</a> library which
    -			provides support for ANSI color codes on Windows machines.  On a
    -			Windows host, if this property is set to true, then you should
    -			put "org.fusesource.jansi:jansi:${jansi.version}" on the class
    -			path. Note that Unix-based operating systems such as Linux and
    -			Mac OS X support ANSI color codes by default.
    -
    -      <p>Under the Eclipse IDE, you might want to try the <a
    -      href="http://www.mihai-nita.net/eclipse/">ANSI in Eclipse
    -      Console</a> plugin.
    -      </p>
    -			</td>
    -		</tr>
    -
    -	</table>
    -	
    -	<p>Here is a sample configuration that uses
    -	<code>ConsoleAppender</code>.
    -	</p>
    -
    -
    -
    -  <p class="example">Example: ConsoleAppender configuration
    -  (logback-examples/src/main/resources/chapters/appenders/conf/logback-Console.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('logback_Console');">View as .groovy</span>
    -
    -  <pre id="logback_Console" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg %n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender></b>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p>After you have set your current path to the
    -   <em>logback-examples</em> directory and <a href="../setup.html">set
    -   up your class path</a>, you can give the above configuration file a
    -   whirl by issuing the following command:
    -	 </p>
    -
    -   <p class="source">java <a
    -   href="../xref/chapters/appenders/ConfigurationTester.html">chapters.appenders.ConfigurationTester</a> src/main/java/chapters/appenders/conf/logback-Console.xml</p>
    -	
    -	
    -   <h2 class="doAnchor" name="FileAppender">FileAppender</h2>
    -	
    -   <p>The <a
    -   href="../xref/ch/qos/logback/core/FileAppender.html"><code>FileAppender</code></a>,
    -   a subclass of <code>OutputStreamAppender</code>, appends log events into
    -   a file. The target file is specified by the <span
    -   class="prop">File</span> option.  If the file already exists, it
    -   is either appended to, or truncated depending on the value of the
    -   <span class="prop">append</span> property.   
    -   </p>
    -	
    -   <table class="bodyTable properties striped">
    -     <tr>
    -       <th>Property Name</th>
    -       <th>Type</th>
    -       <th>Description</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fileApppender">append</span></td>
    -       <td><code>boolean</code></td>
    -       <td>If true, events are appended at the end of an existing
    -       file.  Otherwise, if <span class="prop">append</span> is false,
    -       any existing file is truncated. The <span
    -       class="option">append</span> option is set to true by default.
    -       </td>
    -     </tr>
    -     <tr >
    -       <td><span class="prop" container="fileApppender">encoder</span></td>
    -       <td>
    -         <a href="../xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -       </td>
    -       <td>See <code>OutputStreamAppender</code> properties.</td>
    -     </tr>
    -    
    -   
    -     <tr>
    -       <td><span class="prop"
    -       container="fileApppender">file</span></td>
    -       <td><code>String</code></td>
    -       <td>The name of the file to write to. If the file does not
    -       exist, it is created. On the MS Windows platform users
    -       frequently forget to escape back slashes.  For example, the
    -       value <em>c:\temp\test.log</em> is not likely to be interpreted
    -       properly as <em>'\t'</em> is an escape sequence interpreted as
    -       a single tab character <em>(\u0009)</em>.  Correct values can
    -       be specified as <em>c:/temp/test.log</em> or alternatively as
    -       <em>c:\\temp\\test.log</em>.  The <span
    -       class="prop">File</span> option has no default value.
    -
    -       <p>If the parent directory of the file does not exist,
    -       <code>FileAppender</code> will automatically create it,
    -       including any necessary but nonexistent parent directories.
    -       </p>
    -       </td>
    -     </tr>
    -
    -     <tr>
    -       <td><span class="prop" name="prudent">prudent</span></td>
    -       <td><code>boolean</code></td>
    -
    -       <td>In prudent mode, <code>FileAppender</code> will safely
    -         write to the specified file, even in the presence of other
    -         <code>FileAppender</code> instances running in different
    -         JVMs, potentially running on different hosts. The default
    -         value for prudent mode is <code>false</code>.
    -
    -         <p>Prudent mode can be used in conjunction with
    -         <code>RollingFileAppender</code> although some <a
    -         href="#prudentWithRolling">restrictions apply</a>.</p>
    -
    -         <p>Prudent mode implies that <span
    -         class="prop">append</span> property is automatically set to
    -         true.
    -         </p>
    -
    -         <p>Prudent more relies on exclusive file locks. Experiments
    -         show that file locks approximately triple (x3) the cost of
    -         writing a logging event. On an "average" PC writing to a file
    -         located on a <b>local</b> hard disk, when prudent mode is
    -         off, it takes about 10 microseconds to write a single logging
    -         event. When prudent mode is on, it takes approximately 30
    -         microseconds to output a single logging event. This
    -         translates to logging throughput of 100'000 events per second
    -         when prudent mode is off and approximately 33'000 events per
    -         second in prudent mode.
    -         </p>
    -
    -         <p>Prudent mode effectively serializes I/O operations between
    -         all JVMs writing to the same file. Thus, as the number of
    -         JVMs competing to access a file increases so will the delay
    -         incurred by each I/O operation. As long as the <em>total</em>
    -         number of I/O operations is in the order of 20 log requests
    -         per second, the impact on performance should be
    -         negligible. Applications generating 100 or more I/O
    -         operations per second can see an impact on performance and
    -         should avoid using <span class="prop">prudent</span> mode.
    -         </p>
    -
    -         <p><span class="label">Networked file locks</span> When the
    -         log file is located on a networked file system, the cost of
    -         prudent mode is even greater. Just as importantly, file locks
    -         over a networked file system can be sometimes strongly biased
    -         such that the process currently owning the lock immediately
    -         re-obtains the lock upon its release. Thus, while one process
    -         hogs the lock for the log file, other processes starve
    -         waiting for the lock to the point of appearing deadlocked.
    -         </p>
    -         
    -         <p>The impact of prudent mode is highly dependent on network
    -         speed as well as the OS implementation details. We provide an
    -         very small application called <a
    -         href="https://gist.github.com/2794241">FileLockSimulator</a>
    -         which can help you simulate the behavior of prudent mode in
    -         your environment.
    -         </p>
    -
    -
    -       </td>
    -       
    -     </tr>
    -   </table>
    -	
    -   <p><span class="label notice">Immediate Flush</span> By default,
    -   each log event is immediately flushed to the underlying output
    -   stream. This default approach is safer in the sense that logging
    -   events are not lost in case your application exits without properly
    -   closing appenders. However, for significantly increased logging
    -   throughput, you may want to set the <span
    -   class="prop">immediateFlush</span> property to
    -   <code>false</code>.</p>
    -
    -   <p>Below is an example of a configuration file for
    -   <code>FileAppender</code>:
    -	 </p>
    -
    -   <p class="example">Example: FileAppender configuration
    -   (logback-examples/src/main/resources/chapters/appenders/conf/logback-fileAppender.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('logback-fileAppender');">View as .groovy</span>
    -   <pre id="logback-fileAppender"  class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>testFile.log&lt;/file>
    -    &lt;append>true&lt;/append>
    -    &lt;!-- set immediateFlush to false for much higher logging throughput -->
    -    &lt;immediateFlush>true&lt;/immediateFlush>
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender></b>
    -	
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p>After changing the current directory to
    -   <em>logback-examples</em>, run this example by launching the
    -   following command:
    -   </p>
    -	
    -   <p class="source">java chapters.appenders.ConfigurationTester
    -   src/main/java/chapters/appenders/conf/logback-fileAppender.xml</p>
    -	
    -	
    -   <h3 class="doAnchor" name="uniquelyNamed">Uniquely named files (by
    -   timestamp)</h3>
    -   
    -   <p>During the application development phase or in the case of
    -   short-lived applications, e.g. batch applications, it is desirable
    -   to create a new log file at each new application launch. This is
    -   fairly easy to do with the help of the <code>&lt;timestamp></code>
    -   element. Here's an example.</p>
    -
    -
    -   <p class="example">Example: Uniquely named FileAppender
    -   configuration by timestamp
    -   (logback-examples/src/main/resources/chapters/appenders/conf/logback-timestamp.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('logback-timestamp');">View as .groovy</span>
    -   <pre id="logback-timestamp" class="prettyprint source">&lt;configuration>
    -
    -  &lt;!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
    -       the key "bySecond" into the logger context. This value will be
    -       available to all subsequent configuration elements. -->
    -  <b>&lt;timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/></b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;!-- use the previously created timestamp to create a uniquely
    -         named log file -->
    -    &lt;file><b>log-${bySecond}.txt</b>&lt;/file>
    -    &lt;encoder>
    -      &lt;pattern>%logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -   <p>The timestamp element takes two mandatory attributes <span
    -   class="attr">key</span> and <span class="attr">datePattern</span>
    -   and an optional <span class="attr">timeReference</span>
    -   attribute. The <span class="attr">key</span> attribute is the name
    -   of the key under which the timestamp will be available to
    -   subsequent configuration elements <a
    -   href="configuration.html#variableSubstitution">as a
    -   variable</a>. The <span class="attr">datePattern</span> attribute
    -   denotes the date pattern used to convert the current time (at which
    -   the configuration file is parsed) into a string. The date pattern
    -   should follow the conventions defined in <a
    -   href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>. The
    -   <span class="attr">timeReference</span> attribute denotes the time
    -   reference for the time stamp. The default is the
    -   interpretation/parsing time of the configuration file, i.e. the
    -   current time. However, under certain circumstances it might be
    -   useful to use the context birth time as time reference. This can be
    -   accomplished by setting the <span class="attr">timeReference</span>
    -   attribute to <code>"contextBirth"</code>.
    -   </p>
    -
    -   <p>Experiment with the <code>&lt;timestamp></code> element by
    -   running the command:</p>
    -
    -   <p class="command">java chapters.appenders.ConfigurationTester src/main/resources/chapters/appenders/conf/logback-timestamp.xml</p>
    -
    -   <p>To use the logger context birth date as time reference, you
    -   would set the <span class="attr">timeReference</span> attribute to
    -   "contextBirth" as shown below.</p>
    -
    -
    -   <p class="example">Example: Timestamp using context birth date as time reference
    -   (logback-examples/src/main/resources/chapters/appenders/conf/logback-timestamp-contextBirth.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('logback-timestamp-contextBirth');">View as .groovy</span>   
    -   <pre id="logback-timestamp-contextBirth" class="prettyprint source">&lt;configuration>
    -  &lt;timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" 
    -             <b>timeReference="contextBirth"</b>/>
    -  ...
    -&lt;/configuration></pre>
    -
    -   <h2 class="doAnchor" name="RollingFileAppender">RollingFileAppender
    -   </h2>
    -   
    -   <p><a
    -	 href="../xref/ch/qos/logback/core/rolling/RollingFileAppender.html"><code>RollingFileAppender</code></a>
    -	 extends <code>FileAppender</code> with the capability to rollover log
    -	 files.  For example, <code>RollingFileAppender</code> can log to a
    -	 file named <em>log.txt</em> file and, once a certain condition is
    -	 met, change its logging target to another file.
    -   </p>
    -     
    -   <p>There are two important sub-components that interact with
    -   <code>RollingFileAppender</code>. The first
    -   <code>RollingFileAppender</code> sub-component, namely
    -   <code>RollingPolicy</code>, (<a href="#onRollingPolicies">see
    -   below</a>) is responsible for undertaking the actions required for
    -   a rollover. A second sub-component of
    -   <code>RollingFileAppender</code>, namely
    -   <code>TriggeringPolicy</code>, (<a href="#TriggeringPolicy">see
    -   below</a>) will determine if and exactly when rollover
    -   occurs. Thus, <code>RollingPolicy</code> is responsible for the
    -   <em>what</em> and <code>TriggeringPolicy</code> is responsible for
    -   the <em>when</em>. </p>
    -	
    -   <p>To be of any use, a <code>RollingFileAppender</code> must have
    -   both a <code>RollingPolicy</code> and a
    -   <code>TriggeringPolicy</code> set up. However, if its
    -   <code>RollingPolicy</code> also implements the
    -   <code>TriggeringPolicy</code> interface, then only the former needs
    -   to be specified explicitly.
    -   </p>
    -	
    -   <p>Here are the available properties for <code>RollingFileAppender</code>:</p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>Property Name</th>
    -       <th>Type</th>
    -       <th>Description</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="rfa">file</span></td>
    -       <td><code>String</code></td>
    -       <td>See <code>FileAppender</code> properties.</td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">append</span></td>
    -       <td><code>boolean</code></td>
    -       <td>See <code>FileAppender</code> properties.</td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">encoder</span></td>
    -       <td>
    -         <a href="../xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -       </td>
    -       <td>See <code>OutputStreamAppender</code> properties.</td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="rfa">rollingPolicy</span></td>
    -       <td><code>RollingPolicy</code></td>
    -       <td>This option is the component that will dictate
    -       <code>RollingFileAppender</code>'s behavior when rollover
    -       occurs. See more information below.
    -       </td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">triggeringPolicy</span></td>
    -       <td><code>TriggeringPolicy</code></td>
    -       <td>
    -         This option is the component that will tell 
    -         <code>RollingFileAppender</code> when to activate the rollover
    -         procedure. See more information below.
    -       </td>
    -     </tr>	
    -     <tr>
    -       <td valign="top"><span class="prop" name="prudentWithRolling">prudent</span></td>
    -
    -       <td valign="top"><code>boolean</code></td>
    -
    -       <td  valign="top">
    -         <a
    -         href="#FixedWindowRollingPolicy"><code>FixedWindowRollingPolicy</code></a>
    -         is not supported in prudent mode.
    -
    -         <p> <code>RollingFileAppender</code> supports the prudent
    -         mode in conjunction with <a
    -         href="#TimeBasedRollingPolicy"><code>TimeBasedRollingPolicy</code></a>
    -         albeit with two restrictions.
    -         </p>
    -
    -         <ol>
    -           <li>In prudent mode, file compression is not supported nor
    -           allowed. (We can't have one JVM writing to a file while
    -           another JVM is compressing it.)  </li>
    -           
    -           <li>The <span class="prop">file</span> property of
    -           <code>FileAppender</code> cannot be set and must be left
    -           blank. Indeed, most operating systems do not allow renaming
    -           of a file while another process has it opened.
    -           </li>
    -           
    -         </ol>
    -
    -         See also properties for <code>FileAppender</code>.
    -       </td>
    -     </tr>
    -   </table>
    -	
    -   <h3 class="doAnchor" name="onRollingPolicies">Overview of rolling
    -   policies</h3>
    -	
    -   <p><a
    -   href="../xref/ch/qos/logback/core/rolling/RollingPolicy.html"><code>RollingPolicy</code></a>
    -   is responsible for the rollover procedure which involves file
    -   moving and renaming.</p>
    -	
    -   <p>The <code>RollingPolicy</code> interface is presented below:</p>
    -   
    -   <pre class="prettyprint source">package ch.qos.logback.core.rolling;  
    -
    -import ch.qos.logback.core.FileAppender;
    -import ch.qos.logback.core.spi.LifeCycle;
    -
    -public interface RollingPolicy extends LifeCycle {
    -
    -  <b>public void rollover() throws RolloverFailure;</b>
    -  public String getActiveFileName();
    -  public CompressionMode getCompressionMode();
    -  public void setParent(FileAppender appender);
    -}</pre>
    -
    -   <p>The <code>rollover</code> method accomplishes the work involved
    -   in archiving the current log file.  The
    -   <code>getActiveFileName()</code> method is called to compute the
    -   file name of the current log file (where live logs are written
    -   to). As indicated by <code>getCompressionMode</code> method a
    -   RollingPolicy is also responsible for determining the compression
    -   mode. Lastly, a <code>RollingPolicy</code> is given a reference to
    -   its parent via the <code>setParent</code> method.
    -   </p>
    -
    -   <!-- =================
    -        ================= -->
    -
    -	
    -   <h4 class="doAnchor" name="TimeBasedRollingPolicy">TimeBasedRollingPolicy </h4>
    -
    -   <p><a
    -   href="../xref/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.html">
    -   <code>TimeBasedRollingPolicy</code></a> is possibly the most
    -   popular rolling policy. It defines a rollover policy based on time,
    -   for example by day or by month.
    -   <code>TimeBasedRollingPolicy</code> assumes the responsibility for
    -   rollover as well as for the triggering of said rollover. Indeed,
    -   <code>TimeBasedTriggeringPolicy</code> implements <em>both</em>
    -   <code>RollingPolicy</code> and <code>TriggeringPolicy</code>
    -   interfaces.
    -   </p>
    -
    -   <p><code>TimeBasedRollingPolicy</code>'s configuration takes one
    -   mandatory <span class="prop">fileNamePattern</span> property and
    -   several optional properties.
    -   </p>
    -
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>Property Name</th>
    -       <th>Type</th>
    -       <th>Description</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="tbrp">fileNamePattern</span></td>
    -       <td><code>String</code></td>
    -       <td>
    -         The mandatory <span class="prop">fileNamePattern</span>
    -         property defines the name of the rolled-over (archived) log
    -         files. Its value should consist of the name of the file, plus
    -         a suitably placed <em>%d</em> conversion specifier.  The
    -         <em>%d</em> conversion specifier may contain a date-and-time
    -         pattern as specified by the
    -         <code>java.text.SimpleDateFormat</code> class.  If the
    -         date-and-time pattern is omitted, then the default pattern
    -         <em>yyyy-MM-dd</em> is assumed. <b>The rollover period is
    -         inferred from the value of <span
    -         class="prop">fileNamePattern</span>.</b>
    -
    -
    -         <p>Note that the <span class="prop">file</span> property in
    -         <code>RollingFileAppender</code> (the parent of
    -         <code>TimeBasedRollingPolicy</code>) can be either set or
    -         omitted. By setting the <span class="prop">file</span>
    -         property of the containing <code>FileAppender</code>, you can
    -         decouple the location of the active log file and the location
    -         of the archived log files. The current logs will be always
    -         targeted at the file specified by the <span
    -         class="prop">file</span> property. It follows that the name
    -         of the currently active log file will not change over
    -         time. However, if you choose to omit the <span
    -         class="prop">file</span> property, then the active file
    -         will be computed anew for each period based on the value of
    -         <span class="prop">fileNamePattern</span>.  The examples
    -         below should clarify this point.
    -         </p>
    -
    -         <p>The date-and-time pattern, as found within the accolades
    -         of %d{} follow java.text.SimpleDateFormat conventions. The
    -         forward slash '/' or backward slash '\' characters anywhere
    -         within the <span class="option">fileNamePattern</span>
    -         property or within the date-and-time pattern will be
    -         interpreted as directory separators.
    -         </p>
    -
    -         <h5>Multiple %d specifiers</h5> 
    -
    -         <p>It is possible to specify multiple %d specifiers but only
    -         one of which can be primary, i.e. used to infer the rollover
    -         period. All other tokens <em>must</em> be marked as auxiliary
    -         by passing the 'aux' parameter (see examples below).</p>
    -         
    -         <p>Multiple %d specifiers allow you to organize archive files
    -         in a folder structure different than that of the roll-over
    -         period. For example, the file name pattern shown below
    -         organizes log folders by year and month but roll-over log
    -         files every day at midnight.</p>
    -         
    -         <pre>/var/log/<b>%d{yyyy/MM, aux}</b>/myapplication.<b>%d{yyyy-MM-dd}</b>.log</pre>
    -
    -         <h5>TimeZone</h5> 
    -
    -         <p>Under certain circumstances, you might wish to roll-over
    -         log files according to a clock in a timezone different than
    -         that of the host. It is possible to pass a timezone argument
    -         following the date-and-time pattern within the %d conversion
    -         specifier. For example:</p>
    -
    -         <pre>aFolder/test.<b>%d</b>{yyyy-MM-dd-HH, <b>UTC</b>}.log</pre>
    -
    -         <p>If the specified timezone identifier is unknown or
    -         misspelled, the GMT timezone is assumed as dictated by the <a
    -         href="http://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)">TimeZone.getTimeZone(String)</a>
    -         method specification.
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="tbrp">maxHistory</span></td>
    -       <td>int</td>
    -       <td>The optional <span class="prop">maxHistory</span> property
    -       controls the maximum number of archive files to keep,
    -       asynchronously deleting older files. For example, if you
    -       specify monthly rollover, and set maxHistory to 6, then 6
    -       months worth of archives files will be kept with files older
    -       than 6 months deleted. Note as old archived log files are
    -       removed, any folders which were created for the purpose of log
    -       file archiving will be removed as appropriate.
    -       </td>
    -     </tr>
    -
    -     <tr>
    -       <td><span class="prop" container="tbrp">totalSizeCap</span></td>
    -       <td>int</td>
    -       <td><p>The optional <span class="prop">totalSizeCap</span>
    -       property controls the total size of all archive files. Oldest
    -       archives are deleted asynchronously when the total size cap is
    -       exceeded. The <span class="prop">totalSizeCap</span> property
    -       requires <span class="option">maxHistory</span> property to be
    -       set as well.  Moreover, the "max history" restriction is always
    -       applied first and the "total size cap" restriction applied
    -       second. </p>
    -       </td>
    -     </tr>
    -
    -     <tr >
    -       <td><span class="prop"
    -       container="tbrp">cleanHistoryOnStart</span></td>
    -       <td>boolean</td>
    -       <td>
    -         <p>If set to true, archive removal will be executed on
    -         appender start up. By default this property is set to
    -         false. </p>
    -
    -         <p>Archive removal is normally performed during roll
    -         over. However, some applications may not live long enough for
    -         roll over to be triggered. It follows that for such
    -         short-lived applications archive removal may never get a
    -         chance to execute.  By setting <span
    -         class="prop">cleanHistoryOnStart</span> to true, archive
    -         removal is performed at appender start up.</p>
    -       </td>
    -     </tr>
    -   </table>
    -
    -
    -   <p>Here are a few <code>fileNamePattern</code> values with an
    -   explanation of their effects.</p>
    -
    -  
    -   
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>
    -         <span class="prop">fileNamePattern</span>
    -       </th>
    -       <th>Rollover schedule</th>
    -       <th>Example</th>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo.%d</em>
    -       </td>
    -       <td>Daily rollover (at midnight). Due to the omission of the
    -       optional time and date pattern for the <em>%d</em> token
    -       specifier, the default pattern of <em>yyyy-MM-dd</em> is
    -       assumed, which corresponds to daily rollover.
    -       </td>
    -
    -       <td>
    -         <p><span class="prop">file</span> property not set: During November
    -         23rd, 2006, logging output will go to the file
    -         <em>/wombat/foo.2006-11-23</em>.  At midnight and for the
    -         rest of the 24th, logging output will be directed to
    -         <em>/wombat/foo.2006-11-24</em>.
    -       </p>
    -
    -         <p><span class="prop">file</span> property set to
    -         <em>/wombat/foo.txt</em>: During November 23rd, 2006, logging
    -         output will go to the file <em>/wombat/foo.txt</em>. At
    -         midnight, <em>foo.txt</em> will be renamed as
    -         <em>/wombat/foo.2006-11-23</em>. A new
    -         <em>/wombat/foo.txt</em> file will be created and for the
    -         rest of November 24th logging output will be directed to
    -         <em>foo.txt</em>.
    -       </p>
    -
    -       </td>
    -     </tr>
    -     
    -
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/%d{yyyy/MM}/foo.txt</em>
    -       </td>
    -       <td>Rollover at the beginning of each month.</td>
    -       <td>
    -         <p><span class="prop">file</span> property not set: During
    -         the month of October 2006, logging output will go to
    -         <em>/wombat/2006/10/foo.txt</em>.  After midnight of October
    -         31st and for the rest of November, logging output will be
    -         directed to <em>/wombat/2006/11/foo.txt</em>.
    -         </p>
    -
    -         <p><span class="prop">file</span> property set to
    -         <em>/wombat/foo.txt</em>: The active log file will always be
    -         <em>/wombat/foo.txt</em>. During the month of October 2006,
    -         logging output will go to <em>/wombat/foo.txt</em>. At
    -         midnight of October 31st, <em>/wombat/foo.txt</em> will be
    -         renamed as <em>/wombat/2006/10/foo.txt</em>. A new
    -         <em>/wombat/foo.txt</em> file will be created where logging
    -         output will go for the rest of November. At midnight of
    -         November 30th, <em>/wombat/foo.txt</em> will be renamed as
    -         <em>/wombat/2006/11/foo.txt</em> and so on.
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo.%d{yyyy-ww}.log</em>
    -       </td>
    -       
    -       <td>Rollover at the first day of each week. Note that the first
    -       day of the week depends on the locale.</td>
    -       
    -       <td>Similar to previous cases, except that rollover will occur
    -       at the beginning of every new week.  
    -       </td>     
    -     </tr>	
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo%d{yyyy-MM-dd_HH}.log</em>
    -       </td>
    -       <td>Rollover at the top of each hour.</td>
    -       <td>Similar to previous cases, except that rollover will occur
    -       at the top of every hour.
    -       </td>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo%d{yyyy-MM-dd_HH-mm}.log</em>
    -       </td>
    -       <td>Rollover at the beginning of every minute.</td>
    -       <td>Similar to previous cases, except that rollover will occur
    -       at the beginning of every minute.  
    -       </td>     
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo%d{yyyy-MM-dd_HH-mm, UTC}.log</em>
    -       </td>
    -       <td>Rollover at the beginning of every minute.</td>
    -       <td>Similar to previous cases, except that file names will be
    -       expressed in UTC.
    -       </td>     
    -     </tr>
    -
    -
    -     <tr>
    -       <td class="small">
    -         <em>/foo/%d{yyyy-MM,<b>aux</b>}/%d.log</em>
    -       </td>
    -       <td>Rollover daily. Archives located under a folder containing
    -       year and month.
    -       </td>
    -       <td>In this example, the first %d token is marked as
    -       <b>aux</b>iliary. The second %d token, with time and date
    -       pattern omitted, is then assumed to be primary. Thus, rollover
    -       will occur daily (default for %d) and the folder name will
    -       depend on the year and month. For example, during the month of
    -       November 2006, archived files will all placed under the
    -       /foo/2006-11/ folder, e.g <em>/foo/2006-11/2006-11-14.log</em>.
    -       </td>     
    -       
    -     </tr>
    -   </table>
    -   
    -   <p>Any forward or backward slash characters are interpreted as
    -   folder (directory) separators. Any required folder will be created
    -   as necessary. You can thus easily place your log files in separate
    -   folders.
    -   </p>
    -
    -
    - 	 <p><code>TimeBasedRollingPolicy</code> supports automatic file
    - 	 compression.  This feature is enabled if the value of the <span
    - 	 class="prop">fileNamePattern</span> option ends with <em>.gz</em>
    - 	 or <em>.zip</em>.
    -   </p>
    -
    -   <table class="bodyTable striped">
    -     <tr class="a">
    -       <th><span class="prop">fileNamePattern</span></th>
    -       <th>Rollover schedule</th>
    -       <th>Example</th>
    -     </tr>
    -     <tr>
    -       <td><em>/wombat/foo.%d.gz</em></td>
    -       <td>Daily rollover (at midnight) with automatic GZIP compression of the 
    -       archived files.</td>
    -       <td>
    -         <p><span class="prop">file</span> property not set: During
    -         November 23rd, 2009, logging output will go to the file
    -         <em>/wombat/foo.2009-11-23</em>. However, at midnight that
    -         file will be compressed to become
    -         <em>/wombat/foo.2009-11-23.gz</em>.  For the 24th of November,
    -         logging output will be directed to
    -         <em>/wombat/folder/foo.2009-11-24</em> until it's rolled over
    -         at the beginning of the next day.
    -         </p>
    -         
    -         <p><span class="prop">file</span> property set to
    -         /wombat/foo.txt: During November 23rd, 2009, logging output
    -         will go to the file <em>/wombat/foo.txt</em>. At midnight that
    -         file will be compressed and renamed as
    -         <em>/wombat/foo.2009-11-23.gz</em>.  A new
    -         <em>/wombat/foo.txt</em> file will be created where logging
    -         output will go for the rest of November 24rd. At midnight
    -         November 24th, <em>/wombat/foo.txt</em> will be compressed and
    -         renamed as <em>/wombat/foo.2009-11-24.gz</em> and so on.
    -         </p>
    -       </td>
    -     </tr>
    -   </table>
    -   
    -   <p>The <span class="prop">fileNamePattern</span> serves a dual
    -   purpose. First, by studying the pattern, logback computes the
    -   requested rollover periodicity. Second, it computes each archived
    -   file's name. Note that it is possible for two different patterns to
    -   specify the same periodicity. The patterns <em>yyyy-MM</em> and
    -   <em>yyyy@MM</em> both specify monthly rollover, although the
    -   resulting archive files will carry different names.
    -   </p>
    -
    -	 <p>By setting the <span class="prop">file</span> property you can
    -	 decouple the location of the active log file and the location of
    -	 the archived log files. The logging output will be targeted into
    -	 the file specified by the <span class="prop">file</span>
    -	 property. It follows that the name of the active log file will not
    -	 change over time. However, if you choose to omit the <span
    -	 class="prop">file</span> property, then the active file will be
    -	 computed anew for each period based on the value of <span
    -	 class="prop">fileNamePattern</span>. By leaving the <span
    -	 class="prop">file</span> option unset you can avoid file <a
    -	 href="../codes.html#renamingError">renaming errors</a> which occur
    -	 while there exist external file handles referencing log files during
    -	 roll over.
    -   </p>
    -	
    -   <p>The <span class="prop">maxHistory</span> property controls the
    -   maximum number of archive files to keep, deleting older files. For
    -   example, if you specify monthly rollover, and set <span
    -   class="prop">maxHistory</span> to 6, then 6 months worth of
    -   archives files will be kept with files older than 6 months
    -   deleted. Note as old archived log files are removed, any folders
    -   which were created for the purpose of log file archiving will be
    -   removed as appropriate.
    -   </p>
    -
    -   <p>For various technical reasons, rollovers are not clock-driven
    -   but depend on the arrival of logging events. For example, on 8th of
    -   March 2002, assuming the <span
    -   class="prop">fileNamePattern</span> is set to <em>yyyy-MM-dd</em>
    -   (daily rollover), the arrival of the first event after midnight
    -   will trigger a rollover. If there are no logging events during, say
    -   23 minutes and 47 seconds after midnight, then rollover will
    -   actually occur at 00:23'47 AM on March 9th and not at 0:00 AM.
    -   Thus, depending on the arrival rate of events, rollovers might be
    -   triggered with some latency.  However, regardless of the delay, the
    -   rollover algorithm is known to be correct, in the sense that all
    -   logging events generated during a certain period will be output in
    -   the correct file delimiting that period.
    -   </p>
    -	
    -   <p>Here is a sample configuration for
    -   <code>RollingFileAppender</code> in conjunction with a
    -   <code>TimeBasedRollingPolicy</code>.
    -   </p>
    -	
    -   <p class="example">Example: Sample configuration of a
    -   <code>RollingFileAppender</code> using a
    -   <code>TimeBasedRollingPolicy</code>
    -   (logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingTimeBased.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('logback-RollingTimeBased');">View as .groovy</span>
    -   <pre id="logback-RollingTimeBased" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    -    &lt;file>logFile.log&lt;/file>
    -    <b>&lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    -      &lt;!-- daily rollover -->
    -      &lt;fileNamePattern>logFile.%d{yyyy-MM-dd}.log&lt;/fileNamePattern>
    -
    -      &lt;!-- keep 30 days' worth of history capped at 3GB total size -->
    -      &lt;maxHistory>30&lt;/maxHistory>
    -      &lt;totalSizeCap>3GB&lt;/totalSizeCap>
    -
    -    &lt;/rollingPolicy></b>
    -
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender> 
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -    <p>The next configuration sample illustrates the use of
    -    <code>RollingFileAppender</code> associated with
    -    <code>TimeBasedRollingPolicy</code> in <span class="prop">prudent</span>
    -    mode.
    -    </p>
    -
    -   <p class="example">Example: Sample configuration of a
    -   <code>RollingFileAppender</code> using a
    -   <code>TimeBasedRollingPolicy</code>
    -   (logback-examples/src/main/resources/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('logback-PrudentTimeBasedRolling');">View as .groovy</span>
    -  <pre id="logback-PrudentTimeBasedRolling" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    -    <b>&lt;!-- Support multiple-JVM writing to the same log file --></b>
    -    <b>&lt;prudent>true&lt;/prudent></b>
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
    -      &lt;fileNamePattern>logFile.%d{yyyy-MM-dd}.log&lt;/fileNamePattern>
    -      &lt;maxHistory>30&lt;/maxHistory> 
    -      &lt;totalSizeCap>3GB&lt;/totalSizeCap>
    -    &lt;/rollingPolicy>
    -
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender> 
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  
    -
    -    <h3 class="doAnchor" name="SizeAndTimeBasedRollingPolicy">Size and
    -    time based rolling policy</h3>
    -
    -    
    -     <p>Sometimes you may wish to archive files essentially by date but
    -    at the same time limit the size of each log file, in particular if
    -    post-processing tools impose size limits on the log files. In
    -    order to address this requirement, logback ships with
    -    <code>SizeAndTimeBasedRollingPolicy</code>.</p>
    -
    -    <p>Note that <code>TimeBasedRollingPolicy</code> already allows
    -    limiting the combined size of archived log files. If you only wish
    -    to limit the combined size of log archives, then
    -    <code>TimeBasedRollingPolicy</code> described above and setting
    -    the <a href="#tbrpTotalSizeCap"><span
    -    class="option">totalSizeCap</span></a> property should be amply
    -    sufficent.
    -    </p>
    -
    -    <p>Here is a sample configuration file demonstrating time and size
    -    based log file archiving.</p>
    -    
    -  <p class="example">Example: Sample configuration for
    -  <code>SizeAndTimeBasedFNATP</code> 
    -  (logback-examples/src/main/resources/chapters/appenders/conf/logback-sizeAndTime.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('logback-sizeAndTime');">View as .groovy</span>
    -  <pre id="logback-sizeAndTime" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender">
    -    &lt;file>mylog.txt&lt;/file>
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
    -      &lt;!-- rollover daily -->
    -      &lt;fileNamePattern><b>mylog-%d{yyyy-MM-dd}.<span class="big">%i</span>.txt</b>&lt;/fileNamePattern>
    -       <b>&lt;!-- each file should be at most 100MB, keep 60 days worth of history, but at most 20GB --></b>
    -       <b>&lt;maxFileSize>100MB&lt;/maxFileSize></b>    
    -       &lt;maxHistory>60&lt;/maxHistory>
    -       &lt;totalSizeCap>20GB&lt;/totalSizeCap>
    -    &lt;/rollingPolicy>
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="ROLLING" />
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -    
    -    <p>Note the "%i" conversion token in addition to "%d". <b>Both the
    -    %i and %d tokens are mandatory.</b> Each time the current log file
    -    reaches <span class="prop">maxFileSize</span> before the current
    -    time period ends, it will be archived with an increasing index,
    -    starting at 0.</p>
    -
    -    <p>Size and time based archiving supports deletion of old archive
    -    files. You need to specify the number of periods to preserve with
    -    the <span class="prop">maxHistory</span> property. When your
    -    application is stopped and restarted, logging will continue at the
    -    correct location, i.e. at the largest index number for the current
    -    period.
    -    </p>
    -
    -    <p>In versions prior to 1.1.7, this document mentioned a component
    -    called <code>SizeAndTimeBasedFNATP</code>. However, given that
    -    <code>SizeAndTimeBasedFNATP</code> offers a simpler configuration
    -    structure, we no longer document
    -    <code>SizeAndTimeBasedFNATP</code>.  Nevertheless, earlier
    -    configuration files using <code>SizeAndTimeBasedFNATP</code> will
    -    continue to work just fine. In fact,
    -    <code>SizeAndTimeBasedRollingPolicy</code> is implemented with a
    -    <code>SizeAndTimeBasedFNATP</code> subcomponent.</p>
    -
    -   <h4 class="doAnchor" name="FixedWindowRollingPolicy">FixedWindowRollingPolicy</h4>
    -
    -   <p>When rolling over, <a
    -   href="../xref/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.html">
    -   <code>FixedWindowRollingPolicy</code></a> renames files according
    -   to a fixed window algorithm as described below.
    -   </p>
    -
    -   <p>The <span class="prop">fileNamePattern</span> option
    -   represents the file name pattern for the archived (rolled over) log
    -   files.  This option is required and must include an integer token
    -   <em>%i</em> somewhere within the pattern.
    -   </p>
    -	
    -   <p>Here are the available properties for
    -   <code>FixedWindowRollingPolicy</code>
    -   </p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>Property Name</th>
    -       <th>Type</th>
    -       <th>Description</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">minIndex</span></td>
    -       <td><code>int</code></td>
    -       <td>
    -         <p>This option represents the lower bound for the window's
    -         index.
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">maxIndex</span></td>
    -       <td><code>int</code></td>
    -       <td>
    -         <p>This option represents the upper bound for the window's
    -         index.
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">fileNamePattern</span></td>
    -       <td><code>String</code></td>
    -       <td>
    -         <p>This option represents the pattern that will be followed
    -         by the <code>FixedWindowRollingPolicy</code> when renaming
    -         the log files. It must contain the string <em>%i</em>, which
    -         will indicate the position where the value of the current
    -         window index will be inserted.
    -         </p>
    -         <p>For example, using <em>MyLogFile%i.log</em> associated
    -         with minimum and maximum values of <em>1</em> and <em>3</em>
    -         will produce archive files named <em>MyLogFile1.log</em>,
    -         <em>MyLogFile2.log</em> and <em>MyLogFile3.log</em>.
    -         </p>
    -         <p>Note that file compression is also specified via this
    -         property. For example, <span
    -         class="prop">fileNamePattern</span> set to
    -         <em>MyLogFile%i.log.zip</em> means that archived files must be
    -         compressed using the <em>zip</em> format; <em>gz</em> format
    -         is also supported.
    -         </p>
    -       </td>
    -     </tr>			
    -   </table>
    -   
    -   <p>Given that the fixed window rolling policy requires as many file
    -   renaming operations as the window size, large window sizes are
    -   strongly discouraged. When large values are specified by the user,
    -   the current implementation will automatically reduce the window
    -   size to 20.
    -   </p>
    -
    -   <p>Let us go over a more concrete example of the fixed window
    -   rollover policy. Suppose that <span class="prop">minIndex</span> is
    -   set to <em>1</em>, <span class="prop">maxIndex</span> set to
    -   <em>3</em>, <span class="prop">fileNamePattern</span> property set
    -   to <em>foo%i.log</em>, and that <span class="prop">file</span>
    -   property is set to <em>foo.log</em>.
    -   </p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>Number of rollovers</th>
    -       <th>Active output target</th>
    -       <th>Archived log files</th>
    -       <th>Description</th>
    -     </tr>
    -		<tr>
    -			<td>0</td>
    -			<td>foo.log</td>
    -			<td>-</td>
    -			<td>No rollover has happened yet, logback logs into the initial
    -			file.
    -			</td>
    -     </tr>		
    -     <tr>
    -       <td>1</td>
    -       <td>foo.log</td>
    -       <td>foo1.log</td>
    -       <td>First rollover. <em>foo.log</em> is renamed as
    -       <em>foo1.log</em>. A new <em>foo.log</em> file is created and
    -       becomes the active output target.
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>2</td>
    -       <td>foo.log</td>
    -       <td>foo1.log, foo2.log</td>
    -       <td>Second rollover. <em>foo1.log</em> is renamed as
    -       <em>foo2.log</em>.  <em>foo.log</em> is renamed as
    -       <em>foo1.log</em>. A new <em>foo.log</em> file is created and
    -       becomes the active output target.
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>3</td>
    -       <td>foo.log</td>
    -       <td>foo1.log, foo2.log, foo3.log</td>
    -       <td>Third rollover.  <em>foo2.log</em> is renamed as
    -       <em>foo3.log</em>. <em>foo1.log</em> is renamed as
    -       <em>foo2.log</em>.  <em>foo.log</em> is renamed as
    -       <em>foo1.log</em>. A new <em>foo.log</em> file is created and
    -       becomes the active output target.
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>4</td>
    -       <td>foo.log</td>
    -       <td>foo1.log, foo2.log, foo3.log</td>
    -       <td>In this and subsequent rounds, the rollover begins by
    -       deleting <em>foo3.log</em>. Other files are renamed by
    -       incrementing their index as shown in previous steps. In this and
    -       subsequent rollovers, there will be three archive logs and one
    -       active log file.
    -       </td>
    -     </tr>
    -   </table>
    -	
    -   <p>The configuration file below gives an example of configuring
    -   <code>RollingFileAppender</code> and
    -   <code>FixedWindowRollingPolicy</code>. Note that the <span
    -   class="prop">File</span> option is mandatory even if it contains
    -   some of the same information as conveyed with the <span
    -   class="prop">fileNamePattern</span> option.
    -   </p>
    -	
    -   <p class="example">Example: Sample configuration of a <code>RollingFileAppender</code> using a 
    -   <code>FixedWindowRollingPolicy</code> (logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingFixedWindow.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('logback-RollingFixedWindow');">View as .groovy</span>
    -   <pre id="logback-RollingFixedWindow" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    -    <b>&lt;file>test.log&lt;/file></b>
    -
    -    <b>&lt;rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    -      &lt;fileNamePattern>tests.%i.log.zip&lt;/fileNamePattern>
    -      &lt;minIndex>1&lt;/minIndex>
    -      &lt;maxIndex>3&lt;/maxIndex>
    -    &lt;/rollingPolicy></b>
    -
    -    &lt;triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    -      &lt;maxFileSize>5MB&lt;/maxFileSize>
    -    &lt;/triggeringPolicy>
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -	
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -		<h2>
    -      <a name="TriggeringPolicy" href="#TriggeringPolicy">Overview of
    -      triggering policies</a>
    -    </h2>
    -		
    -		<p><a
    -		href="../xref/ch/qos/logback/core/rolling/TriggeringPolicy.html"><code>TriggeringPolicy</code></a>
    -		implementations are responsible for instructing the
    -		<code>RollingFileAppender</code> when to rollover.</p>
    -		
    -		<p>The <code>TriggeringPolicy</code> interface contains only one
    -		method.</p>
    -	
    -    <pre class="prettyprint source">package ch.qos.logback.core.rolling;
    -
    -import java.io.File;
    -import ch.qos.logback.core.spi.LifeCycle;
    -
    -public interface TriggeringPolicy&lt;E&gt; extends LifeCycle {
    -
    -  <b>public boolean isTriggeringEvent(final File activeFile, final &lt;E&gt; event);</b>
    -}</pre>
    -
    -		<p>The <code>isTriggeringEvent()</code> method takes as parameters
    -		the active file and the logging event currently being
    -		processed. The concrete implementation determines whether the
    -		rollover should occur or not, based on these parameters.
    -		</p>
    -
    -    <p>The most widely-used triggering policy, namely
    -    <code>TimeBasedRollingPolicy</code> which also doubles as a
    -    rolling policy, was already <a
    -    href="#TimeBasedRollingPolicy">discussed earlier</a> along with
    -    other rolling policies. </p>
    -		
    -		<h4><a name="SizeBasedTriggeringPolicy"
    -		href="#SizeBasedTriggeringPolicy">SizeBasedTriggeringPolicy</a></h4>
    -
    -		<p><a
    -		href="../xref/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.html">
    -		<code>SizeBasedTriggeringPolicy</code></a> looks at the size of the
    -		currently active file. If it grows larger than the specified size,
    -		it will signal the owning <code>RollingFileAppender</code> to
    -		trigger the rollover of the existing active file.
    -		</p>
    -
    -		<p><code>SizeBasedTriggeringPolicy</code> accepts only one
    -		parameter, namely <span class="prop">maxFileSize</span>, with a
    -		default value of 10 MB.
    -		</p>
    -
    -		<p>The <span class="prop">maxFileSize</span> option can be
    -		specified in bytes, kilobytes, megabytes or gigabytes by suffixing
    -		a numeric value with <em>KB</em>, <em>MB</em> and respectively
    -		<em>GB</em>. For example, <em>5000000</em>, <em>5000KB</em>,
    -		<em>5MB</em> and <em>2GB</em> are all valid values, with the first
    -		three being equivalent.
    -		</p>
    -
    -		<p>Here is a sample configuration with a
    -		<code>RollingFileAppender</code> in conjunction with
    -		<code>SizeBasedTriggeringPolicy</code> triggering rollover when
    -		the log file reaches 5MB in size.
    -		</p>
    -
    -    <p class="example">Example: Sample configuration of a
    -    <code>RollingFileAppender</code> using a
    -    <code>SizeBasedTriggeringPolicy</code>
    -    (logback-examples/src/main/resources/chapters/appenders/conf/logback-RollingSizeBased.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('logback-RollingSizeBased');">View as .groovy</span>
    -    <pre id="logback-RollingSizeBased" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    -    &lt;file>test.log&lt;/file>
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
    -      &lt;fileNamePattern>test.%i.log.zip&lt;/fileNamePattern>
    -      &lt;minIndex>1&lt;/minIndex>
    -      &lt;maxIndex>3&lt;/maxIndex>
    -    &lt;/rollingPolicy>
    -
    -    <b>&lt;triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    -      &lt;maxFileSize>5MB&lt;/maxFileSize>
    -    &lt;/triggeringPolicy></b>
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -	
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -	
    -    <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx -->
    -		<a name="Classic"></a>
    -		<h2>Logback Classic</h2>
    -				
    -    
    -		<p>While logging events are generic in logback-core, within
    -		logback-classic they are always instances of
    -		<code>ILoggingEvent</code>. Logback-classic is nothing more than a
    -		specialized processing pipeline handling instances of
    -		<code>ILoggingEvent</code>.
    -
    -    </p>
    -
    -		<h3 class="doAnchor" name="SocketAppender">SocketAppender and
    -		SSLSocketAppender
    -    </h3>
    -		
    -		<p>The appenders covered thus far are only able to log to local
    -		resources.  In contrast, the <a
    -		href="../xref/ch/qos/logback/classic/net/SocketAppender.html">
    -		<code>SocketAppender</code></a> is designed to log to a remote
    -		entity by transmitting serialized <code>ILoggingEvent</code>
    -		instances over the wire.  When using <code>SocketAppender</code>
    -		logging events on the wire are sent in the clear.  However, when
    -		using  <a href="../xref/ch/qos/logback/classic/net/SSLSocketAppender.html">
    -		<code>SSLSocketAppender</code></a>, logging events are delivered over
    -		a secure channel.</p>
    -
    -    <p>
    -		The actual type of the serialized event
    -		is <a
    -		href="../xref/ch/qos/logback/classic/spi/LoggingEventVO.html"><code>LoggingEventVO</code></a>
    -		which implements the <code>ILoggingEvent</code>
    -		interface. Nevertheless, remote logging is non-intrusive as far as
    -		the logging event is concerned.  On the receiving end after
    -		deserialization, the event can be logged as if it were generated
    -		locally. Multiple <code>SocketAppender</code> instances running on
    -		different machines can direct their logging output to a central
    -		log server whose format is fixed.  <code>SocketAppender</code>
    -		does not take an associated layout because it sends serialized
    -		events to a remote server.  <code>SocketAppender</code> operates
    -		above the <em>Transmission Control Protocol (TCP)</em> layer which
    -		provides a reliable, sequenced, flow-controlled end-to-end octet
    -		stream.  Consequently, if the remote server is reachable, then log
    -		events will eventually arrive there. Otherwise, if the remote
    -		server is down or unreachable, the logging events will simply be
    -		dropped. If and when the server comes back up, then event
    -		transmission will be resumed transparently.  This transparent
    -		reconnection is performed by a connector thread which periodically
    -		attempts to connect to the server.
    -		</p>
    -		
    -		<p>Logging events are automatically buffered by the native TCP
    -		implementation.  This means that if the link to server is slow but
    -		still faster than the rate of event production by the client, the
    -		client will not be affected by the slow network
    -		connection. However, if the network connection is slower than the
    -		rate of event production, then the client can only progress at the
    -		network rate. In particular, in the extreme case where the network
    -		link to the server is down, the client will be eventually blocked.
    -		Alternatively, if the network link is up, but the server is down,
    -		the client will not be blocked, although the log events will be
    -		lost due to server unavailability.
    -		</p>
    -		
    -		<p>Even if a <code>SocketAppender</code> is no longer attached to
    -		any logger, it will not be garbage collected in the presence of a
    -		connector thread.  A connector thread exists only if the
    -		connection to the server is down.  To avoid this garbage
    -		collection problem, you should close the
    -		<code>SocketAppender</code> explicitly. Long lived applications
    -		which create/destroy many <code>SocketAppender</code> instances
    -		should be aware of this garbage collection problem. Most other
    -		applications can safely ignore it.  If the JVM hosting the
    -		<code>SocketAppender</code> exits before the
    -		<code>SocketAppender</code> is closed, either explicitly or
    -		subsequent to garbage collection, then there might be
    -		untransmitted data in the pipe which may be lost. This is a common
    -		problem on Windows based systems.  To avoid lost data, it is
    -		usually sufficient to <code>close()</code> the
    -		<code>SocketAppender</code> either explicitly or by calling the
    -		<code>LoggerContext</code>'s <code>stop()</code>
    -		method before exiting the application.
    -		</p>
    -		
    -		<p>The remote server is identified by the <span
    -		class="prop">remoteHost</span> and <span
    -		class="prop">port</span> properties.
    -		<code>SocketAppender</code> properties are listed in the following
    -		table.  <code>SSLSocketAppender</code> supports many additional
    -		configuration properties, which are detailed in the section 
    -		entitled <a href="usingSSL.html">Using SSL</a>.
    -		</p>
    -
    -    <table class="bodyTable striped">
    -      <tr>
    -			<th>Property Name</th>
    -			<th>Type</th>
    -			<th>Description</th>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>
    -          <p>
    -            The <span class="prop" container="socket">includeCallerData</span> option takes a boolean value. 
    -            If true, the caller data will be available to the remote host. 
    -            By default no caller data is sent to the server.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">port</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p>
    -            The port number of the remote server.
    -          </p>
    -        </td>
    -      </tr>	
    -      <tr>
    -        <td><span class="prop"
    -        container="socket">reconnectionDelay</span></td>
    -        <td><code><a
    -        href="../apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -        <td>
    -          The <span class="prop">reconnectionDelay</span> option takes
    -          a duration string, such "10 seconds" representing the time
    -          to wait between each failed connection attempt to the
    -          server.  The default value of this option is 30 seconds.
    -          Setting this option to zero turns off reconnection
    -          capability.  Note that in case of successful connection to
    -          the server, there will be no connector thread present.
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">queueSize</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p>The <span class="prop">queueSize</span> property takes an
    -          integer (greater than zero) representing the number of logging
    -          events to retain for delivery to the remote receiver.  When
    -          the queue size is one, event delivery to the remote
    -          receiver is synchronous.  When the queue size is greater
    -          than one, new events are enqueued, assuming that there is
    -          space available in the queue. Using a queue length greater
    -          than one can improve performance by eliminating delays caused
    -          by transient network delays.
    -          </p>
    -
    -          <p>See also the <span class="prop">eventDelayLimit</span>
    -          property.</p>
    -
    -        </td>
    -      </tr>	
    -
    -      <tr>
    -        <td><span class="prop" container="socket">eventDelayLimit</span></td>
    -        <td><code><a
    -        href="../apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -        <td>
    -          The <span class="prop">eventDelayLimit</span> option takes a
    -          duration string, such "10 seconds". It represents the time
    -          to wait before dropping events in case the local queue is
    -          full, i.e. already contains <span
    -          class="prop">queueSize</span> events.  This may occur if the
    -          remote host is persistently slow accepting events. The
    -          default value of this option is 100 milliseconds.
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">remoteHost</span></td>
    -        <td><code>String</code></td>
    -        <td>
    -          The host name of the server.
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td><span class="prop" container="socket">ssl</span></td>
    -        <td><code>SSLConfiguration</code></td>
    -        <td>Supported only for <code>SSLSocketAppender</code>, this
    -            property provides the SSL configuration that will be used by
    -            the appender, as described in <a href="usingSSL.html">Using SSL</a>.
    -        </td>
    -      </tr>
    -    </table>
    -    
    -    <h4>Logging Server Options</h4>
    -    <p>The standard Logback Classic distribution includes two options
    -    for servers that can be used to receive logging events from
    -    <code>SocketAppender</code> or <code>SSLSocketAppender</code>.</p>
    -    <ul>
    -      <li><code>ServerSocketReceiver</code> and its SSL-enabled 
    -      counterpart <code>SSLServerSocketReceiver</code> are receiver
    -      components which can be configured in the <em>logback.xml</em> 
    -      configuration file of an application in order receive events
    -      from a remote socket appender.  See <a href="receivers.html">
    -      Receivers</a> for configuration details and usage examples.
    -      </li>
    -      <li><code>SimpleSocketServer</code> and its SSL-enabled counterpart
    -      <code>SimpleSSLSocketServer</code> both offer an 
    -      easy-to-use standalone Java application that is designed to
    -      be configured and run from your shell's command line interface.
    -      These applications simply wait for logging events from 
    -      <code>SocketAppender</code> or <code>SSLSocketAppender</code>
    -      clients.  Each received event is logged according to local server 
    -      policy.  Usage examples are given below.
    -      </li>
    -    </ul>
    -    
    -    <h4><a name="simpleSocketServer"></a>Using SimpleSocketServer</h4>
    -    <p>
    -    The <code>SimpleSocketServer</code> application takes two command-line
    -    arguments: <em>port</em> and <em>configFile</em>; 
    -    where <em>port</em> is the port to listen on and
    -    <em>configFile</em> is a configuration script in XML format.
    -    </p>
    -	
    -    <p>
    -      Assuming you are in the <em>logback-examples/</em> directory, 
    -      start <code>SimpleSocketServer</code> with the following command:
    -    </p>
    -    
    -    <p class="source">java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
    -  src/main/java/chapters/appenders/socket/server1.xml</p>
    -
    -    <p>where 6000 is the port number to listen on and
    -    <em>server1.xml</em> is a configuration script that adds a
    -    <code>ConsoleAppender</code> and a
    -    <code>RollingFileAppender</code> to the root logger.  After you
    -    have started <code>SimpleSocketServer</code>, you can send it log
    -    events from multiple clients using <code>SocketAppender</code>.
    -    The examples associated with this manual include two such clients:
    -    <code>chapters.appenders.SocketClient1</code> and
    -    <code>chapters.appenders.SocketClient2</code> Both clients wait for the user
    -    to type a line of text on the console.  The text is encapsulated
    -    in a logging event of level debug and then sent to the remote
    -    server. The two clients differ in the configuration of the
    -    <code>SocketAppender</code>. <code>SocketClient1</code> configures
    -    the appender programmatically while <code>SocketClient2</code>
    -    requires a configuration file.
    -    </p>
    -	
    -    <p>Assuming <code>SimpleSocketServer</code> is running on the
    -    local host, you connect to it with the following command:
    -    </p>
    -	
    -    <p class="source">java chapters.appenders.socket.SocketClient1 localhost 6000</p>
    -
    -		<p>Each line that you type should appear on the console of the
    -		<code>SimpleSocketServer</code> launched in the previous step. If
    -		you stop and restart the <code>SimpleSocketServer</code> the
    -		client will transparently reconnect to the new server instance,
    -		although the events generated while disconnected will be simply
    -		(and irrevocably) lost.
    -		</p>
    -
    -		<p>
    -			Unlike
    -			<code>SocketClient1</code>, the sample application
    -			<code>SocketClient2</code> does not configure logback by itself. 
    -			It requires a configuration file in XML format. 
    -			The configuration file <em>client1.xml</em>
    -			shown below creates a <code>SocketAppender</code>
    -			and attaches it to the root logger.
    -		</p>
    -
    -		<p class="example">Example: SocketAppender configuration
    -		(logback-examples/src/main/resources/chapters/appenders/socket/client1.xml)</p>
    -    <span class="asGroovy" onclick="return asGroovy('client1');">View as .groovy</span>
    -<pre id="client1" class="prettyprint source">&lt;configuration>
    -	  
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender">
    -    &lt;remoteHost>${host}&lt;/remoteHost>
    -    &lt;port>${port}&lt;/port>
    -    &lt;reconnectionDelay>10000&lt;/reconnectionDelay>
    -    &lt;includeCallerData>${includeCallerData}&lt;/includeCallerData>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SOCKET" />
    -  &lt;/root>  
    -
    -&lt;/configuration></pre>
    -	
    -		<p>
    -			Note that in the above configuration scripts the values for the 
    -			<span class="prop">remoteHost</span>, <span class="prop">port</span> and
    -			<span class="prop">includeCallerData</span> properties
    -			are not given directly but as substituted variable keys. The values for the variables 
    -			can be specified as system properties: 
    -		</p>
    -	
    -    <p class="source">java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \
    -  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml</p>
    -
    -		<p>This command should give similar results to the previous
    -			<code>SocketClient1</code>
    -			example.
    -		</p>
    -		
    -		<p>Allow us to repeat for emphasis that serialization of logging
    -		events is not intrusive. A deserialized event carries the same
    -		information as any other logging event. It can be manipulated as
    -		if it were generated locally; except that serialized logging
    -		events by default do not include caller data. Here is an example
    -		to illustrate the point. First, start
    -		<code>SimpleSocketServer</code> with the following command:
    -		</p>
    -
    -    <p class="source"> java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
    -  src/main/java/chapters/appenders/socket/server2.xml</p>
    -
    -   <p>The configuration file <em>server2.xml</em> creates a
    -   <code>ConsoleAppender</code> whose layout outputs the caller's file
    -   name and line number along with other information. If you run
    -   <code>SocketClient2</code> with the configuration file
    -   <em>client1.xml</em> as previously, you will notice that the output
    -   on the server side will contain two question marks between
    -   parentheses instead of the file name and the line number of the
    -   caller:
    -		</p>
    -
    -    <p class="source">2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi</p>
    -
    -		<p>The outcome can be easily changed by instructing the
    -		<code>SocketAppender</code> to include caller data by setting the
    -		<span class="prop">includeCallerData</span> option to
    -		true. Using the following command will do the trick:
    -		</p>
    -
    -   <pre class="source">java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
    -  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml</pre>
    -
    -		<p>As deserialized events can be handled in the same way as
    -		locally generated events, they even can be sent to a second server
    -		for further treatment.  As an exercise, you may wish to setup two
    -		servers where the first server tunnels the events it receives from
    -		its clients to a second server.
    -		</p>
    -		
    -		<h4><a name="simpleSSLSocketServer"></a>Using SimpleSSLSocketServer</h4>
    -
    -    <p>The <code>SimpleSSLSocketServer</code> requires the same
    -    <em>port</em> and <em>configFile</em> command-line arguments used
    -    by <code>SimpleSocketServer</code>.  Additionally, you must provide
    -    the location and password for your logging server's X.509 credential
    -    using system properties specified on the command line.
    -    </p>
    -    
    -    <p>Assuming you are in the <em>logback-examples/</em> directory, 
    -    start <code>SimpleSSLSocketServer</code> with the following command:
    -    </p>
    -
    -    <p class="source">java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -    -Djavax.net.ssl.keyStorePassword=changeit \
    -    ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \
    -    src/main/java/chapters/appenders/socket/ssl/server.xml
    -    </p>
    -	
    -    <p>This example runs <code>SimpleSSLSocketServer</code> using an
    -    X.509 credential that is suitable for testing and experimentation, 
    -    only.  <strong>Before using <code>SimpleSSLSocketServer</code> in a
    -    production setting you should obtain an appropriate X.509 credential
    -    to identify your logging server</strong>.  See 
    -    <a href="usingSSL.html">Using SSL</a> for more details.
    -    </p>
    -    
    -    <p>Because the server configuration has <code>debug="true"</code>
    -    specified on the root element, you'll will see in the server's
    -    startup logging the SSL configuration that will be used.  This is
    -    useful in validating that local security policies are properly
    -    implemented.
    -    </p>
    -
    -    <p>With <code>SimpleSSLSocketServer</code> running, you can connect
    -    to the server using an <code>SSLSocketAppender</code>.  The following
    -    example shows the appender configuration needed:
    -    </p>
    -      
    -   	<p class="example">Example: SSLSocketAppender configuration
    -		(logback-examples/src/main/resources/chapters/appenders/socket/ssl/client.xml)</p>
    -    <span class="asGroovy" onclick="return asGroovy('sslclient');">View as .groovy</span>
    -<pre id="sslclient" class="prettyprint source">&lt;configuration debug="true">
    -	  
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
    -    &lt;remoteHost>${host}&lt;/remoteHost>
    -    &lt;port>${port}&lt;/port>
    -    &lt;reconnectionDelay>10000&lt;/reconnectionDelay>
    -    &lt;ssl>
    -      &lt;trustStore>
    -        &lt;location>${truststore}&lt;/location>
    -        &lt;password>${password}&lt;/password>
    -      &lt;/trustStore>
    -    &lt;/ssl>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SOCKET" />
    -  &lt;/root>  
    -
    -&lt;/configuration></pre>
    -	  
    -	  <p>Note that, just as in the previous example, the values for
    -	  <span class="prop">remoteHost</span>, <span class="prop">port</span>
    -	  are specified using substituted variable keys.  Additionally, note
    -	  the presence of the <span class="prop">ssl</span> property and its
    -	  nested <span class="prop">trustStore</span> property, which specifies
    -	  the location and password of a trust store using substituted
    -	  variables.  This configuration is necessary because our example
    -	  server is using a self-signed certificate.  See 
    -	  <a href="usingSSL.html">Using SSL</a> for more information on 
    -	  SSL configuration properties for <code>SSLSocketAppender</code>.
    -	  </p>
    -
    -    <p>We can run a client application using this configuration by
    -    specifying the substitution variable values on the command line as
    -    system properties:
    -    </p>
    -    	  	
    -    <p class="source">java -Dhost=localhost -Dport=6000 \
    -    -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -    -Dpassword=changeit \
    -    chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml
    - 	  </p>
    - 	  
    - 	  <p>As in the previous examples, you can type in a message when 
    - 	  prompted by the client application, and the message will be delivered
    - 	  to the logging server (now over a secure channel) where it will be
    - 	  displayed on the console.
    - 	  </p>
    - 	  
    - 	  <p>Note that the <em>truststore</em> property given on the command
    - 	  line specifies a file URL that identifies the location of the
    - 	  trust store.  You may also use a classpath URL as described in <a
    - 	  href="usingSSL.html">Using SSL</a>.
    - 	  </p>
    - 
    -    <p>As we saw previously at server startup, because the client
    -    configuration has <code>debug="true"</code> specified on the root
    -    element, the client's startup logging includes the details of the
    -    SSL configuration as aid to auditing local policy conformance.
    -    </p>
    - 	  
    - 	  
    -    <h3 class="doAnchor" name="serverSocketAppender">
    -      ServerSocketAppender and SSLServerSocketAppender</h3>
    -    
    -    <p>The <code>SocketAppender</code> component (and its SSL-enabled
    -    counterpart) discussed previously are designed to allow an
    -    application to connect to a remote logging server over the network
    -    for the purpose of delivering logging events to the server.  In
    -    some situations, it may be inconvenient or infeasible to have an
    -    application initiate a connection to a remote logging server.  For
    -    these situations, Logback offers <a
    -    href="../xref/ch/qos/logback/classic/net/server/ServerSocketAppender">
    -    <code>ServerSocketAppender</code></a>.
    -    </p>
    -    
    -    <p>Instead of initiating a connection to a remote logging server, 
    -    <code>ServerSocketAppender</code> passively listens on a TCP socket
    -    awaiting incoming connections from remote clients.  Logging events
    -    that are delivered to the appender are distributed to each connected
    -    client.  Logging events that occur when no client is connected are
    -    <em>summarily discarded</em>.
    -    </p>
    -    
    -    <p>In addition to the basic <code>ServerSocketAppender</code>, Logback
    -    offers <a href="../xref/ch/qos/logback/classic/net/server/SSLServerSocketAppender">
    -    <code>SSLServerSocketAppender</code></a>, which distributes logging events
    -    to each connected client using a secure, encrypted channel.  Moreover, the
    -    SSL-enabled appender fully supports mutual certificate-based authentication,
    -    which can be used to ensure that only authorized clients can connect to
    -    the appender to receive logging events.   
    -    </p>
    -   
    -    <p>The approach to encoding logging events for transmission on the wire 
    -    is identical to that used by <code>SocketAppender</code>; each event is 
    -    a serialized instance of <code>ILoggingEvent</code>.  Only the direction 
    -    of connection initiation is reversed.  While <code>SocketAppender</code> 
    -    acts as the active peer in establishing the connection to a logging server,
    -    <code>ServerSocketAppender</code> is passive; it listens for
    -    incoming connections from clients.</p>  
    -
    -    <p>The <code>ServerSocketAppender</code> subtypes are intended to be
    -    used exclusively with Logback <em>receiver</em> components.  See
    -    <a href="receivers.html">Receivers</a> for additional information on
    -    this component type.</p>
    -    
    -    <p>The following configuration properties are supported by 
    -    <code>ServerSocketAppender</code>:</p>
    -    
    -    <table class="bodyTable striped">
    -      <tr>
    -      <th>Property Name</th>
    -      <th>Type</th>
    -      <th>Description</th>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">address</span></td>
    -        <td><code>String</code></td>
    -        <td>The local network interface address on which the appender
    -        will listen.  If this property is not specified, the appender
    -        will listen on all network interfaces.</td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>
    -          <p>
    -            If true, the caller data will be available to the remote host. 
    -            By default no caller data is sent to the client.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">port</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p>
    -            The port number on which the appender will listen.
    -          </p>
    -        </td>
    -      </tr> 
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">ssl</span></td>
    -        <td><code>SSLConfiguration</code></td>
    -        <td>Supported only for <code>SSLServerSocketAppender</code>, this
    -            property provides the SSL configuration that will be used by
    -            the appender, as described in <a href="usingSSL.html">Using SSL</a>.
    -        </td>
    -      </tr>
    -    </table>
    -    
    -    <p>The following example illustrates a configuration that uses
    -      <code>ServerSocketAppender</code>:
    -    </p>
    -
    -    <p class="example">Example: Basic ServerSocketAppender Configuration
    -    (logback-examples/src/main/resources/chapters/appenders/socket/server4.xml)</p>
    -<pre id="SocketReceiver" class="prettyprint source">&lt;configuration debug="true">
    -  &lt;appender name="SERVER" 
    -    class="ch.qos.logback.classic.net.server.ServerSocketAppender">
    -    &lt;port>${port}&lt;/port>
    -    &lt;includeCallerData>${includeCallerData}&lt;/includeCallerData>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="SERVER" />
    -  &lt;/root>  
    -
    -&lt;/configuration>
    -</pre>
    -    <p>Note that this configuration differs from previous examples using
    -    <code>SocketAppender</code> only in the <em>class</em> specified for
    -    the appender, and in the absence of the <span class="prop">remoteHost</span>
    -    property &mdash; this appender waits passively for inbound connections 
    -    from remote hosts rather than opening a connection to a remote logging
    -    server.
    -    </p>
    -    
    -    <p>The following example illustrates a configuration using
    -    <code>SSLServerSocketAppender</code>.</p>
    -        
    -     <p class="example">Example: Basic SSLServerSocketAppender Configuration
    -    (logback-examples/src/main/resources/chapters/appenders/socket/ssl/server3.xml)</p>
    -<pre id="SocketReceiver" class="prettyprint source">&lt;configuration debug="true">
    -  &lt;appender name="SERVER" 
    -    class="ch.qos.logback.classic.net.server.SSLServerSocketAppender">
    -    &lt;port>${port}&lt;/port>
    -    &lt;includeCallerData>${includeCallerData}&lt;/includeCallerData>
    -    &lt;ssl>
    -      &lt;keyStore>
    -        &lt;location>${keystore}&lt;/location>
    -        &lt;password>${password}&lt;/password>
    -      &lt;/keyStore>
    -    &lt;/ssl>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="SERVER" />
    -  &lt;/root>  
    -
    -&lt;/configuration>
    -</pre>
    -   
    -    <p>The principal differences between this configuration and the
    -    previous configuration is that the appender's <em>class</em> attribute
    -    identifies the <code>SSLServerSocketAppender</code> type, and the
    -    presence of the nested <span class="prop">ssl</span> element which
    -    specifies, in this example, configuration of a key store containing
    -    an X.509 credential for the appender.  See <a href="usingSSL.html">
    -    Using SSL</a> for information regarding SSL configuration properties.
    -    </p>
    -    
    -    <p>Because the <code>ServerSocketAppender</code> subtypes are designed
    -    to be used with receiver components, we will defer presenting 
    -    illustrative examples to the chapter entitled 
    -    <a href="receivers.html">Receivers</a>.</p>
    -    
    -   <h3 class="doAnchor">SMTPAppender</h3>
    -   
    -   <p>The <a
    -   href="../xref/ch/qos/logback/classic/net/SMTPAppender.html"><code>SMTPAppender</code></a>
    -   accumulates logging events in one or more fixed-size buffers and
    -   sends the contents of the appropriate buffer in an email after a
    -   user-specified event occurs.  SMTP email transmission (sending) is
    -   performed asynchronously. By default, the email transmission is
    -   triggered by a logging event of level ERROR. Moreover, by default,
    -   a single buffer is used for all events.
    -   </p>
    -		
    -   <p>The various properties for <code>SMTPAppender</code> are
    -   summarized in the following table.
    -	 </p>
    -		
    -		<table class="bodyTable striped">
    -      <tr>
    -        <th>Property Name</th>
    -        <th>Type</th>
    -        <th>Description</th>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">smtpHost</span></td>
    -        <td><code>String</code></td>
    -        <td>The host name of the SMTP server. This parameter is mandatory.</td>
    -      </tr>
    -      
    -      <tr>
    -        <td><span class="prop" container="smtp">smtpPort</span></td>
    -        <td><code>int</code></td>
    -        <td>The port where the SMTP server is listening. Defaults to
    -        25.</td>
    -      </tr>
    -      
    -      <tr>
    -        <td><span class="prop" name="smtpTo">to</span></td>
    -        <td><code>String</code></td>
    -        <td>The email address of the recipient as a
    -        <em>pattern</em>. The pattern is evaluated anew with the
    -        triggering event as input for each outgoing email. Multiple
    -        recipients can be specified by separating the destination
    -        addresses with commas.  Alternatively, multiple recipients can
    -        also be specified by using multiple <code>&lt;to></code>
    -        elements. 
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">from</span></td>
    -        <td><code>String</code></td>
    -        <td>The originator of the email messages sent by
    -        <code>SMTPAppender</code> in the <a
    -        href="http://en.wikipedia.org/wiki/Email_address">usual email
    -        address format</a>. If you wish to include the sender's name,
    -        then use the format
    -        "Adam&nbsp;Smith&nbsp;&amp;lt;smith@moral.org&amp;gt;" so that
    -        the message appears as originating from
    -        "Adam&nbsp;Smith&nbsp;&lt;smith@moral.org&gt;".
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop">subject</span></td>
    -        <td><code>String</code></td>
    -        <td> 
    -          <p>The subject of the email. It can be any value accepted as
    -          a valid conversion pattern by <a
    -          href="layouts.html#ClassicPatternLayout">PatternLayout</a>. Layouts
    -          will be discussed in the next chapter.
    -          </p>
    -          
    -          <p>The outgoing email message will have a subject line
    -          corresponding to applying the pattern on the logging event
    -          that triggered the email message.
    -          </p>
    -
    -          <p>Assuming the <span class="prop">subject</span> option
    -          is set to "Log: %logger - %msg" and the triggering event's
    -          logger is named "com.foo.Bar", and contains the message
    -          "Hello world", then the outgoing email will have the subject
    -          line "Log: com.foo.Bar - Hello World".
    -          </p>
    -
    -          <p>By default, this option is set to "%logger{20} - %m".</p>
    -        </td>
    -        
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">discriminator</span></td>
    -        <td><code><a href="../xref/ch/qos/logback/core/sift/Discriminator.html">Discriminator</a></code></td>
    -        <td>
    -          <p>With the help of a <span
    -          class="prop">Discriminator</span>,
    -          <code>SMTPAppender</code> can scatter incoming events into
    -          different buffers according to the value returned by the
    -          discriminator. The default discriminator always returns the
    -          same value so that the same buffer is used for all events.
    -          </p>
    -
    -          <p>By specifying a discriminator other than the default
    -          one, it is possible to receive email messages
    -          containing a events pertaining to a particular user, user
    -          session or client IP address.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" name="smtpAppender_Evaluator">evaluator</span></td>
    -        <td><code><a
    -        href="../xref/ch/qos/logback/classic/boolex/IEvaluator.html">IEvaluator</a></code></td>
    -        <td>
    -          <p>This option is declared by creating a new
    -          <code>&lt;EventEvaluator/></code> element. The name of the
    -          class that the user wishes to use as the
    -          <code>SMTPAppender</code>'s <code>Evaluator</code> needs
    -          to be specified via the <span class="attr">class</span>
    -          attribute.
    -          </p>
    -          
    -          
    -          <p>In the absence of this option, <code>SMTPAppender</code>
    -          is assigned an instance of <a
    -          href="../xref/ch/qos/logback/classic/boolex/OnErrorEvaluator.html">OnErrorEvaluator</a>
    -          which triggers email transmission when it encounters an
    -          event of level <em>ERROR</em> or higher.
    -          </p>
    -
    -          <!--
    -          <p><code>EventEvaluator</code> objects are subclasses of the
    -          <code>JaninoEventEvaluatorBase</code> which depends on
    -          Janino. See the <a href="../dependencies.html">dependencies
    -          page</a> for more information.
    -          </p>
    -          -->
    -
    -          <p>Logback ships with several other evaluators, namely <a
    -          href="../xref/ch/qos/logback/classic/boolex/OnMarkerEvaluator.html"><code>OnMarkerEvaluator</code></a>
    -          (discussed below) and a powerful evaluator called <a
    -          href="../xref/ch/qos/logback/classic/boolex/JaninoEventEvaluator.html"><code>JaninoEventEvaluator</code></a>,
    -          discussed in <a href="filters.html#evalutatorFilter">another
    -          chapter</a>. The more recent versions of logback ship with
    -          an even more powerful evaluator called <a
    -          href="filters.html#GEventEvaluator"><code>GEventEvaluator</code></a>.
    -          </p>
    -
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td  valign="top"><span class="prop" container="smtp">cyclicBufferTracker</span></td>
    -        <td><a href="../xref/ch/qos/logback/core/spi/CyclicBufferTracker.html"><code>CyclicBufferTracker</code></a>
    -        </td>
    -        <td>
    -          <p>As the name indicates, an instance of the
    -          <code>CyclicBufferTracker</code> class tracks cyclic
    -          buffers. It does so based on the keys returned by the <span
    -          class="prop">discriminator</span> (see above).
    -          </p>
    -          <p>If you don't specify a <span
    -          class="prop">cyclicBufferTracker</span>, an instance of <a
    -          href="../xref/ch/qos/logback/core/spi/CyclicBufferTracker.html">CyclicBufferTracker</a>
    -          will be automatically created. By default, this instance
    -          will keep events in a cyclic buffer of size 256. You may
    -          change the size with the help of the <span
    -          class="prop">bufferSize</span> option (see below).</p>
    -        </td>        
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">username</span></td>
    -        <td><code>String</code></td> <td>The username value to use
    -        during plain user/password authentication. By default, this
    -        parameter is null.  </td> 
    -      </tr> 
    -      <tr class="alt">
    -        <td><span class="prop" container="smtp">password</span></td>
    -        <td><code>String</code></td>
    -        <td>The password value to use for plain user/password
    -        authentication. By default, this parameter is null.  
    -        </td>
    -      </tr>
    -      <tr> 
    -        <td><span class="prop" container="smtp">STARTTLS</span> </td>
    -        <td><code>boolean</code></td> 
    -        <td>If this parameter is set to true, then this appender
    -        will issue the STARTTLS command (if the server supports it)
    -        causing the connection to switch to SSL. Note that the
    -        connection is initially non-encrypted. By default, this
    -        parameter is set to false.
    -        </td> 
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">SSL</span></td>
    -        <td><code>boolean</code></td> <td>If this parameter is set to
    -        true, then this appender will open an SSL connection to the
    -        server. By default, this parameter is set to false.  </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">charsetEncoding</span></td>
    -        <td><code>String</code></td>
    -        <td>The outgoing email message will be encoded in the
    -        designated <a
    -        href="https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html">charset</a>. The
    -        default charset encoding is "UTF-8" which works well for most
    -        purposes.
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">localhost</span></td>
    -        <td><code>String</code></td>
    -        <td>In case the hostname of the SMTP client is not properly
    -        configured, e.g. if the client hostname is not fully
    -        qualified, certain SMTP servers may reject the HELO/EHLO
    -        commands sent by the client. To overcome this issue, you may
    -        set the value of the <span class="prop">localhost</span>
    -        property to the fully qualified name of the client host. See
    -        also the "mail.smtp.localhost" property in the documentation
    -        for the <a
    -        href="http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html">com.sun.mail.smtp</a>
    -        package.</td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">asynchronousSending</span></td>
    -        <td><code>boolean</code></td>
    -        <td>This property determines whether email transmission is
    -        done asynchronously or not. By default, the <span
    -        class="prop">asynchronousSending</span> property is
    -        'true'. However, under certain circumstances asynchronous
    -        sending may be inappropriate. For example if your application
    -        uses <code>SMTPAppender</code> to send alerts in response to a
    -        fatal error, and then exits, the relevant thread may not have
    -        the time to send the alert email. In this case, set <span
    -        class="prop">asynchronousSending</span> property to 'false'
    -        for synchronous email transmission.
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>By default, <span class="prop">includeCallerData</span> is
    -        set to <code>false</code>. You should set <span
    -        class="prop">includeCallerData</span> to <code>true</code> if
    -        <span class="prop">asynchronousSending</span> is enabled and
    -        you wish to include caller data in the logs. </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">sessionViaJNDI</span></td>
    -        <td><code>boolean</code></td>
    -        <td><code>SMTPAppender</code> relies on
    -        <code>javax.mail.Session</code> to send out email messages. By
    -        default, <span class="prop">sessionViaJNDI</span> is set to
    -        <code>false</code> so the <code>javax.mail.Session</code>
    -        instance is built by <code>SMTPAppender</code> itself with the
    -        properties specified by the user. If the <span
    -        class="prop">sessionViaJNDI</span> property is set to
    -        <code>true</code>, the <code>javax.mail.Session</code> object
    -        will be retrieved via JNDI. See also the <span
    -        class="prop">jndiLocation</span> property.
    -
    -        <p>Retrieving the <code>Session</code> via JNDI can reduce the
    -        number of places you need to configure/reconfigure the same
    -        information, making your application <a
    -        href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">dryer</a>. For
    -        more information on configuring resources in Tomcat see <a
    -        href="http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html#JavaMail_Sessions">JNDI
    -        Resources How-to</a>. <span class="label">beware</span> As
    -        noted in that document, make sure to remove <em>mail.jar</em>
    -        and <em>activation.jar</em> from your web-applications
    -        <em>WEB-INF/lib</em> folder when retrieving the
    -        <code>Session</code> from JNDI.
    -        </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">jndiLocation</span></td>
    -        <td><code>String</code></td>
    -        <td>The location where the javax.mail.Session is placed in
    -        JNDI. By default, <span class="prop">jndiLocation</span> is
    -        set to <span style="white-space:nowrap">"java:comp/env/mail/Session"</span>.
    -        </td>
    -      </tr>
    -
    -		</table>		
    -		
    -		<p>The <code>SMTPAppender</code> keeps only the last 256 logging
    -		events in its cyclic buffer, throwing away older events when its
    -		buffer becomes full.  Thus, the number of logging events delivered
    -		in any e-mail sent by <code>SMTPAppender</code> is upper-bounded
    -		by 256. This keeps memory requirements bounded while still
    -		delivering a reasonable amount of application context.
    -		</p>
    -		
    -		<p>The <code>SMTPAppender</code> relies on the JavaMail API.  It
    -		has been tested with JavaMail API version 1.4.  The JavaMail API
    -		requires the JavaBeans Activation Framework package.  You can
    -		download the <a
    -		href="http://java.sun.com/products/javamail/">JavaMail API</a> and
    -		the <a
    -		href="http://java.sun.com/beans/glasgow/jaf.html">JavaBeans
    -		Activation Framework</a> from their respective websites.  Make
    -		sure to place these two jar files in the classpath before trying
    -		the following examples.
    -		</p>
    -		
    -		<p>A sample application, <a
    -		href="../xref/chapters/appenders/mail/EMail.html"><code>chapters.appenders.mail.EMail</code></a>
    -		generates a number of log messages followed by a single
    -		error message. It takes two parameters. The first parameter is an
    -		integer corresponding to the number of logging events to
    -		generate. The second parameter is the logback configuration
    -		file. The last logging event generated by <em>EMail</em>
    -		application, an ERROR, will trigger the transmission of an email
    -		message.
    -		</p>
    -
    -		<p>Here is a sample configuration file intended for the
    -		<code>Email</code> application:
    -		</p>	
    -		
    -    <p class="example">Example: A sample <code>SMTPAppender</code> configuration (logback-examples/src/main/resources/chapters/appenders/mail/mail1.xml)</p>	
    -    <span class="asGroovy" onclick="return asGroovy('mail1');">View as .groovy</span>	
    -    <pre id="mail1" class="prettyprint source">&lt;configuration>	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>ADDRESS-OF-YOUR-SMTP-HOST&lt;/smtpHost>
    -    &lt;to>EMAIL-DESTINATION&lt;/to>
    -    &lt;to>ANOTHER_EMAIL_DESTINATION&lt;/to> &lt;!-- additional destinations are possible --&gt;
    -    &lt;from>SENDER-EMAIL&lt;/from>
    -    &lt;subject>TESTING: %logger{20} - %m&lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout">
    -      &lt;pattern>%date %-5level %logger{35} - %message%n&lt;/pattern>
    -    &lt;/layout>	    
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -		<p>Before trying out <code>chapters.appenders.mail.Email</code> application
    -		with the above configuration file, you must set the <span
    -		class="prop">smtpHost</span>, <span class="prop">to</span> and
    -		<span class="prop">from</span> properties to values appropriate for
    -		your environment. Once you have set the correct values in the
    -		configuration file, execute the following command:
    -		</p>
    -		
    -<div class="source"><pre>java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml</pre></div>
    -
    -		<p>The recipient you specified should receive an email message
    -		containing 100 logging events formatted by
    -		<code>PatternLayout</code> The figure below is the resulting email
    -		message as shown by Mozilla Thunderbird.
    -		</p>
    -    
    -    <p><img src="images/chapters/appenders/smtpAppender1.jpg" alt="resulting email"/></p>
    -		
    -		<p>In the next example configuration file <em>mail2.xml</em>, the
    -		values for the <span class="prop">smtpHost</span>, <span
    -		class="prop">to</span> and <span class="prop">from</span>
    -		properties are determined by variable substitution. Here is the
    -		relevant part of <em>mail2.xml</em>.
    -		</p>		
    -
    -    <pre class="prettyprint source">&lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -  &lt;smtpHost>${smtpHost}&lt;/smtpHost>
    -  &lt;to>${to}&lt;/to>
    -  &lt;from>${from}&lt;/from>
    -  &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/>
    -&lt;/appender></pre>
    -		
    -		<p>You can pass the required parameters on the command line:</p>
    -		
    -<div class="source"><pre>java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
    -  chapters.appenders.mail.EMail 10000 src/main/java/chapters/appenders/mail/mail2.xml
    -</pre></div>
    -
    -		<p>Be sure to replace with values as appropriate for your
    -		environment.
    -		</p>
    -		
    -		<p>Note that in this latest example, <code>PatternLayout</code>
    -		was replaced by <code>HTMLLayout</code> which formats logs as an
    -		HTML table. You can change the list and order of columns as well
    -		as the CSS of the table. Please refer to <a
    -		href="layouts.html#ClassicHTMLLayout">HTMLLayout</a> documentation
    -		for further details.
    -    </p>
    -    
    -    <p>Given that the size of the cyclic buffer is 256, the recipient
    -    should see an email message containing 256 events conveniently
    -    formatted in an HTML table. Note that this run of the
    -    <code>chapters.appenders.mail.Email</code> application generated
    -    10'000 events of which only the last 256 were included in the
    -    outgoing email.
    -		</p>
    -		
    -    <p><img src="images/chapters/appenders/smtpAppender2.jpg" alt="2nd email"/></p>
    -
    -    <p>Email clients such as Mozilla Thunderbird, Eudora or MS
    -    Outlook, offer reasonably good CSS support for HTML email.
    -    However, they sometimes automatically downgrade HTML to
    -    plaintext. For example, to view HTML email in Thunderbird, the
    -    "View&rarr;Message&nbsp;Body&nbsp;As&rarr;Original HTML" option
    -    must be set. Yahoo! Mail's support for HTML email, in particular
    -    its CSS support is very good. Gmail on the other hand, while it
    -    honors the basic HTML table structure, ignores the internal CSS
    -    formatting. Gmail supports inline CSS formatting but since inline
    -    CSS would make the resulting output too voluminous,
    -    <code>HTMLLayout</code> does not use inline CSS.
    -    </p>
    -
    -    <h3 class="doAnchor" name="cyclicBufferSize">Custom buffer
    -    size</h3>
    -
    -    <p>By default, the outgoing message will contain the last 256
    -    messages seen by <code>SMTPAppender</code>. If your heart so
    -    desires, you may set a different buffer size as shown in the next example.
    -    </p>
    -
    -   <p class="example">Example:  <code>SMTPAppender</code> configuration with a custom bufer size (logback-examples/src/main/resources/chapters/appenders/mail/customBufferSize.xml)</p>	
    -    <pre class="prettyprint source">&lt;configuration>   
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>${smtpHost}&lt;/smtpHost>
    -    &lt;to>${to}&lt;/to>
    -    &lt;from>${from}&lt;/from>
    -    &lt;subject>%logger{20} - %m&lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/>
    -
    -    <b>&lt;cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker"></b>
    -      <b>&lt;!-- send just one log entry per email --></b>
    -      <b>&lt;bufferSize>1&lt;/bufferSize></b>
    -    <b>&lt;/cyclicBufferTracker></b>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration>    </pre>
    -    
    -
    -    <h3 class="doAnchor">Triggering event</h3>
    -
    -    <p>If the Evaluator property is not set, the
    -    <code>SMTPAppender</code> defaults to an <a
    -    href="../xref/ch/qos/logback/classic/boolex/OnErrorEvaluator.html">OnErrorEvaluator</a>
    -    instance which triggers email transmission when it encounters an
    -    event of level ERROR. While triggering an outgoing email in
    -    response to an error is relatively reasonable, it is possible to
    -    override this default behavior by providing a different
    -    implementation of the <code>EventEvaluator</code> interface.
    -    </p>
    -		
    -		<p>The <code>SMTPAppender</code> submits each incoming event to
    -		its evaluator by calling <code>evaluate()</code> method in order
    -		to check whether the event should trigger an email or just be
    -		placed in the cyclic buffer.  When the evaluator gives a positive
    -		answer to its evaluation, an email is sent out.  The
    -		<code>SMTPAppender</code> contains one and only one evaluator
    -		object.  This object may manage its own internal state. For
    -		illustrative purposes, the <code>CounterBasedEvaluator</code>
    -		class listed next implements an event evaluator whereby every
    -		1024th event triggers an email message.
    -		</p>
    -
    -    <p class="example">Example: A <code>EventEvaluator</code> implementation
    -that evaluates to <code>true</code> every 1024th event (<a href="../xref/chapters/appenders/mail/CounterBasedEvaluator.html">logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java</a>)</p>
    -   
    -   <pre class="prettyprint source">package chapters.appenders.mail;
    -
    -import ch.qos.logback.core.boolex.EvaluationException;
    -import ch.qos.logback.core.boolex.EventEvaluator;
    -import ch.qos.logback.core.spi.ContextAwareBase;
    -
    -public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator {
    -
    -  static int LIMIT = 1024;
    -  int counter = 0;
    -  String name;
    -
    -  <b>public boolean evaluate(Object event) throws NullPointerException,
    -      EvaluationException {
    -    counter++;
    -
    -    if (counter == LIMIT) {
    -      counter = 0;
    -
    -      return true;
    -    } else {
    -      return false;
    -    }
    -  }</b>
    -
    -  public String getName() {
    -    return name;
    -  }
    -
    -  public void setName(String name) {
    -    this.name = name;
    -  }
    -}</pre>
    -
    -		<p>Note that this class extends <code>ContextAwareBase</code> and
    -		implements <code>EventEvaluator</code>. This allows the user to
    -		concentrate on the core functions of her
    -		<code>EventEvaluator</code> and let the base class provide the
    -		common functionality.
    -		</p>
    -
    -		<p>Setting the <span class="prop">Evaluator</span> option of
    -		<code>SMTPAppender</code> instructs it to use a custom evaluator.
    -		The next configuration file attaches a <code>SMTPAppender</code>
    -		to the root logger.  This appender uses a
    -		<code>CounterBasedEvaluator</code> instance as its event
    -		evaluator.
    -		</p>
    -
    -    <p class="example">Example: <code>SMTPAppender</code> with custom 
    -    <code>Evaluator</code> and buffer size (logback-examples/src/main/resources/chapters/appenders/mail/mail3.xml)</p>
    -    <span class="asGroovy" onclick="return asGroovy('mail3');">View as .groovy</span>	
    -    <pre id="mail3" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    <b>&lt;evaluator class="chapters.appenders.mail.CounterBasedEvaluator" /></b>
    -    &lt;smtpHost>${smtpHost}&lt;/smtpHost>
    -    &lt;to>${to}&lt;/to>
    -    &lt;from>${from}&lt;/from>
    -    &lt;subject>%logger{20} - %m&lt;/subject>
    -
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -    
    -
    -    <h3 class="doAnchor" name="OnMarkerEvaluator">Marker based
    -    triggering </h3>
    -
    -    <p>Although reasonable, the default triggering policy whereby every
    -    event of level ERROR triggers an outgoing email may result in too
    -    many emails, cluttering the targeted user's mailbox. Logback ships
    -    with another triggering policy, called <a
    -    href="../xref/ch/qos/logback/classic/boolex/OnMarkerEvaluator.html">OnMarkerEvaluator</a>. It
    -    is based on markers. In essence, emails are triggered only if the
    -    event is marked with a user-specified marker. The next example
    -    should make the point clearer.
    -    </p>
    -
    -    <p>The <a
    -    href="../xref/chapters/appenders/mail/Marked_EMail.html">Marked_EMail</a>
    -    application contains several logging statements some of which are
    -    of level ERROR. One noteworthy statement contains a marker. Here
    -    is the relevant code.
    -    </p>
    -
    -    <pre class="prettyprint source">Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
    -logger.error(<b>notifyAdmin</b>,
    -  "This is a serious an error requiring the admin's attention",
    -   new Exception("Just testing"));</pre>
    -
    -   <p>The next configuration file will trigger outgoing emails only in
    -   presence of events bearing the NOTIFY_ADMIN or the
    -   TRANSACTION_FAILURE markers.
    -   </p>
    -
    -   <p class="example">Example: <code>SMTPAppender</code> with 
    -   <code>OnMarkerEvaluator</code> (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('mailWithMarker');">View as .groovy</span>	
    -   <pre id="mailWithMarker" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    <b>&lt;evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
    -      &lt;marker>NOTIFY_ADMIN&lt;/marker>
    -      &lt;!-- you specify add as many markers as you want -->
    -      &lt;marker>TRANSACTION_FAILURE&lt;/marker>
    -    &lt;/evaluator></b>
    -    &lt;smtpHost>${smtpHost}&lt;/smtpHost>
    -    &lt;to>${to}&lt;/to>
    -    &lt;from>${from}&lt;/from>
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/>
    -  &lt;/appender>
    -
    -  &lt;root>
    -    &lt;level value ="debug"/>
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -    
    -    <p>Give it a whirl with the following command:</p>
    -
    -    <pre class="source">java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
    -  chapters.appenders.mail.Marked_EMail src/main/java/chapters/appenders/mail/mailWithMarker.xml</pre>
    -
    -
    -  <h4 class="doAnchor" name="marker_JaninoEventEvaluator">Marker-based
    -  triggering with JaninoEventEvaluator</h4>
    -
    -    <p>Note that instead of using the marker-centric
    -    <code>OnMarkerEvaluator</code>, we could use the much more generic
    -    <a
    -    href="filters.html#JaninoEventEvaluator"><code>JaninoEventEvaluator</code></a>
    -    or its even more powerful cousin <a
    -    href="filters.html#GEventEvaluator"><code>GEventEvaluator</code></a>.
    -    For example, the following configuration file uses
    -    <code>JaninoEventEvaluator</code> instead of
    -    <code>OnMarkerEvaluator</code> but is otherwise equivalent to the
    -    previous configuration file.
    -    </p>
    -
    -    <p class="example">Example: <code>SMTPAppender</code> with 
    -   <code>JaninoEventEvaluator</code> (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker_Janino.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('mailWithMarker_Janino');">View as .groovy</span>	
    -    <pre id="mailWithMarker_Janino" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
    -      &lt;expression>
    -        (marker != null) &amp;&amp;
    -        (marker.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE"))
    -      &lt;/expression>
    -    &lt;/evaluator>    
    -    ... same as above
    -  &lt;/appender>
    -&lt;/configuration></pre>
    -
    -    <h4 class="doAnchor" name="marker_GEventEvaluator">Marker-based
    -    triggering with GEventEvaluator</h4>
    -
    -    <p>Here is the equivalent evaluator using <a
    -    href="filters.html#GEventEvaluator">GEventEvaluator</a>.</p>
    -
    -    <p class="example">Example: the same with 
    -   <code>GEventEvaluator</code> (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMarker_GEvent.xml)</p>
    -   <span class="asGroovy" onclick="return asGroovy('mailWithMarker_GEventEvaluator');">View as .groovy</span>	
    -
    -   <pre id="mailWithMarker_GEventEvaluator" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator">
    -      &lt;expression>
    -        e.marker?.contains("NOTIFY_ADMIN") || e.marker?.contains("TRANSACTION_FAILURE")
    -      &lt;/expression>
    -    &lt;/evaluator>    
    -    ... same as above
    -  &lt;/appender>
    -&lt;/configuration></pre>
    -
    -    <p>Note that since the event may lack a marker, the value of
    -    e.marker can be null. Hence the use of Groovy's <a
    -    href="http://groovy.codehaus.org/Null+Object+Pattern">safe
    -    dereferencing operator</a>, that is the .? operator.
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="smtpAuthentication">Authentication/STARTTLS/SSL</h3>
    -
    -    <p><code>SMTPAppender</code> supports authentication via plain
    -    user passwords as well as both the STARTTLS and SSL
    -    protocols. Note that STARTTLS differs from SSL in that, in
    -    STARTTLS, the connection is initially non-encrypted and only after
    -    the STARTTLS command is issued by the client (if the server
    -    supports it) does the connection switch to SSL. In SSL mode, the
    -    connection is encrypted right from the start.
    -    </p>
    -
    -    <h3 class="doAnchor" name="gmailSSL">SMTPAppender configuration
    -    for Gmail (SSL)</h3>
    -
    -    <p>The next example shows you how to configure
    -    <code>SMTPAppender</code> for Gmail with the SSL protocol. </p>
    -    
    -    <p class="example">Example:: <code>SMTPAppender</code> to Gmail
    -    using SSL
    -    (logback-examples/src/main/resources/chapters/appenders/mail/gmailSSL.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('gmailSSLExample');">View as .groovy</span>	
    -    <pre id="gmailSSLExample" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    <b>&lt;smtpHost>smtp.gmail.com&lt;/smtpHost></b>
    -    <b>&lt;smtpPort>465&lt;/smtpPort></b>
    -    <b>&lt;SSL>true&lt;/SSL></b>
    -    <b>&lt;username>YOUR_USERNAME@gmail.com&lt;/username></b>
    -    <b>&lt;password>YOUR_GMAIL_PASSWORD&lt;/password></b>
    -
    -    &lt;to>EMAIL-DESTINATION&lt;/to>
    -    &lt;to>ANOTHER_EMAIL_DESTINATION&lt;/to> &lt;!-- additional destinations are possible -->
    -    &lt;from>YOUR_USERNAME@gmail.com&lt;/from>
    -    &lt;subject>TESTING: %logger{20} - %m&lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout">
    -      &lt;pattern>%date %-5level %logger{35} - %message%n&lt;/pattern>
    -    &lt;/layout>	    
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -
    -    <h3 class="doAnchor" name="gmailSTARTTLS">SMTPAppender for Gmail
    -    (STARTTLS)</h3>
    -
    -    <p>The next example shows you how to configure
    -    <code>SMTPAppender</code> for Gmail for the STARTTLS protocol. </p>
    -
    -    <p class="example">Example: <code>SMTPAppender</code> to GMAIL using STARTTLS (logback-examples/src/main/resources/chapters/appenders/mail/gmailSTARTTLS.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('gmailSTARTTLSExample');">View as .groovy</span>	
    -    <pre id="gmailSTARTTLSExample" class="prettyprint source">&lt;configuration>	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>smtp.gmail.com&lt;/smtpHost>
    -    &lt;smtpPort>587&lt;/smtpPort>
    -    &lt;STARTTLS>true&lt;/STARTTLS>
    -    &lt;username>YOUR_USERNAME@gmail.com&lt;/username>
    -    &lt;password>YOUR_GMAIL_xPASSWORD&lt;/password>
    -    
    -    &lt;to>EMAIL-DESTINATION&lt;/to>
    -    &lt;to>ANOTHER_EMAIL_DESTINATION&lt;/to> &lt;!-- additional destinations are possible -->
    -    &lt;from>YOUR_USERNAME@gmail.com&lt;/from>
    -    &lt;subject>TESTING: %logger{20} - %m&lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout">
    -      &lt;pattern>%date %-5level %logger - %message%n&lt;/pattern>
    -    &lt;/layout>	    
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -
    -    <h3 class="doAnchor" name="smtpDiscriminator">SMTPAppender with MDCDiscriminator</h3>
    -
    -
    -    <p>As mentioned earlier, by specifying a discriminator other than
    -    the default one, <code>SMTPAppender</code> will generate email
    -    messages containing events pertaining to a particular user, user
    -    session or client IP address, depending on the specified discriminator.
    -    </p>
    -
    -    <p>The next example illustrates the use of <a
    -    href="../xref/ch/qos/logback/classic/sift/MDCBasedDiscriminator.html">MDCBasedDiscriminator</a>
    -    in conjunction with the MDC key named "req.remoteHost", assumed to
    -    contain the IP address of the remote host accessing a fictitious
    -    application. In a web-application, you could use <a
    -    href="mdc.html#mis">MDCInsertingServletFilter</a> to populate MDC
    -    values.
    -    </p>
    -
    -    <p class="example">Example: <code>SMTPAppender</code> with
    -    MDCBasedDsicriminator
    -    (logback-examples/src/main/resources/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('mailWithMDCBasedDiscriminator');">View as .groovy</span>	
    -    <pre id="mailWithMDCBasedDiscriminator" class="prettyprint source">&lt;configuration>	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>ADDRESS-OF-YOUR-SMTP-HOST&lt;/smtpHost>
    -    &lt;to>EMAIL-DESTINATION&lt;/to>
    -    &lt;from>SENDER-EMAIL&lt;/from>
    -
    -    <b>&lt;discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"></b>
    -      <b>&lt;key>req.remoteHost&lt;/key></b>
    -      <b>&lt;defaultValue>default&lt;/defaultValue></b>
    -    <b>&lt;/discriminator></b>
    -
    -    &lt;subject>${HOSTNAME} -- %X{req.remoteHost} %msg"&lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -      &lt;pattern>%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg&lt;/pattern>
    -    &lt;/layout>
    -  &lt;/appender>
    -
    -  &lt;root>
    -    &lt;level level="DEBUG"/>
    -    &lt;appender-ref ref="EMAIL" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -    <p>Thus, each outgoing email generated by
    -    <code>SMTPAppender</code> will belong to a <em>unique</em> remote
    -    host, greatly facilitating problem diagnosis.
    -    </p>
    -    
    -    <h4 class="doAnchor" name= "bufferManagement">Buffer management in
    -    very busy systems</h4>
    -
    -    <p>Internally, each distinct value returned by the discriminator
    -    will cause the creation of a new cyclic buffer. However, at most
    -    <span class="prop">maxNumberOfBuffers</span> (by default 64)
    -    will be maintained.  Whenever the number of buffers rises above
    -    <span class="prop">maxNumberOfBuffers</span>, the least recently
    -    updated buffer is automatically discarded. As a second safety
    -    measure, any buffer which has not been updated in the last 30
    -    minutes will be automatically discarded as well.</p>
    -
    -    <p>On systems serving a large number of transactions per minute,
    -    allowing only a small number for <span
    -    class="prop">maxNumberOfBuffers</span> (by default 64) will
    -    often cause the number of events in the outgoing email to be
    -    unnecessarily small. Indeed, in the presence of a large number of
    -    transactions, there will be more than one buffer associated with
    -    the same transaction as buffers will be killed and re-born in
    -    succession for the same discriminator value (or transaction). Note
    -    that in even such very busy systems, the maximum number of cyclic
    -    buffers is capped by <span
    -    class="prop">maxNumberOfBuffers</span>.
    -    </p>
    -
    -    <p>To avoid such yo-yo effects, <code>SMTPAppender</code> will
    -    release the buffer associated with a given discriminator key as
    -    soon as it sees an event marked as "FINALIZE_SESSION". This will
    -    cause the appropriate buffer to be discarded at the end of each
    -    transaction. You can then safely increase the value of <span
    -    class="prop">maxNumberOfBuffers</span> to a larger value such as
    -    512 or 1024 without risking running out of memory.
    -    </p>
    -
    -    <p>There are three distinct but complementary mechanisms working
    -    together to manage cyclic buffers. They ensure that only relevant
    -    buffers are kept alive at any given moment, even in very busy
    -    systems.</p>
    -
    -    <!-- =========================================================== -->
    -    <!-- =========================================================== -->
    -
    -
    -    <h3 class="doAnchor" name="DBAppender">DBAppender</h3>
    -		
    -		<p>The <a
    -		href="../xref/ch/qos/logback/classic/db/DBAppender.html"><code>DBAppender</code></a>
    -		inserts logging events into three database tables in a format
    -		independent of the Java programming language.
    -		</p>
    -
    -		<p>These three tables are <em>logging_event</em>,
    -		<em>logging_event_property</em> and
    -		<em>logging_event_exception</em>. They must exist before
    -		<code>DBAppender</code> can be used. Logback ships with SQL
    -		scripts that will create the tables.  They can be found under the
    -		<em>logback-classic/src/main/java/ch/qos/logback/classic/db/script</em>
    -		folder. There is a specific script for each of the most popular
    -		database systems.  If the script for your particular type of
    -		database system is missing, it should be quite easy to write one,
    -		taking example on the already existing scripts. If you send them
    -		to us, we will gladly include missing scripts in future releases.
    -		</p>
    -
    -		<p>If your JDBC driver supports the <code>getGeneratedKeys</code>
    -		method introduced in JDBC 3.0 specification, assuming you have
    -		created the appropriate database tables as mentioned above, then
    -		no additional steps are required. Otherwise, there must be an
    -		<code>SQLDialect</code> appropriate for your database
    -		system. Currently, logback has dialects for H2, HSQL, MS SQL
    -		Server, MySQL, Oracle, PostgreSQL, SQLLite and Sybase. </p>
    -
    -		<p>The table below summarizes the database types and their support
    -		of the <code>getGeneratedKeys()</code> method.
    -		</p>
    -
    -		<table class="bodyTable striped" border="0" cellpadding="4">
    -			<tr>
    -				<th>RDBMS</th>
    -        <th>tested version(s)
    -        </th>
    -        <th>tested JDBC driver version(s)
    -				</th>
    -        <th>
    -					supports
    -					<br />
    -					<code>getGeneratedKeys()</code>
    -					method
    -				</th>		
    -
    -        <th>is a dialect <br/>provided by logback</th>
    -			</tr>
    -
    -      <tr>
    -				<td>DB2</td>
    -        <td>untested</td>
    -				<td>untested</td>
    -				<td>unknown</td>
    -        <td>NO</td>
    -			</tr>
    -
    -      <tr>
    -        <td>H2</td>
    -        <td>1.2.132</td>
    -        <td>-</td>
    -				<td>unknown</td>
    -        <td>YES</td>
    -			</tr>
    -
    -      <tr>
    -        <td>HSQL</td>
    -        <td>1.8.0.7</td>
    -        <td>-</td>
    -				<td>NO </td>
    -        <td>YES</td>
    -			</tr>
    -
    -      <tr>
    -        <td>Microsoft SQL Server</td>
    -        <td>2005</td>
    -        <td>2.0.1008.2 (sqljdbc.jar)</td>
    -				<td>YES</td>
    -        <td>YES</td>
    -			</tr>
    -
    -      <tr>
    -				<td>MySQL</td>
    -        <td>5.0.22</td>
    -        <td>5.0.8 (mysql-connector.jar)</td>        
    -				<td>YES</td>
    -        <td>YES</td>
    -			</tr>
    -
    -			<tr>
    -				<td>PostgreSQL</td>
    -        <td>8.x</td>
    -        <td>8.4-701.jdbc4</td>
    -				<td>NO</td>
    -        <td>YES</td>
    -
    -			</tr>
    -		
    -			<tr>
    -				<td>Oracle</td>
    -        <td>10g</td>
    -        <td>10.2.0.1 (ojdbc14.jar)</td>
    -				<td>YES</td>
    -        <td>YES</td>
    -			</tr>
    -	
    -      <tr>
    -        <td>SQLLite</td>
    -        <td>3.7.4</td>
    -        <td>-</td>
    -        <td>unknown</td>
    -        <td>YES</td>
    -      </tr>
    -	
    -			
    -      <tr>
    -        <td>Sybase SQLAnywhere</td>
    -        <td>10.0.1</td>
    -        <td>-</td>
    -        <td>unknown</td>
    -        <td>YES</td>
    -      </tr>
    -
    -		</table>
    -		
    -		<p>Experiments show that writing a single event into the database
    -		takes approximately 10 milliseconds, on a "standard" PC. If pooled
    -		connections are used, this figure drops to around 1
    -		millisecond. Note that most JDBC drivers already ship with
    -		connection pooling support.
    -		</p>
    -		
    -		<p>Configuring logback to use <code>DBAppender</code> can be done
    -		in several different ways, depending on the tools one has to
    -		connect to the database, and the database itself. The key issue in
    -		configuring <code>DBAppender</code> is about setting its
    -		<code>ConnectionSource</code> object, as we shall discover
    -		shortly.
    -		</p>
    -		
    -		<p>Once <code>DBAppender</code> is configured for your database,
    -		logging events are sent to the specified database. As stated
    -		previously, there are three tables used by logback to store
    -		logging event data.
    -		</p>
    -		
    -		<p>
    -			The <em>logging_event</em> table contains the following fields:
    -		</p>
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Field</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><b>timestamp</b></td>
    -				<td><code>big int</code></td>
    -				<td>The timestamp that was valid at the logging event's creation.</td>
    -			</tr>
    -			<tr>
    -				<td><b>formatted_message</b></td>
    -				<td><code>text</code></td>
    -
    -				<td>The message that has been added to the logging event,
    -				after formatting with
    -				<code>org.slf4j.impl.MessageFormatter</code>, in case objects
    -				were passed along with the message.</td>
    -			</tr>
    -			<tr>
    -				<td><b>logger_name</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The name of the logger used to issue the logging request.</td>
    -			</tr>
    -			<tr>
    -				<td><b>level_string</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The level of the logging event.</td>
    -			</tr>
    -			<tr>
    -				<td><b>reference_flag</b></td>
    -				<td><code>smallint</code></td>
    -				<td>
    -					<p>This field is used by logback to identify logging events
    -					that have an exception or <code>MDC</code>property values
    -					associated.
    -					</p>
    -
    -					<p>Its value is computed by
    -					<code>ch.qos.logback.classic.db.DBHelper</code>. A logging
    -					event that contains <code>MDC</code> or <code>Context</code>
    -					properties has a flag number of <em>1</em>. One that
    -					contains an exception has a flag number of <em>2</em>. A
    -					logging event that contains both elements has a flag number
    -					of <em>3</em>.
    -					</p>
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_filename</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The name of the file where the logging request was issued.</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_class</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The class where the logging request was issued.</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_method</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The name of the method where the logging request was issued.</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_line</b></td>
    -				<td><code>char</code></td>
    -				<td>The line number where the logging request was issued.</td>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>The database id of the logging event.</td>
    -			</tr>
    -		</table>
    -		
    -		<p>
    -			The <em>logging_event_property</em> is used to store the keys and values
    -			contained in the <code>MDC</code> or the <code>Context</code>. 
    -			It contains these fields:
    -		</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Field</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>The database id of the logging event.</td>
    -			</tr>
    -			<tr>
    -				<td><b>mapped_key</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The key of the <code>MDC</code> property</td>
    -			</tr>		
    -			<tr>
    -				<td><b>mapped_value</b></td>
    -				<td><code>text</code></td>
    -				<td>The value of the <code>MDC</code> property</td>
    -			</tr>				
    -		</table>
    -		
    -		<p>
    -			The <em>logging_event_exception</em> table contains the following fields:
    -		</p>
    -		
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Field</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>The database id of the logging event.</td>
    -			</tr>
    -			<tr>
    -				<td><b>i</b></td>
    -				<td><code>smallint</code></td>
    -				<td>The index of the line in the full stack trace.</td>
    -			</tr>		
    -			<tr>
    -				<td><b>trace_line</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The corresponding line</td>
    -			</tr>				
    -		</table>
    -		
    -		<p>
    -			To give a more visual example of the work done by <code>DBAppender</code>, here
    -			is a screenshot of a MySQL database with content provided by <code>DBAppender</code>.
    -		</p>
    -		
    -		<p>The <em>logging_event</em> table:</p>
    -
    -		<img src="images/chapters/appenders/dbAppenderLE.gif" alt="Logging Event table" />
    -
    -		<p>The <em>logging_event_exception</em> table:</p>
    -		
    -		<img src="images/chapters/appenders/dbAppenderLEException.gif" alt="Logging Event Exception table" />
    -
    -		<p>The <em>logging_event_property</em> table:</p>
    -		
    -		<img src="images/chapters/appenders/dbAppenderLEProperty.gif" alt="Logging Event Property table" />
    -
    -		
    -		<h4>ConnectionSource</h4>
    -		
    -		<p>The <code>ConnectionSource</code> interface provides a
    -		pluggable means of transparently obtaining JDBC connections for
    -		logback classes that require the use of a
    -		<code>java.sql.Connection</code>. There are currently three
    -		implementations of <code>ConnectionSource</code>, namely
    -		<code>DataSourceConnectionSource</code>,
    -		<code>DriverManagerConnectionSource</code> and
    -		<code>JNDIConnectionSource</code>.
    -		</p>
    -		
    -		<p>
    -			The first example that we will review is a configuration using
    -			<code>DriverManagerConnectionSource</code> and a MySQL database.
    -			The following configuration file is what one would need.
    -		</p>
    -		
    -    <p class="example">Example: <code>DBAppender</code> configuration (logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-driverManager.xml)</p>
    -    <span class="asGroovy" onclick="return asGroovy('append-toMySQL-with-driverManager');">View as .groovy</span>	
    -    <pre id="append-toMySQL-with-driverManager" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
    -    &lt;connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
    -      &lt;driverClass>com.mysql.jdbc.Driver&lt;/driverClass>
    -      &lt;url>jdbc:mysql://host_name:3306/datebase_name&lt;/url>
    -      &lt;user>username&lt;/user>
    -      &lt;password>password&lt;/password>
    -    &lt;/connectionSource>
    -  &lt;/appender></b>
    -  
    -  &lt;root level="DEBUG" >
    -    &lt;appender-ref ref="DB" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>
    -			The correct driver must be declared. Here, the <code>com.mysql.jdbc.Driver</code>
    -			class is used. The <span class="prop">url</span> must begin with <em>jdbc:mysql://</em>.
    -		</p>
    -		
    -		<p>
    -			The 
    -			<a href="../xref/ch/qos/logback/core/db/DriverManagerConnectionSource.html">
    -			<code>DriverManagerConnectionSource</code></a> is an implementation of
    -			<code>ConnectionSource</code> that obtains the connection in the
    -			traditional JDBC manner based on the connection URL.
    -		</p>
    -		<p>
    -			Note that this class will establish a new
    -			<code>Connection</code> for each call to
    -			<code>getConnection()</code>. It is recommended that you either
    -			use a JDBC driver that natively supports connection pooling or
    -			that you create your own implementation of
    -			<code>ConnectionSource</code> that taps into whatever pooling
    -			mechanism you are already using. If you have access to a JNDI
    -			implementation that supports <code>javax.sql.DataSource</code>,
    -			e.g. within a J2EE application server, see <a
    -			href="#JNDIConnectionSource"><code>JNDIConnectionSource</code></a>
    -			below.
    -		</p>
    -<!-- 
    -		
    -		HAS TO BE TESTED
    -
    -		<p>
    -			If you do not have another connection pooling mechanism built
    -			into your application, you can use the
    -			<a href="http://jakarta.apache.org/commons/dbcp/index.html">
    -		  commons-dbcp </a> package from Apache:
    -		</p>
    -
    -<pre class="prettyprint source">
    -  &lt;connectionSource
    -    class=&quot;ch.qos.logback.core.db.DriverManagerConnectionSource&quot;&gt;
    -    &lt;param name=&quot;driver&quot; value=&quot;org.apache.commons.dbcp.PoolingDriver&quot;/&gt; 
    -    &lt;param name=&quot;url&quot; value=&quot;jdbc:apache:commons:dbcp:/myPoolingDriver&quot;/&gt; 
    -  &lt;/connectionSource&gt;
    -</pre>
    -		
    -		<p>
    -			Then the configuration information for the commons-dbcp
    -			package goes into the file <em>myPoolingDriver.jocl</em> and is
    -			placed in the classpath. See the
    -			<a href="http://jakarta.apache.org/commons/dbcp/index.html"> commons-dbcp </a>
    -			documentation for details.
    -		</p>
    - -->
    - 
    -		<p>Connecting to a database using a <code>DataSource</code> is
    -		rather similar.  The configuration now uses <a
    -		href="../xref/ch/qos/logback/core/db/DataSourceConnectionSource.html">
    -		<code>DataSourceConnectionSource</code></a>, which is an
    -		implementation of <code>ConnectionSource</code> that obtains the
    -		<code>Connection</code> in the recommended JDBC manner based on a
    -		<code>javax.sql.DataSource</code>.
    -		</p>
    -	
    -    <p class="example">Example: <code>DBAppender</code> configuration (logback-examples/src/main/resources/chapters/appenders/db/append-with-datasource.xml)</p>	
    -
    -
    -    <span class="asGroovy" onclick="return asGroovy('append-with-datasource');">View as .groovy</span>	
    -    <pre id="append-with-datasource" class="prettyprint source">&lt;configuration  debug="true">
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
    -     <b>&lt;connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
    -       
    -       &lt;dataSource class="${dataSourceClass}">
    -       	 </b>&lt;!-- Joran cannot substitute variables
    -       	 that are not attribute values. Therefore, we cannot
    -       	 declare the next parameter like the others. 
    -       	 -->
    -         <b>&lt;param name="${url-key:-url}" value="${url_value}"/>
    -         &lt;serverName>${serverName}&lt;/serverName>
    -         &lt;databaseName>${databaseName}&lt;/databaseName>
    -       &lt;/dataSource></b>
    -       
    -       &lt;user>${user}&lt;/user>
    -       &lt;password>${password}&lt;/password>
    -     &lt;/connectionSource>
    -  &lt;/appender>
    -
    -  &lt;root level="INFO">
    -    &lt;appender-ref ref="DB" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -		<p>Note that in this configuration sample, we make heavy use of
    -		substitution variables.  They are sometimes handy when connection
    -		details have to be centralized in a single configuration file and
    -		shared by logback and other frameworks.
    -		</p>	
    -		
    -<!-- TO BE TESTED 
    -
    -     <p>The connection created by
    -     <code>DataSourceConnectionSource</code> can be placed in a JNDI
    -     context by using <code>BindDataSourceToJNDIAction</code>. In that
    -     case, one has to specify the use of this class by adding a new
    -     rule to Joran, logback's configuration framework. Here is an
    -     excerpt of such a configuration file.  </p>
    -		
    -<div class="source"><pre>&lt;configuration>
    -  ..
    -  <b>&lt;newRule pattern="configuration/bindDataSourceToJNDI" 
    -           actionClass="ch.qos.logback.core.db.BindDataSourceToJNDIAction"/>
    -  	    
    -  &lt;bindDataSourceToJNDI /></b>
    -  ..
    -&lt;/configuration></pre></div>
    -
    -		<p> The <em>newRule</em> element teaches Joran to use specified
    -		action class with the given pattern.  Then, we simply declare the
    -		given element. The action class will be called and our connection
    -		source will be bound to a JNDI context.  </p>
    -
    -		<p>This is a very powerful capability of Joran. If you'd like to
    -		read more about Joran, please see the <a
    -		href="onJoran.html">chapter to Joran</a>.  </p>
    -		
    -		-->
    -
    -    <h4 class="doAnchor"
    -    name="JNDIConnectionSource">JNDIConnectionSource</h4>
    -
    -		<p><a
    -		href="../xref/ch/qos/logback/core/db/JNDIConnectionSource.html">
    -		<code>JNDIConnectionSource</code></a> is another
    -		<code>ConnectionSource</code> implementation shipping in logback.
    -		As its name indicates, it retrieves a
    -		<code>javax.sql.DataSource</code> from a JNDI and then leverages
    -		it to obtain a <code>java.sql.Connection</code>
    -		instance. <code>JNDIConnectionSource</code> is primarily designed
    -		to be used inside J2EE application servers or by application
    -		server clients, assuming the application server supports remote
    -		access of <code>javax.sql.DataSource</code>.  Thus, one can take
    -		advantage of connection pooling and whatever other goodies the
    -		application server provides. More importantly, your application
    -		will be <a
    -		href="http://en.wikipedia.org/wiki/Don't_repeat_yourself">dryer</a>
    -		as it will be no longer necessary to define a
    -		<code>DataSource</code> in <em>logback.xml</em>.</p>
    -
    -    <p>For example, here is a configuration snippet for Tomcat. It
    -    assumes PostgreSQL as the database although any of the supported
    -    database systems (listed above) would work.</p>
    -
    -<pre  class="prettyprint source">&lt;Context docBase="/path/to/app.war" path="/myapp">
    -  ...
    -  &lt;Resource <b>name="jdbc/logging"</b>
    -               auth="Container"
    -               type="javax.sql.DataSource"
    -               username="..."
    -               password="..."
    -               driverClassName="org.postgresql.Driver"
    -               url="jdbc:postgresql://localhost/..."
    -               maxActive="8"
    -               maxIdle="4"/>
    -  ...
    -&lt;/Context></pre>
    -		
    -   <p>Once a <code>DataSource</code> is defined in the J2EE server, it
    -   can be easily referenced by your logback configuration file, as
    -   shown in the next example.</p>
    -   
    -   <p class="example">Example: <code>DBAppender</code> configuration
    -   by <code>JNDIConnectionSource</code>
    -   (logback-examples/src/main/resources/chapters/appenders/db/append-via-jndi.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('append-via-jndi');">View as .groovy</span>	
    - 
    -
    -<pre id="append-via-jndi" class="prettyprint source">&lt;configuration debug="true">
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
    -    &lt;connectionSource class=&quot;ch.qos.logback.core.db.JNDIConnectionSource&quot;&gt;
    -      <b>&lt;!-- please note the "java:comp/env/" prefix --&gt;</b>
    -      <b>&lt;jndiLocation>java:comp/env/jdbc/logging&lt;/jndiLocation></b>
    -    &lt;/connectionSource&gt;
    -  &lt;/appender>
    -  &lt;root level="INFO">
    -    &lt;appender-ref ref="DB" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -		<p>
    -			Note that this class will obtain an
    -			<code>javax.naming.InitialContext</code>
    -			using the no-argument constructor. This will usually work
    -			when executing within a J2EE environment. When outside the
    -			J2EE environment, make sure that you provide a
    -			<em>jndi.properties</em>
    -			file as described by your JNDI provider's documentation.
    -		</p>
    -		
    -		<h4 class="doAnchor">Connection pooling</h4>
    -		
    -		<p>Logging events can be created at a rather fast pace. To keep up
    -		with the flow of events that must be inserted into a database, it
    -		is recommended to use connection pooling with
    -		<code>DBAppender</code>.
    -		</p>
    -		
    -		<p>
    -			Experiment shows that using connection pooling with <code>DBAppender</code>
    -			gives a big performance boost. With the following
    -			configuration file, logging events are sent to a MySQL database,
    -			without any pooling.
    -		</p>
    -    
    -    <p class="example">Example: <code>DBAppender</code> configuration
    -    without pooling
    -    (logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('append-toMySQL-with-datasource');">View as .groovy</span>	
    -    <pre id="append-toMySQL-with-datasource" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
    -    &lt;connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource">
    -      &lt;dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource">
    -        &lt;serverName>${serverName}&lt;/serverName>
    -        &lt;port>${port$&lt;/port>
    -        &lt;databaseName>${dbName}&lt;/databaseName>
    -        &lt;user>${user}&lt;/user>
    -        &lt;password>${pass}&lt;/password>
    -      &lt;/dataSource>
    -    &lt;/connectionSource>
    -  &lt;/appender>
    -    
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="DB" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>With this configuration file, sending 500 logging events to a
    -		MySQL database takes a whopping 5 seconds, that is 10 milliseconds
    -		per request. This figure is unacceptable when dealing with large
    -		applications.
    -		</p>
    -
    -		<p>A dedicated external library is necessary to use connection
    -		pooling with <code>DBAppender</code>. The next example uses <a
    -		href="http://sourceforge.net/projects/c3p0">c3p0</a>. To be able
    -		to use c3p0, one must download it and place
    -		<em>c3p0-VERSION.jar</em> in the classpath.
    -		</p>
    -
    -    <p class="example">Example: <code>DBAppender</code> configuration
    -    with pooling
    -    (logback-examples/src/main/resources/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml)</p>
    -    <span class="asGroovy" onclick="return asGroovy('append-toMySQL-with-datasource-and-pooling');">View as .groovy</span>	
    -    <pre id="append-toMySQL-with-datasource-and-pooling" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
    -    &lt;connectionSource
    -      class="ch.qos.logback.core.db.DataSourceConnectionSource">
    -      <b>&lt;dataSource
    -        class="com.mchange.v2.c3p0.ComboPooledDataSource">
    -        &lt;driverClass>com.mysql.jdbc.Driver&lt;/driverClass>
    -        &lt;jdbcUrl>jdbc:mysql://${serverName}:${port}/${dbName}&lt;/jdbcUrl>
    -        &lt;user>${user}&lt;/user>
    -        &lt;password>${password}&lt;/password>
    -      &lt;/dataSource></b>
    -    &lt;/connectionSource>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="DB" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>With this new configuration, sending 500 logging requests to
    -		the aforementioned MySQL database takes around 0.5 seconds, for an
    -		average of 1 millisecond per request, that is a tenfold
    -		improvement in performance.
    -		</p>
    -
    -		<h3 class="doAnchor" name="SyslogAppender">SyslogAppender</h3>
    -
    -		<p>The syslog protocol is a very simple protocol: a syslog sender
    -		sends a small message to a syslog receiver.  The receiver is
    -		commonly called <em>syslog daemon</em> or <em>syslog server</em>.
    -		Logback can send messages to a remote syslog daemon. This is
    -		achieved by using <a
    -		href="../xref/ch/qos/logback/classic/net/SyslogAppender.html"><code>SyslogAppender</code></a>.
    -		</p>
    -		
    -		<p>Here are the properties you can pass to a SyslogAppender.</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Property Name</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">syslogHost</span></td>
    -				<td><code>String</code></td>
    -				<td>The host name of the syslog server.</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">port</span></td>
    -				<td><code>String</code></td>
    -				<td>The port number on the syslog server to connect
    -				to. Normally, one would not want to change the default value
    -				of <em>514</em>.
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">facility</span></td>
    -				<td><code>String</code></td>
    -				<td>
    -					<p>The <span class="prop">facility</span> is meant to identify 
    -						the source of a message.</p>
    -					<p>The <span class="prop">facility</span> option must be set
    -					to one of the strings <em>KERN, USER, MAIL, DAEMON, AUTH,
    -					SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, NTP, AUDIT,
    -					ALERT, CLOCK, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4,
    -					LOCAL5, LOCAL6, LOCAL7</em>. Case is not important.</p>
    -				</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="syslog">suffixPattern</span></td>
    -				<td><code>String</code></td>
    -				<td><p>The <span class="prop">suffixPattern</span> option
    -				specifies the format of the non-standardized part of the
    -				message sent to the syslog server. By default, its value is
    -				<em>[%thread] %logger %msg</em>. Any value that a
    -				<code>PatternLayout</code> could use is a correct <span
    -				class="prop">suffixPattern</span> value.
    -					</p>
    -				</td>
    -			</tr>
    -
    -      <tr>
    -        <td><span class="prop"
    -        container="syslog">stackTracePattern</span></td>
    -				<td><code>String</code></td>
    -				<td><p>The <span class="prop">stackTracePattern</span>
    -				property allows the customization of the string appearing just
    -				before each stack trace line. The default value for this
    -				property is "\t", i.e. the tab character. Any value accepted
    -				by <code>PatternLayout</code> is a valid value for <span
    -				class="prop">stackTracePattern</span>.</p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td><span class="prop" container="syslog">throwableExcluded</span></td>
    -				<td><code>boolean</code></td>
    -				<td>Setting <span class="prop">throwableExcluded</span> to
    -				<code>true</code> will cause stack trace data associated with
    -				a Throwable to be omitted. By default, <span
    -				class="prop">throwableExcluded</span> is set to
    -				<code>false</code> so that stack trace data is sent to the
    -				syslog server. </td>
    -			</tr>
    -
    -
    -		</table>
    -		
    -		<p>The syslog severity of a logging event is converted from the
    -		level of the logging event.  The <em>DEBUG</em> level is converted
    -		to <em>7</em>, <em>INFO</em> is converted to <em>6</em>,
    -		<em>WARN</em> is converted to <em>4</em> and <em>ERROR</em> is
    -		converted to <em>3</em>.
    -		</p>
    -		
    -		<p>Since the format of a syslog request follows rather strict
    -		rules, there is no layout to be used with
    -		<code>SyslogAppender</code>. However, using the <span
    -		class="prop">suffixPattern</span> option lets the user display
    -		whatever information she wishes.
    -		</p>
    -		
    -		<p>Here is a sample configuration using a
    -		<code>SyslogAppender</code>.</p>
    -		
    -    <p class="example">Example: <code>SyslogAppender</code> configuration (logback-examples/src/main/resources/chapters/appenders/conf/logback-syslog.xml)</p>	
    -    <span class="asGroovy" onclick="return asGroovy('logback-syslog');">View as .groovy</span>	
    -    <pre id="logback-syslog" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender">
    -    &lt;syslogHost>remote_home&lt;/syslogHost>
    -    &lt;facility>AUTH&lt;/facility>
    -    &lt;suffixPattern>[%thread] %logger %msg&lt;/suffixPattern>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SYSLOG" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>When testing this configuration, you should verify that the
    -		remote syslog daemon accepts requests from an external
    -		source. Experience shows that, by default, syslog daemons usually
    -		deny requests coming via a network connection.
    -		</p>
    -		
    -
    -    <h3 class="doAnchor" name="SiftingAppender">SiftingAppender</h3>
    -
    -    <p>As its name implies, a <code>SiftingAppender</code> can be used
    -    to separate (or sift) logging according to a given runtime
    -    attribute. For example, <code>SiftingAppender</code> can separate
    -    logging events according to user sessions, so that the logs
    -    generated by different users go into distinct log files, one log
    -    file per user.
    -    </p>
    -
    -
    -    
    -    <table class="bodyTable striped">
    -			<tr>
    -				<th>Property Name</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="sift">timeout</span></td>
    -				<td><code><a
    -				href="../apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -				<td>A nested appender which has not been accessed beyond the
    -				<span class="prop">timeout</span> duration is deemed stale. A
    -				stale appender is closed and unreferenced by
    -				<code>SiftingAppender</code>. The default value for <span
    -				class="prop">timeout</span> is 30 minutes.</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="sift">maxAppenderCount</span></td>
    -				<td><code>integer</code></td>
    -				<td>The maximum number of nested appenders
    -				<code>SiftingAppender</code> may create and track. Default
    -				value for <span class="prop">maxAppenderCount</span> is
    -				Integer.MAX_VALUE.</td>
    -			</tr>
    -  </table>
    -
    -    <p><code>SiftingAppender</code> achieves this feat by creating
    -    nested appenders on the fly. Nested appenders are created based on
    -    a template specified within the configuration of the
    -    <code>SiftingAppender</code> itself (enclosed within the
    -    <code>&lt;sift></code> element, see example
    -    below). <code>SiftingAppender</code> is responsible for managing
    -    the lifecycle of child appenders. For example,
    -    <code>SiftingAppender</code> will automatically close and remove
    -    any stale appender. A nested appender is considered stale when no
    -    accesses it beyond the duration specified by the <span
    -    class="prop">timeout</span> parameter.
    -    </p>
    -
    -    <p>When handling a logging event, <code>SiftingAppender</code>
    -    will select a child appender to delegate to. The selection
    -    criteria are computed at runtime by a discriminator. The user can
    -    specify the selection criteria with the help of a <code><a
    -    href="../xref/ch/qos/logback/core/sift/Discriminator.html">Discriminator</a></code>. Let
    -    us now study an example.
    -    </p>
    - 
    -    <h4>Example</h4>
    -
    -    <p>The <a
    -    href="../xref/chapters/appenders/sift/SiftExample.html">SiftExample</a>
    -    application logs a message stating that the application has
    -    started. It then sets the MDC key "userid" to "Alice" and logs a
    -    message. Here is the salient code:</p>
    -   
    -    <p class="source">logger.debug("Application started");
    -MDC.put("userid", "Alice");
    -logger.debug("Alice says hello"); </p>
    -
    -    <p>The template for the configuration file illustrates the use of
    -    <code>SiftingAppender</code>.</p>
    -
    -
    -    <p class="example">Example: <code>SiftingAppender</code>
    -    configuration
    -    (logback-examples/src/main/resources/chapters/appenders/sift/byUserid.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('byUserid');">View as .groovy</span>
    -
    -    <pre id="byUserid" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"></b>
    -    &lt;!-- in the absence of the class attribute, it is assumed that the
    -         desired discriminator type is
    -         ch.qos.logback.classic.sift.MDCBasedDiscriminator -->
    -    <b>&lt;discriminator></b>
    -      <b>&lt;key><span class="green">userid</span>&lt;/key></b>
    -      <b>&lt;defaultValue>unknown&lt;/defaultValue></b>
    -    <b>&lt;/discriminator></b>
    -    <b>&lt;sift></b>
    -      <b>&lt;appender name="FILE-<span class="green">${userid}</span>" class="ch.qos.logback.core.FileAppender"></b>
    -        <b>&lt;file><span class="green">${userid}</span>.log&lt;/file></b>
    -        <b>&lt;append>false&lt;/append></b>
    -        <b>&lt;layout class="ch.qos.logback.classic.PatternLayout"></b>
    -          <b>&lt;pattern>%d [%thread] %level %mdc %logger{35} - %msg%n&lt;/pattern></b>
    -        <b>&lt;/layout></b>
    -      <b>&lt;/appender></b>
    -    <b>&lt;/sift></b>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SIFT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -    
    -    
    -    <p>In the absence of a class attribute, it is assumed that the
    -    discriminator type is <a
    -    href="../xref/ch/qos/logback/classic/sift/MDCBasedDiscriminator.html">MDCBasedDiscriminator</a>.
    -    The discriminating value is the MDC value associated with the key
    -    given by the <span class="prop">key</span> property. However, if
    -    that MDC value is null, then <span
    -    class="prop">defaultValue</span> is used as the discriminating
    -    value.
    -    </p>
    -
    -    <p>The <code>SiftingAppender</code> is unique in its capacity to
    -    reference and configure child appenders. In the above example,
    -    <code>SiftingAppender</code> will create multiple
    -    <code>FileAppender</code> instances, each
    -    <code>FileAppender</code> instance identified by the value
    -    associated with the "userid" MDC key. Whenever the "userid" MDC
    -    key is assigned a new value, a new <code>FileAppender</code>
    -    instance will be built from scratch. The
    -    <code>SiftingAppender</code> keeps track of the appenders it
    -    creates. Appenders unused for 30 minutes will be automatically
    -    closed and discarded.
    -    </p>
    -
    -    <p><span class="label notice">Variable export</span> It is not
    -    enough to have different appender instances; each instance must
    -    output to a distinct target resource. To allow such
    -    differentiation, within the appender template, the key passed to
    -    the discriminator, "userid" in the above example, is exported and
    -    becomes a <a
    -    href="configuration.html#variableSubstitution">variable</a>. Consequently,
    -    this variable can be used to differentiate the actual resource
    -    used by a given child appender.
    -    </p>
    -
    -    <p>Running the <code>SiftExample</code> application with the
    -    "byUserid.xml" configuration file shown above, will result in two
    -    distinct log files, "unknown.log" and "Alice.log".
    -		</p>
    -
    -    <p><span class="label">local-scoped variables</span> As of version
    -    1.0.12, properties defined in local scope within the configuration
    -    file will be available to nested appenders. Moreover, you can <a
    -    href="configuration.html#definingProps">define variables</a> or <a
    -    href="configuration.html#definingPropsOnTheFly">dynamically
    -    compute</a> variables from <em>within</em> the the
    -    <code>&lt;sift></code> element. Combining a variable from parts
    -    defined outside and within the <code>&lt;sift></code> element is
    -    also supported.
    -    </p>
    -
    -    <h4 class="doAnchor" name="siftGettingTimeoutRight">Getting the
    -    <span class="prop">timeout</span> right</h4>
    -
    -    <p>For certain types of applications, it may be difficult to get
    -    the <span class="prop">timeout</span> parameter right. If the
    -    <span class="prop">timeout</span> is too small, a nested appender
    -    might be removed just to be created anew a few seconds later. This
    -    phenomenon is called <em>trashing</em>. If the <span
    -    class="prop">timeout</span> is too long and appenders are created
    -    in quick succession, you might run out of resources. Similarly,
    -    setting <span class="prop">maxAppenderCount</span> too low might
    -    cause trashing as well.
    -    </p>
    -
    -    <p>In many case, it may be easier to pinpoint a location in your
    -    code after which a nested appender is no longer needed. If such a
    -    location exists, even approximately, log from that location using
    -    the <a
    -    href="../apidocs/ch/qos/logback/classic/ClassicConstants.html#FINALIZE_SESSION_MARKER">FINALIZE_SESSION</a>
    -    marker. Whenever SiftingAppender sees a logging event marked as
    -    <code>FINALIZE_SESSION</code> it will end-of-life the associated
    -    nested appender. Upon reaching its end-of-life, a nested appender
    -    will linger for a few seconds to process any late coming events
    -    (if any) and then will be closed.
    -    </p>
    -    
    -    <pre class="prettyprint source">import org.slf4j.Logger;
    -import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;
    -
    -  void job(String jobId) {
    -   
    -    MDC.put("jobId", jobId);
    -    logger.info("Starting job.");
    -
    -    ... do whather the job needs to do
    -    
    -    // will cause the nested appender reach end-of-life. It will
    -    // linger for a few seconds.
    -    logger.info(FINALIZE_SESSION_MARKER, "About to end the job");
    -
    -    try {
    -      .. perform clean up
    -    } catch(Exception e);  
    -      // This log statement will be handled by the lingering appender. 
    -      // No new appender will be created.
    -      logger.error("unexpected error while cleaning up", e);
    -    }
    -  }
    -
    -</pre>
    -
    -    <h3 class="doAnchor" name="AsyncAppender">AsyncAppender</h3>
    -
    -    <p>AsyncAppender logs <a
    -    href="../apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvent</a>s
    -    asynchronously. It acts solely as an event dispatcher and must
    -    therefore reference another appender in order to do anything
    -    useful.</p>
    -
    -    <p><span class="label notice">Lossy by default if 80% full</span>
    -    AsyncAppender buffers events in a <a
    -    href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html">
    -    BlockingQueue</a>. A worker thread created by
    -    <code>AsyncAppender</code> takes events from the head of the
    -    queue, and dispatches them to the single appender attached to
    -    <code>AsyncAppender</code>. Note that by default,
    -    <code>AsyncAppender</code> will drop events of level TRACE, DEBUG
    -    and INFO if its queue is 80% full. This strategy has an amazingly
    -    favorable effect on performance at the cost of event loss.
    -    </p>
    -
    -    <p><span class="label">Application stop/redeploy</span> Upon
    -    application shutdown or redeploy, <code>AsyncAppender</code> must
    -    be stopped in order to stop and reclaim the worker thread and to
    -    flush the logging events from the queue. This can be achieved by
    -    <a href="configuration.html#stopContext">stopping the
    -    LoggerContext</a> which will close all appenders, including any
    -    <code>AsyncAppender</code> instances. <code>AsyncAppender</code>
    -    will wait for the worker thread to flush up to the timeout specified
    -    in <span class="prop">maxFlushTime</span>. If you find that queued events
    -    are being discarded during close of the <code>LoggerContext</code>, you
    -    may need to increase the time out. Specifying a value of 0 for 
    -    <span class="prop">maxFlushTime</span> will force the <code>AsyncAppender</code>
    -    to wait for all queued events to be flushed before returning from 
    -   	the stop method.
    -    </p>
    -    
    -    <p><span class="label">Post shutdown cleanup</span>
    -    Depending on the mode of JVM shutdown, the worker thread processing the 
    -    queued events can be interrupted causing events to be strandeds in the
    -    queue. This generally occurs when the <code>LoggerContext</code> is not
    -    stopped cleanly or when the JVM terminates outside of the typical control
    -    flow. In order to avoid interrupting the worker thread under these 
    -    conditions, a shutdown hook can be inserted to the JVM runtime that 
    -    <a href="configuration.html#stopContext">stops the LoggerContext properly</a>
    -    after JVM shutdown has been initiated. A shutdown hook may also be the
    -    preferred method for cleanly shutting down Logback when other shutdown hooks
    -    attempt to log events.
    -    </p>
    -
    -
    -    <p>Here is the list of properties admitted by
    -    <code>AsyncAppender:</code></p>
    -
    -		<table class="bodyTable striped">
    -      <tr>
    -        <th>Property Name</th>
    -        <th>Type</th>
    -        <th>Description</th>
    -      </tr>
    -			<tr>
    -        <td><span class="prop" container="async">queueSize</span></td>
    -        <td><code>int</code></td>
    -        <td>The maximum capacity of the blocking queue. By default,
    -        <span class="prop">queueSize</span> is set to 256.
    -				</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="async">discardingThreshold</span></td>
    -        <td><code>int</code></td>
    -        <td>By default, when the blocking queue has 20% capacity
    -        remaining, it will drop events of level TRACE, DEBUG and INFO,
    -        keeping only events of level WARN and ERROR. To keep all
    -        events, set <span class="prop">discardingThreshold</span> to
    -        0.
    -			</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="async">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>Extracting caller data can be rather expensive. To improve
    -        performance, by default, caller data associated with an event
    -        is not extracted when the event added to the event queue. By
    -        default, only "cheap" data like the thread name and the <a
    -        href="mdc.html">MDC</a> are copied. You can direct this
    -        appender to include caller data by setting the <span
    -        class="prop">includeCallerData</span> property to true.
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="async">maxFlushTime</span></td>
    -        <td><code>int</code></td>
    -        <td>Depending on the queue depth and latency to the referenced appender,
    -        the <code>AsyncAppender</code> may take an unacceptable amount of
    -        time to fully flush the queue. When the <code>LoggerContext</code> is 
    -        stopped, the <code>AsyncAppender stop</code> method waits 
    -        up to this timeout for the worker thread to complete. Use 
    -        <span class="prop">maxFlushTime</span> to specify a maximum queue flush
    -        timeout in milliseconds. Events that cannot be processed within this
    -        window are discarded. Semantics of this value are identical to that of 
    -        <a href="http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html#join(long)">Thread.join(long)</a>.
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="async">neverBlock</span></td>
    -        <td><code>boolean</code></td>
    -        <td>If <code>false</code> (the default) the appender will block on
    -        appending to a full queue rather than losing the message. Set to
    -        <code>true</code> and the appender will just drop the message and
    -        will not block your application.</td>
    -      </tr>
    -    </table>
    -
    -    <p>By default, event queue is configured with a maximum capacity
    -    of 256 events.  If the queue is filled up, then application
    -    threads are blocked from logging new events until the worker
    -    thread has had a chance to dispatch one or more events. When the
    -    queue is no longer at its maximum capacity, application threads
    -    are able to start logging events once again. Asynchronous logging
    -    therefore becomes pseudo-synchronous when the appender is
    -    operating at or near the capacity of its event buffer. This is not
    -    necessarily a bad thing. The appender is designed to allow the
    -    application to keep on running, albeit taking slightly more time
    -    to log events until the pressure on the appenders buffer eases.
    -    </p>
    -
    -    <p>Optimally tuning the size of the appenders event queue for
    -    maximum application throughput depends upon several factors. Any
    -    or all of the following factors are likely to cause
    -    pseudo-synchronous behavior to be exhibited:</p>
    -  
    -    <ul>
    -      <li>Large numbers of application threads</li>
    -      <li>Large numbers of logging events per application call</li>
    -      <li>Large amounts of data per logging event</li>
    -      <li>High latency of child appenders</li>
    -    </ul>
    -
    -    <p>To keep things moving, increasing the size of the queue will
    -    generally help, at the expense of heap available to the
    -    application.
    -    </p>
    -
    -    
    -    <p><span class="label notice">Lossy behavior</span> In light of
    -    the discussion above and in order to reduce blocking, by default,
    -    when less than 20% of the queue capacity remains,
    -    <code>AsyncAppender</code> will drop events of level TRACE, DEBUG
    -    and INFO keeping only events of level WARN and ERROR. This
    -    strategy ensures non-blocking handling of logging events (hence
    -    excellent performance) at the cost loosing events of level TRACE,
    -    DEBUG and INFO when the queue has less than 20% capacity. Event
    -    loss can be prevented by setting the <span
    -    class="prop">discardingThreshold</span> property to 0 (zero).
    -    </p>
    -
    -    <p class="example">Example: <code>AsyncAppender</code>
    -    configuration
    -    (logback-examples/src/main/resources/chapters/appenders/conc/logback-async.xml)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('asyncAppender');">View as .groovy</span>
    -
    -    <pre id="asyncAppender" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="<b>FILE</b>" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>myapp.log&lt;/file>
    -    &lt;encoder>
    -      &lt;pattern>%logger{35} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  <b>&lt;appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"></b>
    -    <b>&lt;appender-ref ref="FILE" /></b>
    -  <b>&lt;/appender></b>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="<b>ASYNC</b>" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -  
    -
    -		<h3 class="doAnchor" name="WriteYourOwnAppender">Writing your own
    -		Appender</h3>
    -
    -
    -    <p>You can easily write your appender by subclassing
    -    <code>AppenderBase</code>.  It handles support for filters, status
    -    messages and other functionality shared by most appenders.  The
    -    derived class only needs to implement one method, namely
    -    <code>append(Object eventObject)</code>.
    -    </p>
    -
    -    <p>The <code>CountingConsoleAppender</code>, which we list next,
    -    appends a limited number of incoming events on the console. It
    -    shuts down after the limit is reached.  It uses a
    -    <code>PatternLayoutEncoder</code> to format the events and accepts
    -    a parameter named <code>limit</code>. Therefore, a few more
    -    methods beyond <code>append(Object eventObject)</code> are
    -    needed. As shown below, these parameters are handles
    -    auto-magically by logback's various configuration mechanisms.
    -    </p>
    -    
    -    <em>Example 4.<span class="autoExec"/>:
    -    <code>CountingConsoleAppender</code>
    -    (logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java)</em>
    -    <pre class="prettyprint source">package chapters.appenders;
    -
    -import java.io.IOException;
    -
    -import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.AppenderBase;
    -
    -
    -public class CountingConsoleAppender extends AppenderBase&lt;ILoggingEvent> {
    -  static int DEFAULT_LIMIT = 10;
    -  int counter = 0;
    -  int limit = DEFAULT_LIMIT;
    -  
    -  PatternLayoutEncoder encoder;
    -  
    -  public void setLimit(int limit) {
    -    this.limit = limit;
    -  }
    -
    -  public int getLimit() {
    -    return limit;
    -  }
    -  
    -  @Override
    -  public void start() {
    -    if (this.encoder == null) {
    -      addError("No encoder set for the appender named ["+ name +"].");
    -      return;
    -    }
    -    
    -    try {
    -      encoder.init(System.out);
    -    } catch (IOException e) {
    -    }
    -    super.start();
    -  }
    -
    -  public void append(ILoggingEvent event) {
    -    if (counter >= limit) {
    -      return;
    -    }
    -    // output the events as formatted by our layout
    -    try {
    -      this.encoder.doEncode(event);
    -    } catch (IOException e) {
    -    }
    -
    -    // prepare for next event
    -    counter++;
    -  }
    -
    -  public PatternLayoutEncoder getEncoder() {
    -    return encoder;
    -  }
    -
    -  public void setEncoder(PatternLayoutEncoder encoder) {
    -    this.encoder = encoder;
    -  }
    -}</pre>
    -
    -		<p>The <code>start()</code> method checks for the presence of a
    -		<code>PatternLayoutEncoder</code>.  In case the encoder is not
    -		set, the appender fails to start and emits an error message.
    -		</p>
    -		
    -		<p>This custom appender illustrates two points:</p>
    -		
    -		<ul>
    -
    -      <li>All properties that follow the setter/getter JavaBeans
    -      conventions are handled transparently by logback
    -      configurators. The <code>start()</code> method, which is called
    -      automatically during logback configuration, has the
    -      responsibility of verifying that the various properties of the
    -      appender are set and are coherent.
    -			</li>
    -
    -			<li>The <code>AppenderBase.doAppend()</code> method invokes the
    -			append() method of its derived classes.  Actual output
    -			operations occur in the <code>append</code>() method.  In
    -			particular, it is in this method that appenders format events by
    -			invoking their layouts.
    -			</li>
    -		</ul>
    -		
    -		<p>The <a
    -		href="../xref/chapters/appenders/CountingConsoleAppender.html"><code>CountingConsoleAppender</code></a>
    -		can be configured like any other appender.  See sample
    -		configuration file
    -		<em>logback-examples/src/main/resources/chapters/appenders/countingConsole.xml</em>
    -		for an example.
    -		</p>
    -  
    -
    -		<h2 class="doAnchor" name="logback_access">Logback Access</h2>
    -		
    -		<p>Most of the appenders found in logback-classic have their
    -		equivalent in logback-access. These work essentially in the same
    -		way as their logback-classic counterparts. In the next section, we
    -		will cover their use.
    -		</p>
    -		
    -		<h3 class="doAnchor" name="AccessSocketAppender">SocketAppender
    -		and SSLSocketAppender</h3>
    -		
    -		<p>The <a
    -		href="../xref/ch/qos/logback/access/net/SocketAppender.html">
    -		<code>SocketAppender</code></a> is designed to log to a remote
    -		entity by transmitting serialized <code>AccessEvent</code> objects
    -		over the wire.  Remote logging is non-intrusive as far as the
    -		access event is concerned.  On the receiving end after
    -		deserialization, the event can be logged as if it were generated
    -		locally.
    -		</p>
    -		
    -		<p>The <a href="../xref/ch/qos/logback/access/net/SSLSocketAppender.html">
    -    <code>SSLSocketAppender</code></a> extends the basic 
    -    <code>SocketAppender</code> allowing logging to a remote entity over
    -    the Secure Sockets Layer (SSL).
    -    </p>
    -    
    -		<p>
    -			The properties of access' <code>SocketAppender</code> are the same as those available
    -			for classic's <code>SocketAppender</code>.
    -		</p>
    -
    -    <h3 class="doAnchor"
    -    name="AccessServerSocketAppender">ServerSocketAppender and
    -    SSLServerSocketAppender</h3>
    -    
    -    <p>Like <code>SocketAppender</code>, the <a
    -    href="../xref/ch/qos/logback/access/net/server/ServerSocketAppender.html">
    -    <code>ServerSocketAppender</code></a> is designed to log to a remote
    -    entity by transmitting serialized <code>AccessEvent</code> objects
    -    over the wire.  However, when using <code>ServerSocketAppender</code>
    -    the appender acts as a server, passively listening on a TCP socket awaiting
    -    inbound connections from interested clients.  Logging events delivered
    -    to the appender are distributed to all connected clients.
    -    </p>
    -    
    -    <p>The <a href="../xref/ch/qos/logback/access/net/server/SSLServerSocketAppender.html">
    -    <code>SSLSocketAppender</code></a> extends the basic 
    -    <code>ServerSocketAppender</code> allowing logging to a remote entity
    -    over the Secure Sockets Layer (SSL).
    -    </p>
    -    
    -    <p>The properties of access' <code>ServerSocketAppender</code> are
    -    the same as those available for classic's
    -    <code>ServerSocketAppender</code>.
    -    </p>
    -    
    -	 	
    -		<h3 class="doAnchor" name="AccessSMTPAppender">SMTPAppender</h3>
    -		
    -		<p>Access' <a
    -		href="../xref/ch/qos/logback/access/net/SMTPAppender.html">
    -		<code>SMTPAppender</code></a> works in the same way as its Classic
    -		counterpart.  However, the <span class="prop">evaluator</span>
    -		option is rather different.  By default, a
    -		<code>URLEvaluator</code> object is used by
    -		<code>SMTPAppender</code>. This evaluator contains a list of URLs
    -		that are checked against the current request's URL. When one of
    -		the pages given to the <code>URLEvaluator</code> is requested,
    -		<code>SMTPAppender</code> sends an email.
    -		</p>
    -		
    -		<p>
    -			Here is a sample configuration of a <code>SMTPAppender</code> in the access environment.
    -		</p>
    -    <p class="example">Example: <code>SMTPAppender</code>
    -    configuration
    -    (logback-examples/src/main/resources/chapters/appenders/conf/access/logback-smtp.xml)</p>
    -
    -<pre class="prettyprint source">&lt;appender name="SMTP"
    -  class="ch.qos.logback.access.net.SMTPAppender">
    -  &lt;layout class="ch.qos.logback.access.html.HTMLLayout">
    -    &lt;pattern>%h%l%u%t%r%s%b&lt;/pattern>
    -  &lt;/layout>
    -    
    -  <b>&lt;Evaluator class="ch.qos.logback.access.net.URLEvaluator">
    -    &lt;URL>url1.jsp&lt;/URL>
    -    &lt;URL>directory/url2.html&lt;/URL>
    -  &lt;/Evaluator></b>
    -  &lt;from>sender_email@host.com&lt;/from>
    -  &lt;smtpHost>mail.domain.com&lt;/smtpHost>
    -  &lt;to>recipient_email@host.com&lt;/to>
    -&lt;/appender></pre>
    -
    -		<p>This way of triggering the email lets users select pages that
    -		are important steps in a specific process, for example.  When such
    -		a page is accessed, the email is sent with the pages that were
    -		accessed previously, and any information the user wants to be
    -		included in the email.
    -		</p>
    -
    -		<h3 class="doAnchor" name="AccessDBAppender">DBAppender</h3>
    -		
    -		<p><a
    -		href="../xref/ch/qos/logback/access/db/DBAppender.html"><code>DBAppender</code></a>
    -		is used to insert the access events into a database.
    -		</p>
    -
    -		<p>Two tables are used by <code>DBAppender</code>:
    -		<em>access_event</em> and <em>access_event_header</em>. They both
    -		must exist before <code>DBAppender</code> can be used. Logback
    -		ships with SQL scripts that will create the tables.  They can be
    -		found in the
    -		<em>logback-access/src/main/java/ch/qos/logback/access/db/script</em>
    -		directory. There is a specific script for each of the most popular
    -		database systems.  If the script for your particular type of
    -		database system is missing, it should be quite easy to write one,
    -		taking as example one of the existing scripts. You are encouraged
    -		to contribute such missing scripts back to the project.
    -		</p>
    -		
    -		<p>The <em>access_event</em> table's fields are described below:</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Field</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><b>timestamp</b></td>
    -				<td><code>big int</code></td>
    -				<td>The timestamp that was valid at the access event's creation.</td>
    -			</tr>
    -			<tr>
    -				<td><b>requestURI</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The URI that was requested.</td>
    -			</tr>
    -			<tr>
    -				<td><b>requestURL</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The URL that was requested. This is a string composed of the request method,
    -				the request URI and the request protocol.
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteHost</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The name of the remote host.</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteUser</b></td>
    -				<td><code>varchar</code></td>
    -				<td>
    -					The name of the remote user.
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteAddr</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The remote IP address.</td>
    -			</tr>
    -			<tr>
    -				<td><b>protocol</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The request protocol, like <em>HTTP</em> or <em>HTTPS</em>.</td>
    -			</tr>
    -			<tr>
    -				<td><b>method</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The request method, usually <em>GET</em> or <em>POST</em>.</td>
    -			</tr>
    -			<tr>
    -				<td><b>serverName</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The name of the server that issued the request.</td>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>The database id of the access event.</td>
    -			</tr>
    -		</table>
    -		
    -		<p>
    -			The <em>access_event_header</em> table contains the header of each
    -			request. The information is organised as shown below:
    -		</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Field</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>The database id of the corresponding access event.</td>
    -			</tr>
    -			<tr>
    -				<td><b>header_key</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The header name, for example <em>User-Agent</em>.</td>
    -			</tr>
    -			<tr>
    -				<td><b>header_value</b></td>
    -				<td><code>varchar</code></td>
    -				<td>The header value, for example <em>Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0</em></td>
    -			</tr>
    -			</table>
    -
    -		<p>All properties of classic's <code>DBAppender</code> are available
    -			in access's <code>DBAppender</code>. The latter offers one more option,
    -			described below.
    -		</p>
    -		
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>Property Name</th>
    -				<th>Type</th>
    -				<th>Description</th>
    -			</tr>
    -			<tr>
    -				<td>
    -					<b>
    -						<span class="prop">insertHeaders</span>
    -					</b>
    -				</td>
    -				<td>
    -					<code>boolean</code>
    -				</td>
    -				<td>
    -					Tells the <code>DBAppender</code> to populate the database with the header
    -					information of all incoming requests.
    -				</td>
    -			</tr>
    -		</table>
    -
    -		<p>Here is a sample configuration that uses <code>DBAppender</code>.</p>
    -
    -    <p class="example">Example: DBAppender configuration <em>(logback-examples/src/main/resources/chapters/appenders/conf/access/logback-DB.xml)</em></p>
    -    
    -    <pre class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="DB" class="ch.qos.logback.access.db.DBAppender">
    -    &lt;connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource">
    -      &lt;driverClass>com.mysql.jdbc.Driver&lt;/driverClass>
    -      &lt;url>jdbc:mysql://localhost:3306/logbackdb&lt;/url>
    -      &lt;user>logback&lt;/user>
    -      &lt;password>logback&lt;/password>
    -    &lt;/connectionSource>
    -    &lt;insertHeaders>true&lt;/insertHeaders>
    -  &lt;/appender>
    -
    -  &lt;appender-ref ref="DB" />
    -&lt;/configuration></pre>
    -
    -
    -    <h3 class="doAnchor"
    -    name="AccessSiftingAppender">SiftingAppender</h3>
    -   
    -    <p>The SiftingAppender in logback-access is quite similar to its
    -    logback-classic counterpart. The main difference is that in
    -    logback-access the default discriminator, namely <a
    -    href="../xref/ch/qos/logback/access/sift/AccessEventDiscriminator.html">AccessEventDiscriminator</a>,
    -    is not MDC based. As its name suggests, AccessEventDiscriminator,
    -    uses a designated field in AccessEvent as the basis for selecting a
    -    nested appender. If the value of the designated field is null,
    -    then the value specified in the <span
    -    class="prop">defaultValue</span> property is used.
    -    </p>
    -
    -    <p>The designated AccessEvent field can be one of COOKIE,
    -    REQUEST_ATTRIBUTE, SESSION_ATTRIBUTE, REMOTE_ADDRESS, LOCAL_PORT,
    -    REQUEST_URI. Note that the first three fields require that the
    -    <span class="prop">AdditionalKey</span> property also be
    -    specified.</p>
    -    
    -    <p>Below is an example configuration file.</p>
    -
    -    <p class="example">Example: SiftingAppender configuration (logback-examples/src/main/resources/chapters/appenders/conf/sift/access-siftingFile.xml)</p>
    -    
    -    <pre class="prettyprint source">&lt;configuration>
    -  &lt;appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender">
    -    &lt;Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator">
    -      &lt;Key>id&lt;/Key>
    -      &lt;FieldName>SESSION_ATTRIBUTE&lt;/FieldName>
    -      &lt;AdditionalKey>username&lt;/AdditionalKey>
    -      &lt;defaultValue>NA&lt;/defaultValue>
    -    &lt;/Discriminator>
    -    &lt;sift>
    -       &lt;appender name="${id}" class="ch.qos.logback.core.FileAppender">
    -        &lt;file>byUser/${id}.log&lt;/file>
    -        &lt;layout class="ch.qos.logback.access.PatternLayout">
    -          &lt;pattern>%h %l %u %t \"%r\" %s %b&lt;/pattern>
    -        &lt;/layout>
    -      &lt;/appender>
    -    &lt;/sift>
    -  &lt;/appender>
    -  &lt;appender-ref ref="SIFTING" />
    -&lt;/configuration></pre>
    -
    -
    -    <p>In the above configuration file, a <code>SiftingAppender</code>
    -    nests <code>FileAppender</code> instances. The key "id" is
    -    designated as a variable which will be available to the nested
    -    <code>FileAppender</code> instances. The default discriminator,
    -    namely <code>AccessEventDiscriminator</code>, will search for a
    -    "username" session attribute in each <code>AccessEvent</code>. If
    -    no such attribute is available, then the default value "NA" will
    -    be used.  Thus, assuming the session attribute named "username"
    -    contains the username of each logged on user, there will be a log
    -    file under the <em>byUser/</em> folder (of the current folder)
    -    named after each user containing the access logs for that user.
    -    </p>
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -
    -  </div>
    -  
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/appenders_ja.html b/logback-site/src/site/pages/manual/appenders_ja.html
    deleted file mode 100644
    index 476e213543..0000000000
    --- a/logback-site/src/site/pages/manual/appenders_ja.html
    +++ /dev/null
    @@ -1,2871 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第4章 アペンダー</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content" class="chapter">
    -
    -    <h1>第4章 アペンダー</h1>
    -
    -    <div class="quote">
    -
    -      <p><em>西部について教えたいことが多すぎるのでどこから始めたらいいのかわからないよ。1つを選べば残りの100を捨てることになってしまう。最初の1つを決めるにはどうしたいいんだろう?</em></p>
    -  
    -      <p>—JOHN STEINBECK, <em>East of Eden</em></p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -    
    -    <h2 class="doAnchor" name="whatIsAnAppender">アペンダーについて</h2>
    -    
    -		<p>logback はロギングイベントを出力する仕事を、アペンダーと呼ばれるコンポーネントに任せています。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/Appender.html"><code>ch.qos.logback.core.Appender</code></a>インターフェイスを実装したものがアペンダーとして利用できます。このインターフェイスに宣言された重要なメソッドは次のとおりです。</p>
    -		<pre class="prettyprint source">package ch.qos.logback.core;
    -  
    -import ch.qos.logback.core.spi.ContextAware;
    -import ch.qos.logback.core.spi.FilterAttachable;
    -import ch.qos.logback.core.spi.LifeCycle;
    -  
    -
    -public interface Appender&lt;E&gt; extends LifeCycle, ContextAware, FilterAttachable {
    -
    -  public String getName();
    -  public void setName(String name);
    -  <b>void doAppend(E event);</b>
    -  
    -}</pre>
    -
    -	<p><code>Appender</code>インターフェイスのほとんどのメソッドはゲッター、あるいはセッターです。<code>doAppend()</code>メソッドだけは特別で、唯一の引数として型<em>E</em>のオブジェクトを取ります。<em>E</em>の実際の型は、logbackモジュールによって異なります。logback-classic モジュールの場合、<em>E</em>は<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvent</a>になるでしょうし、logback-access モジュールでは<a href="http://logback.qos.ch/apidocs/ch/qos/logback/access/spi/AccessEvent.html">AccessEvent</a>になるでしょう。<code>doAppend()</code>メソッドは、おそらくlogbackフレームワークの中で最も重要なメソッドです。ロギングイベントを、適切な書式で、適切な出力デバイスに出力する役目があります。
    -  </p>
    -
    -  <p>アペンダーには名前を付けられます。設定ファイル中で参照しやすいようにするためです。<code>Appender</code>インターフェイスは<code>FilterAttachable</code>インターフェイスを継承しています。つまり、アペンダーのインスタンスには一つ以上のフィルターを割り当てられるのです。フィルターについては、後の章で詳しく説明します。
    -	</p>
    -	
    -	<p>アペンダーには、ロギングイベントを出力することについて最終的な責任があります。しかし、アペンダー自体が書式化をするのではなく、<code>Layout</code>あるいは<code>Encoder</code>オブジェクトに処理を委譲します。レイアウトやエンコーダーは、ただ一つのアペンダーにだけ関連付けられ、そのアペンダーだけが参照することになります。アペンダーによっては、組み込みの、あるいは固定の書式が指定されています。そういうアペンダーには、レイアウトもエンコーダーも不要です。たとえば、 <code>SocketAppender</code>は、ロギングイベントを接続したリモートホストに転送する前に単純にシリアライズするだけです。
    -	</p>
    -	
    -	
    -	<h2 class="doAnchor" name="AppenderBase">AppenderBase</h2>
    -	
    -	<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/AppenderBase.html">ch.qos.logback.core.AppenderBase</a></code>は、<code>Appender</code>インターフェイスを実装した抽象クラスです。このクラスは、どんなアペンダーにも必要な共通の機能として、名前や活性化状態やレイアウト、フィルターに対するゲッターおよびセッターメソッドが実装されています。logback の配布物に含まれるアペンダーはすべてこのクラスの派生クラスです。<code>AppenderBase</code>は抽象クラスですが、<code>Append</code>インターフェイスの<code>doAppend()</code>メソッドを実装しています。<code>AppenderBase</code>クラスの話は、実際にソースコードの抜粋を見ながら進めるのが一番わかりやすいと思います。
    -	</p>
    -	
    -<pre class="prettyprint source">public synchronized void doAppend(E eventObject) {
    -
    -  // prevent re-entry.
    -  if (guard) {
    -    return;
    -  }
    -
    -  try {
    -    guard = true;
    -
    -    if (!this.started) {
    -      if (statusRepeatCount++ &lt; ALLOWED_REPEATS) {
    -        addStatus(new WarnStatus(
    -            "Attempted to append to non started appender [" + name + "].",this));
    -      }
    -      return;
    -    }
    -
    -    if (getFilterChainDecision(eventObject) == FilterReply.DENY) {
    -      return;
    -    }
    -    
    -    // ok, we now invoke the derived class's implementation of append
    -    this.append(eventObject);
    -
    -  } finally {
    -    guard = false;
    -  }
    -}</pre>
    -	
    -	<p><code>doAppend()</code>メソッドはsynchronizedメソッドになっています。つまり、別のスレッドから同じアペンダーに安全にロギングできるのです。スレッド<em>T</em>が<code>doAppend()</code>メソッドを実行している間は、<em>T</em>だけが排他的にアペンダーにアクセスできるので、他のスレッドからの呼び出しはキューイングされます。
    -	</p>
    -
    -  <p>synchronized が不適切な場合もあります。そういうときのために、logback の配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/core/AppenderBase.html"><code>AppenderBase</code></a>とよく似た<a href="http://logback.qos.ch/xref/ch/qos/logback/core/UnsynchronizedAppenderBase.html"><code>ch.qos.logback.core.UnsynchronizedAppenderBase</code></a>も含まれています。ややこしくなるので、<code>UnsynchronizedAppenderBase</code>については本章の後半で説明します。
    -  </p>
    -
    -
    -  <p><code>doAppend()</code>メソッドでは、まず最初にguard変数にtrueが設定されているかどうかをチェックします。trueだったら、ただちに終了します。そうでなければ、まずguard変数にtrueを設定してから次の処理に進みます。guard変数によって、<code>doAppend()</code>メソッドを再帰的に呼び出してしまうことを防いでいるのです。あるコンポーネントが、ログを取得するために<code>append()</code>というメソッドを呼び出したとしましょう。そうすると、今呼び出したアペンダーと同じアペンダーを直接呼び出すことになってしまうので、無限ループした結果スタックオーバーフローが発生してしまいます。
    -	</p>
    -	
    -	<p>次の式では<code>started</code>フィールドにtrueが設定されているかどうかをチェックします。trueでなければ、警告メッセージを出力して<code>doAppend()</code>メソッドは終了します。これは、クローズされたアペンダーには何も書き込めなくなるということです。<code>Appender</code>オブジェクトは<code>LifeCycle</code>インターフェイスを実装しているので、つまり<code>start()</code>メソッド、<code>stop()</code>メソッド、<code>isStarted()</code>メソッドを実装しています。Joran設定フレームワークは、アペンダーの全てのプロパティを設定してから、アペンダーを開始して活性化状態をアクティブにするため<code>start()</code>メソッドを呼び出します。アペンダーにもよりますが、指定されていないプロパティがあったり、プロパティに指定した値の影響で、開始できないことがあります。たとえば、<code>FIleAppender</code>の場合ファイルを作成するかどうかは指定された切り捨てモードに依存しています。ですので、<code>File</code>オプションが指定されていたとしても、Appendオプションに正しい値が指定されていなければ、正常に動作しません。開始するまでの処理順序が明確なので、アペンダーは自身のプロパティがちゃんと設定されていることを<em>前提として</em>動作することができます。
    -	</p>
    -	
    -	<p>アペンダーが開始できなかったとき、あるいは、停止してしまったときは、logback の内部状態管理システムによって警告メッセージが出力されます。何度か(ロギングを?)試みたあと、同じ内容の警告メッセージで溢れかえってしまうのを避けるため、<code>doAppend()</code>メソッドは警告メッセージを出力しないようになります。
    -  </p>
    -
    -	<p>その次の<code>if</code>文では割り当てられたフィルターの応答をチェックします。フィルターチェインの応答によって、ロギングイベントを拒否するか受け入れるかが決まります。フィルターチェインが何も決めなかったら、デフォルトでロギングイベントは受け入れられるようになっています。
    -	</p>
    -	
    -	<p>その後は、派生クラスで実装されてた<code>append()</code>メソッドを呼び出します。このメソッドは、実際に適切なデバイスへロギングイベントを出力します。
    -	</p>
    -	
    -  <p>最後にguard変数が開放され、キューイングされた<code>doAppend()</code>メソッドの呼び出しが処理できるようになります。
    -  </p>
    -
    -	<p>ここの説明では "プロパティ" という言葉の代わりに "オプション" という言葉を使いました。これは、JavaBeanの規約に従ったゲッターおよびセッターメソッドが用意された属性のことです。</p>
    -	
    -	<h1>logback-coreモジュール</h1>
    -	
    -	<p>logback-coreモジュールは、他のモジュールを構築する基盤となるモジュールです。一般的に、logbac-core モジュールのコンポーネントは、ある程度の(少なくとも最小限の)カスタマイズが必要です。以降の節では、カスタマイズしないでもすぐに利用できるアペンダーを紹介していきます。
    -  </p>
    -
    -
    -	
    -	<h2 class="doAnchor" name="OutputStreamAppender">OutputStreamAppender</h2>
    -	
    -	<p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/OutputStreamAppender.html"><code>OutputStreamAppender</code></a>は<code>java.io.OutputStream</code>にロギングイベントを出力します。このクラスは他のアペンダーを構築するための基本的なサービスを提供するものです。普通なら、利用者が<code>OutputStreamAppender</code>を直接インスタンス化することはありません。<code>java.io.OutputStream</code>を文字列で表現することはできないので、設定ファイルから<code>OutputStream</code>オブジェクトを指定することはできないのです。簡単に言うと、設定ファイルでは<code>OutputStreamAppender</code>を設定することはできません。しかし、<code>OutputStreamAppender</code>に設定できるプロパティが無いという意味ではありません。次のようなプロパティがあります。
    -	</p>
    -	
    -  <table class="bodyTable striped">
    -    <tr>
    -      <th>プロパティ名</th>
    -      <th>型</th>
    -      <th>説明</th>
    -    </tr>
    -    
    -    <tr>
    -      <td><span class="prop" name="osaEncoder">encoder</span></td>
    -
    -      <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a></td>
    -
    -      <td><code>OutputStreamAppender</code>への出力を書式化する。エンコーダについては<a href="./05-encoders.html">別の章</a>で説明しています。
    -			</td>
    -		</tr>
    -	
    -	</table>
    -    
    -  <p><code>OutputStreamAppender</code>は<code>ConsoleAppender</code> 、 <code>FileAppender</code>の基底クラスです。<code>RollingFileAppender</code>の基底クラスはFileAppenderなので、これも含めておきます。次の図は<code>OutputStreamAppender</code>とそのサブクラスの関係を図示したものです。
    -	</p>
    -	
    -	<img src="images/chapters/appenders/appenderClassDiagram.jpg" alt="OutputStreamAppenderとサブクラスを示すUML図">
    -	
    -
    -	<h2 class="doAnchor" name="ConsoleAppender">ConsoleAppender</h2>
    -	
    -  <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/ConsoleAppender.html">ConsoleAppender</a></code>は名前のとおり、ロギングイベントをコンソールに出力します。正確に言うと、<em>System.out</em>あるいは<em>System.err</em>に出力します。デフォルトではSystem.outが使われます。<code>ConsoleAppender</code>は、利用者が指定したエンコーダーを使ってロギングイベントを書式化します。エンコーダーについては別の章で説明します。<em>System.out</em>と<em>System.err</em>の型は<code>java.io.PrintStream</code>です。つまり、いずれもバッファIO操作のための<code>OutputStreamWriter</code>にラップされているということです。
    -	</p>
    -	
    -	<table class="bodyTable striped">
    -			<tr>
    -			<th>プロパティ名</th>
    -			<th>型</th>
    -			<th>説明</th>
    -		</tr>
    -		<tr>
    -			<td><span class="prop" container="conApp">encoder</span></td>
    -      <td>
    -        <a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -      </td>
    -			<td><code>OutputStreamAppender</code>と同じです。</td>
    -		</tr>
    -		<tr>
    -			<td><span class="prop" container="conApp">target</span></td>
    -			<td><code>String</code></td>
    -			<td>文字列で、<em>System.out</em>か<em>System.err</em>を指定します。デフォルトは<em>System.outに</em>です。
    -			</td>
    -		</tr>
    -
    -		<tr>
    -			<td><span class="prop" container="conApp">withJansi</span></td>
    -			<td><code>boolean</code></td>
    -			<td><span class="prop">withJansi</span>プロパティの値にはデフォルトで<code>false</code>が設定されています。<span class="prop">withJansi</span>プロパティに<code>true</code>を指定すると、<a href="http://jansi.fusesource.org/">Jansi</a>ライブラリが有効化され、Windowsマシン上でANSIカラーコードがサポートされるようになります。Windows のホスト上でこのプロパティにtrueを指定する場合、クラスパス上にjansiライブラリのjarファイルを置かなければなりません。なお、UnixベースのOSであるLinuxやMac OS Xのターミナルは、デフォルトでANSIカラーコードをサポートしています。
    -
    -      <p>Eclipse IDEから利用する場合は、<a href="http://www.mihai-nita.net/eclipse/">ANSI in Eclipse Console</a>プラグインをインストールしてみましょう。
    -      </p>
    -			</td>
    -		</tr>
    -
    -	</table>
    -	
    -	<p><code>ConsoleAppender</code>の設定サンプルを見てください。
    -	</p>
    -
    -
    -
    -  <p class="example">例:ConsoleAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-Console.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-Console.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;logback_Console&#39;);">Groovyで表示</span>
    -
    -  <pre id="logback_Console" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg %n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;</b>
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p><em>logback-examples</em>ディレクトリに移動して、<a href="http://logback.qos.ch/setup.html">クラスパスを設定</a>すれば、次のコマンドで上の設定ファイルを使って実行できます。</p>
    -
    -   <p class="source">java <a href="http://logback.qos.ch/xref/chapters/appenders/ConfigurationTester.html">chapters.appenders.ConfigurationTester</a> src/main/java/chapters/appenders/conf/logback-Console.xml</p>
    -	
    -	
    -   <h2 class="doAnchor" name="FileAppender">FileAppender</h2>
    -	
    -   <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/FileAppender.html"><code>FileAppender</code></a>は<code>OutputStreamAppender</code>の派生クラスで、ファイルにロギングイベントを出力します。<span class="prop">file</span>オプションで対象のファイルを指定します。既にファイルが存在するとき、ファイル内容を切り捨てるか、追加するかどうかは、<span class="prop">append</span>プロパティの値に応じて決まります。
    -   </p>
    -	
    -   <table class="bodyTable properties striped">
    -     <tr>
    -       <th>プロパティ名</th>
    -       <th>型</th>
    -       <th>説明</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fileApppender">append</span></td>
    -       <td><code>boolean</code></td>
    -       <td>trueの場合、既存のファイルの末尾にロギングイベントを追加します。<span class="prop">append</span>プロパティがfalseの場合、既存のファイルの内容は捨てられてしまいます。デフォルトは<span class="option">true</span>です。
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fileApppender">encoder</span></td>
    -       <td>
    -         <a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -       </td>
    -       <td><code>OutputStreamAppender</code>と同じです。</td>
    -     </tr>
    -    
    -   
    -     <tr>
    -       <td><span class="prop" container="fileApppender">file</span></td>
    -       <td><code>String</code></td>
    -       <td>書き込み先のファイル名です。ファイルが存在しない場合、新しく作成します。Windowsプラットフォームの利用者は、バックスラッシュをエスケープするの忘れがちなので注意してください。たとえば、<em>c:\temp\test.log</em>という文字列は意図したように解釈されません。<em>'\t'</em>はエスケープシーケンスの<em>タブ文字{\u0009}</em>として解釈されるでしょう。<em>c:/temp/test.log</em>と書くか、<em>c:\\temp\\test.log</em>のように書けばよいでしょう。デフォルト値はありません。
    -
    -       <p>指定したファイルの親ディレクトリが存在しない場合、 <code>FileAppender</code>は途中の全てのディレクトリを作成します。
    -       </p>
    -       </td>
    -     </tr>
    -   
    -
    -     <tr>
    -       <td><span class="prop" name="prudent">prudent</span></td>
    -       <td><code>boolean</code></td>
    -
    -       <td>prudentモードでは、指定されたファイルに安全に書き込むようになります。同じファイルを対象にした他の<code>FileAppender</code>がいるとしても、それが別のJVMで動いているとしても、ましてや別のホストで動いているとしても、です。デフォルト値は<code>false</code>です。
    -
    -         <p>prudent モードは、<a href="./appenders.html#prudentWithRolling">多少の制限</a>はあるものの、<code>RollingFileAppender</code>と組み合わせて利用することもできます。</p>
    -
    -         <p>prudent モードがtrueなら、<span class="prop">append</span>プロパティも自動的にtrueになります。
    -         </p>
    -
    -         <p>prudentモードは排他的なファイルロックを使用します。ファイルロックを使うと、ロギングイベントの出力にかかるコストがおよそ3倍程度になることが分かっています。標準的なPCで、<b>ローカル</b>のハードディスク上のファイルへ一つロギングイベントを出力をするとき、prudentモードがオフの場合はおよそ10マイクロ秒かかります。prudentモードをオンにすると、30マイクロ秒になります。言い換えると、prudentモードがオフの場合は毎秒100,000回のロギングイベントが処理できるのに対して、prudentモードがオンの場合は毎秒33,000回しか処理できなくなるということです。
    -         </p>
    -
    -         <p>prudentモードでは、複数のJVMから同じファイルに行うIO操作を効果的にシリアライズします。したがって、JVMの数が多くなればなるほど、IO操作ごとの待ち時間が長くなります。IO操作の<em>合計時間</em>が、毎秒20回オーダーのロギング要求が処理できる程度であれば、性能への影響は無視してもよいでしょう。アプリケーションが毎秒100回以上のIO操作をするようであれば、きっと性能影響があるので<span class="prop">prudent</span>モードを使うのはやめてください。
    -         </p>
    -
    -         <p><span class="label">ネットワークファイルロック</span>ネットワークファイルシステム上のログファイルを対象にすると非常にコストが高くなります。ネットワークファイルシステム越しのファイルロックは、プロセスがすでに所有しているロックをリリースする前に再取得する、という振る舞いに強く依存していることも同じくらい重要です。したがって1つのプロセスがログファイルを占有していると、他のプロセスからはデッドロックしているように見えるので、ロックを待ち続けることになってしまいます。
    -         </p>
    -         
    -         <p>prudentモードは、ネットワークの速度と同じくらいの影響をOSの実装からも受けます。私たちの提供しているとても小さいアプリケーションの<a href="https://gist.github.com/2794241">FileLockSimulator</a>を使えば、あなたの環境でprudentモードがどのように振る舞うかシミュレートできます。
    -         </p>
    -
    -
    -       </td>
    -       
    -     </tr>
    -   </table>
    -	
    -   <p><span class="label notice">即時フラッシュ</span>
    -デフォルトでは、それぞれのロギングイベントは、最終的な出力ストリームに即時にフラッシュされます。これは、あなたのアプリケーションがアペンダーをちゃんとクローズしていない場合、ロギングイベントが失われないようにするためのより安全な方法です。しかし、ロギング要求のスループットが大幅に増加してしまう場合には、<code>Encorder</code>の<span class="prop">immediateFlush</span>プロパティに<code>false</code>を指定することもできます。エンコーダー、特に<a href="./encoders.html#LayoutWrappingEncoder"><code>LayoutWrappingEncoder</code></a>については別の章で説明します。</p>
    -
    -   <p><code>FileAppender</code>の設定例を見てください。</p>
    -
    -   <p class="example">例:FileAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-fileAppender.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-fileAppender.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;logback-fileAppender&#39;);">Groovyとして表示</span>
    -   <pre id="logback-fileAppender" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;testFile.log&lt;/file&gt;
    -    &lt;append&gt;true&lt;/append&gt;
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;</b>
    -	
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p><em>logback-examples</em>ディレクトリに移動すれば、次のコマンドで上の設定ファイルを使って実行できます。
    -</p>
    -	
    -   <p class="source">java chapters.appenders.ConfigurationTester
    -   src/main/java/chapters/appenders/conf/logback-fileAppender.xml</p>
    -	
    -	
    -   <h3 class="doAnchor" name="uniquelyNamed">ファイル名を一意にする(タイムスタンプを使う)</h3>
    -   
    -   <p>アプリケーションの開発フェーズの間や、アプリケーションを実行している時間が短い(たとえば、バッチアプリケーション)場合は、アプリケーションを実行するたびに新しいログファイルを作るほうがよいでしょう。<code>timestamp要素</code>を使えば簡単に実現できます。以下に例を示します。</p>
    -
    -
    -   <p class="example">例:タイムスタンプを使ってファイル名を一意にするFileAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-timestamp.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-timestamp.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;logback-timestamp&#39;);">Groovyとして表示</span>
    -   <pre id="logback-timestamp" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;!-- Insert the current time formatted as "yyyyMMdd'T'HHmmss" under
    -       the key "bySecond" into the logger context. This value will be
    -       available to all subsequent configuration elements. --&gt;
    -  <b>&lt;timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss"/&gt;</b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;!-- use the previously created timestamp to create a uniquely
    -         named log file --&gt;
    -    &lt;file&gt;<b>log-${bySecond}.txt</b>&lt;/file&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <p>timestamp要素には、<span class="attr">key属性</span>および<span class="attr">datePattern属性</span>という2つの必須属性と、<span class="attr">timeReference属性</span>という任意属性があります。<span class="attr">key属性</span>には、<a href="./configuration.html#variableSubstitution">変数</a>と同じく、他の設定要素からタイムスタンプを参照するときの名前を指定します。<span class="attr">datePattern</span>属性には、設定ファイルを解釈した時点の日時を文字列に変換するための日付パターン文字列を指定します。日付パターン文字列に指定できるのは、<a href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>で利用できるものと同じです。<span class="attr">timeReference属性</span>には、タイムスタンプの基準時間を指定します。デフォルトでは、現在の日時、つまり、設定ファイルを解析、解釈した時点の日時になります。ですが、コンテキストを生成した時間を基準時間としたほうが便利な場合もあるでしょう。そういう場合は、<span class="attr">timeReference属性</span>に<code>"contextBirth"</code>を指定すればよいです。
    -   </p>
    -
    -   <p>次のコマンドを実行して、<code>timestamp要素</code>がどうなるのか試してみましょう。</p>
    -
    -   <p class="command">java chapters.appenders.ConfigurationTester src/main/java/chapters/appenders/conf/logback-timestamp.xml</p>
    -
    -   <p>ロガーコンテキストを生成した日時を基準時間として使う場合、<span class="attr">timeReference属性</span>に"contextBirth"を指定します。</p>
    -
    -
    -   <p class="example">例:タイムスタンプの基準時間にロガーコンテキストを生成した日時を使用する(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/blogback-timestamp-contextBirth.xml">logback-examples/src/main/java/chapters/appenders/conf/blogback-timestamp-contextBirth.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;logback-timestamp-contextBirth&#39;);">Groovyとして表示</span>   
    -   <pre id="logback-timestamp-contextBirth" class="prettyprint source">&lt;configuration&gt;
    -  &lt;timestamp key="bySecond" datePattern="yyyyMMdd'T'HHmmss" 
    -             <b>timeReference="contextBirth"</b>/&gt;
    -  ...
    -&lt;/configuration&gt;</pre>
    -
    -   <h2 class="doAnchor" name="RollingFileAppender">RollingFileAppender</h2>
    -   
    -   <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/RollingFileAppender.html"><code>RollingFileAppender</code></a>は<code>FileAppender</code>を拡張して、ログファイルを切り替えられるようにしたものです。たとえば、<code>RollingFileAppender</code>では、<em>log.txt</em>という名前のファイルにログを出力するようにした上で、一定の条件が満たされたら、出力先を別のファイルに変えることができます。
    -   </p>
    -     
    -   <p><code>RollingFileAppender</code>と連携する2つの重要なサブコンポーネントがあります。一つは<code>RollingPolicy</code>です。これはファイルを切り替えるために必要な処理を行います。<a href="./appenders.html#onRollingPolicies">詳しくは後述します</a>。もう一つは<code>TriggeringPolicy</code>です。これはいつ切り替えるのか決定するものです。こちらも<a href="./appenders.html#TriggeringPolicy">詳しくは後述します</a>。つまり、<code>RollingPolicy</code>が<em>what</em>を、<code>TriggeringPolicy</code>が<em>when</em>を表しているのです。</p>
    -	
    -   <p>どんな使い方をするにしても、<code>RollingFileAppender</code>には<code>RollingPolicy</code>と<code>TriggeringPolicy</code>の両方を設定しなければなりません。<code>RollingPolicy</code>は<code>TriggeringPolicy</code>インターフェイスを実装しているので、少なくともRollingPolicyだけは明示的に設定しなければなりません。
    -   </p>
    -	
    -   <p><code>RollingFileAppender</code>で利用出来るプロパティは次のとおりです。</p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>プロパティ名</th>
    -       <th>型</th>
    -       <th>説明</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="rfa">file</span></td>
    -       <td><code>String</code></td>
    -       <td><code>FileAppender</code>と同じです。</td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">append</span></td>
    -       <td><code>boolean</code></td>
    -       <td><code>FileAppender</code>と同じです。</td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">encoder</span></td>
    -       <td>
    -         <a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/Encoder.html"><code>Encoder</code></a>
    -       </td>
    -       <td><code>OutputStreamAppender</code>と同じです。</td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="rfa">rollingPolicy</span></td>
    -       <td><code>RollingPolicy</code></td>
    -       <td>このオプションに指定するのは、<code>RollingFileAppender</code>がファイルを切り替える際に処理を委譲するコンポーネントです。詳細は後述します。
    -       </td>
    -     </tr>	
    -     <tr>
    -       <td><span class="prop" container="rfa">triggeringPolicy</span></td>
    -       <td><code>TriggeringPolicy</code></td>
    -       <td>このオプションに指定するのは、<code>RollingFileAppender</code>にファイルを切り替えるタイミングを通知するコンポーネントです。詳細は後述します。
    -       </td>
    -     </tr>	
    -     <tr>
    -       <td valign="top"><span class="prop" name="prudentWithRolling">prudent</span></td>
    -
    -       <td valign="top"><code>boolean</code></td>
    -
    -       <td valign="top">
    -         prudentモードでは、<a href="./appenders.html#FixedWindowRollingPolicy"><code>FixedWindowRollingPolicy</code></a>はサポートされていません。
    -
    -         <p> <a href="./appenders.html#TimeBasedRollingPolicy"><code>TimeBasedRollingPolicy</code></a>を使えば<code>RollingFileAppender</code>でもprudentモードを利用できますが、二つの制限があります。
    -         </p>
    -
    -         <ol>
    -           <li>purudentモードでは、ファイル圧縮オプションをサポートしていませんし、利用することもできません。(他のJVMがログファイルを圧縮している間、書き込むことはできません)</li>
    -           
    -           <li><code>FileAppender</code>の<span class="prop">file</span>プロパティを指定することはできません。空のままにしておかなければなりません。ほとんどのOSでは、他のプロセスが開いているファイルの名前を変えることはできません。
    -           </li>
    -           
    -         </ol>fileプロパティについては<code>FileAppender</code>の説明を参照してください。
    -       </td>
    -     </tr>
    -   </table>
    -	
    -   <h3 class="doAnchor" name="onRollingPolicies">ローリングポリシーの概要</h3>
    -	
    -   <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/RollingPolicy.html"><code>RollingPolicy</code></a>は、ファイルの切り替えに伴う移動や名前の変更を行います。</p>
    -	
    -   <p><code>RollingPolicy</code>インターフェイスは次のようなものです。</p>
    -   
    -   <pre class="prettyprint source">package ch.qos.logback.core.rolling;  
    -
    -import ch.qos.logback.core.FileAppender;
    -import ch.qos.logback.core.spi.LifeCycle;
    -
    -public interface RollingPolicy extends LifeCycle {
    -
    -  <b>public void rollover() throws RolloverFailure;</b>
    -  public String getActiveFileName();
    -  public CompressionMode getCompressionMode();
    -  public void setParent(FileAppender appender);
    -}</pre>
    -
    -   <p><code>rollover</code>メソッドによって、現在のログファイルのアーカイブ処理を行います。<code>getActiveFileName()</code>メソッドは、今まさにログを出力しているはずのログファイル名を算出するために呼び出されます。<code>getCompressionMode</code>メソッドの名前が示すとおり、RollingPolicyにはファイル圧縮モードを決める役割もあります。<code>RollingPolicy</code>から親となるアペンダーへの参照は、<code>setParent()</code>メソッドによって設定します。
    -   </p>
    -
    -   <!-- =================
    -        ================= -->
    -
    -	
    -   <h4 class="doAnchor" name="TimeBasedRollingPolicy">TimeBasedRollingPolicy</h4>
    -
    -   <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/TimeBasedRollingPolicy.html">TimeBasedRollingPolicy</a></code>はおそらく一番人気のあるポリシーです。これは、ファイル切り替えポリシーを日付や月などの日時に基いて定義するものです。
    -   <code>TimeBasedRollingPolicy</code>にはファイルを切り替えるタイミングを通知する役割もあります。実際に、 <code>TimeBasedTriggeringPolicy</code>は<code>RollingPolicy</code>と<code>TriggeringPolicy</code>の<em>両方</em>のインターフェイスを実装しています。
    -   </p>
    -
    -   <p><code>TimeBasedRollingPolicy</code>には、必須プロパティの<span class="prop">fileNamePattern</span>と、いくつかの任意のプロパティが設定できます。
    -   </p>
    -
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>プロパティ名</th>
    -       <th>型</th>
    -       <th>説明</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="tbrp">fileNamePattern</span></td>
    -       <td><code>String</code></td>
    -       <td><span class="prop">fileNamePattern</span>プロパティは必須プロパティで、切り替えるときのログファイル名を指定します。指定する値には、ファイル名と<em>%d変換指示子</em>が含まれます。<em>%d変換指示子</em>には、<code>java.text.SimpleDateFormat</code>クラスで定義された日付時刻のパターン文字列を指定します。日付時刻パターンが省略された場合、パターン文字列として<em>yyyy-MM-dd</em>が使われます。<b>ファイルの切り替え周期は、<span class="prop">fileNamePattern</span>に指定された値から推測されます。</b>
    -
    -
    -         <p><code>TimeBasedRollingPolicyの親アペンダーである<code>RollingFileAppender</code>の<span class="prop">file</span>プロパティは、指定することもできるし省略することもできます。</code><span class="prop">file</span>プロパティにファイル名を指定した場合、ログを出力するファイルとアーカイブファイルの場所を別々にすることができます。ログは常に<span class="prop">file</span>プロパティで指定されたファイルに出力されます。
    -そうすると、有効なログファイルを常に同じ名前にしておくことができます。<span class="prop">file</span>プロパティを省略した場合、有効なログファイル名は、<span class="prop">fileNamePattern</span>プロパティに指定した値に基いて定期的に新しい名前になります。この振る舞いを明確に説明する例を以降に示します。
    -         </p>
    -
    -         <p>%d{}変換指示子の内側に書かれる日付と時刻のパターン文字列は、java.text.SimpleDateFormatの規約にしたがったものです。<span class="option">fileNamePattern</span>プロパティの中であれば、日付と時刻のパターン文字列の中でも外でも、スラッシュ'/'およびバックスラッシュ'\'はディレクトリの区切り文字として扱われます。
    -         </p>
    -
    -         <p>%dトークンはいくつでも指定することができますが、ファイル切り替え周期を推測するために使われるのは最初のものだけです。他のすべてのトークンには、'aux'パラメータを指定して補助であるという印を<em>付けなければなりません</em>。</p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="tbrp">maxHistory</span></td>
    -       <td>int</td>
    -       <td><span class="prop">maxHistory</span>プロパティは、任意プロパティです。削除せずに保持しておくアーカイブファイルの最大数を指定します。たとえば毎月切り替えるつもりでmaxHistoryに6を指定した場合、過去6ヶ月以内のアーカイブファイルは保持され、過去6ヶ月より古いファイルは削除されるでしょう。古くなったファイルだけが削除されるので、logbackが作成したディレクトリは削除されないので注意してください。
    -       </td>
    -     </tr>
    -
    -     <tr>
    -       <td><span class="prop" container="tbrp">cleanHistoryOnStart</span></td>
    -       <td>boolean</td>
    -       <td>
    -         <p>trueを指定した場合、アペンダーの開始時に古いアーカイブを削除します。デフォルトではfalseが設定されています。</p>
    -
    -         <p>通常は、ファイルを切り替えるタイミングで古いアーカイブも削除されます。しかし、アプリケーションによっては切り替えのタイミングが来るまで実行し続けないことがあります。そういう短命なアプリケーションでは、古いアーカイブを削除するタイミングが来ない可能性があるのです。<span class="prop">cleanHistoryOnStart</span>プロパティにtrueを指定しておけば、アペンダーの開始時に古いアーカイブを削除することができます。</p>
    -       </td>
    -     </tr>
    -   </table>
    -
    -
    -   <p><code>fileNamePattern</code>の値とその効果について具体例を示します。</p>
    -
    -  
    -   
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>
    -         <span class="prop">fileNamePattern</span>
    -       </th>
    -       <th>切り替えタイミング</th>
    -       <th>具体例</th>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo.%d</em>
    -       </td>
    -       <td>毎日深夜に切り替え。<em>%d変換指示子</em>に日付時刻パターンが省略されているので、デフォルトの<em>yyyy-MM-dd</em>が指定されたことになります。このパターン文字列の場合は、毎日切り替えることになります。
    -       </td>
    -
    -       <td>
    -         <p><span class="prop">file</span>プロパティを指定しない場合:2006年11月23日中は<em>/wombat/foo.2006-11-23</em>に出力されます。23日の24時(24日の0時)以降、24日中は<em>/wombat/foo.2006-11-24</em>に出力されます。
    -       </p>
    -
    -         <p><span class="prop">file</span>プロパティに<em>/wombat/foo.txt</em>を指定した場合:2006年11月23日中は<em>/wombat/foo.txt</em>に出力されます。23日24時(24日0時)に<em>foo.txt</em>は<em>/wombat/foo.2006-11-23</em>に変更されます。24日中は、新しく作成された<em>/wombat/foo.txt</em>に出力されます。
    -       </p>
    -
    -       </td>
    -     </tr>
    -     
    -
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/%d{yyyy/MM}/foo.txt</em>
    -       </td>
    -       <td>月初めに切り替え。</td>
    -       <td>
    -         <p><span class="prop">file</span>プロパティを指定しない場合:2006年10月中は、<em>/wombat/2006/10/foo.txt</em>に出力されます。10月31日の24時(つまり11月1日の0時)以降、11月中は<em>/wombat/2006/11/foo.txt</em>に出力されます。
    -         </p>
    -
    -         <p><span class="prop">file</span>プロパティに<em>/wombat/foo.txt</em>を指定した場合:出力先は常に<em>/wombat/foo.txt</em>です。2006年10月中は、<em>/wombat/foo.txt</em>に出力されます。10月31日の24時(つまり11月1日の0時)に<em>/wombat/foo.txt</em>は<em>/wombat/2006/10/foo.txt</em>に変更されます。11月中は、新しく作成された<em>/wombat/foo.txt</em>に出力されます。11月30日の24時(つまり12月1日の0時)に<em>/wombat/foo.txt</em>は<em>/wombat/2006/11/foo.txt</em>に変更されます。
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo.%d{yyyy-ww}.log</em>
    -       </td>
    -       
    -       <td>週初めに切り替え。週の最初の日は、ロケールに依存するので注意してください。</td>
    -       
    -       <td>前の例と同じですが、切り替えが発生するのは週の初日です。
    -       </td>     
    -     </tr>	
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo%d{yyyy-MM-dd_HH}.log</em>
    -       </td>
    -       <td>毎時0分に切り替え。</td>
    -       <td>前の例と同じですが、切り替えが発生するのは毎時0分です。
    -       </td>
    -     </tr>
    -     <tr>
    -       <td class="small">
    -         <em>/wombat/foo%d{yyyy-MM-dd_HH-mm}.log</em>
    -       </td>
    -       <td>毎分0秒に切り替え。</td>
    -       <td>前の例と同じですが、切り替えが発生するのは毎分0秒です。
    -
    -       </td>     
    -     </tr>
    -
    -
    -     <tr>
    -       <td class="small">
    -         <em>/foo/%d{yyyy-MM,<b>aux</b>}/%d.log</em>
    -       </td>
    -       <td>毎日深夜に切り替え。年と月からなる名前のフォルダにアーカイブを作成する。
    -       </td>
    -       <td>この例では、最初の%dトークンは補助(<b>aux</b>iliary)であるという印が付いています。したがって、日付時刻パターンの省略された二つ目の%dトークンが最初のものとして使われます。したがって、切り替えタイミングは日次(%dのデフォルトパターン)になり、アーカイブするフォルダ名はそのときの年と月になります。例えば、2006年11月中にアーカイブされたファイルはすべて<em>/foo/2006-11</em>というフォルダに置かれます。例えば<em>/foo/2006-11/2006-11-14.log</em>のようになります。
    -       </td>     
    -       
    -     </tr>
    -   </table>
    -   
    -   <p>スラッシュまたはバックスラッシュ文字はフォルダ(ディレクトリ)の区切り文字として扱われます。存在しないフォルダは必要に応じて作成されるので、別々のフォルダにログファイルを作るのは簡単です。
    -   </p>
    -
    -
    - 	 <p><code>TimeBasedRollingPolicy</code>は、<code>FixedWindowRollingPolicy</code>のように自動ファイル圧縮をサポートしています。<span class="prop">fileNamePattern</span>オプションの値が<em>.gz</em>または<em>.zip</em>で終わっている場合、このフィーチャが有効になります。
    -   </p>
    -
    -   <table class="bodyTable striped">
    -     <tr class="a">
    -       <th><span class="prop">fileNamePattern</span></th>
    -       <th>切り替えタイミング</th>
    -       <th>具体例</th>
    -     </tr>
    -     <tr>
    -       <td><em>/wombat/foo.%d.gz</em></td>
    -       <td>毎日深夜に切り替え。アーカイブファイルは自動的にgz圧縮する。</td>
    -       <td>
    -         <p><span class="prop">file</span>プロパティを指定しない場合:2009年11月23日中は<em>/wombat/foo.2009-11-23</em>に出力します。23日24時(24日0時)になったら、それまでログを出力していたファイルはgz圧縮されて<em>/wombat/foo.2009-11-23.gz</em>になります。11月24日中は<em>/wombat/folder/foo.2009-11-24</em>に出力されます。
    -         </p>
    -         
    -         <p><span class="prop">file</span>プロパティに<em>/wombat/foo.txt</em>を指定した場合:2009年11月23中は<em>/wombat/foo.txt</em>に出力されます。23日24時(24日0時)にgz圧縮されて<em>/wombat/foo.2009-11-23.gz</em>になります。11月24日中は新しく作られた<em>/wombat/foo.txt</em>に出力されます。24日24時(25日0時)に<em>/wombat/foo.txt</em>はgz圧縮されて<em>/wombat/foo.2009-11-24.gz</em>になります。
    -         </p>
    -       </td>
    -     </tr>
    -   </table>
    -   
    -   <p><span class="prop">fileNamePattern</span>には2つの目的があります。1つは、パターン文字列を処理して、logbackに切り替えタイミングを指示することです。もう1つはアーカイブファイル名を決めることです。パターン文字列の形が違っても切り替えタイミングが同じになることがあるので気をつけてください。パターン文字列が<em>yyyy@MM</em>でも<em>yyyy-MM</em>でも、切り替えタイミングは毎月ですが、アーカイブファイル名は異なります。
    -   </p>
    -
    -	 <p><span class="prop">file</span>プロパティを設定すると、アクティブなログファイルの場所とアーカイブファイルの場所を別々にすることができます。ログは<span class="prop">file</span>プロパティで指定したファイルに出力されます。つまり、アクティブなログファイルの名前は常に同じになるのです。ただし、<span class="prop">file</span>プロパティを省略した場合、アクティブなログファイルの名前は<span class="prop">fileNamePattern</span>に基づいたものになります。<span class="prop">file</span>オプションを設定しなければ、<a href="http://logback.qos.ch/codes.html#renamingError">ログファイルの名前変更エラー</a>を割けられます。これは外部からログファイルを参照している間に切り替えようとすると発生するエラーです。存在するときに発生する。
    -   </p>
    -	
    -   <p><span class="prop">maxHistory</span>プロパティは、削除せずに保持しておくアーカイブファイルの最大数を指定します。たとえば、切り替えタイミングが毎月で、<span class="prop">maxHistory</span>に6を指定していたら、6ヶ月以内のアーカイブは保持されますが、6ヶ月を超える古いアーカイブは削除されます。古いアーカイブは削除されてしまうので注意しましょう。logbackによって作られたフォルダも必要に応じて削除されてしまいます。
    -   </p>
    -
    -   <p>技術的な都合により、ファイルの切り替えは時間ベースではなくロギングイベントの到着ベースで行われます。例えば、2002年3月8日時点で<span class="prop">fileNamePattern</span>に<em>YYYY-MM-DD</em>(毎日切り替え)が指定されていることにしましょう。すると、8日24時(9日0時)を過ぎてから最初のロギングイベントが到着した際にファイルの切り替えが行われます。しばらくロギングイベントが発生しなかった場合、たとえば0時を過ぎてから23分47秒後にロギングイベントが発生した場合、3月9日0時0分ではなく、0時23分47秒にファイルの切り替えが行われることになります。したがって、ファイル切り替えにはロギングイベントの頻度に応じた遅延が伴います。どれくらいの遅延があろうとも、切り替えアルゴリズムは常に正しく動作します。したがってあらゆるロギングイベントはそれが発生した時刻に基いて適切なファイルに出力されます。
    -   </p>
    -	
    -   <p><code>RollingFileAppender</code>と<code>TimeBasedRollingPolicy</code>の設定例を見てください。
    -   </p>
    -	
    -   <p class="example">例:<code>RollingFileAppender</code>と<code>TimeBasedRollingPolicy</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-RollingTimeBased.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-RollingTimeBased.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;logback-RollingTimeBased&#39;);">Groovyとして表示</span>
    -   <pre id="logback-RollingTimeBased" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
    -    &lt;file&gt;logFile.log&lt;/file&gt;
    -    <b>&lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;
    -      &lt;!-- daily rollover --&gt;
    -      &lt;fileNamePattern&gt;logFile.%d{yyyy-MM-dd}.log&lt;/fileNamePattern&gt;
    -
    -      &lt;!-- keep 30 days' worth of history --&gt;
    -      &lt;maxHistory&gt;30&lt;/maxHistory&gt;
    -    &lt;/rollingPolicy&gt;</b>
    -
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt; 
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>次の例はprudentモードの例です。
    -    </p>
    -
    -   <p class="example">例:prudentモードにした<code>RollingFileAppender</code>と<code>TimeBasedRollingPolicy</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-PrudentTimeBasedRolling.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;logback-PrudentTimeBasedRolling&#39;);">Groovyとして表示</span>
    -  <pre id="logback-PrudentTimeBasedRolling" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
    -    <b>&lt;!-- Support multiple-JVM writing to the same log file --&gt;</b>
    -    <b>&lt;prudent&gt;true&lt;/prudent&gt;</b>
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;
    -      &lt;fileNamePattern&gt;logFile.%d{yyyy-MM-dd}.log&lt;/fileNamePattern&gt;
    -      &lt;maxHistory&gt;30&lt;/maxHistory&gt; 
    -    &lt;/rollingPolicy&gt;
    -
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt; 
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <h4 class="doAnchor" name="FixedWindowRollingPolicy">FixedWindowRollingPolicy</h4>
    -
    -   <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/FixedWindowRollingPolicy.html">FixedWindowRollingPolicy</a></code>を使うと、固定幅アルゴリズムに従ってファイルの名前を変更することができます。
    -   </p>
    -
    -   <p><span class="prop">fileNamePattern</span>オプションには、アーカイブファイル名のパターンを指定します。このオプションは必須<em>で</em>、パターン文字列中のどこかに整数トークンの<em>%i</em>を含めなければなりません。
    -   </p>
    -	
    -   <p><code>FixedWindowRollingPolicy</code>のプロパティは次のとおりです。
    -   </p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>プロパティ名</th>
    -       <th>型</th>
    -       <th>説明</th>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">minIndex</span></td>
    -       <td><code>int</code></td>
    -       <td>
    -         <p>このオプションには固定幅の添字の下限値を指定します。
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">maxIndex</span></td>
    -       <td><code>int</code></td>
    -       <td>
    -         <p>このオプションには固定幅の添字の上限値を指定します。
    -         </p>
    -       </td>
    -     </tr>
    -     <tr>
    -       <td><span class="prop" container="fwrp">fileNamePattern</span></td>
    -       <td><code>String</code></td>
    -       <td>
    -         <p>このオプションには<code>FixedWindowRollingPolicy</code>がログファイルをアーカイブするファイル名のパターン文字列を指定します。パターン文字列には<em>%i</em>を含めなければなりません。これは、現在の幅に追加する位置の添字になります。
    -         </p>
    -         <p>例えば、パターン文字列が<em>MyLogFile%i.log</em>、<em>minIndex</em>が1、<em>maxIndex</em>が3だとしたら、アーカイブファイル名は<em>MyLogFile1.log</em>、<em>MyLogFile2.log</em>、<em>MyLogFile3.log</em>のいずれかになります。
    -         </p>
    -         <p>自動ファイル圧縮を有効にする場合もこのプロパティで指定することになります。例えば、<span class="prop">fileNamePattern</span>に<em>MyLogFile%i.log.zip</em>が指定されている場合、アーカイブされたファイルは<em>zip形式</em>で圧縮されることになります。<em>gz形式</em>も有効です。
    -         </p>
    -       </td>
    -     </tr>			
    -   </table>
    -   
    -   <p>固定幅切り替えポリシーでは、指定した窓の大きさによってはたくさんのファイル名を変更しなければなりません。ですので、あまりに大きな値を指定することは極力避けるようにしてください。利用者が指定した窓が大きすぎる場合、現在の実装は窓の大きさを自動的に20にしてします。
    -   </p>
    -
    -   <p>固定幅切り替えポリシーの具体的な例を見ていきましょう。それぞれ、<span class="prop">minIndex</span>が<em>1</em>、<span class="prop">maxIndex</span>が<em>3</em>、<span class="prop">fileNamePattern</span>が<em>foo%i.log</em>、<span class="prop">file</span>プロパティが<em>foo.log</em>となっていることを想定しています。;
    -   </p>
    -	
    -   <table class="bodyTable striped">
    -     <tr>
    -       <th>切り替え回数</th>
    -       <th>現在の出力対象</th>
    -       <th>アーカイブファイル</th>
    -       <th>説明</th>
    -     </tr>
    -		<tr>
    -			<td>0</td>
    -			<td>foo.log</td>
    -			<td>-</td>
    -			<td>まだ切り替えは発生していません。logbackは最初に指定されたファイルにログを出力しています。
    -			</td>
    -     </tr>		
    -     <tr>
    -       <td>1</td>
    -       <td>foo.log</td>
    -       <td>foo1.log</td>
    -       <td>切り替えが発生しました。<em>foo.log</em>が<em>foo1.log</em>という名前に変更されました。新しく<em>foo.log</em>が作成され、現在の出力対象になりました。
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>2</td>
    -       <td>foo.log</td>
    -       <td>foo1.log、foo2.log</td>
    -       <td>切り替えが発生しました。<em>foo1.log</em>が<em>foo2.log</em>という名前に変更され、<em>foo.log</em>が<em>foo1.log</em>という名前に変更されました。新しく<em>foo.log</em>が作成され、現在の出力対象になりました。
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>3</td>
    -       <td>foo.log</td>
    -       <td>foo1.log、foo2.log、foo3.log</td>
    -       <td>切り替えが発生しました。<em>foo2.log</em>が<em>foo3.log</em>という名前に変更され、<em>foo1.log</em>が<em>foo2.log</em>という名前に変更され、<em>foo.log</em>が<em>foo1.log</em>という名前に変更されました。新しく<em>foo.log</em>が作成され、現在の出力対象になりました。
    -
    -       </td>
    -     </tr>
    -     <tr>
    -       <td>4</td>
    -       <td>foo.log</td>
    -       <td>foo1.log、foo2.log、foo3.log</td>
    -       <td>以降の切り替えでは、最初にアーカイブ<em>foo3.log</em>を削除します。その他のファイルは、前のステップと同じように、添字を増やした名前に変更されます。以降は、常に1つのログファイル、3つのアーカイブファイルが存在することになります。
    -       </td>
    -     </tr>
    -   </table>
    -	
    -   <p><code>RollingFileAppender</code>と<code>FixedWindowRollingPolicy</code>の設定例を次に示します。<span class="prop">fileNamePattern</span>オプションに同じ情報が含まれているとはいえ、 <span class="prop">File</span>オプションは必須なので注意してください。
    -   </p>
    -	
    -   <p class="example">例:<code>RollingFileAppender</code>と<code>FixedWindowRollingPolicy</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-RollingFixedWindow.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-RollingFixedWindow.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;logback-RollingFixedWindow&#39;);">Groovyとして表示</span>
    -   <pre id="logback-RollingFixedWindow" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
    -    <b>&lt;file&gt;test.log&lt;/file&gt;</b>
    -
    -    <b>&lt;rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"&gt;
    -      &lt;fileNamePattern&gt;tests.%i.log.zip&lt;/fileNamePattern&gt;
    -      &lt;minIndex&gt;1&lt;/minIndex&gt;
    -      &lt;maxIndex&gt;3&lt;/maxIndex&gt;
    -    &lt;/rollingPolicy&gt;</b>
    -
    -    &lt;triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"&gt;
    -      &lt;maxFileSize&gt;5MB&lt;/maxFileSize&gt;
    -    &lt;/triggeringPolicy&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -	
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <h3 class="doAnchor" name="SizeAndTimeBasedFNATP">日時<b>と</b>サイズに基づいたログファイルのアーカイブ</h3>
    -
    -    <p>基本的には日付でファイルをアーカイブしたいけど、後続処理ツールに指定できるログファイルにサイズ制限があるので、ログファイルのサイズも制限したいことがあるでしょう。そんなときは、logbackの配布物に含まれる<code>TimeBasedRollingPolicy</code>のサブコンポーネントである<code>SizeAndTimeBasedFNATP</code>を使うとよいでしょう。FNATP は File Naming And Triggering Policy の略です。</p>
    -
    -    <p>日時とサイズに基づいてログファイルをアーカイブする設定ファイルの例を示します。</p>
    -    
    -  <p class="example">例:<code>SizeAndTimeBasedFNATP</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-sizeAndTime.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-sizeAndTime.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;logback-sizeAndTime&#39;);">Groovyとして表示</span>
    -  <pre id="logback-sizeAndTime" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="ROLLING" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
    -    &lt;file&gt;mylog.txt&lt;/file&gt;
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"&gt;
    -      &lt;!-- rollover daily --&gt;
    -      &lt;fileNamePattern&gt;<b>mylog-%d{yyyy-MM-dd}.<span class="big">%i</span>.txt</b>&lt;/fileNamePattern&gt;
    -      <b>&lt;timeBasedFileNamingAndTriggeringPolicy</b>
    -            <b>class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP"&gt;</b>
    -        &lt;!-- or whenever the file size reaches 100MB --&gt;
    -        <b>&lt;maxFileSize&gt;100MB&lt;/maxFileSize&gt;</b>
    -      <b>&lt;/timeBasedFileNamingAndTriggeringPolicy&gt;</b>
    -    &lt;/rollingPolicy&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="ROLLING" /&gt;
    -  &lt;/root&gt;
    -
    -&lt;/configuration&gt;</pre>
    -    
    -    <p>"%d"トークンに加えて、"%i"トークンがあることに気づきましたか。次の切り替えタイミングがくるまでの間に、現在のログファイルが<span class="prop">maxFileSize</span>に指定したサイズを越えたら、添字を加算してアーカイブします。添字は0から始まります。</p>
    -
    -    <p>日時とサイズに基づいたログファイルのアーカイブをしていても、古いアーカイブを削除することができます。残しておくアーカイブの数を<span class="prop">maxHistory</span>プロパティで指定しなければなりません。アプリケーションが停止してその後再起動した場合でも、正しい添字のファイルにログを出力します。
    -    </p>
    -
    -		<h2>
    -      <a name="TriggeringPolicy" href="./appenders.html#TriggeringPolicy">トリガーポリシーについて</a>
    -    </h2>
    -		
    -		<p><code>RollingFileAppender</code>にファイルを切り替えるタイミングを通知するのが<a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/TriggeringPolicy.html"><code>TriggeringPolicy</code></a>です。</p>
    -		
    -		<p><code>TriggeringPolicy</code>インターフェイスには1つだけメソッドが宣言されています。</p>
    -	
    -    <pre class="prettyprint source">package ch.qos.logback.core.rolling;
    -
    -import java.io.File;
    -import ch.qos.logback.core.spi.LifeCycle;
    -
    -public interface TriggeringPolicy&lt;E&gt; extends LifeCycle {
    -
    -  <b>public boolean isTriggeringEvent(final File activeFile, final &lt;E&gt; event);</b>
    -}</pre>
    -
    -		<p><code>isTriggeringEvent()</code>メソッドには、二つの引数があります。1つは現在有効なファイル、もう1つは処理中のロギングイベントです。これらのパラメータに基づいて、ファイルの切り替えをするべきかどうかを判断します。
    -		</p>
    -
    -    <p>最もよく使われているトリガーポリシーは<code>TimeBasedRollingPolicy</code>です。これはローリングポリシーの別名です。他のローリングポリシーの説明と合わせて<a href="./appenders.html#TimeBasedRollingPolicy">説明</a>したとおりです。</p>
    -		
    -		<h4><a name="SizeBasedTriggeringPolicy" href="./appenders.html#SizeBasedTriggeringPolicy">SizeBasedTriggeringPolicy</a></h4>
    -
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/rolling/SizeBasedTriggeringPolicy.html">SizeBasedTriggeringPolicy</a></code>は現在有効なファイルのサイズを判断します。指定したサイズより大きくなった場合、<code>RollingFileAppender</code>に現在のファイルを切り替えるよう通知します。
    -		</p>
    -
    -		<p><code>SizeBasedTriggeringPolicy</code>は<span class="prop">maxFileSize</span>パラメータだけを受け付けます。デフォルトは10MBです。</p>
    -
    -		<p><span class="prop">maxFileSize</span>オプションはバイト、キロバイト、メガバイト、ギガバイト単位で指定できます。それぞれ<em>KB</em>、<em>MB</em>、<em>GB</em>という接尾辞を使うこともできます。たとえば、<em>5000000</em>、<em>5000KB</em>、<em>5MB</em>、<em>2GB</em>はすべて正しい値です。最初の三つは同じ値になります。
    -		</p>
    -
    -		<p>ファイルサイズが5MBを越えたら切り替える場合の
    -<code>RollingFileAppender</code>と<code>SizeBasedTriggeringPolicy</code>の設定例を見てみましょう。
    -		</p>
    -
    -    <p class="example">例:<code>RollingFileAppender</code>と<code>SizeBasedTriggeringPolicy</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-RollingSizeBased.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-RollingSizeBased.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;logback-RollingSizeBased&#39;);">Groovyとして表示</span>
    -    <pre id="logback-RollingSizeBased" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"&gt;
    -    &lt;file&gt;test.log&lt;/file&gt;
    -    &lt;rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy"&gt;
    -      &lt;fileNamePattern&gt;test.%i.log.zip&lt;/fileNamePattern&gt;
    -      &lt;minIndex&gt;1&lt;/minIndex&gt;
    -      &lt;maxIndex&gt;3&lt;/maxIndex&gt;
    -    &lt;/rollingPolicy&gt;
    -
    -    <b>&lt;triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"&gt;
    -      &lt;maxFileSize&gt;5MB&lt;/maxFileSize&gt;
    -    &lt;/triggeringPolicy&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -	
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -	
    -    <!-- XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXx -->
    -		<a name="Classic"></a>
    -		<h2>logback-classicモジュール</h2>
    -				
    -    
    -		<p>ロギングイベントはlogback-coreモジュールの中ではジェネリック型ですが、logback-classicでは<code>ILoggingEvent</code>のインスタンスになります。logback-classicでは、<code>ILoggingEvent</code>のインスタンスの具体的なパイプライン処理を行います。
    -
    -    </p>
    -
    -		<h3 class="doAnchor" name="SocketAppender">SocketAppenderとSSLSocketAppender</h3>
    -		
    -		<p>ここまでに紹介してきたアペンダーは、ローカルリソースだけにログを出力するものでした。対照的に、 <code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SocketAppender.html">SocketAppender</a></code>はログをシリアライズした<code>ILoggingEvent</code>のインスタンスとしてリモートホストに送信するものとして設計されています。<code>SocketAppender</code>では、ロギングイベントを平文で送信します。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SSLSocketAppender.html">SSLSocketAppender</a></code>では、暗号化された安全なチャネルを介してロギングイベントを送信します。</p>
    -
    -    <p>シリアライズされたロギングイベントの実際の型は、<code>ILoggingEvent</code>インターフェイスを実装した<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/spi/LoggingEventVO.html"><code>LoggingEventVO</code></a>です。ロギングイベントがどれだけ心配なのはわかりますが、リモートロギングが盗聴されることはありません。★受信したロギングイベントをデシリアライズしたら、ローカルで生成されたものとまったく同じように扱われます。それぞれのマシンで実行されている<code>SocketAppender</code>のインスタンスの出力書式がどれも同じになっていれば、中央ログサーバに直接ロギング出力を送りつけることができます。<code>SocketAppender</code>に割り当てられたレイアウトをリモートサーバに送ることはありません。シリアライズされたイベントを送信するからです。<code>SocketAppender</code>は<em>Transmission Control Protocol(TCP)</em>レイヤーで動作します。TCPレイヤーは、信頼性、順序性、フロー制御を備えた端末間のオクテットストリームによる通信を提供するものです。つまり、リモートサーバがネットワークから到達可能な場合、ロギングイベントは確実に到着します。リモートサーバーがダウンしているか到達不能である場合、ロギングイベントは単純に破棄されてしまいます。リモートサーバが復旧した場合、ロギングイベントの送信は透過的に再開されます。この透過的な再接続は、定期的にサーバへの接続を試みるコネクタースレッドによって行われます。
    -		</p>
    -		
    -		<p>ロギングイベントはネイティブのTCP実装によって自動的にバッファへ保存されます。つまり、リモートサーバへの通信速度がクライアントでロギングイベントを生成する速度より遅かったとしても、クライアントアプリケーションが通信速度に影響を受けることはない、ということです。通信速度がロギングイベントを生成する速度よりも遅ければ、クライアントは通信速度に合わせた速度でしか処理を進めることができません。極端な具体例ですが、リモートサーバへのネットワーク接続がダウンしている場合、最終的にクライアントはブロックしてしまいます。また、ネットワーク接続は生きててもリモートサーバがダウンしている場合、ロギングイベントはサーバが利用できないために破棄されてしまいますが、クライアントがブロックされることはありません。
    -		</p>
    -		
    -		<p>あらゆるロガーに<code>SocketAppender</code>が割り当てられていなかったとしても、コネクタースレッドが生きているかぎりGCに回収されることはありません。コネクタースレッドが死ぬのは、リモートサーバへの接続がダウンしたときだけです。このGCに伴う問題を回避するには、<code>SocketAppender</code>を明示的にクローズしなければなりません。長時間稼働するアプリケーションでは、非常に多くの<code>SocketAppender</code>のインスタンスを生成、破棄することになるので、このGCに伴う問題には注意してください。ほとんどのアプリケーションでは無視してもたいして問題になりません。<code>SocketAppender</code>をホストしているJVMが、<code>SocketAppender</code>をクローズする前に終了してしまうとしたら、それが明示的に行われるとしても、あるいは、GCの後であろうとも、通信用のパイプの中に残っていた未送信のデータは失われてしまうでしょう。これはWindowsベースのシステムに共通する問題です。データの消失を避けるには、普通ならアプリケーションを終了する前に<code>SocketAppender</code>の<code>close()</code>メソッドを明示的に呼び出すか、<code>LoggerContext</code>の<code>stop()</code>メソッドを介して呼び出すようにするだけで十分です。
    -		</p>
    -		
    -		<p>リモートサーバは<span class="prop">remotehost</span>プロパティと<span class="prop">port</span>プロパティで指定します。
    -		<code>SocketAppender</code>のプロパティを表にまとめました。<code>SSLSocketAppender</code>にはこれよりも多くのプロパティが追加されています。詳しくは<a href="./usingSSL.html">SSLを使用する</a>の章を参照してください。</p>
    -
    -    <table class="bodyTable striped">
    -      <tr>
    -			<th>プロパティ名</th>
    -			<th>型</th>
    -			<th>説明</th>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>
    -          <p><span class="prop" container="socket">includeCallerDataオプション</span>には真偽値を指定します。trueの場合、リモートホストでログの送信者情報が利用できるようになります。デフォルトでは発信者情報をサーバに送信しません。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">port</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p>リモートサーバのポート番号です。
    -          </p>
    -        </td>
    -      </tr>	
    -      <tr>
    -        <td><span class="prop" container="socket">reconnectionDelay</span></td>
    -        <td><code><a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -        <td><span class="prop">reconnectionDelay</span>オプションには、待ち時間を表す文字列を指定します。たとえば、"10 seconds"と指定した場合、サーバへの接続が失敗するたびに10秒間待ってから、再接続を試みます。このオプションのデフォルト値は30秒​​です。0を指定すると再接続機能が無効になります。サーバへの接続が成功した場合、コネクタースレッドは存在しないはずなので注意してください。
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">queueSize</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p><span class="prop">queueSize</span>プロパティには、受信側に配信するために蓄えておくロギングイベントの数を、非負の整数で指定します。キューサイズが0の場合、イベントの配信は同期になります。キューサイズが0より大きい場合、キューに空きがあれば新しいロギングイベントはキューに入れられるようになります。キューの長さを0以外にすると、一時的なネットワークの遅延によって発生する配信の遅れを排除することができるので、性能が向上します。
    -          </p>
    -
    -          <p><span class="prop">eventDelayLimit</span>プロパティのことも参照してください。</p>
    -
    -        </td>
    -      </tr>	
    -
    -      <tr>
    -        <td><span class="prop" container="socket">eventDelayLimit</span></td>
    -        <td><code><a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -        <td><span class="prop">eventDelayLimit</span>オプションには、"10 seconds"のような待ち時間を表す文字列を指定します。これは、ローカルキューが満杯になった際、蓄積されたロギングイベントを削除する前に待機する時間を表しています。リモートホストがロギングイベントを受信するのが継続的に遅い場合に発生する可能性があります。このオプションのデフォルト値は100ミリ秒です。
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="socket">remoteHost</span></td>
    -        <td><code>String</code></td>
    -        <td>リモートサーバのホスト名です。
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td><span class="prop" container="socket">ssl</span></td>
    -        <td><code>SSLConfiguration</code></td>
    -        <td><code>SSLSocketAppender</code>だけがサポートするプロパティです。アペンダーから利用されるSSL設定を指定します。詳しくは<a href="./usingSSL.html">SSLを使用する</a>を参照してください。</td>
    -      </tr>
    -    </table>
    -    
    -    <h4>サーバ用のオプション</h4>
    -    <p>標準的なlogback-classicの配布物には、<code>SocketAppender</code>あるいは<code>SSLSocketAppender</code>からロギングイベントを受け取るための2つのサーバ用オプションが含まれています。</p>
    -    <ul>
    -      <li><code>ServerSocketReceiver</code>とそのSSL対応版の<code>SSLServerSocketReceiver</code>は、リモートホストのソケットアペンダーからロギングイベントを受け取るための受信用コンポーネントです。<em>logback.xml</em>で設定できるようになっています。設定内容については使用例と<a href="./receivers.html">レシーバーの章</a>を参照してください。
    -      </li>
    -      <li><code>SimpleSocketServer</code>とそのSSL対応版の<code>SimpleSSLSocketServer</code>は、スタンドアローンJavaアプリケーションから簡単に使うために用意されたものです。UnixシェルのCUI(コマンドラインインターフェイス)で設定できるようになっています。これらのアペンダーを設定したアプリケーションは、単純に<code>SocketAppender</code>または<code>SSLSocketAppender</code>のクライアントからロギングイベントが送信されてくるのを待ちます。受信したロギングイベントは、自身の設定に従ってロギングされます。使用例は次のとおりです。
    -      </li>
    -    </ul>
    -    
    -    <h4><a name="simpleSocketServer"></a> SimpleSocketServerの使い方</h4>
    -    <p><code>SimpleSocketServer</code>アプリケーションはコマンドライン引数を2つ取ります。<em>port</em>と<em>configFile</em>です。 <em>port</em>には待ち受けるポート番号を、<em>configFile</em>にはXML形式の設定ファイルを指定します。
    -    </p>
    -	
    -    <p><em>logback-examples</em>ディレクトリに移動してから、次のコマンドを実行すると<code>SimpleSocketServer</code>を開始できます。</p>
    -    
    -    <p class="source">java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
    -  src/main/java/chapters/appenders/socket/server1.xml</p>
    -
    -    <p>待ち受けるポート番号として6000、設定ファイルとして<em>server1.xml</em>を指定しています。この設定ファイルでは、ルートロガーに<code>ConsoleAppender</code>と<code>RollingFileAppender</code>を割り当てています。<code>SimpleSocketServer</code>を開始すれば、複数のロギングクライアントから<code>SocketAppender</code>を使ってロギングイベントを送信できるようになります。このマニュアルでは2つのクライアントを用意しています。<code>chapters.appenders.SocketClient1</code>と<code>chapters.appenders.SocketClient2</code>です。どちらもコンソールで利用者が何かキー入力するのを待つようになっています。利用者がキー入力したテキストはログレベルがDEBUGのロギングイベントに包まれてリモートサーバに送信されます。それぞれのクライアントの<code>SocketAppender</code>の設定は異なります。<code>SocketClient1</code>がプログラムでアペンダーを設定しているのに対して、<code>SocketClient2</code>には設定ファイルが必要です。
    -    </p>
    -	
    -    <p><code>SimpleSocketServer</code>をローカルホスト上で実行しているなら、次のようなコマンドで接続することができます。</p>
    -	
    -    <p class="source">java chapters.appenders.socket.SocketClient1 localhost 6000</p>
    -
    -		<p>クライアントのコンソールに入力したテキストは、前の手順で開始した<code>SimpleSocketServer</code>のコンソールに1行ずつ出力されます。<code>SimpleSocketServer</code>を一旦停止してから再び開始しても、クライアント側では何もなかったかのように透過的に新しいサーバインスタンスに再接続します。しかし、切断中に発生したロギングイベントは単純に破棄されてしまい、取り返すことができません。
    -		</p>
    -
    -		<p><code>SocketClient1</code>と違って、<code>SocketClient2</code>ではアプリケーション自体でlogbackを設定していません。XML形式の設定ファイルが必要です。設定ファイル<em>client1.xml</em>の内容は次のとおりです。<code>SocketAppender</code>を定義して、ルートロガーに割り当てています。
    -		</p>
    -
    -		<p class="example">例:SocketAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/socket/client1.xml">logback-examples/src/main/java/chapters/appenders/socket/client1.xml</a>)</p>
    -    <span class="asGroovy" onclick="return asGroovy(&#39;client1&#39;);">Groovyとして表示</span>
    -<pre id="client1" class="prettyprint source">&lt;configuration&gt;
    -	  
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SocketAppender"&gt;
    -    &lt;remoteHost&gt;${host}&lt;/remoteHost&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;reconnectionDelay&gt;10000&lt;/reconnectionDelay&gt;
    -    &lt;includeCallerData&gt;${includeCallerData}&lt;/includeCallerData&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SOCKET" /&gt;
    -  &lt;/root&gt;  
    -
    -&lt;/configuration&gt;</pre>
    -	
    -<p>上記の設定スクリプトでは、<span class="prop">remoteHost</span>,
    -<span class="prop">port</span>、<span class="prop">includeCallerData</span>の値が変数で指定されているのがわかりますか。これらの変数の値は、システムプロパティとして指定することができます。</p>
    -	
    -    <p class="source">java -Dhost=localhost -Dport=6000 -DincludeCallerData=false \
    -  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml</p>
    -
    -		<p>このコマンドを実行すると、前の<code>SocketClient1</code>と同じ設定になります。
    -		</p>
    -		
    -		<p>ロギングイベントのシリアライズは非侵入型であることをもう一度アピールさせていただきます。デシリアライズされたロギングイベントは、普通のロギングイベントと全く同じ情報を持っています。したがって、自分が生成したロギングイベントであるかのように扱うことができるのです。ただし、デフォルトではシリアライズされたロギングイベントには送信者情報が含まれていません。この点を説明する例を示します。まず<code>SimpleSocketServer</code>を準備しましょう。</p>
    -
    -    <p class="source"> java ch.qos.logback.classic.net.SimpleSocketServer 6000 \
    -  src/main/java/chapters/appenders/socket/server2.xml</p>
    -
    -   <p>設定ファイル<em>server2.xml</em>では<code>ConsoleAppender</code>を定義しています。レイアウトには、他の情報と合わせて送信元のファイル名と行番号を指定しています。前のように設定ファイル<em>client1.xml</em>を引数として<code>SocketClient2</code>を起動します。サーバ側のコンソールに出力されたログには、送信元のファイル名と行番号の代わりに2つのクエスチョンマークが出力されているはずです。</p>
    -
    -    <p class="source">2006-11-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi</p>
    -
    -		<p>この出力は簡単に変更できます。<code>SocketAppender</code>が送信者情報を含めるように設定するには、<span class="prop">includeCallerDataオプションにtrueを指定するだけです。</span>次のように実行すればよいです。</p>
    -
    -   <pre class="source">java -Dhost=localhost -Dport=6000 -DincludeCallerData=true \
    -  chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/client1.xml</pre>
    -
    -		<p>デシリアライズされたロギングイベントは、ローカルで生成されたロギングイベントと同じように扱うことができます。つまり、追加で何か処理するためにさらに別のリモートサーバに送信することができるのです。サーバを2つ用意して、最初のサーバがクライアントから受け取ったロギングイベントをトンネルのようにそのまま二つ目のサーバに転送するところを確認するのは、読者の演習課題にしておきます。
    -		</p>
    -		
    -		<h4><a name="simpleSSLSocketServer"></a> SimpleSSLSocketServerの使い方</h4>
    -
    -    <p><code>SimpleSSLSocketServer</code>では、<code>SimpleSocketServer</code>と同様にコマンドライン引数で<em>port</em>と<em>configFile</em>を指定します。それに加えて、ロギングサーバのX.509証明書ファイルの場所とパスワードをシステムパラメータで指定しなければなりません。
    -    </p>
    -    
    -    <p><em>logback-examples</em>ディレクトリに移動してから、次のコマンドを実行すると<code>SimpleSSLSocketServer</code>を開始できます。</p>
    -
    -    <p class="source">java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -    -Djavax.net.ssl.keyStorePassword=changeit \
    -    ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \
    -    src/main/java/chapters/appenders/socket/ssl/server.xml
    -    </p>
    -	
    -    <p>この例では、テストや検証で使う用のX.509証明書ファイルを指定して<code>SimpleSSLSocketServer</code>を実行しています。<strong>本番環境で<code>SimpleSSLSocketServer</code>を使う前に、ロギングサーバを識別するための正式なX.509証明書を手に入れなければなりません</strong> 。詳しくは<a href="./usingSSL.html">SSLを使用する</a>を参照してください。
    -    </p>
    -    
    -    <p>サーバの使用する設定ファイルのルート要素に<code>debug="true"</code>を指定しているので、サーバの開始ログを見ればSSLを設定していることがわかるでしょう。これはセキュリティポリシーが正しく設定されていることを確認するのに便利です。
    -    </p>
    -
    -    <p>実行中の<code>SimpleSSLSocketServer</code>には、<code>SSLSocketAppender</code>を使って接続することができます。アペンダーの設定例は次のとおりです。</p>
    -      
    -   	<p class="example">例:SSLSocketAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/socket/ssl/client.xml">logback-examples/src/main/java/chapters/appenders/socket/ssl/client.xml</a>)</p>
    -    <span class="asGroovy" onclick="return asGroovy(&#39;sslclient&#39;);">Groovyとして表示</span>
    -<pre id="sslclient" class="prettyprint source">&lt;configuration debug="true"&gt;
    -	  
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"&gt;
    -    &lt;remoteHost&gt;${host}&lt;/remoteHost&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;reconnectionDelay&gt;10000&lt;/reconnectionDelay&gt;
    -    &lt;ssl&gt;
    -      &lt;trustStore&gt;
    -        &lt;location&gt;${truststore}&lt;/location&gt;
    -        &lt;password&gt;${password}&lt;/password&gt;
    -      &lt;/trustStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SOCKET" /&gt;
    -  &lt;/root&gt;  
    -
    -&lt;/configuration&gt;</pre>
    -	  
    -	  <p>前の例と同じく、<span class="prop">remoteHost</span>と<span class="prop">port</span>の値は変数になっています。また、<span class="prop">ssl</span>プロパティとネストしている<span class="prop">trustStore</span>プロパティが増えているので注意してください。locationとpasswordも変数になっています。例として用意したサーバが自己署名証明書を使っているので、これらの設定は必須です。<code>SSLSocketAppender</code>のSSL設定について、詳しくは<a href="./usingSSL.html">SSLを使用する</a>を参照してください。
    -	  </p>
    -
    -    <p>コマンドラインから、設定ファイルで使用している変数をシステムプロパティとして指定すれば、クライアントアプリケーションを実行することができます。</p>
    -    	  	
    -    <p class="source">java -Dhost=localhost -Dport=6000 \
    -    -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -    -Dpassword=changeit \
    -    chapters.appenders.socket.SocketClient2 src/main/java/chapters/appenders/socket/ssl/client.xml
    - 	  </p>
    - 	  
    - 	  <p>前の例と同じく、クライアントアプリケーションのコンソールにメッセージを入力することができます。そうすると、メッセージは(安全な通信路上で)ロギングサーバに送信されます。そして、サーバ側のコンソールにログが出力されます。
    - 	  </p>
    - 	  
    - 	  <p>コマンドラインで指定した<em>truststore</em>プロパティには、信頼できるキーストアのURLを指定することに注意しましょう。<a href="./usingSSL.html">SSLを使用する</a>でも説明していますが、URLでクラスパス上のリソースを指定することもできます。</p>
    - 
    -    <p>前の例でサーバの起動時にいろいろと出力されているのと同じように、クライアントの設定ファイルのルート要素に<code>debug="true"</code>と指定しているので、クライアントの起動時にもSSL設定に関する情報が出力されています。ローカルポリシーの適合性の監査に役立つでしょう。
    -    </p>
    - 	  
    - 	  
    -    <h3 class="doAnchor" name="serverSocketAppender">ServerSocketAppenderとSSLServerSocketAppender</h3>
    -    
    -    <p>前に説明した<code>SocketAppender</code>コンポーネントとSSL対応版のSSLSocketAppenderは、ネットワークの向こう側のサーバにロギングイベントを配信するためのものです。アプリケーションがリモートロギングサーバに接続するために設計されています。場合によりますが、アプリケーションから特定のリモートロギングサーバへの接続を確立するのが不便だったり不可能だったりすることがあります。そういう場合のためにlogbackでは<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/server/ServerSocketAppender">ServerSocketAppender</a></code>が用意されています。
    -    </p>
    -    
    -    <p><code>ServerSocketAppender</code>は、特定のリモートロギングサーバとの接続を確立する代わりに、リモートクライアントからのTCPソケット接続を待ち受けます。アペンダーに送信されたロギングイベントは、接続しているクライアントに配布されます。接続しているクライアントがいなければ、ロギングイベントは<em>すぐに破棄されます</em>。
    -    </p>
    -    
    -    <p>logback は、普通の<code>ServerSocketAppender</code>だけでなく、SSL対応版の<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/server/SSLServerSocketAppender"><code>SSLServerSocketAppender</code></a>も用意しています。安全な、暗号化された通信路で接続したクライアントにロギングイベントを配布します。さらに、SSL対応版のアペンダーは、完全な相互認証をサポートしています。つまり、認証されたクライアントだけがロギングイベントを受信するためにアペンダーに接続できることが保証されるのです。
    -    </p>
    -   
    -    <p>通信路上でのロギングイベントの符号化方法は使用している<code>SocketAppender</code>に関わらず同一です。ロギングイベントは<code>ILoggingEvent</code>のインスタンスがシリアライズされたものです。接続を確立する方向だけが逆転しています。<code>SocketAppender</code>が特定のロギングサーバに対して接続を確立しようとする活性ピアとして振る舞うのに対して、<code>ServerSocketAppender</code>は受動的にクライアントからの接続を待ち受けます。</p>  
    -
    -    <p><code>ServerSocketAppender</code>の派生タイプは、logbackの他の<em>レシーバーコンポーネント</em>とは排他的に利用されることを想定しています。コンポーネントタイプに関する詳細については<a href="./receivers.html">レシーバー</a>を参照してください。</p>
    -    
    -    <p><code>ServerSocketAppender</code>の設定可能なプロパティを表にまとめました。</p>
    -    
    -    <table class="bodyTable striped">
    -      <tr>
    -      <th>プロパティ名</th>
    -      <th>型</th>
    -      <th>説明</th>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">address</span></td>
    -        <td><code>String</code></td>
    -        <td>アペンダーが待ち受けるためのローカルネットワークインターフェイスに割り当てられたIPアドレス。このプロパティが指定されていない場合、アペンダーはすべてのネットワークインターフェイスで待ち受けます。</td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>
    -          <p>trueの場合、リモートホスト側で送信者情報が利用できるようになります。デフォルトでは、送信者情報はクライアントに送信されません。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">port</span></td>
    -        <td><code>int</code></td>
    -        <td>
    -          <p>アペンダーが待ち受けるポート番号。
    -          </p>
    -        </td>
    -      </tr> 
    -      <tr>
    -        <td><span class="prop" container="serverSocketAppender">ssl</span></td>
    -        <td><code>SSLConfiguration</code></td>
    -        <td><code>SSLServerSocketAppender</code>でのみ使用出来るプロパティ。<a href="./usingSSL.html">SSLを使用する</a>で説明したように、アペンダーの使用するSSLの設定を指定します。</td>
    -      </tr>
    -    </table>
    -    
    -    <p><code>ServerSocketAppender</code>の使用例を次に示します。</p>
    -
    -    <p class="example">例:ServerSocketAppenderの基本的な設定(<a href="http://logback.qos.ch/xref/chapters/appenders/socket/server4.xml">logback-examples/src/main/java/chapters/appenders/socket/server4.xml</a>)</p>
    -<pre id="SocketReceiver" class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="SERVER" 
    -    class="ch.qos.logback.classic.net.server.ServerSocketAppender"&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;includeCallerData&gt;${includeCallerData}&lt;/includeCallerData&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="SERVER" /&gt;
    -  &lt;/root&gt;  
    -
    -&lt;/configuration&gt;
    -</pre>
    -    <p>前の例との違いは、<em>class属性</em>に指定しているのが<code>SocketAppender</code>ではないこと、<span class="prop">remoteHost</span>プロパティが無いことだけなのがわかりましたか。このアペンダーはリモートロギングサーバに接続するのではなく、リモートホストからの接続を受動的に待ち受けます。
    -    </p>
    -    
    -    <p><code>SSLServerSocketAppender</code>の設定例は次のとおりです。</p>
    -        
    -    <p class="example">例:SSLServerSocketAppenderの基本的な設定(<a href="http://logback.qos.ch/xref/chapters/appenders/socket/server3.xml">logback-examples/src/main/java/chapters/appenders/socket/server3.xml</a>)</p>
    -<pre id="SocketReceiver" class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="SERVER" 
    -    class="ch.qos.logback.classic.net.server.SSLServerSocketAppender"&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;includeCallerData&gt;${includeCallerData}&lt;/includeCallerData&gt;
    -    &lt;ssl&gt;
    -      &lt;keyStore&gt;
    -        &lt;location&gt;${keystore}&lt;/location&gt;
    -        &lt;password&gt;${password}&lt;/password&gt;
    -      &lt;/keyStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="SERVER" /&gt;
    -  &lt;/root&gt;  
    -
    -&lt;/configuration&gt;
    -</pre>
    -   
    -    <p>前の例との主な違いは、<em>class属性</em>に<code>SSLServerSocketAppender</code>を指定していることと、ネストしている<span class="prop">ssl要素</span>があることです。この例ではアペンダー用のX.509証明書が置かれたキーストアが指定されています。SSLの設定について詳細は<a href="./usingSSL.html">SSLを使用する</a>を参照してください。
    -    </p>
    -    
    -    <p></p>
    -    
    -   <h3 class="doAnchor">SMTPAppender</h3>
    -   
    -   <p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SMTPAppender.html"><code>SMTPAppender</code></a>は、一つ以上の固定サイズのバッファにロギングイベントを蓄積し、利用者が指定したイベントが発生したら適切なバッファを選んで内容をメールで送信します。SMTPによるメール送信は非同期で実行されます。デフォルトでは、ERRORレベルのロギングイベントがトリガとなってメールが送信されます。また、1つのバッファがすべてのロギングイベントから使用されます。
    -   </p>
    -		
    -   <p><code>SMTPAppender</code>の設定可能なプロパティを表にまとめました。
    -	 </p>
    -		
    -		<table class="bodyTable striped">
    -      <tr>
    -        <th>プロパティ名</th>
    -        <th>型</th>
    -        <th>説明</th>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">smtpHost</span></td>
    -        <td><code>String</code></td>
    -        <td>SMTPサーバのホスト名。これは必須パラメータです。</td>
    -      </tr>
    -      
    -      <tr>
    -        <td><span class="prop" container="smtp">smtpPort</span></td>
    -        <td><code>int</code></td>
    -        <td>SMTPサーバーの待ち受けポート番号。デフォルトでは25です。</td>
    -      </tr>
    -      
    -      <tr>
    -        <td><span class="prop" name="smtpTo">to</span></td>
    -        <td><code>String</code></td>
    -        <td>recipient のメールアドレスの<em>パターン</em>を指定します。指定したパターンは、送信されるメールそれぞれに対して、トリガとなったロギングイベントと共に評価されます。複数のrecipientを指定するときは、宛先アドレスをカンマ区切りにします。また、<code>to要素</code>を複数並べることもできます。
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">from</span></td>
    -        <td><code>String</code></td>
    -        <td><code>SMTPAppender</code>の送信するメールメッセージの originator を<a href="http://en.wikipedia.org/wiki/Email_address">一般的なメールアドレス形式</a>で指定します。送信者名を含めたい場合は "Adam Smith &amp;lt;smith@moral.org&amp;gt;" のようにします(XML形式の設定ファイルではブラケットを文字実態参照にします)。そうすると、メールでは "Adam Smith &lt;smith@moral.org&gt;" のようになります。
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop">subject</span></td>
    -        <td><code>String</code></td>
    -        <td> 
    -          <p>メールの件名。<a href="./layouts.html#ClassicPatternLayout">PatternLayout</a>で利用できる全ての値を指定できます。レイアウトについては次の章で説明します。
    -          </p>
    -          
    -          <p>送信されるメールメッセージの件名には、メール送信をトリガしたロギングイベントを適用したパターンが指定されます。
    -          </p>
    -
    -          <p><span class="prop">subject</span>オプションに指定されたパターンが "Log: %logger - %msg" で、"com.foo.Bar"ロガーが "Hello World" というメッセージのロギングイベントを発生して、このロギングイベントがトリガとなった場合、送信されるメールの件名は "Log: com.foo.Bar - Hello World" になるでしょう。
    -          </p>
    -
    -          <p>デフォルトでは、"%logger{20} - %m" が設定されています。</p>
    -        </td>
    -        
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">discriminator</span></td>
    -        <td><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/sift/Discriminator.html">Discriminator</a></code></td>
    -        <td>
    -          <p><code>SMTPAppender</code>は、<span class="prop">弁別器</span>の返す値に基づいてバッファを選択して、発生したロギングイベントを振り分けます。デフォルトの弁別器は常に同じ値を返すので、全てのロギングイベントが常に同じバッファに振り分けられます。
    -          </p>
    -
    -          <p>デフォルト以外の弁別器を指定すれば、特定のユーザー、ユーザーセッション、クライアントのIPアドレスに関連するロギングイベントを含むメールだけを送信することもできます。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td><span class="prop" name="smtpAppender_Evaluator">evaluator</span></td>
    -        <td><code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/IEvaluator.html">IEvaluator</a></code></td>
    -        <td>
    -          <p>このオプションを指定するには、新しく<code>EventEvaluator要素</code>を宣言します。<code>SMTPAppender</code>の<code>Evaluator</code>として使いたいクラスの完全クラス名を、<span class="attr">class属性</span>に指定します。
    -          </p>
    -          
    -          
    -          <p>このオプションを指定しなかった場合、<code>SMTPAppender</code>は<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/OnErrorEvaluator.html">OnErrorEvaluator</a>のインスタンスを使用します。これは<em>ERROR</em>以上のロギングイベントが発生したらメール送信をトリガします。
    -          </p>
    -
    -          <!--
    -          <p><code>EventEvaluator</code> objects are subclasses of the
    -          <code>JaninoEventEvaluatorBase</code> which depends on
    -          Janino. See the <a href="../dependencies.html">dependencies
    -          page</a> for more information.
    -          </p>
    -          -->
    -
    -          <p>logback の配布物には他の評価器もいくつか含まれています。<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/OnMarkerEvaluator.html"><code>OnMarkerEvaluator</code></a>(詳しくは後述します)と、より強力な評価器の<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/JaninoEventEvaluator.html"><code>JaninoEventEvaluator</code></a>です。後者については<a href="./filters.html#evalutatorFilter">別の章</a>で紹介します。最新バージョンの配布物にはさらに強力な評価器である<a href="./filters.html#GEventEvaluator"><code>GEventEvaluator</code></a>が含まれます。
    -          </p>
    -
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td valign="top"><span class="prop" container="smtp">cyclicBufferTracker</span></td>
    -        <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/CyclicBufferTracker.html"><code>CyclicBufferTracker</code></a>
    -        </td>
    -        <td>
    -          <p>名前が示す通り、<code>CyclicBufferTracker</code>クラスは循環バッファを追跡します。追跡は、前に説明した<span class="prop">弁別器</span>の返すキーに基づいて行われます。
    -          </p>
    -          <p><span class="prop">cyclicBufferTracker</span>指定しなかった場合、自動的に<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/CyclicBufferTrackerImpl.html">CyclicBufferTrackerImpl</a>のインスタンスを生成して使用します。デフォルトで256個のロギングイベントを循環バッファに保持します。<span class="prop">bufferSize</span>オプションを指定してサイズを変更することもできます(下記参照)。</p>
    -        </td>        
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">username</span></td>
    -        <td><code>String</code></td> <td>平文のパスワード認証をするときに使用するユーザー名。デフォルトではnullです。</td> 
    -      </tr> 
    -      <tr class="alt">
    -        <td><span class="prop" container="smtp">password</span></td>
    -        <td><code>String</code></td>
    -        <td>平文のパスワード認証をするときに使用するパスワード。デフォルトではnullです。
    -
    -        </td>
    -      </tr>
    -      <tr> 
    -        <td><span class="prop" container="smtp">STARTTLS</span> </td>
    -        <td><code>boolean</code></td> 
    -        <td>trueを指定すると、サーバがサポートしているならSSL接続に切り替えるため、STARTTLSコマンドを発行します。接続が確立した時点では暗号化されていないので注意してください。デフォルトではfalseに設定されています。
    -        </td> 
    -      </tr>
    -      <tr>
    -        <td><span class="prop" container="smtp">SSL</span></td>
    -        <td><code>boolean</code></td> <td>trueを指定するとサーバにSSL接続をします。デフォルトではfalseです。</td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">charsetEncoding</span></td>
    -        <td><code>String</code></td>
    -        <td>送信されるメッセージは指定された<a href="https://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html">文字セット</a>でエンコードされます。デフォルトの文字セットは"UTF-8"ですが、ほとんどの場合十分でしょう。
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">localhost</span></td>
    -        <td><code>String</code></td>
    -        <td>SMTPクライアントのホスト名が正しく設定されていない場合(例えば、ホスト名が完全修飾名ではない場合)、SMTPサーバの中にはそういうクライアントから送信された HELO/EHLO コマンドを拒否することがあります。この問題を解決するには、クライアントのホスト名として、<span class="prop">localhost</span>に完全修飾名を指定することができます。<a href="http://javamail.kenai.com/nonav/javadocs/com/sun/mail/smtp/package-summary.html">com.sun.mail.smtp</a>パッケージのドキュメントで説明されている "mail.smtp.localhost" パラメータも参考にしてください。</td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">asynchronousSending</span></td>
    -        <td><code>boolean</code></td>
    -        <td>メール送信を非同期で行うかどうかを指定します。デフォルトはtrueです。しかし、非同期送信が不適切な場合もあります。たとえば、あなたのアプリケーションで致命的なエラーが発生した際、<code>SMTPAppender</code>でアラートメールを送信してから終了するとしても、その仕事を任されたスレッドにはメールを送信する時間が残されていないからです。そういう場合は同期的にメール送信をするためfalseを指定しましょう。
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>デフォルトは<code>false</code>です。<span class="prop">asynchronousSending</span>が有効でログに送信者情報を含めたいときは<code>true</code>を指定しなければなりません。</td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">sessionViaJNDI</span></td>
    -        <td><code>boolean</code></td>
    -        <td><code>javax.mail.Session</code>を使用してメールを送信します。デフォルトは<code>false</code>なので、<code>SMTPAppender</code>は利用者の指定した設定に従って<code>javax.mail.Session</code>インスタンスを構築します。<code>true</code>を指定するとJNDIから<code>javax.mail.Session</code>インスタンスを取得します。<span class="prop">jndiLocation</span>プロパティも参照してください。
    -
    -        <p><span class="label">注意</span>JNDIから<code>Session</code>を取得すれば、設定する場所は減るし、同じ情報をあちこちで指定しなくてもよくなります。そうすると、アプリケーションはより<a href="http://en.wikipedia.org/wiki/Don&#39;t_repeat_yourself">DRY</a>になります。TomcatでJNDIリソースを設定する方法については<a href="http://tomcat.apache.org/tomcat-6.0-doc/jndi-resources-howto.html#JavaMail_Sessions">JNDIリソースハウツー</a>を参照してください。JNDIから<code>Session</code>オブジェクトを取得する場合は、ドキュメントに記載されているように、<em>mail.jar</em>と<em>activation.jar</em>をWebアプリケーションの<em>WEB-INF/lib</em>フォルダから取り除いてください。</p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td><span class="prop" container="smtp">jndiLocation</span></td>
    -        <td><code>String</code></td>
    -        <td>JNDIのjavax.mail.Sessionの位置を指定します。デフォルトは<span style="white-space:nowrap">"java:comp/env/mail/Session"</span>です。
    -        </td>
    -      </tr>
    -
    -		</table>		
    -		
    -		<p><code>SMTPAppender</code>は循環バッファで最新の256個のロギングイベントだけを保持します。バッファが一杯になったら古いロギングイベントを捨てます。したがって、<code>SMTPAppender</code>からメールで送信するロギングイベントの最大値は256個になります。つまり、アプリケーションコンテキストに妥当なメモリ量を割り当てられるよう、メモリ要件に合わせて制限できるということです。
    -		</p>
    -		
    -		<p><code>SMTPAppender</code>はJavaMail APIにも依存しています。テストされているJavaMail APIのバージョンは1.4です。JavaMail APIにはJavaBeans Activation フレームワークが必要です。<a href="http://java.sun.com/products/javamail/">JavaMail API</a>と<a href="http://java.sun.com/beans/glasgow/jaf.html">JavaBeans Activation フレームワーク</a>はそれぞれのウェブサイトからダウンロードすることができます。以降の例を試す前に、クラスパス上にこれらのjarファイルが配置されていることを確かめてください。
    -		</p>
    -		
    -		<p>サンプルアプリケーションでは、<a href="http://logback.qos.ch/xref/chapters/appenders/mail/EMail.html"><code>chapters.appenders.mail.EMail</code></a>がいくつかのログメッセージを生成したあとで、エラーメッセージを1つ生成しています。このアプリケーションの引数は二つあります。一つ目のパラメータは生成するロギングイベントの数となる整数値です。二つ目のパラメータはlogbackの設定ファイルです。<em>EMail</em>アプリケーションが最後に生成するロギングイベントのログレベルはERRORなので、メール送信をトリガします。
    -		</p>
    -
    -		<p><code>Email</code>アプリケーションから使う設定ファイルは次のとおりです。</p>	
    -		
    -    <p class="example">例:<code>SMTPAppender</code>の設定例(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/mail1.xml">logback-examples/src/main/java/chapters/appenders/mail/mail1.xml</a>)</p>	
    -    <span class="asGroovy" onclick="return asGroovy(&#39;mail1&#39;);">Groovyとして表示</span>	
    -    <pre id="mail1" class="prettyprint source">&lt;configuration&gt;	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;smtpHost&gt;ADDRESS-OF-YOUR-SMTP-HOST&lt;/smtpHost&gt;
    -    &lt;to&gt;EMAIL-DESTINATION&lt;/to&gt;
    -    &lt;to&gt;ANOTHER_EMAIL_DESTINATION&lt;/to&gt; &lt;!-- additional destinations are possible --&gt;
    -    &lt;from&gt;SENDER-EMAIL&lt;/from&gt;
    -    &lt;subject&gt;TESTING: %logger{20} - %m&lt;/subject&gt;
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt;
    -      &lt;pattern&gt;%date %-5level %logger{35} - %message%n&lt;/pattern&gt;
    -    &lt;/layout&gt;	    
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -		<p>上記の設定ファイルで<code>chapters.appenders.mail.Email</code>アプリケーションを動かしてみる前に、smtpHostや<span class="prop">to</span>や<span class="prop">from</span>へ、あなたの環境に適した値を指定しなければなりません。設定ファイルに正しい値を指定したら次のコマンドを実行しましょう。</p>
    -		
    -<div class="source"><pre>java chapters.appenders.mail.EMail 100 src/main/java/chapters/appenders/mail/mail1.xml</pre></div>
    -
    -		<p>指定したrecipientには<code>PatternLayout</code>で指定したとおりに書式化された100個のロギングイベントが含まれたメールが届くはずです。Mozilla Thunderbird で受信したメールを開いたところを示します。
    -		</p>
    -    
    -    <p><img src="images/chapters/appenders/smtpAppender1.jpg" alt="結果の電子メール"></p>
    -		
    -		<p>設定ファイル<em>mail2.xml</em>では、<span class="prop">smtpHost</span>や<span class="prop">to</span>や<span class="prop">from</span>が変数で指定されています。<em>mail2.xml</em>の大事な部分を次に示します。
    -		</p>		
    -
    -    <pre class="prettyprint source">&lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -  &lt;smtpHost&gt;${smtpHost}&lt;/smtpHost&gt;
    -  &lt;to&gt;${to}&lt;/to&gt;
    -  &lt;from&gt;${from}&lt;/from&gt;
    -  &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/&gt;
    -&lt;/appender&gt;</pre>
    -		
    -		<p>コマンドラインで必要なパラメータを指定します。</p>
    -		
    -<div class="source"><pre>java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
    -  chapters.appenders.mail.EMail 10000 src/main/java/chapters/appenders/mail/mail2.xml
    -</pre></div>
    -
    -		<p>値はあなたの環境で利用できるものに置き換えてください。
    -		</p>
    -		
    -		<p>最後の例では、<code>PatternLayout</code>が<code>HTMLLayout</code>になっていました。これはログの内容をHTMLのテーブルとして書式化します。列の並びと順番は変更することができます。CSSも変更できます。<a href="./layouts.html#ClassicHTMLLayout">HTMLLayout</a>について詳しくはドキュメントを参照してください。
    -    </p>
    -    
    -    <p>循環バッファのサイズが256なので、recipientが受け取るメールでは256個のロギングイベントが綺麗にHTMLのテーブルに整形されるようになっています。<code>chapters.appenders.mail.Email</code>アプリケーションを実行して10000件のロギングイベントを生成しても、送信されるメールには最新の256件しか含まれないことになるので気をつけてください。
    -		</p>
    -		
    -    <p><img src="images/chapters/appenders/smtpAppender2.jpg" alt="第2回のメール"></p>
    -
    -    <p>Mozilla ThunderbirdやEudoraやMS Outlookといったメールクライアントは、HTML形式のメールでもCSSをわりと上手く処理します。ですが、勝手にHTMLを平文テキストにダウングレードすることがあります。例えば、Thunderbird でHTMLメールを表示するには、"表示→メッセージ本文→オリジナルのHTML" でオプションを指定しなければなりません。Yahoo!メールはHTMLメールをサポートしており、CSSの対応具合はピカ一です。一方、Gmailは普通のHTMLテーブルはそのまま表示してくれますが、内部CSSによる整形はしてくれません。GmailはインラインCSSをサポートしていますが、インラインCSSを使うとHTMLソースコードが膨れ上がってしまうので、<code>HTMLLayout</code>ではインラインCSSを使用しません。</p>
    -
    -    <h3 class="doAnchor" name="cyclicBufferSize">カスタムバッファサイズ</h3>
    -
    -    <p>デフォルトでは、<code>SMTPAppender</code>から送信されるメッセージには最大で256件のロギングメッセージが含まれています。次の例に示すように、別のバッファサイズを指定することができます。
    -    </p>
    -
    -    <p class="example">例: バッファサイズを変更した<code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/customBufferSize.xml">logback-examples/src/main/java/chapters/appender/mail/customBufferSize.xml</a>)</p>	
    -    <pre class="prettyprint source">&lt;configuration&gt;   
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;smtpHost&gt;${smtpHost}&lt;/smtpHost&gt;
    -    &lt;to&gt;${to}&lt;/to&gt;
    -    &lt;from&gt;${from}&lt;/from&gt;
    -    &lt;subject&gt;%logger{20} - %m&lt;/subject&gt;
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/&gt;
    -
    -    <b>&lt;cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker"&gt;</b>
    -      <b>&lt;!-- send just one log entry per email --&gt;</b>
    -      <b>&lt;bufferSize&gt;1&lt;/bufferSize&gt;</b>
    -    <b>&lt;/cyclicBufferTracker&gt;</b>
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;    </pre>
    -    
    -
    -    <h3 class="doAnchor">トリガイベント</h3>
    -
    -    <p>Evaluatorプロパティが指定されなかったら、<code>SMTPAppender</code>はデフォルトで<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/OnErrorEvaluator.html">OnErrorEvaluator</a>を使用して、ERRORレベルのロギングイベントの発生をトリガとしてメールを送信する。エラーの発生に応じて、メール送信をトリガするのはそれなりに合理的ですが、<code>EventEvaluator</code>インターフェイスの別の実装を指定すればデフォルトの振る舞いを上書きすることができます。
    -    </p>
    -		
    -		<p><code>SMTPAppender</code>は<code>evaluate()</code>メソッドを呼び出すことで、受け取ったロギングイベントを評価器に渡します。そのロギングイベントがメール送信をトリガするものなのか、単に循環バッファに入れておくだけでいいのかを判定するためです。評価器が評価した結果が真なら、メールを送信します。<code>SMTPAppender</code>の保持する評価器オブジェクトは1つだけです。このオブジェクトは、自身の内部状態を管理することができます。わかりにくいので、<code>CounterBasedEvaluator</code>クラスのコードで説明します。これはロギングイベントが1024件発生するたびにメール送信をトリガします。
    -		</p>
    -
    -    <p class="example">例: 1024件ごとに評価値<code>true</code>を返す<code>EventEvaluator</code>インターフェイスの実装(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/CounterBasedEvaluator.java">logback-examples/src/main/java/chapters/appenders/mail/CounterBasedEvaluator.java</a>)</p>
    -   
    -   <pre class="prettyprint source">package chapters.appenders.mail;
    -
    -import ch.qos.logback.core.boolex.EvaluationException;
    -import ch.qos.logback.core.boolex.EventEvaluator;
    -import ch.qos.logback.core.spi.ContextAwareBase;
    -
    -public class CounterBasedEvaluator extends ContextAwareBase implements EventEvaluator {
    -
    -  static int LIMIT = 1024;
    -  int counter = 0;
    -  String name;
    -
    -  <b>public boolean evaluate(Object event) throws NullPointerException,
    -      EvaluationException {
    -    counter++;
    -
    -    if (counter == LIMIT) {
    -      counter = 0;
    -
    -      return true;
    -    } else {
    -      return false;
    -    }
    -  }</b>
    -
    -  public String getName() {
    -    return name;
    -  }
    -
    -  public void setName(String name) {
    -    this.name = name;
    -  }
    -}</pre>
    -
    -		<p>このクラスは<code>ContextAwareBase</code>を継承して、<code>EventEvaluator</code>インターフェイスを実装しているのがわかるでしょうか。こうすれば、利用者は<code>EventEvaluator</code>としての中心機能に集中することができるし、基底クラスを使って共通する機能を提供できるようになります。
    -		</p>
    -
    -		<p><code>SMTPAppender</code>の<span class="prop">evaluator</span>オプションを指定するということは、カスタム評価器を使うということです。次の設定ファイルではルートロガーに<code>SMTPAppender</code>を割り当てています。SMTPAppenderでは、<code>CounterBasedEvaluator</code>を評価器として指定しています。
    -		</p>
    -
    -    <p class="example">例: <code>SMTPAppender</code>とカスタム<code>評価器</code>とバッファサイズの設定例(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/mail3.xml">logback-examples/src/main/java/chapters/appenders/mail/mail3.xml</a>)</p>
    -    <span class="asGroovy" onclick="return asGroovy(&#39;mail3&#39;);">Groovyとして表示</span>	
    -    <pre id="mail3" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    <b>&lt;evaluator class="chapters.appenders.mail.CounterBasedEvaluator" /&gt;</b>
    -    &lt;smtpHost&gt;${smtpHost}&lt;/smtpHost&gt;
    -    &lt;to&gt;${to}&lt;/to&gt;
    -    &lt;from&gt;${from}&lt;/from&gt;
    -    &lt;subject&gt;%logger{20} - %m&lt;/subject&gt;
    -
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -    
    -
    -    <h3 class="doAnchor" name="OnMarkerEvaluator">マーカーに基づくトリガ</h3>
    -
    -    <p>全てのERRORレベルのロギングイベントをトリガとしてメールを送信するデフォルトのポリシーは合理的ではありますが、メールを送信する機会が多すぎると対象になっているユーザのメールボックスを埋め尽くしてしまいます。logback の配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/OnMarkerEvaluator.html">OnMarkerEvaluator</a>というトリガポリシーも含まれています。これはマーカーに基づいてトリガするものです。原則として、利用者が指定したマーカーのロギングイベントが発生したときだけメール送信がトリガされます。次の例を見ればどういう動き方をするのかはっきりするでしょう。
    -    </p>
    -
    -    <p><a href="http://logback.qos.ch/xref/chapters/appenders/mail/Marked_EMail.html">Marked_EMailの</a>アプリケーションには、ERRORレベルを含むいくつものロギング式があります。そのうちで、一つのロギング式にだけマーカーが指定されています。該当するコードは次のとおりです。
    -    </p>
    -
    -    <pre class="prettyprint source">Marker notifyAdmin = MarkerFactory.getMarker("NOTIFY_ADMIN");
    -logger.error(<b>notifyAdmin</b>,
    -  "This is a serious an error requiring the admin's attention",
    -   new Exception("Just testing"));</pre>
    -
    -   <p>次の設定ファイルは、マーカーとしてNOTIFY_ADMINあるいはTRANSACTION_FAILUREを指定されたロギングイベントが発生したときだけメール送信をトリガするものです。
    -   </p>
    -
    -   <p class="example">例: <code>OnMarkerEvaluator</code>を指定した<code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/mailWithMarker.xml">logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;mailWithMarker&#39;);">Groovyとして表示</span>	
    -   <pre id="mailWithMarker" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    <b>&lt;evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator"&gt;
    -      &lt;marker&gt;NOTIFY_ADMIN&lt;/marker&gt;
    -      &lt;!-- you specify add as many markers as you want --&gt;
    -      &lt;marker&gt;TRANSACTION_FAILURE&lt;/marker&gt;
    -    &lt;/evaluator&gt;</b>
    -    &lt;smtpHost&gt;${smtpHost}&lt;/smtpHost&gt;
    -    &lt;to&gt;${to}&lt;/to&gt;
    -    &lt;from&gt;${from}&lt;/from&gt;
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"/&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root&gt;
    -    &lt;level value ="debug"/&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -    
    -    <p>次のコマンドを実行してみましょう。</p>
    -
    -    <pre class="source">java -Dfrom=source@xyz.com -Dto=recipient@xyz.com -DsmtpHost=some_smtp_host \
    -  chapters.appenders.mail.Marked_EMail src/main/java/chapters/appenders/mail/mailWithMarker.xml</pre>
    -
    -
    -  <h4 class="doAnchor" name="marker_JaninoEventEvaluator">JaninoEventEvaluatorを使ったマーカーに基づくトリガ</h4>
    -
    -    <p>マーカーだけを対象にした<code>OnMarkerEvaluator</code>の代わりに、より汎用的な<a href="./filters.html#JaninoEventEvaluator"><code>JaninoEventEvaluator</code></a>を使うことができますし、それ以上に強力な<a href="./filters.html#GEventEvaluator"><code>GEventEvaluator</code></a>を使うことも出来ます。たとえば、次の設定ファイルは<code>OnMarkerEvaluator</code>の代わりに<code>JaninoEventEvaluator</code>を指定した以外は前の設定ファイルとまったく同じ内容になります。
    -    </p>
    -
    -    <p class="example">例: <code>JaninoEventEvaluator</code>を指定した<code>SMTPAppender</code>の設定(logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker_Janino.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;mailWithMarker_Janino&#39;);">Groovyとして表示</span>	
    -    <pre id="mailWithMarker_Janino" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"&gt;
    -      &lt;expression&gt;
    -        (marker != null) &amp;&amp;
    -        (marker.contains("NOTIFY_ADMIN") || marker.contains("TRANSACTION_FAILURE"))
    -      &lt;/expression&gt;
    -    &lt;/evaluator&gt;    
    -    ... same as above
    -  &lt;/appender&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <h4 class="doAnchor" name="marker_GEventEvaluator">GEventEvaluatorを使ったマーカーに基づくトリガ</h4>
    -
    -    <p><a href="./filters.html#GEventEvaluator">GEventEvaluator</a>を使っている以外は前の例と同じ内容です。</p>
    -
    -    <p class="example">例:<code>GEventEvaluator</code>を指定したSMTPAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/mailWithMarker_GEvent.xml">logback-examples/src/main/java/chapters/appenders/mail/mailWithMarker_GEvent.xml</a>)</p>
    -   <span class="asGroovy" onclick="return asGroovy(&#39;mailWithMarker_GEventEvaluator&#39;);">Groovyとして表示</span>	
    -
    -   <pre id="mailWithMarker_GEventEvaluator" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"&gt;
    -      &lt;expression&gt;
    -        e.marker?.contains("NOTIFY_ADMIN") || e.marker?.contains("TRANSACTION_FAILURE")
    -      &lt;/expression&gt;
    -    &lt;/evaluator&gt;    
    -    ... same as above
    -  &lt;/appender&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>マーカーを指定されなかったロギングイベントの場合、e.markerはnullになるので注意してください。この例ではGroovyの<a href="http://groovy.codehaus.org/Null+Object+Pattern">安全なデリファレンス演算子</a>である .? 演算子を使っています。
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="smtpAuthentication">認証/ STARTTLS / SSL</h3>
    -
    -    <p><code>SMTPAppender</code>では、平文のユーザーパスワード認証だけでなく、STARTTLSとSSLプロトコルの両方をサポートしています。STARTTLSとSSLの違いは、STARTTLSでは接続を確立するときは暗号化されないこと、そして、クライアントがSTARTTLSコマンドを発行してサーバがサポートしている場合はSSL接続に切り替えることです。SSLでは始めから通信が暗号化されます。
    -    </p>
    -
    -    <h3>GmailにSSLで接続するSMTPAppenderの設定</h3>
    -
    -    <p>次の例はGmailにSSLプロトコルで接続する<code>SMTPAppender</code>の設定です。</p>
    -    
    -    <p class="example">例:GmailにSSLで接続する<code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/gmaliSSL.xml">logback-examples/src/main/java/chapters/appenders/mail/gmaliSSL.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;gmailSSLExample&#39;);">Groovyとして表示</span>	
    -    <pre id="gmailSSLExample" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    <b>&lt;smtpHost&gt;smtp.gmail.com&lt;/smtpHost&gt;</b>
    -    <b>&lt;smtpPort&gt;465&lt;/smtpPort&gt;</b>
    -    <b>&lt;SSL&gt;true&lt;/SSL&gt;</b>
    -    <b>&lt;username&gt;YOUR_USERNAME@gmail.com&lt;/username&gt;</b>
    -    <b>&lt;password&gt;YOUR_GMAIL_PASSWORD&lt;/password&gt;</b>
    -
    -    &lt;to&gt;EMAIL-DESTINATION&lt;/to&gt;
    -    &lt;to&gt;ANOTHER_EMAIL_DESTINATION&lt;/to&gt; &lt;!-- additional destinations are possible --&gt;
    -    &lt;from&gt;YOUR_USERNAME@gmail.com&lt;/from&gt;
    -    &lt;subject&gt;TESTING: %logger{20} - %m&lt;/subject&gt;
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt;
    -      &lt;pattern&gt;%date %-5level %logger{35} - %message%n&lt;/pattern&gt;
    -    &lt;/layout&gt;	    
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <h3 class="doAnchor" name="gmailSTARTTLS">STARTTLSでGmailに接続するSMTPAppenderの設定</h3>
    -
    -    <p>次の例はGmailにSTARTTLSで接続する<code>SMTPAppender</code>の設定です。
    -</p>
    -
    -    <p class="example">例:GmailにSTARTTLSで接続する<code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/gmailSTARTTLS.xml">logback-examples/src/main/java/chapters/appenders/mail/gmailSTARTTLS.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;gmailSTARTTLSExample&#39;);">Groovyとして表示</span>	
    -    <pre id="gmailSTARTTLSExample" class="prettyprint source">&lt;configuration&gt;	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;smtpHost&gt;smtp.gmail.com&lt;/smtpHost&gt;
    -    &lt;smtpPort&gt;587&lt;/smtpPort&gt;
    -    &lt;STARTTLS&gt;true&lt;/STARTTLS&gt;
    -    &lt;username&gt;YOUR_USERNAME@gmail.com&lt;/username&gt;
    -    &lt;password&gt;YOUR_GMAIL_xPASSWORD&lt;/password&gt;
    -    
    -    &lt;to&gt;EMAIL-DESTINATION&lt;/to&gt;
    -    &lt;to&gt;ANOTHER_EMAIL_DESTINATION&lt;/to&gt; &lt;!-- additional destinations are possible --&gt;
    -    &lt;from&gt;YOUR_USERNAME@gmail.com&lt;/from&gt;
    -    &lt;subject&gt;TESTING: %logger{20} - %m&lt;/subject&gt;
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt;
    -      &lt;pattern&gt;%date %-5level %logger - %message%n&lt;/pattern&gt;
    -    &lt;/layout&gt;	    
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <h3 class="doAnchor" name="smtpDiscriminator">MDCDiscriminatorを指定したSMTPAppenderの設定</h3>
    -
    -
    -    <p>前述したように、<code>SMTPAppender</code>にデフォルト以外の弁別器を指定すれば、特定のユーザ、ユーザセッション、送信元のIPアドレスを含むロギングイベントが発生した場合にだけメールメッセージを生成することができます。
    -    </p>
    -
    -    <p>次の例は<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/sift/MDCBasedDiscriminator.html">MDCBasedDiscriminator</a>に"req.remoteHost"というキーを指定したものです。値として、架空のWebアプリケーションにアクセスしてきたクライアントのリモートホストのIPアドレスが設定されることを想定しています。Webアプリケーションなら<a href="./mdc.html#mis">MDCInsertingServletFilter</a>使ってMDCに値を設定することができます。
    -    </p>
    -
    -    <p class="example">例:MDCDiscriminatorを指定した<code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml">logback-examples/src/main/java/chapters/appenders/mail/mailWithMDCBasedDiscriminator.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;mailWithMDCBasedDiscriminator&#39;);">Groovyとして表示</span>	
    -    <pre id="mailWithMDCBasedDiscriminator" class="prettyprint source">&lt;configuration&gt;	  
    -  &lt;appender name="EMAIL" class="ch.qos.logback.classic.net.SMTPAppender"&gt;
    -    &lt;smtpHost&gt;ADDRESS-OF-YOUR-SMTP-HOST&lt;/smtpHost&gt;
    -    &lt;to&gt;EMAIL-DESTINATION&lt;/to&gt;
    -    &lt;from&gt;SENDER-EMAIL&lt;/from&gt;
    -
    -    <b>&lt;discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"&gt;</b>
    -      <b>&lt;key&gt;req.remoteHost&lt;/key&gt;</b>
    -      <b>&lt;defaultValue&gt;default&lt;/defaultValue&gt;</b>
    -    <b>&lt;/discriminator&gt;</b>
    -
    -    &lt;subject&gt;${HOSTNAME} -- %X{req.remoteHost} %msg"&lt;/subject&gt;
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"&gt;
    -      &lt;pattern&gt;%date%level%thread%X{req.remoteHost}%X{req.requestURL}%logger%msg&lt;/pattern&gt;
    -    &lt;/layout&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root&gt;
    -    &lt;level level="DEBUG"/&gt;
    -    &lt;appender-ref ref="EMAIL" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -    <p>こうすると、<code>SMTPAppender</code>の送信するメールそれぞれに<em>固有</em>のIPアドレスが記録されるようになるので、問題解決に役立ちます。
    -    </p>
    -    
    -    <h4 class="doAnchor" name="bufferManagement">高負荷システムにおけるバッファ管理(★要見直し)</h4>
    -
    -    <p>内部的な事情ですが、弁別器が返す値ごとに循環バッファが作成されます。しかし、ほとんどの場合<span class="prop">maxNumberOfBuffers</span>(デフォルト値は64)はそのままです。バッファの数が<span class="prop">maxNumberOfBufferes</span>を越えてしまうと、一番最近更新されたバッファはすぐに自動的に破棄されてしまいます。もう一つの予防策として、直近の30分間に更新されなかったバッファはやはり自動的に破棄されてしまいます。</p>
    -
    -    <p>毎分大量のトランザクションを処理するシステムでは、<span class="prop">maxNumberOfBuffers</span>(デフォルト値は64)が小さいと送信するメールに含まれるロギングイベントの数がとても少なくなってしまいます。大量のトランザクションが発生する場合、同じトランザクションには1つ以上のバッファが関連付けられてしまいます。弁別器が同じトランザクションには同じ値を返すので、バッファの破棄と生成が繰り返されてしまうからです。高負荷システムであっても、循環バッファの上限は<span class="prop">maxNumberOfBufferes</span>によって制御されてしまうので注意してください。
    -    </p>
    -
    -    <p>ヨーヨー効果を避けるため、<code>SMTPAppender</code>は"FINALIZE_SESSION"というマーカーの指定されたロギングイベントを見つけたら、そのロギングイベントに対して弁別器の返す値に関連付けられたバッファを直ちに開放するようになっています。これにより、トランザクションの終了時に適切にバッファを廃棄できるようになります。そうすれば、<span class="prop">maxNumberOfBuffers</span>には、安全のためより大きな値の512や1024を指定することができます。メモリ不足の危険性はありません。
    -    </p>
    -
    -    <p>循環バッファを管理するために協調的に機能する、3つの相補的な仕組みがあります。これらの仕組みが、高負荷システムであっても常に有効なバッファが利用できることを保証するのです。</p>
    -
    -    <!-- =========================================================== -->
    -    <!-- =========================================================== -->
    -
    -
    -    <h3 class="doAnchor" name="DBAppender">DBAppender</h3>
    -		
    -		<p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/db/DBAppender.html"><code>DBAppender</code></a>はデータベース上の3つのテーブルに、Javaプログラミング言語に依存しない形式のロギングイベントを登録します。
    -		</p>
    -
    -		<p>3つのテーブルとは、<em>logging_event</em>、<em>logging_event_property</em>、<em>logging_event_exception</em>です。<code>DBAppender</code>を使う前に事前に用意しておかなければなりません。logack の配布物にテーブルを作成するSQLスクリプトが含まれています。フォルダの場所は <em>logback-classic/src/main/java/ch/qos/logback/classic/db/script</em>です。一般的なデータベースそれぞれのスクリプトが用意されています。あなたの使用するデータベース用のスクリプトが無かったとしても、既存のスクリプトを参考にすれば自分で作るのは簡単です。logback の開発メンバーに教えてくれれば、喜んで今後のリリースに含めるようにします。
    -		</p>
    -
    -		<p>あなたが使用しているJDBCドライバが、JDBC3.0で導入された<code>getGeneratedKeys()</code>メソッドをサポートしているなら、紹介したスクリプトでテーブルを作ること以外に必要な作業はありません。そうはいっても、データベースに対応する<code>SQLDialect</code>を指定しなければなりません。今のところ logback が対応しているSQL方言は、H2、HSQL、MS SQLServer、MySQL、Oracle、PostgreSQL、SQLite、Sybase です。</p>
    -
    -		<p>データベースの種類と、<code>getGeneratedKeys()</code>メソッドの対応状況を表にまとめました。
    -		</p>
    -
    -		<table class="bodyTable striped" border="0" cellpadding="4">
    -			<tr>
    -				<th>データベースの種類</th>
    -        <th>テストしたバージョン</th>
    -        <th>テストしたJDBCドライバのバージョン</th>
    -        <th>{0}getGeneratedKeys(){/0}メソッド
    -の対応状況<br>
    -					</th>		
    -
    -        <th>logbackがSQL方言を提供しているかどうか
    -<br></th>
    -			</tr>
    -
    -      <tr>
    -				<td>DB2</td>
    -        <td>未テスト</td>
    -				<td>未テスト</td>
    -				<td>不明</td>
    -        <td>無し</td>
    -			</tr>
    -
    -      <tr>
    -        <td>H2</td>
    -        <td>1.2.132</td>
    -        <td>-</td>
    -				<td>不明</td>
    -        <td>提供している</td>
    -			</tr>
    -
    -      <tr>
    -        <td>HSQL</td>
    -        <td>1.8.0.7</td>
    -        <td>-</td>
    -				<td>未対応</td>
    -        <td>提供している</td>
    -			</tr>
    -
    -      <tr>
    -        <td>Microsoft SQL Server</td>
    -        <td>2005</td>
    -        <td>2.0.1008.2(sqljdbc.jar)</td>
    -				<td>対応済み</td>
    -        <td>提供している
    -</td>
    -			</tr>
    -
    -      <tr>
    -				<td>MySQL</td>
    -        <td>5.0.22</td>
    -        <td>5.0.8(mysql-connector.jar)</td>        
    -				<td>対応済み</td>
    -        <td>提供している
    -</td>
    -			</tr>
    -
    -			<tr>
    -				<td>PostgreSQL</td>
    -        <td>8.x</td>
    -        <td>8.4-701.jdbc4</td>
    -				<td>未対応</td>
    -        <td>提供している
    -</td>
    -
    -			</tr>
    -		
    -			<tr>
    -				<td>Oracle</td>
    -        <td>10g</td>
    -        <td>10.2.0.1(ojdbc14.jar)</td>
    -				<td>対応済み</td>
    -        <td>提供している
    -</td>
    -			</tr>
    -	
    -      <tr>
    -        <td>SQLLite</td>
    -        <td>3.7.4</td>
    -        <td>-</td>
    -        <td>不明</td>
    -        <td>提供している
    -</td>
    -      </tr>
    -	
    -			
    -      <tr>
    -        <td>Sybase SQLAnywhere</td>
    -        <td>10.0.1</td>
    -        <td>-</td>
    -        <td>不明</td>
    -        <td>提供している
    -</td>
    -      </tr>
    -
    -		</table>
    -		
    -		<p>検証したところ、"標準的"なPCでは1つのロギングイベントをデータベースに書き込むのにおよそ10ミリ秒かかるようです。コネクションプールを使えば1ミリ秒くらいは速くなるでしょう。一般的に利用できるほとんどのJDBCドライバではコネクションプールを使うことができるので、ぜひそうしてください。
    -		</p>
    -		
    -		<p><code>DBAppender</code>の設定方法はいろいろありますが、データベースに接続するツールや、データベース自体によって異なります。<code>DBAppender</code>の設定で重要なのは、<code>ConnectionSource</code>です。どういうものか簡単に説明しましょう。
    -		</p>
    -		
    -		<p><code>DBAppender</code>がデータベースに接続できたら、ロギングイベントは指定されたデータベースに送信されます。前述したとおり、logbackはロギングイベントを3つのテーブルに格納します。
    -		</p>
    -		
    -		<p><em>logging_event</em>テーブルには次のようなカラムがあります。</p>
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>カラム名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><b>timestamp</b></td>
    -				<td><code>big int</code></td>
    -				<td>ロギングイベントが作成された時のタイムスタンプ。</td>
    -			</tr>
    -			<tr>
    -				<td><b>formatted_message</b></td>
    -				<td><code>text</code></td>
    -
    -				<td><code>org.slf4j.impl.MessageFormatter</code>で書式化されてからロギングイベントに設定されたメッセージ。引数のオブジェクトはメッセージにくっついています。</td>
    -			</tr>
    -			<tr>
    -				<td><b>logger_name</b></td>
    -				<td><code>varchar</code></td>
    -				<td>ロギング要求を発行したロガーの名前。</td>
    -			</tr>
    -			<tr>
    -				<td><b>level_string</b></td>
    -				<td><code>varchar</code></td>
    -				<td>ロギングイベントのレベル。</td>
    -			</tr>
    -			<tr>
    -				<td><b>reference_flag</b></td>
    -				<td><code>smallint</code></td>
    -				<td>
    -					<p>このフィールドは、ロギングイベントに例外オブジェクトが含まれているか、もしくは、<code>MDC</code>に関連する値が設定されていないかどうかを判定するため、logback が使用します。
    -					</p>
    -
    -					<p>値は<code>ch.qos.logback.classic.db.DBHelper</code>が算出します。ロギングイベントに<code>MDC</code>あるいは<code>Context</code>プロパティが含まれるバア愛、このフラグ値は<em>1</em>になります。例外オブジェクトが含まれる場合は<em>2</em>になります。両方の要素が含まれている場合は<em>3</em>になります。
    -					</p>
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_filename</b></td>
    -				<td><code>varchar</code></td>
    -				<td>ロギング要求を発行した場所が含まれるファイル名。</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_class</b></td>
    -				<td><code>varchar</code></td>
    -				<td>ロギング要求を発行したクラス名。</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_method</b></td>
    -				<td><code>varchar</code></td>
    -				<td>ロギング要求を発行したメソッド名。</td>
    -			</tr>
    -			<tr>
    -				<td><b>caller_line</b></td>
    -				<td><code>char</code></td>
    -				<td>ロギング要求を発行した場所の行番号。</td>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>データベースが払いだしたロギングイベントのID。</td>
    -			</tr>
    -		</table>
    -		
    -		<p><em>logging_event_property</em>には、<code>MDC</code>または<code>Context</code>に含まれるキーと値を格納します。次のようなカラムがあります。</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>カラム名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>データベースが払いだしたロギングイベントのID。</td>
    -			</tr>
    -			<tr>
    -				<td><b>mapped_key</b></td>
    -				<td><code>varchar</code></td>
    -				<td><code>MDC</code>のキー値。</td>
    -			</tr>		
    -			<tr>
    -				<td><b>mapped_value</b></td>
    -				<td><code>text</code></td>
    -				<td><code>MDC</code>の値。</td>
    -			</tr>				
    -		</table>
    -		
    -		<p><em>logging_event_exception</em>テーブルには、次のようなカラムがあります。</p>
    -		
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>カラム名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>データベースが払いだしたロギングイベントのID。</td>
    -			</tr>
    -			<tr>
    -				<td><b>i</b></td>
    -				<td><code>smallint</code></td>
    -				<td>完全なスタックトレースを文字列化した際の各行の添字。</td>
    -			</tr>		
    -			<tr>
    -				<td><b>trace_line</b></td>
    -				<td><code>varchar</code></td>
    -				<td>スタックトレースの文字列中の1行。</td>
    -			</tr>				
    -		</table>
    -		
    -		<p><code>DBAppender</code>を使用した結果をもっと視覚的にわかりやすくお見せしましょう。次に示すのは、<code>DBAppender</code>がMySQLデータベースを使用した場合のスクリーンショットです。
    -		</p>
    -		
    -		<p><em>logging_event</em>テーブル</p>
    -
    -		<img src="images/chapters/appenders/dbAppenderLE.gif" alt="ロギングイベントテーブル">
    -
    -		<p><em>logging_event_exception</em>テーブル</p>
    -		
    -		<img src="images/chapters/appenders/dbAppenderLEException.gif" alt="ロギングイベント例外テーブル">
    -
    -		<p><em>logging_event_property</em>テーブル</p>
    -		
    -		<img src="images/chapters/appenders/dbAppenderLEProperty.gif" alt="イベントログ記録Propertyテーブル">
    -
    -		
    -		<h4>ConnectionSource</h4>
    -		
    -		<p><code>ConnectionSource</code>インターフェイスは、logbackが<code>java.sql.Connection</code>を取得するためのJDBC接続を透過的に取得するためのプラグイン可能な機能を提供するものです。<code>ConnectionSource</code>の実装クラスは3つあります。<code>DataSourceConnectionSource</code>、<code>DriverManagerConnectionSource</code>、<code>JNDIConnectionSource</code>です。
    -		</p>
    -		
    -		<p>最初に、<code>DriverManagerConnectionSource</code>を使ってMySQLデータベースに接続する例を見てみましょう。次の設定ファイルを見てください。
    -		</p>
    -		
    -    <p class="example">例: <code>DBAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/db/append-toMySQL-with-driveManger.xml">logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-driveManger.xml</a>)</p>
    -    <span class="asGroovy" onclick="return asGroovy(&#39;append-toMySQL-with-driverManager&#39;);">Groovyとして表示</span>	
    -    <pre id="append-toMySQL-with-driverManager" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender"&gt;
    -    &lt;connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"&gt;
    -      &lt;driverClass&gt;com.mysql.jdbc.Driver&lt;/driverClass&gt;
    -      &lt;url&gt;jdbc:mysql://host_name:3306/datebase_name&lt;/url&gt;
    -      &lt;user&gt;username&lt;/user&gt;
    -      &lt;password&gt;password&lt;/password&gt;
    -    &lt;/connectionSource&gt;
    -  &lt;/appender&gt;</b>
    -  
    -  &lt;root level="DEBUG" &gt;
    -    &lt;appender-ref ref="DB" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>正しいJDBCドライバクラス名を指定してください。この例では<code>com.mysql.jdbc.Driver</code>です。<span class="prop">url</span>は<em>jdbc:mysql://</em>で始まらなければなりません。
    -		</p>
    -		
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/db/DriverManagerConnectionSource.html">DriverManagerConnectionSource</a></code>は<code>ConnectionSource</code>の実装クラスで、JDBCの伝統的なやり方(接続用URLに基づくやり方)でデータベース接続を取得します。</p>
    -		<p>このクラスは<code>getConnection()</code>メソッドが呼ばれるたびに新しい<code>Connection</code>を生成することに注意してください。コネクションプーリングをサポートしているJDBCドライバーを使うか、コネクションプーリングを利用する<code>ConnectionSource</code>を自分で実装することをおすすめします。Java EE アプリケーションサーバの上で、<code>javax.sql.DataSource</code>をサポートしたJNDI実装を利用する場合は、後述する<a href="./appenders.html#JNDIConnectionSource"><code>JNDIConnectionSource</code></a>を参照してください。
    -		</p>
    -<!-- 
    -		
    -		HAS TO BE TESTED
    -
    -		<p>
    -			If you do not have another connection pooling mechanism built
    -			into your application, you can use the
    -			<a href="http://jakarta.apache.org/commons/dbcp/index.html">
    -		  commons-dbcp </a> package from Apache:
    -		</p>
    -
    -<pre class="prettyprint source">
    -  &lt;connectionSource
    -    class="ch.qos.logback.core.db.DriverManagerConnectionSource"&gt;
    -    &lt;param name="driver" value="org.apache.commons.dbcp.PoolingDriver"/&gt; 
    -    &lt;param name="url" value="jdbc:apache:commons:dbcp:/myPoolingDriver"/&gt; 
    -  &lt;/connectionSource&gt;
    -</pre>
    -		
    -		<p>
    -			Then the configuration information for the commons-dbcp
    -			package goes into the file <em>myPoolingDriver.jocl</em> and is
    -			placed in the classpath. See the
    -			<a href="http://jakarta.apache.org/commons/dbcp/index.html"> commons-dbcp </a>
    -			documentation for details.
    -		</p>
    - -->
    - 
    -		<p><code>DataSource</code>を使ってデータベースに接続する場合もだいたい同じです。設定ファイルでは<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/db/DataSourceConnectionSource.html">DataSourceConnectionSource</a></code>を指定してください。JDBCの推奨するやり方(<code>javax.sql.DataSource</code>に基づくやり方)でデータベース接続(<code>Connection</code>)を取得します。
    -		</p>
    -	
    -    <p class="example">例: <code>DBAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/db/append-with-datasource.xml">logback-examples/src/main/java/chapters/appenders/db/append-with-datasource.xml</a>)</p>	
    -
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;append-with-datasource&#39;);">Groovyとして表示</span>	
    -    <pre id="append-with-datasource" class="prettyprint source">&lt;configuration  debug="true"&gt;
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender"&gt;
    -     <b>&lt;connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"&gt;
    -       
    -       &lt;dataSource class="${dataSourceClass}"&gt;
    -       	 </b>&lt;!-- Joran cannot substitute variables
    -       	 that are not attribute values. Therefore, we cannot
    -       	 declare the next parameter like the others. 
    -       	 --&gt;
    -         <b>&lt;param name="${url-key:-url}" value="${url_value}"/&gt;
    -         &lt;serverName&gt;${serverName}&lt;/serverName&gt;
    -         &lt;databaseName&gt;${databaseName}&lt;/databaseName&gt;
    -       &lt;/dataSource&gt;</b>
    -       
    -       &lt;user&gt;${user}&lt;/user&gt;
    -       &lt;password&gt;${password}&lt;/password&gt;
    -     &lt;/connectionSource&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="INFO"&gt;
    -    &lt;appender-ref ref="DB" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -		<p>この設定例ではたくさん変数を使っているので気をつけてください。1つの設定ファイルに接続情報の詳細をまとめておくと、logback と他のフレームワークで設定内容を共有できるので便利です。
    -		</p>	
    -		
    -<!-- TO BE TESTED 
    -
    -     <p>The connection created by
    -     <code>DataSourceConnectionSource</code> can be placed in a JNDI
    -     context by using <code>BindDataSourceToJNDIAction</code>. In that
    -     case, one has to specify the use of this class by adding a new
    -     rule to Joran, logback's configuration framework. Here is an
    -     excerpt of such a configuration file.  </p>
    -		
    -<div class="source"><pre>&lt;configuration>
    -  ..
    -  <b>&lt;newRule pattern="configuration/bindDataSourceToJNDI" 
    -           actionClass="ch.qos.logback.core.db.BindDataSourceToJNDIAction"/>
    -  	    
    -  &lt;bindDataSourceToJNDI /></b>
    -  ..
    -&lt;/configuration></pre></div>
    -
    -		<p> The <em>newRule</em> element teaches Joran to use specified
    -		action class with the given pattern.  Then, we simply declare the
    -		given element. The action class will be called and our connection
    -		source will be bound to a JNDI context.  </p>
    -
    -		<p>This is a very powerful capability of Joran. If you'd like to
    -		read more about Joran, please see the <a
    -		href="onJoran.html">chapter to Joran</a>.  </p>
    -		
    -		-->
    -
    -    <h4 class="doAnchor" name="JNDIConnectionSource">JNDIConnectionSource</h4>
    -
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/db/JNDIConnectionSource.html">JNDIConnectionSource</a></code>もlogbackの配布物に含まれる<code>ConnectionSource</code>の実装クラスです。名前のとおり、JNDIから<code>javax.sql.DataSource</code>を取得し、そこから<code>java.sql.Connection</code>を取得します。<code>JNDIConnectionSource</code>は、Java EE アプリケーションサーバの内部か、アプリケーションサーバのクライアント(アプリケーションサーバの<code>javax.sql.DataSource</code>にリモートアクセスできることを想定しています)で使用することを念頭に設計されています。したがって、他にどんな便利機能を提供しているかはともかくとして、少なくともコネクションプーリングを利用することができるはずです。もっと重要なのは、<em>logback.xml</em>で<code>DataSource</code>を定義しなくてもよくなるので、アプリケーションをより<a href="http://en.wikipedia.org/wiki/Don&#39;t_repeat_yourself">DRY</a>にできることです。</p>
    -
    -    <p>次の例はTomcat用の設定ファイルから抜粋したものです。PostgreSQL で使うための設定ですが、サポートしているデータベースならどれでも同じように動くはずです。</p>
    -
    -<pre class="prettyprint source">&lt;Context docBase="/path/to/app.war" path="/myapp"&gt;
    -  ...
    -  &lt;Resource <b>name="jdbc/logging"</b>
    -               auth="Container"
    -               type="javax.sql.DataSource"
    -               username="..."
    -               password="..."
    -               driverClassName="org.postgresql.Driver"
    -               url="jdbc:postgresql://localhost/..."
    -               maxActive="8"
    -               maxIdle="4"/&gt;
    -  ...
    -&lt;/Context&gt;</pre>
    -		
    -   <p>Java EE アプリケーションサーバで定義した<code>DataSource</code>をlogbackの設定ファイルから参照するのは簡単です。</p>
    -   
    -   <p class="example">例:<code>JNDIConnectionSource</code>を使った<code>DBAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/db/append-via-jndi.xml">logback-examples/src/main/java/chapters/appenders/db/append-via-jndi.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;append-via-jndi&#39;);">Groovyとして表示</span>	
    - 
    -
    -<pre id="append-via-jndi" class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender"&gt;
    -    &lt;connectionSource class="ch.qos.logback.core.db.JNDIConnectionSource"&gt;
    -      <b>&lt;!-- please note the "java:comp/env/" prefix --&gt;</b>
    -      <b>&lt;jndiLocation&gt;java:comp/env/jdbc/logging&lt;/jndiLocation&gt;</b>
    -    &lt;/connectionSource&gt;
    -  &lt;/appender&gt;
    -  &lt;root level="INFO"&gt;
    -    &lt;appender-ref ref="DB" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -		<p>このクラスは引数無しのコンストラクタで<code>javax.naming.InitialContext</code>のインスタンスを生成するので注意してください。ほとんどの Java EE コンテナで正常に動作します。Java EE コンテナ以外で動かすときは、JNDIプロバイダのドキュメントで説明されたとおりに<em>jndi.properties</em>を用意してください。
    -		</p>
    -		
    -		<h4 class="doAnchor">コネクションプーリング</h4>
    -		
    -		<p>ロギングイベントはかなり高い頻度で生成されることがあります。ロギングイベントが生成されるのに合わせてデータベースに登録していなければなりません。そのためには、<code>DBAppender</code>でコネクションプーリングを利用するとよいでしょう。
    -		</p>
    -		
    -		<p><code>DBAppender</code>でコネクションプーリングを利用すると、著しく性能が改善することが実証されています。次の設定ファイルは、コネクションプーリング無しでMySQLデータベースにロギングイベントを登録するものです。
    -		</p>
    -    
    -    <p class="example">例: コネクションプーリング無しの<code>DBAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/db/append-toMySQL-with-datasource.xml">logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-datasource.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;append-toMySQL-with-datasource&#39;);">Groovyとして表示</span>	
    -    <pre id="append-toMySQL-with-datasource" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender"&gt;
    -    &lt;connectionSource class="ch.qos.logback.core.db.DataSourceConnectionSource"&gt;
    -      &lt;dataSource class="com.mysql.jdbc.jdbc2.optional.MysqlDataSource"&gt;
    -        &lt;serverName&gt;${serverName}&lt;/serverName&gt;
    -        &lt;port&gt;${port$&lt;/port&gt;
    -        &lt;databaseName&gt;${dbName}&lt;/databaseName&gt;
    -        &lt;user&gt;${user}&lt;/user&gt;
    -        &lt;password&gt;${pass}&lt;/password&gt;
    -      &lt;/dataSource&gt;
    -    &lt;/connectionSource&gt;
    -  &lt;/appender&gt;
    -    
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="DB" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>この設定ファイルでは、MySQLデータベースに500件のロギングイベントを送信するのになんと5秒もかかりました。つまり、1件あたり10ミリ秒もかかるのです。大規模なアプリケーションでは使いものにならないことがよくわかると思います。
    -		</p>
    -
    -		<p><code>DBAppender</code>でコネクションプーリングを利用するには、外部ライブラリが必要です。次の例は<a href="http://sourceforge.net/projects/c3p0">c3p0</a>を使っています。c3p0を利用するには、まずダウンロードして、<em>c3p0-VERSION.jar</em>をクラスパス上に配置しなければなりません。
    -		</p>
    -
    -    <p class="example">例: <code>DBAppender</code>でコネクションプーリングを利用する設定(<a href="http://logback.qos.ch/xref/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml">logback-examples/src/main/java/chapters/appenders/db/append-toMySQL-with-datasource-and-pooling.xml</a>)</p>
    -    <span class="asGroovy" onclick="return asGroovy(&#39;append-toMySQL-with-datasource-and-pooling&#39;);">Groovyとして表示</span>	
    -    <pre id="append-toMySQL-with-datasource-and-pooling" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="DB" class="ch.qos.logback.classic.db.DBAppender"&gt;
    -    &lt;connectionSource
    -      class="ch.qos.logback.core.db.DataSourceConnectionSource"&gt;
    -      <b>&lt;dataSource
    -        class="com.mchange.v2.c3p0.ComboPooledDataSource"&gt;
    -        &lt;driverClass&gt;com.mysql.jdbc.Driver&lt;/driverClass&gt;
    -        &lt;jdbcUrl&gt;jdbc:mysql://${serverName}:${port}/${dbName}&lt;/jdbcUrl&gt;
    -        &lt;user&gt;${user}&lt;/user&gt;
    -        &lt;password&gt;${password}&lt;/password&gt;
    -      &lt;/dataSource&gt;</b>
    -    &lt;/connectionSource&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="DB" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>この設定ファイルを使った場合、MySQLデータベースに500件のロギングイベントを送信するのにかかった時間は約0.5秒でした。1件あたりの所要時間は1ミリ秒です。つまり、10倍高速化できたことになります。
    -		</p>
    -
    -		<h3 class="doAnchor" name="SyslogAppender">SyslogAppender</h3>
    -
    -		<p>syslogプロトコルは非常に単純なプロトコルです。syslogの送信者は小さなメッセージをsyslogの受信者に送信します。一般的に受信者は<em>syslogデーモン</em>や<em>syslogサーバ</em>と呼ばれます。logbackは、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SyslogAppender.html"><code>SyslogAppender</code></a>を使ってリモートのsyslogデーモンにメッセージを送信することができます。
    -		</p>
    -		
    -		<p>SyslogAppenderの設定可能なプロパティは次のとおりです。</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>プロパティ名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">syslogHost</span></td>
    -				<td><code>String</code></td>
    -				<td>syslogサーバのホスト名。</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">port</span></td>
    -				<td><code>String</code></td>
    -				<td>syslogサーバーのポート番号。ほとんどの場合はデフォルト値の<em>514</em>を使うでしょう。
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="syslog">facility</span></td>
    -				<td><code>String</code></td>
    -				<td>
    -					<p><span class="prop">facility</span>は、メッセージの送信元を区別するためのものです。</p>
    -					<p><span class="prop">facility</span>オプションには、次のいずれかの文字列を指定しなければなりません。<em>KERN、USER、MAIL、DAEMON、AUTH、SYSLOG、LPR、NEWS、UUCP、CRON、AUTHPRIV、FTP、NTP、AUDIT、ALERT、CLOCK、LOCAL0、LOCAL1、LOCAL2、LOCAL3、LOCAL4、のlocal5、LOCAL6、LOCAL7。</em>大文字小文字は区別されません。</p>
    -				</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="syslog">suffixPattern</span></td>
    -				<td><code>String</code></td>
    -				<td><p><span class="prop">suffixPattern</span>オプションには、syslogサーバに送信されるメッセージ中の任意部分の書式を指定します。デフォルトは<em>"[%thread] %logger %msg"</em>です。<code>PatternLayout</code>で使用できるものは、全て<span class="prop">suffixPattern</span>に指定することができます。
    -					</p>
    -				</td>
    -			</tr>
    -
    -      <tr>
    -        <td><span class="prop" container="syslog">stackTracePattern</span></td>
    -				<td><code>String</code></td>
    -				<td><p><span class="prop">stackTracePattern</span>プロパティには、スタックトレースの各行の先頭に表示する文字列を指定します。デフォルトはタブ文字、つまり "\t"です。<code>PatternLayout</code>で使用できるものは、全て<span class="prop">stackTracePattern</span>に指定することができます。</p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td><span class="prop" container="syslog">throwableExcluded</span></td>
    -				<td><code>boolean</code></td>
    -				<td><code>true</code>を指定すると、Throwableに関連付けられているスタックトレースの情報を省略するようになります。デフォルトは<code>falese</code>です。ですので、syslogサーバにはスタックトレースの情報が送信されます。</td>
    -			</tr>
    -
    -
    -		</table>
    -		
    -		<p>ロギングイベントに対する syslog の severity(重要度)は、ロギングレベルを変換したものになります。<em>DEBUG</em>は<em>7</em>、<em>INFO</em>は6、<em>WARN</em>は<em>4</em>、<em>ERROR</em>は<em>3</em>に変換されます。
    -		</p>
    -		
    -		<p>syslog要求の書式には厳密なルールがあるので、<code>SyslogAppender</code>はレイアウトを使用しません。しかし、<span class="prop">suffixPattern</span>オプションを使えば思った通りの内容を表示することができます。
    -		</p>
    -		
    -		<p><code>SyslogAppender</code>の設定例を見てみましょう。</p>
    -		
    -    <p class="example">例: <code>SyslogAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/logback-syslog.xml">logback-examples/src/main/java/chapters/appenders/conf/logback-syslog.xml</a>)</p>	
    -    <span class="asGroovy" onclick="return asGroovy(&#39;logback-syslog&#39;);">Groovyとして表示</span>	
    -    <pre id="logback-syslog" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="SYSLOG" class="ch.qos.logback.classic.net.SyslogAppender"&gt;
    -    &lt;syslogHost&gt;remote_home&lt;/syslogHost&gt;
    -    &lt;facility&gt;AUTH&lt;/facility&gt;
    -    &lt;suffixPattern&gt;[%thread] %logger %msg&lt;/suffixPattern&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SYSLOG" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>この設定ファイルを試してみるときは、リモートsyslogデーモンが外部からのリクエストを受け付けるようになっていることを先に確認しておいてください。経験上、デフォルトではsyslogデーモンはネットワークからのリクエストを拒否するようになっていることが多いです。
    -		</p>
    -		
    -
    -    <h3 class="doAnchor" name="SiftingAppender">SiftingAppender</h3>
    -
    -    <p>名前のとおり、<code>SiftingAppender</code>は設定に基づいてロギングイベントを分配(あるいはふるいにかける)ことができます。例えば、<code>SiftingAppender</code>がユーザーセッションに基づいてロギングイベントを分配するようになっていれば、ユーザーごとにログファイルを生成するようになるでしょう。
    -    </p>
    -
    -
    -    
    -    <table class="bodyTable striped">
    -			<tr>
    -				<th>プロパティ名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="sift">timeout</span></td>
    -				<td><code><a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/util/Duration.html">Duration</a></code></td>
    -				<td><span class="prop">タイムアウト時間</span>を経過してもアクセスされなかったアペンダーは、古くなったものと判断されます。古くなったアペンダーは閉じられて、<code>SiftingAppender</code>から切り離されます。デフォルトは30分です。</td>
    -			</tr>
    -			<tr>
    -				<td><span class="prop" container="sift">maxAppenderCount</span></td>
    -				<td><code>integer</code></td>
    -				<td><code>SiftingAppender</code>が作成して管理できるアペンダーの最大数を指定します。デフォルトはInteger.MAX_VALUEです。</td>
    -			</tr>
    -  </table>
    -
    -    <p><code>SiftingAppender</code>は実行時にアペンダーを作成します。<code>SiftingAppender</code>の設定で指定された設定テンプレート(<code>sift要素</code>で囲まれた部分です。後で例を示します)に従って作成します。<code>SiftingAppender</code>には、子アペンダーのライフサイクルを管理する責任があります。たとえば、 <code>SiftingAppender</code>は古くなったアペンダー自動的に閉じて削除します。<span class="prop">タイムアウト時間</span>で指定された時間を過ぎてもアクセスの無かったアペンダーを無効とみなすのです。
    -    </p>
    -
    -    <p>ロギングイベントが発生したら、<code>SiftingAppender</code>は委譲する子アペンダーを選択します。選択条件は弁別器によって実行時に計算されます。利用者は<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/sift/Discriminator.html">Discriminator</a></code>で選択条件を指定することができます。例を見てみましょう。
    -    </p>
    - 
    -    <h4>例</h4>
    -
    -    <p><a href="http://logback.qos.ch/xref/chapters/appenders/sift/SiftExample.html">SiftExample</a>アプリケーションは、アプリケーションが起動したことを表すメッセージをロギングします。その後、MDCのキー"uesrid"に"Alice"を設定して、メッセージをロギングします。該当するコードは次のとおりです。</p>
    -   
    -    <p class="source">logger.debug("Application started");
    -MDC.put("userid", "Alice");
    -logger.debug("Alice says hello"); </p>
    -
    -    <p><code>SiftingAppender</code>で使う設定ファイルのテンプレートは次のようになっています。</p>
    -
    -
    -    <p class="example">例: <code>SiftingAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/sift/byUserid.xml">logback-examples/src/main/java/chapters/appenders/sift/byUserid.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;byUserid&#39;);">Groovyとして表示</span>
    -
    -    <pre id="byUserid" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"&gt;</b>
    -    &lt;!-- in the absence of the class attribute, it is assumed that the
    -         desired discriminator type is
    -         ch.qos.logback.classic.sift.MDCBasedDiscriminator --&gt;
    -    <b>&lt;discriminator&gt;</b>
    -      <b>&lt;key&gt;<span class="green">userid</span>&lt;/key&gt;</b>
    -      <b>&lt;defaultValue&gt;unknown&lt;/defaultValue&gt;</b>
    -    <b>&lt;/discriminator&gt;</b>
    -    <b>&lt;sift&gt;</b>
    -      <b>&lt;appender name="FILE-<span class="green">${userid}</span>" class="ch.qos.logback.core.FileAppender"&gt;</b>
    -        <b>&lt;file&gt;<span class="green">${userid}</span>.log&lt;/file&gt;</b>
    -        <b>&lt;append&gt;false&lt;/append&gt;</b>
    -        <b>&lt;layout class="ch.qos.logback.classic.PatternLayout"&gt;</b>
    -          <b>&lt;pattern&gt;%d [%thread] %level %mdc %logger{35} - %msg%n&lt;/pattern&gt;</b>
    -        <b>&lt;/layout&gt;</b>
    -      <b>&lt;/appender&gt;</b>
    -    <b>&lt;/sift&gt;</b>
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SIFT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -    
    -    
    -    <p>discriminator 要素にclass属性がない場合、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/sift/MDCBasedDiscriminator.html">MDCBasedDiscriminator</a>が設定されます。弁別器は、<span class="prop">key</span>プロパティで指定されたキーのMDC値を返します。MDC値がnullの場合は、<span class="prop">defaultValue</span>プロパティで指定した値を返します。
    -    </p>
    -
    -    <p><code>SiftingAppender</code>による子アペンダーの管理機能は独特なものです。上記の例では、<code>SiftingAppender</code>によって複数の<code>FileAppender</code>が作られます。それぞれの<code>FileAppender</code>はキー"userid"でMDCに登録された値によって区別されます。キー"uesrid"のMDC値が新しい値のときは、新しい<code>FileAppender</code>インスタンスをゼロから作ります。<code>SiftingAppender</code>は自分が作ったアペンダーを追跡し続けます。30分間使われなかったアペンダーは自動的に閉じられ、破棄されます。
    -    </p>
    -
    -    <p><span class="label notice">変数の公開</span>出力先リソースが別々な複数のアペンダーのインスタンスを利用するにはどうしたらいいでしょうか?弁別器のキーが<a href="./configuration.html#variableSubstitution">変数</a>として公開されるので、上記の例の場合は、アペンダーの設定テンプレート内で"userid"を指定しています。こうすると、子アペンダーに別々の出力先を設定できるようになります。
    -    </p>
    -
    -    <p><code>SiftExample</code>アプリケーションの引数に設定ファイル"byUserid.xml"を指定して実行すると、"unknown.log" と"Alice.log"という2つのログファイルが作成されます。
    -		</p>
    -
    -    <p><span class="label">ローカルスコープの変数</span>logback 1.0.12 から、ネストされたアペンダーからローカルスコープの変数が利用できるようになりました。また、<em><code>sift要素</code>内</em>で<a href="./configuration.html#definingProps">変数を定義</a>したり、変数の値を<a href="./configuration.html#definingPropsOnTheFly">実行時に算出</a>できるようになりました。<code>sift要素</code>の外側で定義された変数の値を組み合わせることもできます。
    -    </p>
    -
    -    <h4 class="doAnchor" name="siftGettingTimeoutRight">適切な<span class="prop">タイムアウト</span>を設定する</h4>
    -
    -    <p>アプリケーションによっては、適切な<span class="prop">タイムアウト時間</span>を決めるのが難しいことがあります。<span class="prop">タイムアウト時間</span>が短すぎると、ネストされたアペンダーが削除された直後に新しく生成されるようになってしまいます。これは<em>ゴミ漁り</em>と呼ばれる事象です。<span class="prop">タイムアウト時間</span>が長すぎると、立て続けにアペンダーが生成されるとリソース不足になってしまうかもしれません。<span class="prop">maxAppenderCount</span>が小さすぎても同じようにゴミ漁りが発生するかもしれません。
    -    </p>
    -
    -    <p>どんな場合でも、ネストされたアペンダーが不要になる箇所を特定するのは簡単でしょう。そういう場所が特定できたら、あるいはほぼ確実にそうだと言える場所が特定できたら、そこに書かれているロギング式に<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/ClassicConstants.html#FINALIZE_SESSION_MARKER">FINALIZE_SESSION</a>マーカーを指定しましょう。SiftingAppenderは<code>FINALIZE_SESSION</code>マーカーを指定されたロギングイベントを見つけたら、関連付けられたアペンダーはもう破棄していいいものと判断します。破棄されることになったアペンダーは、数秒間到着するであろうロギングイベントに備えて活性化した後、何も到着しなければそのままクローズします。
    -    </p>
    -    
    -    <pre class="prettyprint source">import org.slf4j.Logger;
    -import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;
    -
    -  void job(String jobId) {
    -   
    -    MDC.put("jobId", jobId);
    -    logger.info("Starting job.");
    -
    -    ... do whather the job needs to do
    -    
    -    // will cause the nested appender reach end-of-life. It will
    -    // linger for a few seconds.
    -    logger.info(FINALIZE_SESSION_MARKER, "About to end the job");
    -
    -    try {
    -      .. perform clean up
    -    } catch(Exception e);  
    -      // This log statement will be handled by the lingering appender. 
    -      // No new appender will be created.
    -      logger.error("unexpected error while cleaning up", e);
    -    }
    -  }
    -
    -</pre>
    -
    -    <h3 class="doAnchor" name="AsyncAppender">AsyncAppender</h3>
    -
    -    <p>AsyncAppenderは<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvent</a>を非同期的にロギングします。単にイベントディスパッチャとして機能するので、意味のある仕事をさせるには他のアペンダーを参照させなければなりません。</p>
    -
    -    <p><span class="label notice">80%を越えると消えてしまう</span>
    -AsyncAppenderはロギングイベントを<a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/BlockingQueue.html">BlockingQueue</a>に蓄積します。<code>AsyncAppender</code>のワーカースレッドは、キューの先頭からロギングイベントを取り出して、<code>AsyncAppender</code>に割り当てられたアペンダーに振り分けます。キューの使用量が80%を超えている場合、デフォルトではTRACE、DEBUG、および、INFOレベルのログを捨ててしまいます。これは、ロギングイベントを失ってしまうリスクに見合うだけの性能影響があります。
    -    </p>
    -
    -    <p><span class="label">アプリケーションの停止と再デプロイ</span>
    -アプリケーションを停止、あるいは<code>再デプロイ</code>する前に、 AsyncAppenderを停止しなければなりません。全てのロギングイベントをキューから吐き出すためです。そのためには、<a href="./configuration.html#stopContext">LoggerContextを停止</a>すればよいです。<code>AsyncAppender</code>を含む全てのアペンダーを閉じます。</p>
    -
    -
    -    <p><code>AsyncAppender</code>の設定可能なプロパティは次のとおりです。</p>
    -
    -		<table class="bodyTable striped">
    -      <tr>
    -        <th>プロパティ名</th>
    -        <th>型</th>
    -        <th>説明</th>
    -      </tr>
    -			<tr>
    -        <td><span class="prop" container="async">queueSize</span></td>
    -        <td><code>int</code></td>
    -        <td>キューの最大容量。デフォルトは256です。
    -				</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="async">discardingThreshold</span></td>
    -        <td><code>int</code></td>
    -        <td>デフォルトは20%です。キューの使用量が閾値を越えたら、INFO以下のロギングイベントは破棄、WARN以上のロギングイベントだけをキューイングするようになります。0を指定するとロギングイベントを破棄しないようになります。
    -			</td>
    -			</tr>
    -      <tr>
    -        <td><span class="prop" container="async">includeCallerData</span></td>
    -        <td><code>boolean</code></td>
    -        <td>送信者情報の抽出自体に時間がかかることがあります。性能上の兼ね合いにより、デフォルトではロギングイベントをキューに追加するとき、関連する送信者情報を抽出しないようになっています。代わりに、スレッド名などが"軽い"情報だけを<a href="./mdc.html">MDC</a>にコピーします。trueを指定した場合、完全な送信者情報を含めるようになります。
    -        </td>
    -      </tr>
    -    </table>
    -
    -    <p>デフォルトでは、イベントキューに登録可能な件数は256件になっています。キューが一杯になると、新しくロギングイベントを登録しようとしていたアプリケーションスレッドは、ワーカースレッドによってキューのロギングイベントが処理されるまで、ブロックします。キューの使用量が最大使用量を下回るまで、アプリケーションスレッドはロギング要求を発行できなくなります。したがって、ロギングイベントのバッファ使用量が最大値付近で推移しているとき、このアペンダーはほぼ同期的なロギングをすることになります。これは必ずしも悪いことではありません。このアペンダーは、ロギングイベントのバッファ使用量が閾値を超えるほどに増加するまでは、ロギング処理よりもアプリケーションの実行時間が長くなるように設計されています。
    -    </p>
    -
    -    <p>アプリケーションのスループットを最大化するためには、アペンダーのイベントキューの大きさを最適化する必要があり、それはいくつもの要因が絡んでいます。次に示す要因のいずれか、あるいは全てが、擬似的な同期的動作をさせる可能性があります。</p>
    -  
    -    <ul>
    -      <li>アプリケーションスレッドが多すぎる</li>
    -      <li>アプリケーションの呼び出しごとのロギング要求が多すぎる</li>
    -      <li>ロギングイベントごとに付随する情報が多すぎる</li>
    -      <li>子アペンダーのレイテンシが遅すぎる</li>
    -    </ul>
    -
    -    <p>一般的には、アプリケーションの使用するヒープを減らして、ロギングイベントのキューを大きくすればよいでしょう。
    -    </p>
    -
    -    
    -    <p><span class="label notice">非可逆な振る舞い</span>
    -上記の議論を踏まえながらブロックする可能性を減らすため、AsyncAppenderはデフォルトでは利用可能なキューが最大値の20%未満になったらTRACE、DEBUG、INFOレベルのロギングイベントを破棄し、WARNとERRORレベルのロギングイベントだけを残すようになっています。そうすると、TRACE、DEBUG、INFOレベルのロギングイベントにコストをかけなくてもよくなるので、非同期処理を妨げず、高い性能を維持することができます。しきい値<span class="prop">discardingThreshold</span>を0にすればロギングイベントを破棄しないようにすることもできます。
    -    </p>
    -
    -    <p class="example">例: <code>AsyncAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conc/logback-async.xml">logback-examples/src/main/java/chapters/appenders/conc/logback-async.xml</a>)</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;asyncAppender&#39;);">Groovyとして表示</span>
    -
    -    <pre id="asyncAppender" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="<b>FILE</b>" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;myapp.log&lt;/file&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%logger{35} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  <b>&lt;appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender"&gt;</b>
    -    <b>&lt;appender-ref ref="FILE" /&gt;</b>
    -  <b>&lt;/appender&gt;</b>
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="<b>ASYNC</b>" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -  
    -
    -		<h3 class="doAnchor" name="WriteYourOwnAppender">アペンダーを自作する</h3>
    -
    -
    -    <p><code>AppenderBase</code>を使えば簡単にアペンダーを自作することができます。この基底クラスでは、フィルターやステータスメッセージの管理などほとんどのアペンダーが備えている機能を提供するものです。派生クラスでやることと言えば、<code>append(Object eventObject)</code>メソッドを実装するだけです。
    -    </p>
    -
    -    <p>次に示す<code>CountingConsoleAppender</code>は、限られた数のロギングイベントをコンソールに出力する自作アペンダーです。指定された数のロギングイベントを処理したらシャットダウンします。ロギングイベントを書式化するために<code>PatternLayoutEncoder</code>を使用します。そして、<code>limit</code>というプロパティを設定することができます。また、<code>append(Object eventObject)</code>メソッド以外にもいくつかメソッドを用意しなければなりません。例を見ればわかりますが、これらのパラメータは logback のさまざまな設定の仕組みによって自動的に設定されます。
    -    </p>
    -    
    -    <em>例4<span class="autoExec"></span>: <code>CountingConsoleAppender</code> (<a href="http://logback.qos.ch/xref/chapters/appenders/CountingConsoleAppender.java">logback-examples/src/main/java/chapters/appenders/CountingConsoleAppender.java</a>)</em>
    -    <pre class="prettyprint source">package chapters.appenders;
    -
    -import java.io.IOException;
    -
    -import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.AppenderBase;
    -
    -
    -public class CountingConsoleAppender extends AppenderBase&lt;ILoggingEvent&gt; {
    -  static int DEFAULT_LIMIT = 10;
    -  int counter = 0;
    -  int limit = DEFAULT_LIMIT;
    -  
    -  PatternLayoutEncoder encoder;
    -  
    -  public void setLimit(int limit) {
    -    this.limit = limit;
    -  }
    -
    -  public int getLimit() {
    -    return limit;
    -  }
    -  
    -  @Override
    -  public void start() {
    -    if (this.encoder == null) {
    -      addError("No encoder set for the appender named ["+ name +"].");
    -      return;
    -    }
    -    
    -    try {
    -      encoder.init(System.out);
    -    } catch (IOException e) {
    -    }
    -    super.start();
    -  }
    -
    -  public void append(ILoggingEvent event) {
    -    if (counter &gt;= limit) {
    -      return;
    -    }
    -    // output the events as formatted by our layout
    -    try {
    -      this.encoder.doEncode(event);
    -    } catch (IOException e) {
    -    }
    -
    -    // prepare for next event
    -    counter++;
    -  }
    -
    -  public PatternLayoutEncoder getEncoder() {
    -    return encoder;
    -  }
    -
    -  public void setEncoder(PatternLayoutEncoder encoder) {
    -    this.encoder = encoder;
    -  }
    -}</pre>
    -
    -		<p><code>start()</code>メソッドでは、<code>PatternLayoutEncoder</code>が設定されているかチェックしています。エンコーダーが設定されなかった場合、アペンダーの起動は失敗となり、エラーメッセージを出力します。
    -		</p>
    -		
    -		<p>この自作アペンダーの見どころは次の2点です。</p>
    -		
    -		<ul>
    -
    -      <li>JavaBeans の規約通りにアクセサを定義したプロパティは、logbackの他の設定と同じように処理されます。<code>start()</code>メソッドはlogbackの設定処理から自動的に呼び出されるようになっています。アペンダーのさまざまなプロパティの設定と、一貫性を整える役割があります。
    -			</li>
    -
    -			<li><code>AppenderBase.doAppend()</code>メソッドは派生クラスのappend()メソッドを呼び出します。実際の出力は<code>append()</code>が行います。レイアウトによってロギングイベントを書式化するのもこのメソッドです。
    -			</li>
    -		</ul>
    -		
    -		<p><a href="http://logback.qos.ch/xref/chapters/appenders/CountingConsoleAppender.html"><code>CountingConsoleAppender</code></a>は他のアペンダーと同じように定義することができます。設定ファイルの例として<a href="http://logback.qos.ch/xref/chapters/appenders/countingConsole.xml"><em>logback-examples/src/main/java/chapters/appenders/countingConsole.xml</em></a>も見てください。
    -		</p>
    -  
    -
    -		<h2 class="doAnchor" name="logback_access">Logback Access</h2>
    -		
    -		<p>logback-classic のほとんどのアペンダーと同じものが logback-access にもあります。基本的には同じように動きます。以降の節ではそれらの使い方を説明します。
    -		</p>
    -		
    -		<h3 class="doAnchor" name="AccessSocketAppender">SocketAppenderとSSLSocketAppender</h3>
    -		
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/net/SocketAppender.html">SocketAppender</a></code>は、シリアライズした<code>AccessEvent</code>をネットワーク上のリモートホストに送信するために設計されています。リモートロギングは、アクセスイベントが重要であろうとなかろうと盗聴できないようになっています。受信したイベントをデシリアライズした後は、自分で生成したロギングイベントと同じように扱うことができます。
    -		</p>
    -		
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/net/SSLSocketAppender.html">SSLSocketAppender</a></code>は基本的な<code>SocketAppender</code>を拡張したもので、Secure Sockets Layer(SSL)を介してリモートホストにログを転送します。
    -    </p>
    -    
    -		<p>logback-access の<code>SocketAppender</code>で設定可能なプロパティは、logback-classic の<code>SocketAppender</code>と同じです。
    -		</p>
    -
    -    <h3 class="doAnchor" name="AccessServerSocketAppender">ServerSocketAppenderとSSLServerSocketAppender</h3>
    -    
    -    <p><code>SocketAppender</code>と同様に、<a href="http://logback.qos.ch/xref/ch/qos/logback/access/net/server/ServerSocketAppender.html"><code>ServerSocketAppender</code></a>はシリアライズした<code>AccessEvent</code>をネットワーク上のリモートホストに送信するために設計されています。<code>ServerSocketAppender</code>はサーバとして動作します。つまり、外部のクライアントがTCP接続してくるのを待ち受けます。アペンダーに渡されたロギングイベントは、接続している全てのクライアントに配布されます。
    -    </p>
    -    
    -    <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/net/server/SSLServerSocketAppender.html">SSLSocketAppender</a></code>は基本的な<code>ServerSocketAppender</code>を拡張したもので、Secure Sockets Layer(SSL)を介してリモートホストにログを転送します。
    -    </p>
    -    
    -    <p>logback-access の<code>ServerSocketAppender</code>で設定可能なプロパティは、logback-classic の<code>ServerSocketAppender</code>と同じです。
    -    </p>
    -    
    -	 	
    -		<h3 class="doAnchor" name="AccessSMTPAppender">SMTPAppender</h3>
    -		
    -		<p>logback-access の<code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/net/SMTPAppender.html">SMTPAppender</a></code>は、logback-classic と同じように動作します。tだ、<span class="prop">評価器</span>の設定はだいぶ違います。デフォルトでは、 <code>URLEvaluator</code>オブジェクトが使用されます。この評価器はロギング要求のURLと突き合わせるURLのリストを持っています。いずれかのURLにマッチするロギング要求が発生したら、<code>SMTPAppender</code>はメールを送信します。
    -		</p>
    -		
    -		<p>logback-access の<code>SMTPAppender</code>の設定例を見てみましょう。
    -		</p>
    -    <p class="example">例: <code>SMTPAppender</code>の設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/access/logback-smtp.xml">logback-examples/src/main/java/chapters/appenders/conf/access/logback-smtp.xml</a>)</p>
    -
    -<pre class="prettyprint source">&lt;appender name="SMTP"
    -  class="ch.qos.logback.access.net.SMTPAppender"&gt;
    -  &lt;layout class="ch.qos.logback.access.html.HTMLLayout"&gt;
    -    &lt;pattern&gt;%h%l%u%t%r%s%b&lt;/pattern&gt;
    -  &lt;/layout&gt;
    -    
    -  <b>&lt;Evaluator class="ch.qos.logback.access.net.URLEvaluator"&gt;
    -    &lt;URL&gt;url1.jsp&lt;/URL&gt;
    -    &lt;URL&gt;directory/url2.html&lt;/URL&gt;
    -  &lt;/Evaluator&gt;</b>
    -  &lt;from&gt;sender_email@host.com&lt;/from&gt;
    -  &lt;smtpHost&gt;mail.domain.com&lt;/smtpHost&gt;
    -  &lt;to&gt;recipient_email@host.com&lt;/to&gt;
    -&lt;/appender&gt;</pre>
    -
    -		<p>このやり方では、特別な処理プロセスの中で重要な手順を通ったときにメールを送信するようなことができます。メールには、トリガとなったWebページにアクセスする一つ前のWebページが含まれます。他の情報を含めることもできます。
    -		</p>
    -
    -		<h3 class="doAnchor" name="AccessDBAppender">DBAppender</h3>
    -		
    -		<p><a href="http://logback.qos.ch/xref/ch/qos/logback/access/db/DBAppender.html"><code>DBAppender</code></a>はアクセスイベントをデータベースに登録するために使用します。
    -		</p>
    -
    -		<p><code>DBAppender</code>は<em>access_event</em>テーブルと<em>access_event_header</em>テーブルを使います。いずれも<code>DBAppender</code>を使用する前に準備しなければなりません。テーブルを作成するSQLスクリプトはlogback の配布物に含まれています。<em>logback-access/src/main/java/ch/qos/logback/access/db/script</em>ディレクトリにあるはずです。一般的なデータベースそれぞれの専用スクリプトがあります。あなたの使用しているデータベース用のスクリプトが無かったとしても、他のスクリプトを参考にすれば簡単に作成できます。あなたの使っているデータベース用のスクリプトが無かった時は、作成したスクリプトをlogbackプロジェクトに送ってください。
    -		</p>
    -		
    -		<p><em>access_event</em>テーブルのカラムは次のとおりです。</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>カラム名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><b>timestamp</b></td>
    -				<td><code>big int</code></td>
    -				<td>アクセスイベントの作成時のタイムスタンプ。</td>
    -			</tr>
    -			<tr>
    -				<td><b>requestURI</b></td>
    -				<td><code>varchar</code></td>
    -				<td>要求されたURI。</td>
    -			</tr>
    -			<tr>
    -				<td><b>requestURL</b></td>
    -				<td><code>varchar</code></td>
    -				<td>要求されたURL。これはリクエストメソッド、リクエストURI、リクエストプロトコルを組み合わせた文字列です。
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteHost</b></td>
    -				<td><code>varchar</code></td>
    -				<td>リモートホストの名前。</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteUser</b></td>
    -				<td><code>varchar</code></td>
    -				<td>リモートユーザの名前。
    -				</td>
    -			</tr>
    -			<tr>
    -				<td><b>remoteAddr</b></td>
    -				<td><code>varchar</code></td>
    -				<td>リモートIPアドレス。</td>
    -			</tr>
    -			<tr>
    -				<td><b>protocol</b></td>
    -				<td><code>varchar</code></td>
    -				<td><em>HTTP</em>または<em>HTTPS</em>などのリクエストプロトコル、。</td>
    -			</tr>
    -			<tr>
    -				<td><b>method</b></td>
    -				<td><code>varchar</code></td>
    -				<td>リクエストメソッド、<em>GET</em>か<em>POST</em>になるでしょう。</td>
    -			</tr>
    -			<tr>
    -				<td><b>serverName</b></td>
    -				<td><code>varchar</code></td>
    -				<td>リクエストを受け付けたサーバーの名前。</td>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>アクセスイベントのデータベース上のID。</td>
    -			</tr>
    -		</table>
    -		
    -		<p><em>access_event_header</em>テーブルには、リクエストのヘッダ情報が登録されます。次のようなものです。</p>
    -
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>カラム名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td><b>event_id</b></td>
    -				<td><code>int</code></td>
    -				<td>アクセスイベントのデータベース上のID。</td>
    -			</tr>
    -			<tr>
    -				<td><b>header_key</b></td>
    -				<td><code>varchar</code></td>
    -				<td><em>User-Agent</em>などのリクエストヘッダー名。</td>
    -			</tr>
    -			<tr>
    -				<td><b>header_value</b></td>
    -				<td><code>varchar</code></td>
    -				<td><em>Mozilla/5.0 (Windows; U; Windows NT 5.1; fr; rv:1.8.1) Gecko/20061010 Firefox/2.0</em>のようなリクエストヘッダー値。</td>
    -			</tr>
    -			</table>
    -
    -		<p>logback-access の<code>DBAppender</code>に設定可能なプロパティは、logback-classic の<code>DBAppender</code>でも利用できます。logback-access の DBAppender でだけ設定可能なプロパティは次のとおりです。
    -		</p>
    -		
    -		<table class="bodyTable striped">
    -			<tr>
    -				<th>プロパティ名</th>
    -				<th>型</th>
    -				<th>説明</th>
    -			</tr>
    -			<tr>
    -				<td>
    -					<b>
    -						<span class="prop">insertHeaders</span>
    -					</b>
    -				</td>
    -				<td>
    -					<code>boolean</code>
    -				</td>
    -				<td>trueの場合、ロギング要求に含まれる全てのヘッダ情報を登録するようになります。
    -				</td>
    -			</tr>
    -		</table>
    -
    -		<p><code>DBAppender</code>の設定例を見てください。</p>
    -
    -    <p class="example">例:DBAppenderの設定(<em><a href="http://logback.qos.ch/xref/chapters/appenders/conf/access/logback-DB.xml</em>">logback-examples/src/main/java/chapters/appenders/conf/access/logback-DB.xml</em></a>)</p>
    -    
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="DB" class="ch.qos.logback.access.db.DBAppender"&gt;
    -    &lt;connectionSource class="ch.qos.logback.core.db.DriverManagerConnectionSource"&gt;
    -      &lt;driverClass&gt;com.mysql.jdbc.Driver&lt;/driverClass&gt;
    -      &lt;url&gt;jdbc:mysql://localhost:3306/logbackdb&lt;/url&gt;
    -      &lt;user&gt;logback&lt;/user&gt;
    -      &lt;password&gt;logback&lt;/password&gt;
    -    &lt;/connectionSource&gt;
    -    &lt;insertHeaders&gt;true&lt;/insertHeaders&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender-ref ref="DB" /&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <h3 class="doAnchor" name="AccessSiftingAppender">SiftingAppender</h3>
    -   
    -    <p>logback-access のSiftingAppenderは、logback-classic のSiftingAppenderとほとんど同じです。主な違いは、デフォルトの弁別器がMDCを参照するものではなく、<a href="http://logback.qos.ch/xref/ch/qos/logback/access/sift/AccessEventDiscriminator.html">AccessEventDiscriminator</a>になっていることです。AccessEventDiscriminatorは、その名のとおり、ネストされたアペンダーを選択するために、AccessEventのフィールドを使用します。指定されたフィールドの値がnullの場合<span class="prop">DefaultValue</span>プロパティの値が使用されます。
    -    </p>
    -
    -    <p>AccessEventのフィールドとして指定できるのはCOOKIE、REQUEST_ATTRIBUTE、SESSION_ATTRIBUTE、REMOTE_ADDRESS、LOCAL_PORT、REQUEST_URIです。最初の3つのフィールドは単独で指定することはできません。<span class="prop">AdditionalKey要素</span>が必要なので注意してください。</p>
    -    
    -    <p>設定ファイルの例を示します。</p>
    -
    -    <p class="example">例:SiftingAppenderの設定(<a href="http://logback.qos.ch/xref/chapters/appenders/conf/sift/access-siftingFile.xml">logback-examples/src/main/java/chapters/appenders/conf/sift/access-siftingFile.xml</a>)</p>
    -    
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="SIFTING" class="ch.qos.logback.access.sift.SiftingAppender"&gt;
    -    &lt;Discriminator class="ch.qos.logback.access.sift.AccessEventDiscriminator"&gt;
    -      &lt;Key&gt;id&lt;/Key&gt;
    -      &lt;FieldName&gt;SESSION_ATTRIBUTE&lt;/FieldName&gt;
    -      &lt;AdditionalKey&gt;username&lt;/AdditionalKey&gt;
    -      &lt;defaultValue&gt;NA&lt;/defaultValue&gt;
    -    &lt;/Discriminator&gt;
    -    &lt;sift&gt;
    -       &lt;appender name="access-$id-$username" class="ch.qos.logback.core.FileAppender"&gt;
    -        &lt;file&gt;byUser/access-$id-$username.log&lt;/file&gt;
    -        &lt;layout class="ch.qos.logback.access.PatternLayout"&gt;
    -          &lt;pattern&gt;%h %l %u %t \"%r\" %s %b&lt;/pattern&gt;
    -        &lt;/layout&gt;
    -      &lt;/appender&gt;
    -    &lt;/sift&gt;
    -  &lt;/appender&gt;
    -  &lt;appender-ref ref="SIFTING" /&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <p>この例では<code>SiftingAppender</code>が<code>FileAppender</code>をネストしています。キー要素の値"id"は、ネストされた<code>FileAppender</code>で変数として使用することができます。デフォルトの弁別器でもある<code>AccessEventDiscriminator</code>は AdditionalKey 要素で指定した"username" を<code>AccessEvent</code>のセッション属性から探します。指定した属性が無かったら、defaultValue要素の値"NA"を使用します。セッション属性の"username"には、アプリケーションのログインユーザー名が含まれていることを想定しています。ユーザーごとのアクセスログは、<em>byUser</em>フォルダの下にユーザー名入りのファイル名で出力されます。
    -    </p>
    -
    -
    -    <script src="http://logback.qos.ch/templates/footer.js" type="text/javascript"></script>
    -
    -
    -  </div>
    -  
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/architecture.html b/logback-site/src/site/pages/manual/architecture.html
    deleted file mode 100755
    index 1b000cbe07..0000000000
    --- a/logback-site/src/site/pages/manual/architecture.html
    +++ /dev/null
    @@ -1,936 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 2: Architecture</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -   <h1>Chapter 2: Architecture</h1>
    -
    -   <a href="architecture_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -   <div class="quote">
    -      <p><em>All true classification is genealogical.</em></p>
    -      <p>&mdash;CHARLES DARWIN, <em>The Origin of Species</em></p>
    -
    -      <p><em>It is difficult, if not impossible, for anyone to learn a
    -      subject purely by reading about it, without applying the
    -      information to specific problems and thereby forcing himself to
    -      think about what has been read. Furthermore, we all learn best
    -      the things that we have discovered ourselves.</em>
    -      </p>
    -      <p>&mdash;DONALD KNUTH, <em>The Art of Computer Programming</em></p>
    -   </div>
    -
    -
    -   <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -    <h2>Logback's architecture</h2>
    -
    -    <p>Logback's basic architecture is sufficiently generic so as to
    -    apply under different circumstances. At the present time, logback
    -    is divided into three modules, logback-core, logback-classic and
    -    logback-access.
    -    </p>
    -
    -    <p>The <em>core</em> module lays the groundwork for the other two
    -    modules.  The <em>classic</em> module extends <em>core</em>. The
    -    classic module corresponds to a significantly improved version of
    -    log4j. Logback-classic natively implements the <a
    -    href="http://www.slf4j.org">SLF4J API</a> so that you can readily
    -    switch back and forth between logback and other logging systems
    -    such as log4j or java.util.logging (JUL) introduced in JDK
    -    1.4. The third module called <em>access</em> integrates with
    -    Servlet containers to provide HTTP-access log functionality. A
    -    separate document covers <a href="../access.html">access module
    -    documentation</a>.
    -    </p>
    -
    -    <p>In the remainder of this document, we will write "logback" to
    -    refer to the logback-classic module.
    -    </p>
    -    
    -		<h2>Logger, Appenders and Layouts</h2>
    -		
    -		<p>Logback is built upon three main classes: <code>Logger</code>,
    -		<code>Appender</code> and <code>Layout</code>. These three types
    -		of components work together to enable developers to log messages
    -		according to message type and level, and to control at runtime how
    -		these messages are formatted and where they are reported.
    -		</p>
    -
    -		<p>The <code>Logger</code> class is part of the logback-classic
    -		module. On the other hand, the <code>Appender</code> and
    -		<code>Layout</code> interfaces are part of logback-core. As a
    -		general-purpose module, logback-core has no notion of
    -		loggers.
    -		</p>
    -    
    -    <h3 class="doAnchor" name="LoggerContext">Logger context</h3>
    -
    -		<p>The first and foremost advantage of any logging API over plain
    -		<code>System.out.println</code> resides in its ability to disable
    -		certain log statements while allowing others to print
    -		unhindered. This capability assumes that the logging space, that
    -		is, the space of all possible logging statements, is categorized
    -		according to some developer-chosen criteria.  In logback-classic,
    -		this categorization is an inherent part of loggers.  Every single
    -		logger is attached to a <code>LoggerContext</code> which is
    -		responsible for manufacturing loggers as well as arranging them in
    -		a tree like hierarchy.
    -		</p>
    -			
    -		<p>Loggers are named entities. Their names are case-sensitive and
    -		they follow the hierarchical naming rule:
    -		</p>
    -
    -		<div class="definition">
    -			<div class="deftitle">Named Hierarchy</div>
    -			<p>
    -				A logger is said to be an ancestor of another logger if
    -				its name followed by a dot is a prefix of the descendant
    -				logger name. A logger is said to be a parent of a child
    -				logger if there are no ancestors between itself and the
    -				descendant logger.
    -			</p>
    -		</div>
    -
    -		<p>For example, the logger named <code>"com.foo"</code> is a
    -		parent of the logger named <code>"com.foo.Bar"</code>.  Similarly,
    -		<code>"java"</code> is a parent of <code>"java.util"</code> and an
    -		ancestor of <code>"java.util.Vector"</code>.  This naming scheme
    -		should be familiar to most developers.
    -		</p>
    -
    -		<p>The root logger resides at the top of the logger hierarchy.  It
    -		is exceptional in that it is part of every hierarchy at its
    -		inception. Like every logger, it can be retrieved by its name, as
    -		follows:
    -		</p>
    -		
    -    <pre class="prettyprint source">Logger rootLogger = LoggerFactory.getLogger(<a
    -    href="http://www.slf4j.org/apidocs/constant-values.html#org.slf4j.Logger.ROOT_LOGGER_NAME">org.slf4j.Logger.ROOT_LOGGER_NAME</a>);</pre>
    -
    -		<p>All other loggers are also retrieved with the class static
    -		<code>getLogger</code> method found in the <a
    -		href="http://www.slf4j.org/api/org/slf4j/Logger.html">org.slf4j.LoggerFactory</a>
    -		class. This method takes the name of the desired logger as a
    -		parameter. Some of the basic methods in the <code>Logger</code>
    -		interface are listed below.
    -		</p>
    -
    -		<pre class="prettyprint source">package org.slf4j; 
    -public interface Logger {
    -
    -  // Printing methods: 
    -  public void trace(String message);
    -  public void debug(String message);
    -  public void info(String message); 
    -  public void warn(String message); 
    -  public void error(String message); 
    -}</pre>
    -
    -
    -
    -    <h3 class="doAnchor" name="effectiveLevel">Effective Level aka
    -    Level Inheritance </h3>
    -
    -		<p>Loggers may be assigned levels. The set of possible levels
    -		(TRACE, DEBUG, INFO, WARN and ERROR) are defined in the
    -		<code>ch.qos.logback.classic.Level</code> class. Note that in
    -		logback, the <code>Level</code> class is final and cannot be
    -		sub-classed, as a much more flexible approach exists in the form
    -		of <code>Marker</code> objects.
    -		</p>
    -
    -		<p>If a given logger is not assigned a level, then it inherits one
    -		from its closest ancestor with an assigned level. More formally:
    -		</p>
    -
    -		<div class="definition">
    -		
    -      
    -			<p>The effective level for a given logger <em>L</em>, is equal
    -			to the first non-null level in its hierarchy, starting at
    -			<em>L</em> itself and proceeding upwards in the hierarchy
    -			towards the root logger.
    -			</p>
    -		</div>
    -	
    -		<p>To ensure that all loggers can eventually inherit a level, the
    -		root logger always has an assigned level. By default, this level
    -		is DEBUG.
    -		</p>
    -
    -		<p>Below are four examples with various assigned level values and
    -		the resulting effective (inherited) levels according to the level
    -		inheritance rule.
    -		</p>
    -
    -		<em>Example 1</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>Logger name</th>
    -				<th>Assigned level</th>
    -				<th>Effective level</th>
    -			</tr>
    -			<tr class="alt">
    -				<td>root</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -			<tr>
    -				<td>X</td>
    -				<td>none</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -
    -			<tr class="alt">
    -				<td>X.Y</td>
    -				<td>none</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -			<tr>
    -				<td>X.Y.Z</td>
    -				<td>none</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -  </table>
    -
    -		<p> In example 1 above, only the root logger is assigned a level.
    -		This level value, <code>DEBUG</code>, is inherited by the other
    -		loggers <code>X</code>, <code>X.Y</code> and <code>X.Y.Z</code>
    -		</p>
    -
    -		<em>Example 2</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>Logger name</th>
    -				<th>Assigned level</th>
    -				<th>Effective level</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>root</td>
    -				<td>ERROR</td>
    -				<td>ERROR</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>WARN</td>
    -				<td>WARN</td>
    -			</tr>
    -		</table>
    -
    -		<p>In example 2 above, all loggers have an assigned level value.
    -		Level inheritance does not come into play.
    -		</p>
    -
    -		<em>Example 3</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>Logger name</th>
    -				<th>Assigned level</th>
    -				<th>Effective level</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>root</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>none</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>ERROR</td>
    -				<td>ERROR</td>
    -			</tr>
    -		</table>
    -
    -		<p>In example 3 above, the loggers <code>root</code>,
    -		<code>X</code> and <code>X.Y.Z</code> are assigned the levels
    -		<code>DEBUG</code>, <code>INFO</code> and <code>ERROR</code>
    -		respectively. Logger <code>X.Y</code> inherits its level value
    -		from its parent <code>X</code>.
    -		</p>
    -
    -		<em>Example 4</em>
    -		<table class="bodyTable">
    -
    -			<tr>
    -				<th>Logger name</th>
    -				<th>Assigned level</th>
    -				<th>Effective level</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>root</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>none</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>none</td>
    -				<td>INFO</td>
    -			</tr>
    -		</table>
    -
    -   
    -		<p>In example 4 above, the loggers <code>root</code> and
    -		<code>X</code> and are assigned the levels <code>DEBUG</code> and
    -		<code>INFO</code> respectively. The loggers <code>X.Y</code> and
    -		<code>X.Y.Z</code> inherit their level value from their nearest
    -		parent <code>X</code>, which has an assigned level.
    -		</p>
    -
    -    <h3 class="doAnchor" name="basic_selection">Printing methods and
    -    the basic selection rule</h3>
    -
    -		<p>By definition, the printing method determines the level of a
    -		logging request. For example, if <code>L</code> is a logger
    -		instance, then the statement <code>L.info("..")</code> is a
    -		logging statement of level INFO.
    -		</p>
    -		
    -    
    -    <p>A logging request is said to be <em>enabled</em> if its level
    -    is higher than or equal to the effective level of its
    -    logger. Otherwise, the request is said to be <em>disabled</em>. As
    -    described previously, a logger without an assigned level will
    -    inherit one from its nearest ancestor. This rule is summarized
    -    below.
    -  </p>
    -
    -     
    -		<div class="definition">
    -			<div class="deftitle">Basic Selection Rule</div>
    -
    -			<p>A log request of level <em>p</em> issued to a logger having
    -			an effective level <em>q</em>, is enabled if
    -			<em>p&nbsp;&gt;=&nbsp;q</em>.
    -			</p>
    -		</div>
    -
    -		<p>This rule is at the heart of logback. It assumes that levels
    -		are ordered as follows:
    -		<code>TRACE&nbsp;&lt;&nbsp;DEBUG&nbsp;&lt;&nbsp;INFO&nbsp;&lt;
    -		&nbsp;WARN&nbsp;&lt;&nbsp;ERROR</code>.
    -		</p>
    -				
    -		<p>In a more graphic way, here is how the selection rule works. In
    -		the following table, the vertical header shows the level of
    -		the logging request, designated by <em>p</em>, while the
    -		horizontal header shows effective level of the logger, designated
    -		by <em>q</em>. The intersection of the rows (level request) and
    -		columns (effective level) is the boolean resulting from the basic
    -		selection rule.
    -		</p>
    -		
    -		<table width="80%">
    -      <tr> 
    -        <td class="lgray_bg" rowspan="2">level of <br/>request <em>p</em></td>
    -				<td style="border-top: 1px solid #DDDDDD;"
    -        align="center" colspan="6">effective level <em>q</em></td>
    -			</tr>
    -			<tr align="left">
    -				<th  style="border-bottom: 1px solid #DDDDDD;">TRACE</th>
    -				<th  style="border-bottom: 1px solid #DDDDDD;">DEBUG</th>
    -				<th  style="border-bottom: 1px solid #DDDDDD;">INFO</th>
    -				<th  style="border-bottom: 1px solid #DDDDDD;">WARN</th>
    -				<th  style="border-bottom: 1px solid #DDDDDD;">ERROR</th>	
    -        <th  style="border-bottom: 1px solid #DDDDDD;">OFF</th>    			
    -			</tr>
    -			<tr align="left" >
    -				<th class="lgray_bg">TRACE</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -
    -			<tr align="left">
    -				<th class="lgray_bg">DEBUG</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left" >
    -				<th class="lgray_bg">INFO</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left" >
    -				<th class="lgray_bg">WARN</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left" >
    -				<th class="lgray_bg">ERROR</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>		
    -		</table>
    -		
    -		<p>Here is an example of the basic selection rule.</p>
    -
    -		<pre class="prettyprint source">import ch.qos.logback.classic.Level;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -....
    -
    -// get a logger instance named "com.foo". Let us further assume that the
    -// logger is of type  ch.qos.logback.classic.Logger so that we can
    -// set its level
    -ch.qos.logback.classic.Logger logger = 
    -        (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.foo");
    -//set its Level to <span class="blue">INFO</span>. The setLevel() method requires a logback logger
    -logger.setLevel(Level. <span class="blue">INFO</span>);
    -
    -Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");
    -
    -// This request is enabled, because <span class="green bold">WARN</span> &gt;= <span class="blue">INFO</span>
    -logger.<span class="green bold">warn</span>("Low fuel level.");
    -
    -// This request is disabled, because <span class="green bold">DEBUG</span> &lt; <span class="blue">INFO</span>. 
    -logger.<span class="green bold">debug</span>("Starting search for nearest gas station.");
    -
    -// The logger instance barlogger, named "com.foo.Bar", 
    -// will inherit its level from the logger named 
    -// "com.foo" Thus, the following request is enabled 
    -// because <span class="green bold">INFO</span> &gt;= <span class="blue">INFO</span>. 
    -barlogger.<span class="green bold">info</span>("Located nearest gas station.");
    -
    -// This request is disabled, because <span class="green bold">DEBUG</span> &lt; <span class="blue">INFO</span>. 
    -barlogger.<span class="green bold">debug</span>("Exiting gas station search");</pre>
    -
    -    <a name="RetrievingLoggers"></a>
    -		<h3>Retrieving Loggers</h3>
    -		<p>
    -			Calling the <code><a href="../apidocs/org/slf4j/LoggerFactory.html#getLogger(java.lang.String)">LoggerFactory.getLogger</a></code>
    -			method with the same name will always return a reference to
    -			the exact same logger object.
    -		</p>
    -
    -		<p>For example, in</p>
    -		<pre class="prettyprint source">Logger x = LoggerFactory.getLogger("wombat"); 
    -Logger y = LoggerFactory.getLogger("wombat");</pre>
    -
    -		<p>
    -			<code>x</code> and <code>y</code> refer to
    -			<em>exactly</em> the same logger object.
    -		</p>
    -
    -		<p>Thus, it is possible to configure a logger and then to retrieve
    -		the same instance somewhere else in the code without passing
    -		around references. In fundamental contradiction to biological
    -		parenthood, where parents always precede their children, logback
    -		loggers can be created and configured in any order. In particular,
    -		a "parent" logger will find and link to its descendants even if it
    -		is instantiated after them.
    -		</p>
    -		<p>Configuration of the logback environment is typically done at
    -		application initialization. The preferred way is by reading a
    -		configuration file. This approach will be discussed shortly.
    -		</p>
    -		<p>Logback makes it easy to name loggers by <em>software
    -		component</em>.  This can be accomplished by instantiating a
    -		logger in each class, with the logger name equal to the fully
    -		qualified name of the class. This is a useful and straightforward
    -		method of defining loggers. As the log output bears the name of
    -		the generating logger, this naming strategy makes it easy to
    -		identify the origin of a log message. However, this is only one
    -		possible, albeit common, strategy for naming loggers. Logback does
    -		not restrict the possible set of loggers. As a developer, you are
    -		free to name loggers as you wish.
    -		</p>
    -
    -		<p>Nevertheless, naming loggers after the class where they are
    -		located seems to be the best general strategy known so far.
    -		</p>
    -
    -    <a name="AppendersAndLayouts"></a>
    -    <h3>Appenders and Layouts</h3>
    -
    -		<p>The ability to selectively enable or disable logging requests
    -		based on their logger is only part of the picture.  Logback allows
    -		logging requests to print to multiple destinations. In logback
    -		speak, an output destination is called an appender. Currently,
    -		appenders exist for the console, files, remote socket servers, to
    -		MySQL, PostgreSQL, Oracle and other databases, JMS, and remote
    -		UNIX Syslog daemons.
    -
    -      <!--It is also possible to log asynchronously. -->
    -		</p>
    -
    -		<p>More than one appender can be attached to a logger.</p>
    -
    -    <p>The <code><a
    -    href="../apidocs/ch/qos/logback/classic/Logger.html#addAppender(ch.qos.logback.core.Appender)">addAppender</a></code>
    -    method adds an appender to a given logger.  Each enabled logging
    -    request for a given logger will be forwarded to all the appenders
    -    in that logger as well as the appenders higher in the
    -    hierarchy. In other words, appenders are inherited additively from
    -    the logger hierarchy. For example, if a console appender is added
    -    to the root logger, then all enabled logging requests will at
    -    least print on the console. If in addition a file appender is
    -    added to a logger, say <em>L</em>, then enabled logging requests
    -    for <em>L</em> and <em>L</em>'s children will print on a file
    -    <em>and</em> on the console.  It is possible to override this
    -    default behavior so that appender accumulation is no longer
    -    additive by setting the additivity flag of a logger to false.
    -		</p>
    -
    -		<p>The rules governing appender additivity are summarized below.
    -		</p>
    -		<div class="definition">
    -
    -			<h4 class="deftitle"><a name="additivity"
    -			href="#additivity"><span class="anchor"/></a>Appender
    -			Additivity</h4>
    -
    -			<p>The output of a log statement of logger <em>L</em> will go to
    -			all the appenders in <em>L</em> and its ancestors. This is the
    -			meaning of the term "appender additivity".
    -			</p>
    -
    -			<p>However, if an ancestor of logger <em>L</em>, say <em>P</em>,
    -			has the additivity flag set to false, then <em>L</em>'s output
    -			will be directed to all the appenders in <em>L</em> and its
    -			ancestors up to and including <em>P</em> but not the appenders in
    -			any of the ancestors of <em>P</em>.
    -			</p>
    -
    -			<p>Loggers have their additivity flag set to true by default.
    -			</p>
    -
    -		</div>
    -		The table below shows an example:
    -
    -		<table class="bodyTable">
    -			<tr>
    -				<th>Logger Name</th>
    -				<th>Attached Appenders</th>
    -				<th>Additivity Flag</th>
    -				<th>Output Targets</th>
    -				<th>Comment</th>
    -			</tr>
    -			<tr>
    -				<td>root</td>
    -				<td>A1</td>
    -				<td>not applicable</td>
    -				<td>A1</td>
    -
    -				<td>Since the root logger stands at the top of the logger
    -				hierarchy, the additivity flag does not apply to it.
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>x</td>
    -				<td>A-x1, A-x2</td>
    -				<td>true</td>
    -				<td>A1, A-x1, A-x2</td>
    -				<td>Appenders of "x" and of root.</td>
    -			</tr>
    -			<tr>
    -				<td>x.y</td>
    -				<td>none</td>
    -				<td>true</td>
    -				<td>A1, A-x1, A-x2</td>
    -				<td>Appenders of "x" and of root.</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>x.y.z</td>
    -				<td>A-xyz1</td>
    -				<td>true</td>
    -				<td>A1, A-x1, A-x2, A-xyz1</td>
    -				<td>Appenders of "x.y.z", "x" and of root.</td>
    -			</tr>
    -			<tr>
    -				<td>security</td>
    -				<td>A-sec</td>
    -				<td class="blue"><span class="blue">false</span></td>
    -				<td>A-sec</td>
    -
    -				<td>
    -					No appender accumulation since the additivity flag is set to
    -					<code>false</code>. Only appender A-sec will be used.
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>security.access</td>
    -				<td>none</td>
    -				<td>true</td>				
    -        <td>A-sec</td>
    -				<td>
    -					Only appenders of "security" because the additivity
    -					flag in "security" is set to
    -					<code>false</code>.
    -				</td>
    -			</tr>
    -		</table>
    -
    -
    -		<p>More often than not, users wish to customize not only the
    -		output destination but also the output format. This is
    -		accomplished by associating a <em>layout</em> with an
    -		appender. The layout is responsible for formatting the logging
    -		request according to the user's wishes, whereas an appender takes
    -		care of sending the formatted output to its destination. The
    -		<code>PatternLayout</code>, part of the standard logback
    -		distribution, lets the user specify the output format according to
    -		conversion patterns similar to the C language <code>printf</code>
    -		function.
    -		</p>
    -
    -		<p>For example, the PatternLayout with the conversion pattern
    -		"%-4relative [%thread] %-5level %logger{32} - %msg%n" will output
    -		something akin to:
    -		</p>
    -
    -		<div class="prettyprint source"><pre>176  [main] DEBUG manual.architecture.HelloWorld2 - Hello world.</pre></div>
    -
    -		<p>The first field is the number of milliseconds elapsed since the
    -		start of the program. The second field is the thread making the
    -		log request. The third field is the level of the log request. The
    -		fourth field is the name of the logger associated with the log
    -		request. The text after the '-' is the message of the request.
    -		</p>
    -
    -
    -		<h3 class="doAnchor" name="parametrized">Parameterized
    -		logging</h3>
    -
    -		<p>Given that loggers in logback-classic implement the <a
    -		href="http://www.slf4j.org/api/org/slf4j/Logger.html">SLF4J's
    -		Logger interface</a>, certain printing methods admit more than one
    -		parameter. These printing method variants are mainly intended to
    -		improve performance while minimizing the impact on the readability
    -		of the code.
    -		</p>
    -
    -		<p>For some Logger <code>logger</code>, writing,</p>
    -
    -		<pre class="prettyprint source">logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));</pre>
    -
    -		<p>incurs the cost of constructing the message parameter, that is
    -		converting both integer <code>i</code> and <code>entry[i]</code>
    -		to a String, and concatenating intermediate strings. This is
    -		regardless of whether the message will be logged or not.
    -		</p>
    -
    -		<p>One possible way to avoid the cost of parameter construction is
    -		by surrounding the log statement with a test. Here is an example.
    -		</p>
    -
    -		<pre class="prettyprint source">if(logger.isDebugEnabled()) { 
    -  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    -}</pre>
    -
    -
    -		<p>This way you will not incur the cost of parameter construction
    -		if debugging is disabled for <code>logger</code>.  On the other
    -		hand, if the logger is enabled for the DEBUG level, you will incur
    -		the cost of evaluating whether the logger is enabled or not,
    -		twice: once in <code>debugEnabled</code> and once in
    -		<code>debug</code>.  In practice, this overhead is insignificant
    -		because evaluating a logger takes less than 1% of the time it
    -		takes to actually log a request.
    -		</p>
    -
    -		<h4>Better alternative</h4>
    -
    -		<p>There exists a convenient alternative based on message
    -		formats. Assuming <code>entry</code> is an object, you can write:
    -		</p>
    -
    -
    -		<pre class="prettyprint source">Object entry = new SomeObject(); 
    -logger.debug("The entry is {}.", entry);</pre>
    -
    -		<p>Only after evaluating whether to log or not, and only if the decision
    -		is positive, will the logger implementation format the message and
    -		replace the '{}' pair with the string value of <code>entry</code>.
    -		In other words, this form does not incur the cost of parameter
    -		construction when the log statement is disabled.
    -		</p>
    -
    -
    -		<p>The following two lines will yield the exact same output.
    -		However, in case of a <em>disabled</em> logging statement, the
    -		second variant will outperform the first variant by a factor of at
    -		least 30.
    -		</p>
    -
    -		<pre class="prettyprint source">logger.debug("The new entry is "+entry+".");
    -logger.debug("The new entry is {}.", entry);</pre>
    -
    -
    -		<p>A two argument variant is also available. For example, you can
    -		write:
    -		</p>
    -
    -		<pre class="prettyprint source">logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);</pre>
    -
    -		<p>If three or more arguments need to be passed, an
    -		<code>Object[]</code> variant is also available. For example, you
    -		can write:
    -		</p>
    -
    -
    -		<pre class="prettyprint source">Object[] paramArray = {newVal, below, above};
    -logger.debug("Value {} was inserted between {} and {}.", paramArray);</pre>
    -
    -  
    -  <a name="UnderTheHood"></a>
    -  <h3>A peek under the hood</h3>
    -
    -  <p>After we have introduced the essential logback components, we are
    -  now ready to describe the steps that the logback framework takes
    -  when the user invokes a logger's printing method. Let us now analyze
    -  the steps logback takes when the user invokes the
    -  <code>info()</code> method of a logger named <em>com.wombat</em>.
    -  </p>
    -
    -  <h4>1. Get the filter chain decision</h4>
    -
    -  <p>If it exists, the <code>TurboFilter</code> chain is
    -  invoked. Turbo filters can set a context-wide threshold, or filter
    -  out certain events based on information such as <code>Marker</code>,
    -  <code>Level</code>, <code>Logger</code>, message, or the
    -  <code>Throwable</code> that are associated with each logging
    -  request.  If the reply of the filter chain is
    -  <code>FilterReply.DENY</code>, then the logging request is
    -  dropped. If it is <code>FilterReply.NEUTRAL</code>, then we continue
    -  with the next step, i.e. step 2. In case the reply is
    -  <code>FilterReply.ACCEPT</code>, we skip the next and directly jump
    -  to step 3.
    -  </p>
    -
    -  <h4>2. Apply the <a href="#basic_selection">basic selection
    -  rule</a></h4>
    -
    -  <p>At this step, logback compares the effective level of the logger
    -  with the level of the request. If the logging request is disabled
    -  according to this test, then logback will drop the request without
    -  further processing. Otherwise, it proceeds to the next step.
    -  </p>
    -
    -  <h4>3. Create a <code>LoggingEvent</code> object</h4>
    -
    -  <p>If the request survived the previous filters, logback will
    -  create a <code>ch.qos.logback.classic.LoggingEvent</code> object
    -  containing all the relevant parameters of the request, such as the
    -  logger of the request, the request level, the message itself, the
    -  exception that might have been passed along with the request, the
    -  current time, the current thread, various data about the class that
    -  issued the logging request and the <code>MDC</code>. Note that some
    -  of these fields are initialized lazily, that is only when they are
    -  actually needed. The <code>MDC</code> is used to decorate the
    -  logging request with additional contextual information. MDC is
    -  discussed in a <a href="mdc.html">subsequent chapter</a>.</p>
    -
    -  <h4>4. Invoking appenders</h4>
    -
    -  <p>After the creation of a <code>LoggingEvent</code> object, logback
    -  will invoke the <code>doAppend()</code> methods of all the
    -  applicable appenders, that is, the appenders inherited from the
    -  logger context.
    -  </p>
    -
    -  <p>All appenders shipped with the logback distribution extend the
    -  <code>AppenderBase</code> abstract class that implements the
    -  <code>doAppend</code> method in a synchronized block ensuring
    -  thread-safety.  The <code>doAppend()</code> method of
    -  <code>AppenderBase</code> also invokes custom filters attached to
    -  the appender, if any such filters exist.  Custom filters, which can
    -  be dynamically attached to any appender, are presented in a <a
    -  href="filters.html">separate chapter</a>.
    -  </p>
    -
    -  <h4>5. Formatting the output</h4>
    -
    -  <p>It is responsibility of the invoked appender to format the
    -  logging event. However, some (but not all) appenders delegate the
    -  task of formatting the logging event to a layout. A layout formats
    -  the <code>LoggingEvent</code> instance and returns the result as a
    -  String. Note that some appenders, such as the
    -  <code>SocketAppender</code>, do not transform the logging event into
    -  a string but serialize it instead.  Consequently, they do not
    -  have nor require a layout.
    -  </p>
    -
    -  <h4>6. Sending out the <code>LoggingEvent</code></h4>
    -
    -  <p>After the logging event is fully formatted it is sent to its
    -  destination by each appender.
    -  </p>
    -  
    -  <p>
    -    Here is a sequence UML diagram to show how everything works. You might
    -    want to click on the image to display its bigger version.
    -  </p>
    -
    -  <a href="underTheHood.html">
    -    <img src="images/chapters/architecture/underTheHoodSequence2_small.gif" 
    -         alt="underTheHoodSequence2_small.gif"/>
    -  </a>
    -
    -  
    -  <h3 class="doAnchor" name="performance">Performance</h3>
    -
    -  <p>One of the often-cited arguments against logging is its
    -  computational cost.  This is a legitimate concern as even
    -  moderately-sized applications can generate thousands of log
    -  requests. Much of our development effort is spent measuring and
    -  tweaking logback's performance.  Independently of these efforts, the
    -  user should still be aware of the following performance issues.
    -  </p>
    -
    -  <h4>1. Logging performance when logging is turned off entirely</h4>
    -
    -  <p>You can turn off logging entirely by setting the level of the
    -  root logger to <code>Level.OFF</code>, the highest possible level.
    -  When logging is turned off entirely, the cost of a log request
    -  consists of a method invocation plus an integer comparison. On a
    -  3.2Ghz Pentium D machine this cost is typically around 20
    -  nanoseconds.
    -  </p>
    -
    -  <p>However, any method invocation involves the "hidden" cost of
    -  parameter construction.  For example, for some logger <em>x</em>
    -  writing,
    -  </p>
    -  
    -  <pre class="prettyprint source">x.debug("Entry number: " + i + "is " + entry[i]);</pre>
    -
    -  <p>incurs the cost of constructing the message parameter,
    -  i.e. converting both integer <code>i</code> and
    -  <code>entry[i]</code> to a string, and concatenating intermediate
    -  strings, regardless of whether the message will be logged or not.
    -  </p>
    -
    -  <p>The cost of parameter construction can be quite high and depends
    -  on the size of the parameters involved. To avoid the cost of
    -  parameter construction you can take advantage of SLF4J's parameterized
    -  logging:
    -  </p>
    -
    -  <pre class="prettyprint source">x.debug("Entry number: {} is {}", i, entry[i]);</pre>
    -
    -  <p>This variant will not incur the cost of parameter
    -  construction. Compared to the previous call to the
    -  <code>debug()</code> method, it will be faster by a wide margin.
    -  The message will be formatted only if the logging request is to be
    -  sent to attached appenders. Moreover, the component that formats
    -  messages is highly optimized.
    -  </p>
    -
    -  <p>Notwithstanding the above placing log statements in tight loops,
    -  i.e. very frequently invoked code, is a lose-lose proposal, likely
    -  to result in degraded performance.  Logging in tight loops will slow
    -  down your application even if logging is turned off, and if logging
    -  is turned on, will generate massive (and hence useless) output.
    -  </p>
    -
    -  <h4>2. The performance of deciding whether to log or not to log when
    -  logging is turned on.</h4>
    -
    -  <p>In logback, there is no need to walk the logger hierarchy. A
    -  logger knows its effective level (that is, its level, once level
    -  inheritance has been taken into consideration) when it is
    -  created. Should the level of a parent logger be changed, then all
    -  child loggers are contacted to take notice of the change. Thus,
    -  before accepting or denying a request based on the effective level,
    -  the logger can make a quasi-instantaneous decision, without needing
    -  to consult its ancestors.
    -  </p>
    -
    -
    -  <h4>3. Actual logging (formatting and writing to the output device)</h4>
    -
    -  <p>This is the cost of formatting the log output and sending it to
    -  its target destination. Here again, a serious effort was made to
    -  make layouts (formatters) perform as quickly as possible.  The same
    -  is true for appenders. The typical cost of actually logging is about
    -  9 to 12 microseconds when logging to a file on the local machine.
    -  It goes up to several milliseconds when logging to a database on a
    -  remote server.
    -  </p>
    -
    -  <p>Although feature-rich, one of the foremost design goals of
    -  logback was speed of execution, a requirement which is second only
    -  to reliability. Some logback components have been rewritten several
    -  times to improve performance.
    -  </p>
    -
    -    
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/architecture_ja.html b/logback-site/src/site/pages/manual/architecture_ja.html
    deleted file mode 100644
    index 5c10ee6244..0000000000
    --- a/logback-site/src/site/pages/manual/architecture_ja.html
    +++ /dev/null
    @@ -1,619 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第2章 アーキテクチャ</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -   <h1>第2章 アーキテクチャ</h1>
    -
    -   <div class="quote">
    -      <p><em>本物の分類学とは、系統学のことなのだ。</em></p>
    -      <p>—CHARLES DARWIN, <em>The Origin of Species</em></p>
    -
    -      <p><em>文字で書かれたものを読むだけで、得られた情報を具体的な問題に適用し、読んだことを自分のものとして考えること無しには、問題そのものを学ぶことは、不可能ではないが困難だ。さらに、私たちは自ら発見した時にこそ最も良い学びを得るのだ。</em>
    -      </p>
    -      <p>—DONALD KNUTH, <em>The Art of Computer Programming</em></p>
    -   </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -    <h2>Logbackのアーキテクチャ</h2>
    -
    -    <p>logback の基本的なアーキテクチャは、さまざまな状況に対応できるよう、十分に汎用的になっています。今のところ、logbackは三つのモジュールに分割されています(logback-core、logback-classic、logback-access)。
    -    </p>
    -
    -    <p><em>coreモジュール</em>は、他の二つのモジュールの足回りとして使用されています。<em>classic</em>モジュールは、 <em>core</em>を拡張するものです。classic モジュールは、著しく改善されたバージョンのlog4jとも考えられます。logback-classic は <a href="http://www.slf4j.org">SLF4J API</a> を直接実装しているので、log4j や java.util.logging(JUL)などの他のロギング実装と切り替えることができます。三つ目の<em>access</em>と呼ばれるモジュールは、HTTPのアクセスログ機能を提供するため、サーブレットコンテナと統合しています。access モジュールについては<a href="http://logback.qos.ch/access.html">別のドキュメント</a>に記載されています。
    -    </p>
    -
    -    <p>このドキュメントの残りの部分では、logback-classicモジュールのことを "logback" と表記しています。
    -    </p>
    -    
    -		<h2>ロガー、アペンダー、レイアウト</h2>
    -		
    -		<p>logback は三つの主要なクラス(<code>Logger</code> 、 0}Appender、<code>Layout</code> )で成り立っています。これらの三つのコンポーネントが協調することで、開発者はメッセージを適切な種類、レベルでロギングできるようになっています。また、書式化や出力先を実行中に変更できるようにもなっています。
    -		</p>
    -
    -		<p><code>Logger</code>クラスは、logback-classicモジュールに含まれています。一方、 <code>Appender</code>と<code>Layout</code>インタフェイスは、logback-coreモジュールに含まれています。logback-core モジュールは共通モジュールなので、logger の責務を含まないのです。
    -		</p>
    -    
    -    <h3 class="doAnchor" name="LoggerContext">ロガーコンテキスト</h3>
    -
    -		<p>どんなロギングAPIであっても単純な<code>System.out.println</code>に勝る第一の、そして最大の利点があります。それは、特定のロギング式を無効にしつつ、他のロギング式には一切影響を与えない機能です。この機能は、ロギング空間、すなわち、すべてのロギング式からなる空間が、開発者の選択した基準に基いて分類されていることを前提としています。logback-classic モジュールにおいて、この分類は logger に固有のものです。全てのロガーは <code>LoggerContext</code> に接続します。<code>LoggerContext</code> には、接続してきたロガーを階層的な木構造として配置する責務があります。
    -		</p>
    -			
    -		<p>ロガーは名前を持ったエンティティです。名前は大文字と小文字が区別され、階層的な命名規則に従うようになっています。</p>
    -
    -		<div class="definition">
    -			<div class="deftitle">名前の階層</div>
    -			<p>あるロガーの名前が、他のロガーの名前の中で「.」(ドット)で区切られた前置詞となっているとき、それぞれが先祖と子孫となります。自分自身や、自分の子にも祖先がいない場合、そのロガーは親になります。
    -			</p>
    -		</div>
    -
    -		<p>たとえば、<code>"com.foo"</code>という名前のロガーは、<code>"com.foo.Bar"</code> というロガーの親になります。同様に、 <code>"java"</code>は<code>"java.util"</code>の親であると同時に、<code>"java.util.Vector"</code> の祖先になります。この命名スキームは、ほとんどの開発者がきちんと理解しなければならないものです。
    -		</p>
    -
    -		<p>ルートロガーは、ロガー階層の最上位に存在するものです。複数の階層構造すべてに含まれるという意味で、例外的な存在です。他のロガーと同じように、名前で取得することができます。こんな風に。</p>
    -		
    -    <pre class="prettyprint source">Logger rootLogger = LoggerFactory.getLogger(<a href="http://www.slf4j.org/apidocs/constant-values.html#org.slf4j.Logger.ROOT_LOGGER_NAME">org.slf4j.Logger.ROOT_LOGGER_NAME</a>);</pre>
    -
    -		<p>他のロガーも、org.slf4j.LoggerFactory</html>クラスの静的クラスメソッドである<code>getLogger</code>によって取得することができます。このメソッドは、パラメータとして欲しいロガーの名前を受け付けます。<code>Logger</code>インタフェイスの基本的なメソッドをいくつか以下に示します。
    -		
    -
    -		<pre class="prettyprint source">package org.slf4j; 
    -public interface Logger {
    -
    -  // Printing methods: 
    -  public void trace(String message);
    -  public void debug(String message);
    -  public void info(String message); 
    -  public void warn(String message); 
    -  public void error(String message); 
    -}</pre>
    -
    -
    -
    -    <h3 class="doAnchor" name="effectiveLevel">有効レベル(別名レベルの継承)</h3>
    -
    -		<p>ロガーにはレベルを割り当てることができます。利用できるレベル(TRACE、DEBUG、INFO、WARNおよびERROR)は<code>ch.qos.logback.classic.Level</code>クラスに定義されています。logback では Level クラスは final として宣言されており、サブクラス化出来ないことに注意してください。より柔軟性のあるアプローチは<code>Marker</code>オブジェクトとして利用可能です。
    -		</p>
    -
    -		<p>レベルの割り当てられていないロガーは、直近の祖先に割り当てられたレベルを継承します。より正確に言うと次のようになります。</p>
    -
    -		<div class="definition">
    -		
    -      
    -			<p>与えられたロガー<em>L</em>の有効なレベルは、ロガー階層において最初に見つかったロガーのレベルに等しい。階層は<em>L</em>から始まり、ロートロガーに向かって進んでいく。
    -			</p>
    -		</div>
    -	
    -		<p>最終的に全てのロガーがレベルを継承できるように、ルートロガーには必ずレベルが割り当てられています。デフォルトではDEBUGになっています。</p>
    -
    -		<p>以下は、レベル継承ルールに従ってロガーに割り当てられた有効レベルの例です。
    -		</p>
    -
    -		<em>例1</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>ロガー名</th>
    -				<th>割り当てられたレベル</th>
    -				<th>有効レベル</th>
    -			</tr>
    -			<tr class="alt">
    -				<td>ルートロガー</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -			<tr>
    -				<td>X</td>
    -				<td>なし</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -
    -			<tr class="alt">
    -				<td>X.Y</td>
    -				<td>なし</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -			<tr>
    -				<td>X.Y.Z</td>
    -				<td>なし</td>
    -				<td>DEBUG</td>
    -		  </tr>
    -  </table>
    -
    -		<p>上記の例では、ルートロガーにだけレベルが割り当てられています。レベルは<code>DEBUG</code>で 、他の全てのロガーに継承されています。
    -		</p>
    -
    -		<em>例2</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>ロガー名</th>
    -				<th>割り当てられたレベル</th>
    -				<th>有効レベル</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>ルート</td>
    -				<td>ERROR</td>
    -				<td>ERROR</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>WARN</td>
    -				<td>WARN</td>
    -			</tr>
    -		</table>
    -
    -		<p>上記の例では、​​すべてのロガーにレベルが割り当てられています。レベルの継承は何も仕事をしていません。
    -		</p>
    -
    -		<em>例3</em>
    -		<table class="bodyTable">
    -			<tr>
    -				<th>ロガー名</th>
    -				<th>割り当てられたレベル</th>
    -				<th>有効レベル</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>ルート</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>なし</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>ERROR</td>
    -				<td>ERROR</td>
    -			</tr>
    -		</table>
    -
    -		<p>上記の例では、ルートロガーにDEBUG、<code>X</code>にINFO、<code>X.Y.Z</code>にERRORが割り当てられています。<code>X.Y</code>は親である<code>X</code>からレベルを継承しています。</p>
    -
    -		<em>例4</em>
    -		<table class="bodyTable">
    -
    -			<tr>
    -				<th>ロガー名</th>
    -				<th>割り当てられたレベル</th>
    -				<th>有効レベル</th>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>ルート</td>
    -				<td>DEBUG</td>
    -				<td>DEBUG</td>
    -			</tr>
    -
    -			<tr align="left">
    -				<td>X</td>
    -				<td>INFO</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr class="alt" align="left">
    -				<td>X.Y</td>
    -				<td>なし</td>
    -				<td>INFO</td>
    -			</tr>
    -			<tr align="left">
    -				<td>X.Y.Z</td>
    -				<td>なし</td>
    -				<td>INFO</td>
    -			</tr>
    -		</table>
    -
    -   
    -		<p>上記の例では、ルートロガーにDEBUG、<code>X</code>にINFOが割り当てられています。<code>X.Y</code>および<code>X.Y.Z</code>は、最も近い親からレベルを継承しています。
    -		</p>
    -
    -    <h3 class="doAnchor" name="basic_selection">印字メソッドと基本的な選択ルール</h3>
    -
    -		<p>定義によると、印字メソッドはロギング要求のレベルを決定するものです。例えば、<code>L</code>がロガーのインスタンスだとすると、式<code>L.info("..")</code>はINFOレベルのロギングであることになります。</p>
    -		
    -    
    -    <p>ロギング要求は、そのロガーの有効レベル以上である場合に<em>有効</em>となります。そうでなければ、ロギング要求は<em>無効</em>になります。前述のように、レベルが割り当てられていないロガーは最も近い祖先​​から継承します。このルールは次のように要約できます。
    -  </p>
    -
    -     
    -		<div class="definition">
    -			<div class="deftitle">基本的な選択ルール</div>
    -
    -			<p>有効レベル<em>q</em>のロガーに発行されたレベル<em>p</em>のログ要求は、<em>p&gt;=q</em>を満たす場合有効になる。
    -			</p>
    -		</div>
    -
    -		<p>このルールはlogbackの中核を為すものです。レベルは次のような順序であることを想定しています。<code>TRACE &lt; DEBUG &lt; INFO &lt;  WARN &lt; ERROR</code></p>
    -				
    -		<p>以下は、選択ルールをより具体的に示したものです。垂直方向のラベルはロギング要求のレベル<em>p</em>です。そして水平方向のラベルはロガーの有効レベル<em>q</em>です。行(ロギング要求のレベル)と列(ロガーの有効レベル)の交点が、基本的な選択ルールの適用結果を表しています。
    -		</p>
    -		
    -		<table width="80%">
    -      <tr> 
    -        <td class="lgray_bg" rowspan="2"><br>ロギング要求のレベル<em>p</em></td>
    -				<td align="center" colspan="6" style="border-top:1px solid #dddddd">ロガーの有効レベル<em>q</em></td>
    -			</tr>
    -			<tr align="left">
    -				<th style="border-bottom:1px solid #dddddd">TRACE</th>
    -				<th style="border-bottom:1px solid #dddddd">DEBUG</th>
    -				<th style="border-bottom:1px solid #dddddd">INFO</th>
    -				<th style="border-bottom:1px solid #dddddd">WARN</th>
    -				<th style="border-bottom:1px solid #dddddd">ERROR</th>	
    -        <th style="border-bottom:1px solid #dddddd">OFF</th>    			
    -			</tr>
    -			<tr align="left">
    -				<th class="lgray_bg">TRACE</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -
    -			<tr align="left">
    -				<th class="lgray_bg">DEBUG</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left">
    -				<th class="lgray_bg">INFO</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left">
    -				<th class="lgray_bg">WARN</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="redBold">NO</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>
    -			<tr align="left">
    -				<th class="lgray_bg">ERROR</th>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -				<td><span class="greenBold">YES</span></td>
    -        <td><span class="redBold">NO</span></td>
    -			</tr>		
    -		</table>
    -		
    -		<p>選択ルールのコード例を次に示します。</p>
    -
    -		<pre class="prettyprint source">import ch.qos.logback.classic.Level;
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -....
    -
    -// "com.foo"という名前のロガーを取得します。
    -// ロガーのインスタンスはレベルを設定するために ch.qos.logback.classic.Logger とします。
    -ch.qos.logback.classic.Logger logger = 
    -        (ch.qos.logback.classic.Logger) LoggerFactory.getLogger("com.foo");
    -// レベルに<span class="blue">INFO</span>を設定します。setLevel() メソッドは logback のロガーにしかありません。
    -.setLevel(Level. <span class="blue">INFO</span>);
    -
    -Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");
    -
    -// このロギング要求は有効です。<span class="green bold">WARN</span> &gt;= <span class="blue">INFO</span>
    -logger.<span class="green bold">warn</span>("Low fuel level.");
    -
    -// このロギング要求は無効です。<span class="green bold">DEBUG</span> &lt; <span class="blue">INFO</span>. 
    -logger.<span class="green bold">debug</span>("Starting search for nearest gas station.");
    -
    -// "com.foo.Bar" という名前のロガーは、"com.foo" ロガーからレベルを継承します。
    -// したがって、このロギング要求は有効です。<span class="green bold">INFO</span> &gt;= <span class="blue">INFO</span>. 
    -barlogger.<span class="green bold">info</span>("Located nearest gas station.");
    -
    -// このロギング要求は無効です。<span class="green bold">DEBUG</span> &lt; <span class="blue">INFO</span>.
    -barlogger.<span class="green bold">debug</span>("Exiting gas station search");</pre>
    -
    -    <a name="RetrievingLoggers"></a>
    -		<h3>ロガーの取得</h3>
    -		<p><code><a href="http://logback.qos.ch/apidocs/org/slf4j/LoggerFactory.html#getLogger(java.lang.String)">LoggerFactory.getLogger</a></code>を呼び出しましょう。
    -同じ名前なら、常に同じロガーインスタンスへの参照を返します。
    -		</p>
    -
    -		<p>例えば次のような場合は常に同じインスタンスを返します。</p>
    -		<pre class="prettyprint source">Logger x = LoggerFactory.getLogger("wombat"); 
    -Logger y = LoggerFactory.getLogger("wombat");</pre>
    -
    -		<p>
    -			<code>x</code>と<code>y</code>は、<em>完全に</em>同じオブジェクトを参照します。
    -		</p>
    -
    -		<p>このように、一度ロガーのインスタンスを設定すれば、わざわざ参照を渡さなくても、コード中のどこででも同じインスタンスを取得することができます。現実世界の生物の親子関係とは矛盾していますが、logback のロガーは親と子のどちらが先に生成されても問題ありません。ただし、「親」ロガーから子孫ロガーを見つけようとするときは、事前にインスタンス化しておく必要があります。
    -		</p>
    -		<p>logback の実行環境の設定は、アプリケーションの初期化時に行われるのが一般的です。設定ファイルを読み込むようにするとよいでしょう。方法についてはすぐ後で説明します。
    -		</p>
    -		<p>logback では、<em>コンポーネント</em>ごとにロガーの名前を付けるのが簡単です。ロガーをクラスごとにインスタンス化すれば、それぞれのロガーの名前はクラスの完全名になります。これはロガーを定義する簡単かつ便利な方法です。クラスの完全名であるロガーの名前をログに出力するようになっていれば、メッセージを出力した箇所を特定するのは簡単です。ですが、これはロガーの命名戦略としてごく一般的な方法の一つでしかありません。logback 自体にロガーのインスタンス数の制限はありません。従って、開発者は自由に名前を付けることが出来ます。
    -		</p>
    -
    -		<p>とはいえ、ロガーの名前にそれが置かれたクラスの完全名を付けることは、一般的に最も良い方法であるということが共通認識になっています。
    -		</p>
    -
    -    <a name="AppendersAndLayouts"></a>
    -    <h3>アペンダーとレイアウト</h3>
    -
    -		<p>ロガーのレベルに応じてロギング要求の有効無効を選択できる機能は、logbackの機能の一部でしかありません。logback は、ロギング要求を複数の宛先の送りつけることができます。logback では、宛先のことをアペンダーと呼びます。現在利用できるアペンダーには、コンソール、ファイル、MySQLやPostgreSQLやOracleなどのデータベースへのリモート接続、JMS、リモートSyslogデーモンなどがあります。
    -
    -      <!--It is also possible to log asynchronously. -->
    -		</p>
    -
    -		<p>ロガーには一つ以上のアペンダーを割り当てることができます。</p>
    -
    -    <p>指定されたロガーにアペンダーを割り当てるには、<code><a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/Logger.html#addAppender(ch.qos.logback.core.Appender)">addAppender</a></code>メソッドを使います。有効なロギング要求は、ロガーに割り当てられた全てのアペンダーについて、階層関係が上位のアペンダーから順に転送されます。別の言い方をすると、ロガー階層からアペンダーも引き継ぐということです。例えば、ルートロガーにコンソールアペンダーを割り当てたなら、有効なロギング要求は少なくともコンソールに出力されることになります。さらに、ロガー<em>L</em>にファイルアペンダーが割り当てられたなら、<em>L</em>とその子孫全てにおいて、有効なロギング要求はコンソールとファイルの両方に出力されます。ロガーの additivity フラグを false に設定すれば、アペンダーを継承しないように振る舞いを変更することができます。
    -		</p>
    -
    -		<p>アペンダーの加算ルールをまとめると次のようになります。
    -		</p>
    -		<div class="definition">
    -
    -			<h4 class="deftitle"><a href="./01-architecture.html#additivity" name="additivity">アペンダーの加算性</a></h4>
    -
    -			<p>ロガー<em>L</em>のログ出力は、<em>L</em>とその祖先も割り当てられた全てのアペンダーに転送される。これが「アペンダーの加算性」の定義である。
    -			</p>
    -
    -			<p>ロガー<em>L</em>の祖先<em>P</em>の aditivity フラグが false の場合、<em>L</em>の出力は<em>L</em>自身に割り当てられたアペンダーと、祖先<em>P</em>に割り当てられたアペンダーだけに転送される。<em>P</em>よりも祖先のロガーのアペンダーには転送されない。</p>
    -
    -			<p>デフォルトでは、ロガーの aditivity は true になっています。
    -			</p>
    -
    -		</div>以上を踏まえた例を次の表に示します。<table class="bodyTable">
    -			<tr>
    -				<th>ロガー名</th>
    -				<th>割り当てられたアペンダー</th>
    -				<th>aditivity フラグ</th>
    -				<th>宛先</th>
    -				<th>コメント</th>
    -			</tr>
    -			<tr>
    -				<td>root</td>
    -				<td>A1</td>
    -				<td>適用できません</td>
    -				<td>A1</td>
    -
    -				<td>ルートロガーは、ロガー階層の最上位になるため、aditivity フラグは無効です。
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>x</td>
    -				<td>A-x1、A-x2</td>
    -				<td>true</td>
    -				<td>A1、A-x1、A-x2</td>
    -				<td>「x」のアペンダーとルートロガーのアペンダーが対象</td>
    -			</tr>
    -			<tr>
    -				<td>x.y</td>
    -				<td>なし</td>
    -				<td>true</td>
    -				<td>A1、A-x1、A-x2</td>
    -				<td>「x」のアペンダーとルートロガーのアペンダーが対象</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>x.y.z</td>
    -				<td>A-xyz1</td>
    -				<td>true</td>
    -				<td>A1、A-x1、A-x2、A-xyz1</td>
    -				<td>「x.y.z」のアペンダーと「×」のアペンダーとルートロガーのアペンダーが対象</td>
    -			</tr>
    -			<tr>
    -				<td>security</td>
    -				<td>A-sec</td>
    -				<td class="blue"><span class="blue">false</span></td>
    -				<td>A-sec</td>
    -
    -				<td>aditivity フラグが<code>false</code>なので、アペンダーは加算されません。A-sec だけが対象になります
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>security.access</td>
    -				<td>なし</td>
    -				<td>true</td>				
    -        <td>A-sec</td>
    -				<td>「security」のaditivityフラグが<code>false</code>なので、「security」のアペンダーだけが加算されます
    -				</td>
    -			</tr>
    -		</table>
    -
    -
    -		<p>利用者は、ほとんどの場合出力先だけでなく出力形式もカスタマイズしたがるでしょう。アペンダーと<em>レイアウト</em>を関連付けることで実現できます。レイアウトは、利用者の指定したとおりにロギング要求を整形するものです。一方、アペンダーは整形されたメッセージを指定された宛先に転送します。利用者は、logback の標準配布物に含まれる<code>PatternLayout</code>を使って、C言語の<code>printf</code>関数で使うような変換指示子によって出力形式を指定します。
    -		</p>
    -
    -		<p>PatternLayoutの変換パターンが "%-4relative [%thread] %-5level %logger{32} - %msg%n" のとき、出力は次のようになります。</p>
    -
    -		<div class="prettyprint source"><pre>176  [main] DEBUG manual.architecture.HelloWorld2 - Hello world.</pre></div>
    -
    -		<p>最初のフィールドはプログラムが開始してからの経過時間をミリ秒にしたものです。二番目のフィールドはログ要求を行ったスレッドです。三番目のフィールドはログ要求のレベルです。四番目のフィールドはログ要求を行ったロガーの名前です。'-' より後のテキストはログ要求に指定されたメッセージになります。</p>
    -
    -
    -		<h3 class="doAnchor" name="parametrized">パラメータ化ロギング</h3>
    -
    -		<p>ogbakc-classicのロガーは、<a href="http://www.slf4j.org/api/org/slf4j/Logger.html">SLF4JのLoggerインターフェイス</a>に含まれる一つ以上のパラメータを受け取る出力メソッドを実装しています。これらのメソッドは、コードの読みやすさへの影響を最小限に抑えながら、性能を改善するために用意されたものです。
    -		</p>
    -
    -		<p></p>
    -
    -		<pre class="prettyprint source">logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));</pre>
    -
    -		<p>こんな書き方をしているロギング式があった場合、メッセージを組み立てるために、整数<code>i</code>と<code>entry[i]</code>を文字列にするコスト、文字列を連結する中間的なコストがかかるでしょう。これは、ロギング要求が有効かどうかに関わらずかかってしまうコストです。
    -		</p>
    -
    -		<p>パラメータ構築のコストを回避するには、ロギング式全体をテスト条件で囲む方法があります。
    -		</p>
    -
    -		<pre class="prettyprint source">if(logger.isDebugEnabled()) { 
    -  logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
    -}</pre>
    -
    -
    -		<p>こうすると、DEBUGレベルのロギング要求が無効になっていればパラメータ構築のコストはかからないでしょう。しかし、有効になっている場合、<code>debugEnabled</code>と<code>debug</code>のそれぞれでロガーのレベルが有効かどうかを判定することになってしまいます。ロガーのレベルの評価はロギング要求に対して1%未満の時間しかかからないので、実際のオーバーヘッドは些細なものです。</p>
    -
    -		<h4>より良い方法</h4>
    -
    -		<p>メッセージフォーマットに基づいた便利な方法があります。<code>entry</code>が何らかのオブジェクトを指すものとして、次のように書くことが出来ます。</p>
    -
    -
    -		<pre class="prettyprint source">Object entry = new SomeObject(); 
    -logger.debug("The entry is {}.", entry);</pre>
    -
    -		<p>ロギング要求が有効かどうかを判断した後にだけ、そして、それが有効な場合にだけ、ロガーはメッセージを書式化して、'{}' を <code>entry</code> の文字列表現で置き換えます。つまり、ロギング要求が無効な場合、このやり方だとパラメータ構築のコストが発生しません。
    -		</p>
    -
    -
    -		<p>以下の二行からはまったく同じ出力が得られます。しかし、 ロギング要求が<em>無効</em>な場合、二行目のやり方は一行目のやり方に比べて少なくとも30倍は遅くなるでしょう。
    -		</p>
    -
    -		<pre class="prettyprint source">logger.debug("The new entry is "+entry+".");
    -logger.debug("The new entry is {}.", entry);</pre>
    -
    -
    -		<p>二つ置換場所を指定することもできます。たとえば、次のように書くことができます。</p>
    -
    -		<pre class="prettyprint source">logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);</pre>
    -
    -		<p>引数が三つ以上になる場合、<code>Object[]</code>でラップしなければなりません。たとえば、次のように書くことができます。</p>
    -
    -
    -		<pre class="prettyprint source">Object[] paramArray = {newVal, below, above};
    -logger.debug("Value {} was inserted between {} and {}.", paramArray);</pre>
    -
    -  
    -  <a name="UnderTheHood"></a>
    -  <h3>内部実装を覗いてみよう</h3>
    -
    -  <p>ここまでで、logback の中心的なコンポーネントについて紹介してきました。次のステップに進む準備は完璧です。利用者が logback の出力メソッドを呼び出した時に、logback フレームワークの内部でどんなことが起きているのか見ていきましょう。利用者が<em>com.wombat</em>という名前のロガーについて、<code>info()</code>を呼び出した時の様子を分析してみましょう。
    -  </p>
    -
    -  <h4>ステップ1. フィルタチェインの決定</h4>
    -
    -  <p><code>TurboFilter</code>が存在するならそれが呼び出されます。Turbo Filter コンテキストにまたがる閾値を設定できるし、いろんなイベントを捨てることができます。捨てるイベントは、<code>Marker</code> 、 <code>Level</code> 、 <code>Logger</code> 、メッセージ、<code>Throwable</code>といったロギング要求に関係する情報から判断します。フィルタチェインの結果が <code>FilterReply.DENY</code> だったら処理中のロギング要求はその時点で破棄します。<code>FilterReply.NEUTRAL</code> だったら次のステップ(ステップ2)に進みます。<code>FilterReply.ACCEPT</code> だったら、次のステップを無視してステップ3にジャンプします。
    -  </p>
    -
    -  <h4>ステップ2. <a href="./02-architecture.html#basic_selection">基本的な選択ルール</a>の適用</h4>
    -
    -  <p>このステップでは、logback はロガーの有効レベルとロギング要求のレベルを比較します。比較した結果ロギング要求が無効の場合は残りの処理は行わず、ロギング要求を破棄します。ロギング要求が破棄されなければ、次のステップに進みます。
    -  </p>
    -
    -  <h4>ステップ3. <code>LoggingEvent</code>オブジェクトの作成</h4>
    -
    -  <p>ロギング要求がここまでのフィルタを通過したら、logback はロギング要求に含まれる必要な情報を全て格納した<code>ch.qos.logback.classic.LoggingEvent</code> オブジェクトを作成します。中には、ロギング要求を受け付けたロガーインスタンス、ロギング要求のレベル、ロギング要求に指定された例外オブジェクト、現在の時間、現在のスレッド、ロギング要求を起こしたクラスに関するさまざまな情報、<code>MDC</code>などが含まれます。フィールドによってはレイジーな初期化となるものがあります。つまり、必要になった時点で初期化される、ということです。<code>MDC</code>は、ロギング要求の付加情報となります。MDCについては<a href="./08-mdc.html">以降の章</a>で詳しく説明します。</p>
    -
    -  <h4>ステップ4. アペンダーの起動</h4>
    -
    -  <p><code>LoggingEvent</code>オブジェクトを作ったら、logback は利用可能な全てのアペンダーについて <code>doAppend()</code>メソッドを呼び出します。ロガーコンテキストから受け継いだアペンダーが対象になります。
    -  </p>
    -
    -  <p>logback の配布物に含まれているアペンダーは、すべて <code>AppenderBase</code> 抽象クラスを継承しています。<code>doAppend()</code>メソッドは synchronized として宣言されており、スレッドセーフであることが保証されています。<code>AppenderBase</code>クラスの<code>doAppend()</code>メソッドでは、アペンダーに割り当てられたフィルターが存在する場合、それを呼び出します。カスタムフィルターは、実行時にアペンダーに割り当てることができるフィルターのことです。<a href="./07-filters.html">別の章</a>で説明しています。
    -  </p>
    -
    -  <h4>ステップ5. メッセージの書式化</h4>
    -
    -  <p>ロギングイベントを書式化するのはアペンダーの責任です。しかし、全てでは無いにしてもいくつかのアペンダーは書式化のタスクをレイアウトに委譲します。レイアウトは、<code>LoggingEvent</code>のインスタンスを書式化して、文字列として返します。アペンダーによっては(<code>SocketAppender</code>など)、ロギングイベントを文字列に変換するのではなく、シリアライズすることがあります。つまり、アペンダーはレイアウトを持っていることもあるし、持っていないこともあるのです。
    -  </p>
    -
    -  <h4>ステップ6. <code>LoggingEvent</code>の送信</h4>
    -
    -  <p>完全に書式化されたロギングイベントは、それぞれのアペンダーの宛先に送信されます。
    -  </p>
    -  
    -  <p>このUMLのシーケンス図は、ここまでで紹介してきたステップ全体を概観するものです。図をクリックすればより大きなサイズの図を見ることができます。
    -  </p>
    -
    -  <a href="underTheHood.html">
    -    <img src="images/chapters/architecture/underTheHoodSequence2_small.gif">
    -  </a>
    -
    -  
    -  <h3 class="doAnchor" name="performance">性能</h3>
    -
    -  <p>ロギングについてよく議論の的になる課題の一つとして、必要な計算コストがあります。そこそこの規模のアプリケーションであっても、数千に及ぶログ要求を生成することになるので、性能に関心があるのは当然です。開発中に私たちが一番多くの力と時間を費やしたのは、logback の性能を測定することと、性能を調整することでした。私たちがどれだけの労力を費やしてきたとしてもそれとは関係無く、利用者は次のような性能問題に注意しなければなりません。
    -  </p>
    -
    -  <h4>問題1. ロギングが完全にオフになっているときの性能</h4>
    -
    -  <p>ルートロガーのレベルに最高レベルの<code>Level.OFF</code>を設定すると、完全にロギングをオフにすることができます。完全にロギングをオフにすると、ロギング要求のコストはメソッド呼び出しと整数比較だけになります。3.2GHz の Pentium D のマシンの場合、通常ならそのコストは 20 マイクロ秒程度になります。
    -  </p>
    -
    -  <p>しかし、メソッド呼び出しによっては隠れたパラメータ構築のコストが含まれます。例えば、ロガー<em>x</em>について次のように実装されていると</p>
    -  
    -  <pre class="prettyprint source">x.debug("Entry number: " + i + "is " + entry[i]);</pre>
    -
    -  <p>パラメータ構築のコストが含まれることになります。つまり、整数<code>i</code>と<code>entry[i]</code>を文字列に変換するコストと、中間文字列を連結するコストです。これらのコストは、メッセージがログに出力されるかどうかは関係無くかかります。
    -  </p>
    -
    -  <p>パラメータ構築のコストは、パラメータの数にもよりますが非常に高くなることがあります。パラメータ構築のコストを回避するため、SLF4Jのパラメータ化されたロギングを利用することができます。</p>
    -
    -  <pre class="prettyprint source">x.debug("Entry number: {} is {}", i, entry[i]);</pre>
    -
    -  <p>このやり方の場合パラメータ構築のコストは発生しません。前の<code>debug()</code>メソッドの呼び出し方に比べると、圧倒的に速くなります。メッセージが書式化されるのは、割り当てられたアペンダーにロギング要求が送信されるときだけだからです。また、メッセージを書式化するコンポーネントには、高度な最適化が行われています。
    -  </p>
    -
    -  <p>そうはいっても、非常に範囲の狭いループ中にロギング式があると、呼び出し回数が非常におおくなります。性能が劣化する可能性があるため、何のメリットもありません。たとえロギングがオフになっていても、狭い範囲のループにロギング式が含まれていると、アプリケーションの動作が緩慢になってしまいます。それに、ロギングをオンにすると非常に大量の(そして役に立たない)出力が生じます。
    -  </p>
    -
    -  <h4>問題2. ロギングをオンにした状態で、ロギングするかどうかを判定する場合の性能</h4>
    -
    -  <p>logbackでは、ロガー階層を渡り歩く必要がありません。ロガーは、インスタンスが作成された時点で自分の有効レベル(ロガー自体のレベルと受け継いたレベルを考慮した結果)を知っています。親ロガーのレベルを変更すると、全ての子ロガーは変更通知を受け取ります。したがって、ロガーは祖先ロガーに問い合わせること無く、有効レベルに基いてロギング要求を受け付けるか拒否するかを瞬時に判断することができます。
    -  </p>
    -
    -
    -  <h4>問題3. 実際にロギングする(書式化と出力デバイスへの書き込み)</h4>
    -
    -  <p>これは、ログ出力を書式化し、宛先へ送信するコストです。レイアウト(フォーマッター)の処理を出来る限り高速化するために、同じように過大な労力を費やしました。同じことがアペンダーにも当てはまります。ローカルマシン上のファイルにロギングするとき、実際のロギングのコストは9マイクロ秒から12マイクロ秒程度になりました。リモートサーバ上のデータベースにロギングするとき、これが数ミリ秒に跳ね上がります。
    -  </p>
    -
    -  <p>logback は豊富な機能を備えていますが、設計上の第一目標は高速な実行速度であり、第二目標として挙げられていたのは信頼性だけでした。logback のコンポーネントは、性能改善のため何度も書き直されています。
    -  </p>
    -
    -    
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/configuration.html b/logback-site/src/site/pages/manual/configuration.html
    deleted file mode 100755
    index 5b1a76e1a7..0000000000
    --- a/logback-site/src/site/pages/manual/configuration.html
    +++ /dev/null
    @@ -1,2169 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 3: Configuration</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -
    -    <script type="text/javascript">prefix='../'</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content" class="chapter">
    -      
    -    <h1>Chapter 3: Logback configuration</h1>
    -
    -    <a href="configuration_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -      
    -    <div class="quote">
    -      <p><em>In symbols one observes an advantage in discovery which
    -      is greatest when they express the exact nature of a thing
    -      briefly and, as it were, picture it; then indeed the labor of
    -      thought is wonderfully diminished.</em>
    -      </p>
    -      <p>&mdash;GOTTFRIED WILHELM LEIBNIZ</p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -
    -    <p>We start by presenting ways for configuring logback, with many
    -    example configuration scripts.  Joran, the configuration framework
    -    upon which logback relies will be presented in <a
    -    href="onJoran.html">a later chapter</a>.
    -    </p>
    -
    -
    -    <h2 class="doAnchor" name="auto_configuration">Configuration in
    -    logback</h2>
    -    
    -    <p>Inserting log requests into the application code requires a
    -    fair amount of planning and effort. Observation shows that
    -    approximately four percent of code is dedicated to
    -    logging. Consequently, even a moderately sized application will
    -    contain thousands of logging statements embedded within its
    -    code. Given their number, we need tools to manage these log
    -    statements.
    -    </p>
    -
    -    <p>Logback can be configured either programmatically or with a
    -    configuration script expressed in XML or Groovy format. By the
    -    way, existing log4j users can convert their
    -    <em>log4j.properties</em> files to <em>logback.xml</em> using our
    -    <a
    -    href="http://logback.qos.ch/translator/">PropertiesTranslator</a>
    -    web-application.
    -    </p>
    -
    -    <p>Let us begin by discussing the initialization steps that
    -    logback follows to try to configure itself:
    -    </p>
    -
    -    <ol>
    -
    -      <li>
    -        <p>Logback tries to find a file called
    -        <em>logback-test.xml</em> <a
    -        href="../faq.html#configFileLocation">in the
    -        classpath</a>.</p> </li>
    -
    -      <li>
    -        <p>If no such file is found, logback tries to find a file called
    -        <em>logback.groovy</em> <a
    -        href="../faq.html#configFileLocation">in the
    -        classpath</a>.</p>
    -      </li>
    -
    -
    -      <li><p>If no such file is found, it checks for the file
    -      <em>logback.xml</em>  <a
    -        href="../faq.html#configFileLocation">in the
    -        classpath</a>..</p>
    -      </li>
    -      
    -      <li><p>If no such file is found, <a
    -      href="http://docs.oracle.com/javase/6/docs/api/java/util/ServiceLoader.html">
    -      service-provider loading facility</a> (introduced in JDK 1.6) is
    -      used to resolve the implementation of <a
    -      href="../xref/ch/qos/logback/classic/spi/Configurator.html">
    -      <code>com.qos.logback.classic.spi.Configurator</code></a>
    -      interface by looking up the file
    -      <em>META-INF\services\ch.qos.logback.classic.spi.Configurator</em>
    -      in the class path. Its contents should specify the fully
    -      qualified class name of the desired <code>Configurator</code>
    -      implementation. 
    -      </p>
    -      </li>
    -      
    -      <li><p>If none of the above succeeds, logback configures itself
    -      automatically using the <a
    -      href="../xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code></a>
    -      which will cause logging output to be directed to the console.
    -      </p> 
    -      </li> 
    -
    -    </ol>
    -
    -    <p>The last step is meant as last-ditch effort to provide a
    -    default (but very basic) logging functionality in the absence of a
    -    configuration file.
    -    </p>
    -
    -
    -    <p>If you are using Maven and if you place the
    -    <em>logback-test.xml</em> under the <em>src/test/resources</em>
    -    folder, Maven will ensure that it won't be included in the
    -    artifact produced. Thus, you can use a different configuration
    -    file, namely <em>logback-test.xml</em> during testing, and another
    -    file, namely, <em>logback.xml</em>, in production. 
    -    </p>
    -
    -    <p><span class="label">Fast start-up</span> It takes about 100
    -    miliseconds for Joran to parse a given logback configuration
    -    file. To shave off those miliseconds at aplication start up, you
    -    can use the service-provider loading facility (item 4 above) to
    -    load your own custom <code>Configurator</code> class with <a
    -    href="../xref/ch/qos/logback/classic/BasicConfigurator.html">BasicConfigrator</a>
    -    serving as a good starting point.</p>
    -
    -
    -    <h3 class="doAnchor" name="automaticConf">Automatically
    -    configuring logback</h3>
    -
    -    <p>The simplest way to configure logback is by letting logback
    -    fall back to its default configuration. Let us give a taste of how
    -    this is done in an imaginary application called
    -    <code>MyApp1</code>.
    -    </p>
    -
    -    <p class="example">Example: Simple example of
    -    <code>BasicConfigurator</code> usage <a
    -    href="../xref/chapters/configuration/MyApp1.html">(logback-examples/src/main/java/chapters/configuration/MyApp1.java)</a></p>
    -
    -    <pre class="prettyprint source">package chapters.configuration;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -public class MyApp1 {
    -  final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
    -
    -  public static void main(String[] args) {
    -    logger.info("Entering application.");
    -
    -    Foo foo = new Foo();
    -    foo.doIt();
    -    logger.info("Exiting application.");
    -  }
    -}</pre>
    -
    -  <p>This class defines a static logger variable. It then instantiates
    -  a <code>Foo</code> object. The <code>Foo</code> class is listed
    -  below:
    -  </p>
    -
    -  <p class="example">Example: Small class doing logging 
    -  <a href="../xref/chapters/configuration/Foo.html">(logback-examples/src/main/java/chapters/configuration/Foo.java)</a>
    -  </p>
    -
    -  <pre class="prettyprint source">package chapters.configuration;
    -  
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -   
    -public class Foo {
    -  static final Logger logger = LoggerFactory.getLogger(Foo.class);
    -  
    -  public void doIt() {
    -    logger.debug("Did it again!");
    -  }
    -}</pre>
    -
    -
    -    <p>In order to run the examples in this chapter, you need to make
    -    sure that certain jar files are present on the class path.  Please
    -    refer to the <a href="../setup.html">setup page</a> for further
    -    details.
    -    </p>
    -
    -    <p>Assuming the configuration files <em>logback-test.xml</em> or
    -    <em>logback.xml</em> are not present, logback will default to
    -    invoking <a
    -    href="../xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code></a>
    -    which will set up a minimal configuration. This minimal
    -    configuration consists of a <code>ConsoleAppender</code> attached
    -    to the root logger.  The output is formatted using a
    -    <code>PatternLayoutEncoder</code> set to the pattern
    -    <em>%d{HH:mm:ss.SSS}&nbsp;[%thread]&nbsp;%-5level&nbsp;%logger{36}&nbsp;-&nbsp;%msg%n</em>. Moreover,
    -    by default the root logger is assigned the <code>DEBUG</code>
    -    level.
    -    </p>
    -
    -    <p>Thus, the output of the command <em>java chapters.configuration.MyApp1</em>
    -    should be similar to:
    -    </p>
    -
    -    <p class="source">16:06:09.031 [main] INFO  chapters.configuration.MyApp1 - Entering application.
    -16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!
    -16:06:09.046 [main] INFO  chapters.configuration.MyApp1 - Exiting application.</p>
    -
    -
    -   <p class="highlight">Except code that configures logback (if such
    -   code exists) client code does not need to depend on
    -   logback. Applications that use logback as their logging framework
    -   will have a compile-time dependency on SLF4J but not logback.
    -   </p>
    -
    -   <p>The <code>MyApp1</code> application links to logback via calls
    -   to <code>org.slf4j.LoggerFactory</code> and
    -   <code>org.slf4j.Logger</code> classes, retrieve the loggers it
    -   wishes to use, and chugs on.  Note that the only dependencies of
    -   the <code>Foo</code> class on logback are through
    -   <code>org.slf4j.LoggerFactory</code> and
    -   <code>org.slf4j.Logger</code> imports.  Except code that configures
    -   logback (if such code exists) client code does not need to depend
    -   on logback. Since SLF4J permits the use of any logging
    -   framework under its abstraction layer, it is easy to migrate
    -   large bodies of code from one logging framework to another.
    -   </p>
    -
    -   <h3>Automatic configuration with <em>logback-test.xml</em> or
    -   <em>logback.xml</em></h3>
    -
    -   <p>As mentioned earlier, logback will try to configure itself using
    -   the files <em>logback-test.xml</em> or <em>logback.xml</em> if
    -   found on the class path. Here is a configuration file equivalent to
    -   the one established by <code>BasicConfigurator</code> we've just
    -   seen.
    -   </p>
    -
    -   <p class="example">Example: Basic configuration file
    -   (logback-examples/src/main/resources/chapters/configuration/sample0.xml)</p>
    -   <span class="asGroovy" onclick="return asGroovy('sample0');">View as .groovy</span>
    -
    -
    -  <pre id="sample0" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p>After you have renamed <em>sample0.xml</em> as
    -   <em>logback.xml</em> (or <em>logback-test.xml</em>) place it into a
    -   directory accessible from the class path. Running the <em>MyApp1</em>
    -   application should give identical results to its previous run.</p>
    -
    -   <h4 class="doAnchor" name="automaticStatusPrinting">Automatic
    -   printing of status messages in case of warning or errors</h4>
    -
    -   <p class="highlight">If warning or errors occur during the parsing
    -   of the configuration file, logback will automatically print its
    -   internal status messages on the console.</p>
    - 
    -   <p>If warnings or errors occur during the parsing of the
    -   configuration file, logback will automatically print its internal
    -   status data on the console. Note that to avoid duplication,
    -   automatic status printing is disabled if the user explicitly
    -   registers a status listener (defined below).</p>
    -
    -   <p>In the absence of warnings or errors, if you still wish to
    -   inspect logback's internal status, then you can instruct logback to
    -   print status data by invoking the <code>print()</code> of the
    -   <code>StatusPrinter</code> class. The <em>MyApp2</em> application
    -   shown below is identical to <em>MyApp1</em> except for the addition
    -   of two lines of code for printing internal status data.</p>
    -
    -    <p class="example">Example: Print logback's internal status
    -    information <a
    -    href="../xref/chapters/configuration/MyApp2.html">(logback-examples/src/main/java/chapters/configuration/MyApp2.java)</a></p>
    -
    -  
    -<pre class="prettyprint lang-java source">
    -public static void main(String[] args) {
    -  // assume SLF4J is bound to logback in the current environment
    -  <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -  // print logback's internal status
    -  <b>StatusPrinter.print(lc);</b>
    -  ...
    -}</pre>
    -
    -  <p>If everything goes well, you should see the following output on the console</p>
    -
    -   <div class="source longline"><pre>17:44:58,578 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
    -17:44:58,671 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
    -17:44:58,671 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
    -17:44:58,687 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
    -17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Popping appender named [STDOUT] from the object stack
    -17:44:58,812 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG
    -17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[root]
    -
    -17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Entering application.
    -17:44:58.828 [main] DEBUG chapters.configuration.Foo - Did it again!
    -17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Exiting application.
    -</pre></div>
    -
    -  <p>At the end of this output, you can recognize the lines that were
    -  printed in the previous example. You should also notice the
    -  logback's internal messages, a.k.a. <code>Status</code> objects,
    -  which allow convenient access to logback's internal state.
    -  </p>
    -
    -   <h4 class="doAnchor" name="dumpingStatusData">Status data</h4>
    -
    -   <p class="highlight">Enabling output of status data usually goes a
    -   long way in the diagnosis of issues with logback. As such, it is
    -   highly recommended and should be considered as a recourse of
    -   <b>first</b> resort.</p>
    -
    -   <p>Instead of invoking <code>StatusPrinter</code> programmatically
    -   from your code, you can instruct the configuration file to dump
    -   status data, even in the absence of errors. To achieve this, you
    -   need to set the <span class="attr">debug</span> attribute of the
    -   <em>configuration</em> element, i.e. the top-most element in the
    -   configuration file, as shown below. Please note that this <span
    -   class="attr">debug</span> attribute relates only to the status
    -   data. It does <em>not</em> affect logback's configuration
    -   otherwise, in particular with respect to logger levels. (If you are
    -   asking, no, the root logger will <em>not</em> be set to
    -   <code>DEBUG</code>.)
    -   </p>
    -
    -   <p class="example">Example: Basic configuration file using debug
    -   mode
    -   (logback-examples/src/main/resources/chapters/configuration/sample1.xml)</p>
    -   <span class="asGroovy" onclick="return asGroovy('sample1');">View as .groovy</span>
    -
    -   <pre id="sample1" class="prettyprint source">
    -&lt;configuration <span class="big bold">debug="true"</span>> 
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    -    &lt;!-- encoders are  by default assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -  
    -   <p>Setting <code>debug="true"</code> within the
    -   &lt;configuration&gt; element will output status information,
    -   assuming that:
    -   </p>
    -   <ol>
    -     <li>the configuration file is found</li>
    -     <li>the configuration file is well-formed XML.</li>
    -   </ol>
    -     
    -   <p>If any of these two conditions is not fulfilled, Joran cannot
    -   interpret the <span class="attr">debug</span> attribute since the
    -   configuration file cannot be read. If the configuration file is
    -   found but is malformed, then logback will detect the error
    -   condition and automatically print its internal status on the
    -   console. However, if the configuration file cannot be found,
    -   logback will not automatically print its status data, since this is
    -   not necessarily an error condition. Programmatically invoking
    -   <code>StatusPrinter.print()</code> as shown in the <a
    -   href="../xref/chapters/configuration/MyApp2.html"><em>MyApp2</em></a>
    -   application above ensures that status information is printed in
    -   every case.</p>
    -
    - 
    - 
    -   <p><span class="label">Forcing status output</span> In the absence
    -   of status messages, tracking down a rogue <em>logback.xml</em>
    -   configuration file can be difficult, especially in production where
    -   the application source cannot be easily modified. To help identify
    -   the location of a rogue configuration file, you can set a
    -   <code>StatusListener</code> via the "logback.statusListenerClass"
    -   system property (<a href="#logback.statusLC">defined below</a>) to
    -   force output of status messages. The "logback.statusListenerClass"
    -   system property can also be used to silence output automatically
    -   generated in case of errors.
    -   </p>
    -
    -   <p>By the way, setting <code>debug="true"</code> is strictly
    -   equivalent to installing an
    -   <code>OnConsoleStatusListener</code>. Status listeners are
    -   disccussed further below. The installation of
    -   <code>OnConsoleStatusListener</code> is shown next. </p>
    -
    -   <p class="example">Example: Registering a status listener
    -   (logback-examples/src/main/resources/chapters/configuration/onConsoleStatusListener.xml)</p>
    -   <span class="asGroovy" onclick="return asGroovy('onConsoleStatusListener');">View as .groovy</span>
    -   <pre id="onConsoleStatusListener" class="prettyprint source">&lt;configuration>
    -  <b>&lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /></b>  
    -
    -  ... the rest of the configuration file  
    -&lt;/configuration></pre>
    -
    -
    -   <p>Enabling output of status data, either via the debug attribute
    -   or equivalently by installing <code>OnConsoleStatusListener</code>,
    -   will go a long way in helping you diagnose logback issues. As such,
    -   enabling logback status data is very highly recommended and should
    -   be considered as a recourse of <b>first</b> resort.
    -   </p>
    -
    -   <h3 class="doAnchor" name="configFileProperty">Specifying the
    -   location of the default configuration file as a system
    -   property</h3>
    -
    -   <p>You may specify the location of the default configuration file
    -   with a system property named
    -   <code>"logback.configurationFile"</code>. The value of this
    -   property can be a URL, a resource on the class path or a path to a
    -   file external to the application.
    -   </p>
    -
    -   <p class="source">java <b>-Dlogback.configurationFile=/path/to/config.xml</b> chapters.configuration.MyApp1</p>
    -
    -   <p>Note that the file extension must be ".xml" or ".groovy". Other
    -   extensions are ignored. <a href="#logback.statusLC">Explicitly
    -   registering a status listener</a> may help debugging issues
    -   locating the configuration file.</p>
    -
    -   <p>Given that <code>"logback.configurationFile"</code> is a Java
    -   system property, it may be set within your application as
    -   well. However, the system property must be set before any logger
    -   instance is created.</p>
    -
    -   <pre class="prettyprint lang-java source">import ch.qos.logback.classic.util.ContextInitializer;
    -
    -public class ServerMain {
    -    public static void main(String args[]) throws IOException, InterruptedException {
    -       // must be set before the first call to  LoggerFactory.getLogger();
    -       // ContextInitializer.CONFIG_FILE_PROPERTY is set to "logback.configurationFile"
    -       System.setProperty(ContextInitializer.CONFIG_FILE_PROPERTY, /path/to/config.xml);
    -       ...
    -    }   
    -}</pre>
    -   
    -   <h3 class="doAnchor" name="autoScan">Automatically reloading
    -   configuration file upon modification</h3>
    -
    -   <p class="highlight">Logback-classic can scan for changes in its
    -   configuration file and automatically reconfigure itself when the
    -   configuration file changes.</p>
    -
    -   <p>If instructed to do so, logback-classic will scan for changes in
    -   its configuration file and automatically reconfigure itself when
    -   the configuration file changes. In order to instruct
    -   logback-classic to scan for changes in its configuration file and
    -   to automatically re-configure itself set the <span
    -   class="attr">scan</span> attribute of the
    -   <code>&lt;configuration></code> element to true, as shown next.
    -
    -   </p>
    -  
    -   <p class="example">Example: Scanning for changes in configuration
    -   file and automatic re-configuration
    -   (logback-examples/src/main/resources/chapters/configuration/scan1.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('scan1');">View as .groovy</span>
    -<pre id="scan1" class="prettyprint source">
    -&lt;configuration <b>scan="true"</b>> 
    -  ... 
    -&lt;/configuration> </pre>
    -
    -
    -   <p>By default, the configuration file will be scanned for changes
    -   once every minute. You can specify a different scanning period by
    -   setting the <span class="attr">scanPeriod</span> attribute of the
    -   <code>&lt;configuration></code> element. Values can be specified in
    -   units of milliseconds, seconds, minutes or hours. Here is an
    -   example:</p>
    -
    -  <p class="example">Example: Specifying a different scanning period
    -  (logback-examples/src/main/resources/chapters/configuration/scan2.xml)</p>
    -  <span class="asGroovy" onclick="return asGroovy('scan2');">View as .groovy</span>
    -  <pre id="scan2" class="prettyprint source">
    -&lt;configuration scan="true" <b>scanPeriod="30 seconds"</b> > 
    -  ...
    -&lt;/configuration> </pre>
    -
    -   <p><span class="label">Note</span> If no unit of time is specified,
    -   then the unit of time is assumed to be milliseconds, which is
    -   usually inappropriate. If you change the default scanning period,
    -   do not forget to specify a time unit.
    -   </p>
    -
    -   <p>Behind the scenes, when you set the <span
    -   class="option">scan</span> attribute to <code>true</code>, a <a
    -   href="../xref/ch/qos/logback/classic/joran/ReconfigureOnChangeTask.html"><code>ReconfigureOnChangeTask</code></a>
    -   will be installed. This task run in a separate thread and will
    -   check whether your configuration file has
    -   changed. <code>ReconfigureOnChangeTask</code> will automatically
    -   watch for any <a href="#fileInclusion">included</a> files as
    -   well.</p>
    -
    -   <p>As it is easy to make errors while editing a configuration file,
    -   in case the latest version of the configuration file has XML syntax
    -   errors, it will fall back to a previous configuration file free of
    -   XML syntax errors.</p>
    -
    -
    -   
    -   <h4 class="doAnchor" name="packagingData">Enabling packaging data in stack traces</h4>
    -
    -   <p class="highlight">While useful, packaging data is expensive to
    -   compute, especially in applications with frequent exceptions.</p>
    -
    -   <p><span class="label notice">NOTE</span> As of version 1.1.4,
    -   packaging data is disabled by default. </p>
    -
    -   <p>If instructed to do so, logback can include packaging data for
    -   each line of the stack trace lines it outputs. Packaging data
    -   consists of the name and version of the jar file whence the class
    -   of the stack trace line originated. Packaging data can be very
    -   useful in identifying software versioning issues. However, it is
    -   rather expensive to compute, especially in application where
    -   exceptions are thrown frequently. Here is a sample output:</p>
    -
    -   <pre>14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
    -java.lang.Exception: 99 is invalid
    -  at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
    -  at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) <b>[struts-1.2.9.jar:1.2.9]</b>
    -  at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
    -  at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
    -  at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) <b>[servlet-api-2.5-6.1.12.jar:6.1.12]</b>
    -  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
    -  at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
    -  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) <b>[jetty-6.1.12.jar:6.1.12]</b>
    -  at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]</pre>
    -
    -
    -  <p>Packaging data is disabled by default but can be enabled by
    -  configuration:</p>
    -
    -<pre class="prettyprint source">
    -&lt;configuration <span class="big bold">packagingData="true"</span>>
    -  ...
    -&lt;/configuration></pre>
    -
    -   <p>Alternatively, packaging data can be enabled/disabled
    -   programmatically by invoking the <a
    -   href="../apidocs/ch/qos/logback/classic/LoggerContext.html#setPackagingDataEnabled(boolean)">setPackagingDataEnabled(boolean)</a>
    -   method in <code>LoggerContext</code>, as shown next:</p>
    -
    -
    -<pre class="prettyprint lang-java source">
    -  LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -  <b>lc.setPackagingDataEnabled(true);</b>
    -</pre>
    -
    -   <h3 class="doAnchor" name="joranDirectly">Invoking
    -   <code>JoranConfigurator</code> directly</h3>
    -
    -   <p>Logback relies on a configuration library called Joran, part of
    -   logback-core. Logback's default configuration mechanism invokes
    -   <code>JoranConfigurator</code> on the default configuration file it
    -   finds on the class path. If you wish to override logback's default
    -   configuration mechanism for whatever reason, you can do so by
    -   invoking <code>JoranConfigurator</code> directly. The next
    -   application, <em>MyApp3</em>, invokes JoranConfigurator on a
    -   configuration file passed as a parameter.</p>
    -   
    -   <p class="example">Example: Invoking <code>JoranConfigurator</code>
    -   directly <a
    -   href="../xref/chapters/configuration/MyApp3.html">(logback-examples/src/main/java/chapters/configuration/MyApp3.java)</a></p>
    -
    -<pre class="prettyprint source">package chapters.configuration;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class MyApp3 {
    -  final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
    -
    -  public static void main(String[] args) {
    -    // assume SLF4J is bound to logback in the current environment
    -    <b>LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -    
    -    <b>try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(context);
    -      // Call context.reset() to clear any previous configuration, e.g. default 
    -      // configuration. For multi-step configuration, omit calling context.reset().
    -      context.reset(); 
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -      // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(context);</b>
    -
    -    logger.info("Entering application.");
    -
    -    Foo foo = new Foo();
    -    foo.doIt();
    -    logger.info("Exiting application.");
    -  }
    -}</pre>
    -
    -   <p>This application fetches the <code>LoggerContext</code>
    -   currently in effect, creates a new <code>JoranConfigurator</code>,
    -   sets the context on which it will operate, resets the logger
    -   context, and then finally asks the configurator to configure the
    -   context using the configuration file passed as a parameter to the
    -   application. Internal status data is printed in case of warnings or
    -   errors. Note that for multi-step configuration,
    -   <code>context.reset()</code> invocation should be omitted.</p>
    -
    -   <h3 class="doAnchor" name="viewingStatusMessages">Viewing status
    -   messages </h3>
    -
    -   <p>Logback collects its internal status data in a <code><a
    -   href="../xref/ch/qos/logback/core/status/StatusManager.html">StatusManager</a></code>
    -   object, accessible via the <code>LoggerContext</code>.
    -   </p>
    -
    -   <p>Given a <code>StatusManager</code> you can access all the status
    -   data associated with a logback context. To keep memory usage at
    -   reasonable levels, the default <code>StatusManager</code>
    -   implementation stores the status messages in two separate parts:
    -   the header part and the tail part. The header part stores the first
    -   <em>H</em> status messages whereas the tail part stores the last
    -   <em>T</em> messages. At present time <em>H</em>=<em>T</em>=150,
    -   although these values may change in future releases.</p>
    -
    -   <p>Logback-classic ships with a servlet called
    -   ViewStatusMessagesServlet. This servlet prints the contents of the
    -   <code>StatusManager</code> associated with the current
    -   <code>LoggerContext</code> as an HTML table. Here is sample output.
    -   </p>
    -   
    -   <a href="images/chapters/configuration/lbClassicStatus.jpg">
    -     <img src="images/chapters/configuration/lbClassicStatus.jpg" alt="click to enlarge" width="90%"/>
    -   </a>
    -
    -   <p>To add this servlet to your web-application, add the following
    -   lines to its <em>WEB-INF/web.xml</em> file.</p>
    -
    -   <pre class="prettyprint source">  &lt;servlet>
    -    &lt;servlet-name>ViewStatusMessages&lt;/servlet-name>
    -    &lt;servlet-class>ch.qos.logback.classic.ViewStatusMessagesServlet&lt;/servlet-class>
    -  &lt;/servlet>
    -
    -  &lt;servlet-mapping>
    -    &lt;servlet-name>ViewStatusMessages&lt;/servlet-name>
    -    &lt;url-pattern>/lbClassicStatus&lt;/url-pattern>
    -  &lt;/servlet-mapping></pre>
    -   
    -   <p>The <code>ViewStatusMessages</code> servlet will be viewable at
    -   the URL <code>http://host/yourWebapp/lbClassicStatus</code>
    -   </p>
    -
    -   <h3 class="doAnchor" name="statusListener">Listening to status
    -   messages</h3>
    -
    -   <p>You may also attach a <code>StatusListener</code> to a
    -   <code>StatusManager</code> so that you can take immediate action in
    -   response to status messages, especially to messages occurring after
    -   logback configuration. Registering a status listener is a
    -   convenient way to supervise logback's internal state without human
    -   intervention.
    -   </p>
    -
    -   <p>Logback ships with a <code>StatusListener</code> implementation
    -   called <code><a
    -   href="../xref/ch/qos/logback/core/status/OnConsoleStatusListener.html">OnConsoleStatusListener</a></code>
    -   which, as its name indicates, prints all <em>new</em> incoming
    -   status messages on the console.
    -   </p>
    -
    -   <p>Here is <a
    -   href="../xref/chapters/configuration/AddStatusListenerApp.html">sample
    -   code</a> to register an <code>OnConsoleStatusListener</code>
    -   instance with the StatusManager.
    -   </p>
    -
    -   <pre class="prettyprint source">   LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 
    -   StatusManager statusManager = lc.getStatusManager();
    -   OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
    -   statusManager.add(onConsoleListener);</pre>
    -
    -   <p>Note that the registered status listener will only receive status
    -   events subsequent to its registration. It will not receive prior
    -   messages. Thus, it is usually a good idea to place status listener
    -   registration directives at top of the configuration file before
    -   other directives.</p>
    -
    -   <p>It is also possible to register one or more status listeners
    -   within a configuration file. Here is an example.</p>
    -
    -   <p class="example">Example: Registering a status listener
    -   (logback-examples/src/main/resources/chapters/configuration/onConsoleStatusListener.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('onConsoleStatusListener');">View as .groovy</span>
    -   <pre id="onConsoleStatusListener" class="prettyprint source">&lt;configuration>
    -  <b>&lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /></b>  
    -
    -  ... the rest of the configuration file  
    -&lt;/configuration></pre>
    -
    -   <h3 class="doAnchor"
    -   name="logback.statusLC">"logback.statusListenerClass" system
    -   property</h3>
    -
    -   <p>One may also register a status listener by setting the
    -   "logback.statusListenerClass" Java system property to the name of
    -   the listener class you wish to register. For example,
    -   </p>
    -
    -   <p class="source">java <b>-Dlogback.statusListenerClass</b>=ch.qos.logback.core.status.OnConsoleStatusListener&nbsp;...</p>
    -   
    -   <p>Logback ships with several status listener implementations. <a
    -   href="../xref/ch/qos/logback/core/status/OnConsoleStatusListener.html">OnConsoleStatusListener</a>
    -   prints incoming status messages on the console, i.e. on
    -   System.out. <a
    -   href="../xref/ch/qos/logback/core/status/OnErrorConsoleStatusListener.html">OnErrorConsoleStatusListener</a>
    -   prints incoming status messages on System.err. <a
    -   href="../xref/ch/qos/logback/core/status/NopStatusListener.html">NopStatusListener</a>
    -   drops incoming status messages.</p>
    -   
    -
    -   <p>Note that <a href="#automaticStatusPrinting">automatic status
    -   printing</a> (in case of errors) is disabled if any status listener
    -   is registered during configuration and in particular if the user
    -   specifies a status listener via the "logback.statusListenerClass"
    -   system. Thus, by setting <code>NopStatusListener</code> as a status
    -   listener, you can silence internal status printing altogether. </p>
    -   
    -   <p class="source">java <b>-Dlogback.statusListenerClass</b>=ch.qos.logback.core.status.NopStatusListener&nbsp;...</p>
    -
    -
    -   <h2 class="doAnchor" name="stopContext">Stopping
    -   logback-classic</h2>
    -
    -   <p>In order to release the resources used by logback-classic, it is
    -   always a good idea to stop the logback context. Stopping the
    -   context will close all appenders attached to loggers defined by the
    -   context and stop any active threads in an orderly way. Please also
    -   read the section on "shutdown hooks" just below.</p>
    -
    -<pre class="prettyprint lang-java source">
    -import org.sflf4j.LoggerFactory;
    -import ch.qos.logback.classic.LoggerContext;
    -...
    -
    -// assume SLF4J is bound to logback-classic in the current environment
    -<b>LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -<b>loggerContext.stop();</b></pre>
    -
    -   <p>In web-applications the above code could be invoked from within
    -   the <a
    -   href="http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html#contextDestroyed(javax.servlet.ServletContextEvent)">contextDestroyed</a>
    -   method of <code>ServletContextListener</code> in order to stop
    -   logback-classic and release resources. Starting with version
    -   1.1.10, the appropriate <code>ServletContextListener</code> is
    -   installed automatically for you (<a href="#webShutdownHook">see just below</a>).
    -   </p>
    -
    -   <h4 class="doAnchor" name="shutdownHook">Stopping logback-classic 
    -   via a shutdown hook</h4>
    -   
    -   <p>Installing a JVM shutdown hook is a convenient way for shutting
    -   down logback and releasing associated resources. 
    -   </p>
    -
    -
    -   <pre class="prettyprint source">&lt;configuration debug="true">
    -   &lt;!-- in the absence of the class attribute, assume 
    -   ch.qos.logback.core.hook.DefaultShutdownHook --&gt;
    -   <b>&lt;shutdownHook/></b>
    -  .... 
    -&lt;/configuration></pre>
    -
    -   <p>Note that you may install a shutdown hook of your own making by
    -   setting the <span class="attr">class</span> attribute to correspond
    -   to your shutdown hook's class name.</p>
    -  
    -   <p>The default shutdown hook, namely <a
    -   href="../apidocs/ch/qos/logback/core/hook/DefaultShutdownHook.html">DefaultShutdownHook</a>,
    -   will <b>stop</b> the logback context after a specified delay (0 by
    -   default). Stopping the context will allow up to 30 seconds for any
    -   log file compression tasks running in the background to finish. In
    -   standalone Java applications, adding a
    -   <code>&lt;shutdownHook/></code> directive to your configuration
    -   file is an easy way to ensure that any ongoing compression tasks
    -   are allowed to finish before JVM exit. In applications within a Web
    -   server, <a href="#webShutdownHook">webShutdownHook</a> will be
    -   installed automatically making <code>&lt;shutdownHook/></code>
    -   directive quite redundant and unnecessary. </p>
    -
    -
    - 
    -    <h4 class="doAnchor" name="webShutdownHook">WebShutdownHook or stopping logback-classic 
    -   in web-applications</h4>
    -  
    -   <p><span class="label">since 1.1.10</span> Logback-classic will
    -   <b>automatically</b> ask the web-server to install a <code><a
    -   href="../apidocs/ch/qos/logback/classic/servlet/LogbackServletContainerInitializer.html">LogbackServletContainerInitializer</a></code>
    -   implementing the <code>ServletContainerInitializer</code> interface
    -   (available in servlet-api 3.x and later). This initializer will in
    -   turn install and instance of <code><a
    -   href="../apidocs/ch/qos/logback/classic/servlet/LogbackServletContextListener.html">LogbackServletContextListener</a></code>.
    -   This listener will stop the current logback-classic context when
    -   the web-app is stopped or reloaded.
    -   </p>
    -
    -   <p>You may disable the automatic the installation of
    -   <code>LogbackServletContextListener</code> by setting a
    -   &lt;context-param> named
    -   <code>logbackDisableServletContainerInitializer</code> in your
    -   web-application's web.xml file. Here is the relevant snippet.</p>
    -
    -   
    -<pre class="prettyprint source">&lt;web-app>
    -    &lt;context-param>
    -        &lt;param-name>logbackDisableServletContainerInitializer&lt;/param-name>
    -        &lt;param-value>true&lt;/param-value>
    -    &lt;/context-param>
    -    .... 
    -&lt;/web-app></pre>
    -
    -    <p>Note that
    -    <code>logbackDisableServletContainerInitializer</code> variable
    -    can also be set as a Java system property an OS environment
    -    variable. The most local setting has priority, i.e. web-app first,
    -    system property second and OS environment last.</p>
    -
    -   
    -
    -   <!-- =================================================================== -->
    -   <!-- =================================================================== -->
    -
    -   <h2 class="doAnchor" name="syntax">Configuration file syntax</h2>
    -
    -   <p>As you have seen thus far in the manual with plenty of examples
    -   still to follow, logback allows you to redefine logging behavior
    -   without needing to recompile your code.  Indeed, you can easily
    -   configure logback so as to disable logging for certain parts of
    -   your application, or direct output to a UNIX Syslog daemon, to a
    -   database, to a log visualizer, or forward logging events to a
    -   remote logback server, which would log according to local server
    -   policy, for example by forwarding the log event to a second logback
    -   server.
    -   </p>
    -	
    -   <p>The remainder of this section presents the syntax of
    -   configuration files. 
    -   </p>
    -
    -   <p>As will be demonstrated over and over, the syntax of logback
    -   configuration files is extremely flexible. As such, it is not
    -   possible to specify the allowed syntax with a DTD file or an XML
    -   schema. Nevertheless, the very basic structure of the configuration
    -   file can be described as, <code>&lt;configuration></code> element,
    -   containing zero or more <code>&lt;appender></code> elements,
    -   followed by zero or more <code>&lt;logger></code> elements,
    -   followed by at most one <code>&lt;root></code> element. The
    -   following diagram illustrates this basic structure.</p>
    -
    -  
    -  <p align="left">
    -    <img src="images/chapters/configuration/basicSyntax.png" 
    -         alt="basic Syntax" title="Basic configuration file structure"/>
    -  </p>
    -
    -
    -    <p class="highlight">If you are unsure which case to use for a
    -    given tag name, just follow the <a
    -    href="http://en.wikipedia.org/wiki/CamelCase">camelCase
    -    convention</a> which is almost always the correct convention.</p>
    -
    -    <h4 class="doAnchor" name="caseSensitivity">Case sensitivity of
    -    tag names</h4>
    -   
    -    <p>Since logback version 0.9.17, tag names pertaining to explicit
    -    rules are case insensitive.  For example, <code>&lt;logger></code>, <code>&lt;Logger></code> and
    -    <code>&lt;LOGGER></code> are valid configuration elements and will
    -    be interpreted in the same way. Note that XML well-formedness
    -    rules still apply, if you open a tag as <code>&lt;xyz></code> you
    -    must close it as <code>&lt;/xyz></code>, <code>&lt;/XyZ></code>
    -    will not work. As for <a href="onJoran.html#implicit">implicit
    -    rules</a>, tag names are case sensitive except for the first
    -    letter.  Thus, <code>&lt;xyz></code> and <code>&lt;Xyz></code> are
    -    equivalent but not <code>&lt;xYz></code>.  Implicit rules usually
    -    follow the <a href="http://en.wikipedia.org/wiki/CamelCase">camelCase</a>
    -    convention, common in the Java world. Since it is not
    -    easy to tell when a tag is associated with an explicit action and
    -    when it is associated with an implicit action, it is not trivial
    -    to say whether an XML tag is case-sensitive or insensitive with
    -    respect to the first letter. If you are unsure which case to use
    -    for a given tag name, just follow the camelCase convention which
    -    is almost always the correct convention.
    -    </p>
    -
    -    <h4 class="doAnchor" name="loggerElement">Configuring loggers, or
    -    the <code>&lt;logger></code> element</h4>
    -
    -    <p>At this point you should have at least some understanding of <a
    -    href="architecture.html#effectiveLevel">level inheritance</a> and
    -    the <a href="architecture.html#basic_selection">basic selection
    -    rule</a>. Otherwise, and unless you are an Egyptologist, logback
    -    configuration will be no more meaningful to you than are
    -    hieroglyphics.
    -    </p>
    -
    -    <p>A logger is configured using the <code>&lt;logger></code>
    -    element. A <code>&lt;logger></code> element takes exactly one
    -    mandatory <span class="attr">name</span> attribute, an optional
    -    <span class="attr">level</span> attribute, and an optional <span
    -    class="attr">additivity</span> attribute, admitting the values
    -    <em>true</em> or <em>false</em>. The value of the <span
    -    class="attr">level</span> attribute admitting one of the
    -    case-insensitive string values TRACE, DEBUG, INFO, WARN, ERROR,
    -    ALL or OFF. The special case-insensitive value <em>INHERITED</em>,
    -    or its synonym <em>NULL</em>, will force the level of the logger
    -    to be inherited from higher up in the hierarchy. This comes in
    -    handy if you set the level of a logger and later decide that
    -    it should inherit its level.
    -    </p>
    -
    -    <p class="highlight"> Note that unlike log4j, logback-classic does
    -    <em>not</em> close nor remove any previously referenced appenders
    -    when configuring a given logger.
    -    </p>
    -
    -    <p>The <code>&lt;logger></code> element may contain zero or more
    -    <code>&lt;appender-ref></code> elements; each appender thus
    -    referenced is added to the named logger. Note that unlike log4j,
    -    logback-classic does <em>not</em> close nor remove any previously
    -    referenced appenders when configuring a given logger.
    -    </p>
    -
    -
    -
    -   <h4 class="doAnchor" name="rootElement">Configuring the root
    -   logger, or the <code>&lt;root></code> element</h4>
    -
    -   <p>The <code>&lt;root></code> element configures the root
    -   logger. It supports a single attribute, namely the <span
    -   class="attr">level</span> attribute. It does not allow any other
    -   attributes because the additivity flag does not apply to the root
    -   logger.  Moreover, since the root logger is already named as
    -   "ROOT", it does not allow a name attribute either. The value of the
    -   level attribute can be one of the case-insensitive strings TRACE,
    -   DEBUG, INFO, WARN, ERROR, ALL or OFF. Note that the level of the
    -   root logger cannot be set to INHERITED or NULL.
    -   </p>
    -
    -   <p class="highlight">Note that unlike log4j, logback-classic does
    -   <em>not</em> close nor remove any previously referenced appenders
    -   when configuring the root logger.</p>
    -
    -   <p> Similarly to the <code>&lt;logger></code> element, the
    -   <code>&lt;root></code> element may contain zero or more
    -   <code>&lt;appender-ref></code> elements; each appender thus
    -   referenced is added to the root logger. Note that unlike log4j,
    -   logback-classic does <em>not</em> close nor remove any previously
    -   referenced appenders when configuring the root logger.  </p>
    -
    -   <h4>Example</h4>
    -
    -   <p>Setting the level of a logger or root logger is as simple as
    -   declaring it and setting its level, as the next example
    -   illustrates. Suppose we are no longer interested in seeing any
    -   DEBUG messages from any component belonging to the
    -   "chapters.configuration" package. The following configuration file
    -   shows how to achieve that.
    -   </p>
    -
    -   <p class="example">Example: Setting the level of a logger
    -   (logback-examples/src/main/resources/chapters/configuration/sample2.xml)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy('sample2');">View as .groovy</span>
    -   <pre id="sample2" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO"/></b>
    -
    -  &lt;!-- Strictly speaking, the level attribute is not necessary since --&gt;
    -  &lt;!-- the level of the root level is set to DEBUG by default.       --&gt;
    -  &lt;root level="DEBUG">		
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>  
    -  
    -&lt;/configuration></pre>
    -
    -  <p>When the above configuration file is given as argument to the
    -  <em>MyApp3</em> application, it will yield the following output:
    -  </p>
    -
    -<pre class="source">17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</pre>
    -
    -  <p>Note that the message of level DEBUG generated by the <a
    -  href="../xref/chapters/configuration/Foo.html">"chapters.configuration.Foo"</a>
    -  logger has been suppressed. See also the Foo class.</p>
    -
    -  <p>You can configure the levels of as many loggers as you wish.  In
    -  the next configuration file, we set the level of the
    -  <em>chapters.configuration</em> logger to INFO but at the same time set the level
    -  of the <em>chapters.configuration.Foo</em> logger to <code>DEBUG</code>.
    -  </p>
    -
    -  <p class="example">Example: Setting the level of multiple loggers
    -  (logback-examples/src/main/resources/chapters/configuration/sample3.xml)</p>
    -  
    -  <span class="asGroovy" onclick="return asGroovy('sample3');">View as .groovy</span>
    -  <pre id="sample3" class="source prettyprint">&lt;configuration>
    -
    -  &lt;appender name="STDOUT"
    -    class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>
    -        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    -     &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO" /></b>
    -  <b>&lt;logger name="chapters.configuration.Foo" level="DEBUG" /></b>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -
    -  <p>Running <code>MyApp3</code> with this configuration file will
    -  result in the following output on the console:
    -  </p>
    -
    -<p class="prettyprint source">17:39:27.593 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -17:39:27.593 [main] DEBUG chapters.configuration.Foo - Did it again!
    -17:39:27.593 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</p>
    -
    -   <p>The table below list the loggers and their levels, after
    -   <code>JoranConfigurator</code> has configured logback with the
    -   <em>sample3.xml</em> configuration file.
    -   </p>
    -
    -   <table class="bodyTable">
    -     <tr>
    -       <th>Logger name</th>
    -       <th>Assigned Level</th>
    -       <th>Effective Level</th>
    -     </tr>
    -     <tr>
    -       <td>root</td>
    -       <td><code>DEBUG</code></td>
    -       <td><code>DEBUG</code></td>
    -     </tr>
    -     <tr class="alt">
    -       <td>chapters.configuration</td>
    -       <td><code>INFO</code></td>
    -       <td><code>INFO</code></td>
    -     </tr>
    -     <tr>
    -       <td>chapters.configuration.MyApp3</td>
    -       <td><code>null</code></td>
    -       <td><code>INFO</code></td>
    -     </tr>
    -     <tr class="alt">
    -       <td>chapters.configuration.Foo</td>
    -       <td><code>DEBUG</code></td>
    -       <td><code>DEBUG</code></td>
    -     </tr>
    -   </table>
    -
    -  <p>It follows that the two logging statements of level
    -  <code>INFO</code> in the <code>MyApp3</code> class as well as the
    -  DEBUG messages in <code>Foo.doIt()</code> are all enabled. Note that
    -  the level of the root logger is always set to a non-null value,
    -  DEBUG by default.
    -  </p>
    -
    -  <p>Let us note that the <a
    -  href="architecture.html#basic_selection">basic-selection rule</a>
    -  depends on the effective level of the logger being invoked, not the
    -  level of the logger where appenders are attached. Logback will first
    -  determine whether a logging statement is enabled or not, and if
    -  enabled, it will invoke the appenders found in the logger hierarchy,
    -  regardless of their level. The configuration file
    -  <em>sample4.xml</em> is a case in point:
    -  </p>
    -
    -  <p class="example">Example: Logger level sample
    -  (logback-examples/src/main/resources/chapters/configuration/sample4.xml)</p>
    -  <span class="asGroovy" onclick="return asGroovy('sample4');">View as .groovy</span>
    -  <pre id="sample4" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT"
    -   class="ch.qos.logback.core.ConsoleAppender">
    -   &lt;encoder>
    -     &lt;pattern>
    -        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO" /></b>
    -
    -  &lt;!-- turn OFF all logging (children can override) -->
    -  &lt;root <b>level="OFF"</b>>
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -
    -  <p>The following table lists the loggers and their levels after
    -  applying the <em>sample4.xml</em> configuration file.
    -  </p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>Logger name</th>
    -      <th>Assigned Level</th>
    -      <th>Effective Level</th>
    -    </tr>
    -    <tr>
    -      <td>root</td>
    -      <td><code>OFF</code></td>
    -      <td><code>OFF</code></td>
    -    </tr>
    -    <tr class="alt">
    -      <td>chapters.configuration</td>
    -      <td><code>INFO</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -    <tr>
    -      <td>chapters.configuration.MyApp3</td>
    -      <td><code>null</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -    <tr class="alt">
    -      <td>chapters.configuration.Foo</td>
    -      <td><code>null</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -  </table>
    -
    -  <p>The ConsoleAppender named <em>STDOUT</em>, the only configured
    -  appender in <em>sample4.xml</em>, is attached to the root logger
    -  whose level is set to <code>OFF</code>. However, running
    -  <em>MyApp3</em> with configuration script <em>sample4.xml</em> will
    -  yield:
    -  </p>
    -
    -  <div class="source"><pre>17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
    -17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.</pre></div>
    -
    -  <p>Thus, the level of the root logger has no apparent effect because
    -  the loggers in <code>chapters.configuration.MyApp3</code> and
    -  <code>chapters.configuration.Foo</code> classes are all enabled for the
    -  <code>INFO</code> level.  As a side note, the <em>chapters.configuration</em>
    -  logger exists by virtue of its declaration in the configuration file
    -  - even if the Java source code does not directly refer to it.
    -  </p>
    -
    -  <h4 class="doAnchor" name="configuringAppenders">Configuring
    -  Appenders</h4>
    -
    -  <p>An appender is configured with the <code>&lt;appender></code>
    -  element, which takes two mandatory attributes <span
    -  class="attr">name</span> and <span class="attr">class</span>.  The
    -  <span class="attr">name</span> attribute specifies the name of the
    -  appender whereas the <span class="attr">class</span> attribute
    -  specifies the fully qualified name of the appender class to
    -  instantiate. The <code>&lt;appender></code> element may contain zero
    -  or one <code>&lt;layout></code> elements, zero or more
    -  <code>&lt;encoder></code> elements and zero or more
    -  <code>&lt;filter></code> elements. Apart from these three common
    -  elements, <code>&lt;appender></code> elements may contain any number
    -  of elements corresponding to JavaBean properties of the appender
    -  class. Seamlessly supporting any property of a given logback
    -  component is one of the major strengths of <a
    -  href="onJoran.html">Joran</a> as discussed in a later chapter. The
    -  following diagram illustrates the common structure. Note that
    -  support for properties is not visible.
    -  </p>
    -
    -  <p align="left">
    -    <img src="images/chapters/configuration/appenderSyntax.png" 
    -         alt="Appender Syntax" title="Appender element syntax"/>
    -  </p>
    -
    -  <p>The <code>&lt;layout></code> element takes a mandatory class
    -  attribute specifying the fully qualified name of the layout class to
    -  instantiate.  As with the <code>&lt;appender></code> element,
    -  <code>&lt;layout></code> may contain other elements corresponding to
    -  properties of the layout instance. Since it's such a common case, if
    -  the layout class is <code>PatternLayout</code>, then the class
    -  attribute can be omitted as specified by <a
    -  href="onJoran.html#defaultClassMapping">default class mapping</a>
    -  rules.
    -  </p>
    -
    -  <p>The <code>&lt;encoder></code> element takes a mandatory class
    -  attribute specifying the fully qualified name of the encoder class
    -  to instantiate. Since it's such a common case, if the encoder class
    -  is <code>PatternLayoutEncoder</code>, then the class attribute can
    -  be omitted as specified by <a
    -  href="onJoran.html#defaultClassMapping">default class mapping</a>
    -  rules.
    -  </p>
    -
    -  <p>Logging to multiple appenders is as easy as defining the various
    -  appenders and referencing them in a logger, as the next
    -  configuration file illustrates:
    -  </p>
    -
    -  <p class="example">Example: Multiple loggers
    -  (logback-examples/src/main/resources/chapters/configuration/multiple.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('multiple');">View as .groovy</span>
    -  <pre id="multiple" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="<b>FILE</b>" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>myApp.log&lt;/file>
    -
    -    &lt;encoder>
    -      &lt;pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender name="<b>STDOUT</b>" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    <b>&lt;appender-ref ref="FILE" /></b>
    -    <b>&lt;appender-ref ref="STDOUT" /></b>
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>These configuration scripts define two appenders called
    -  <em>FILE</em> and <em>STDOUT</em>.  The <em>FILE</em> appender logs
    -  to a file called <em>myApp.log</em>. The encoder for this appender
    -  is a <code>PatternLayoutEncoder</code> that outputs the date, level,
    -  thread name, logger name, file name and line number where the log
    -  request is located, the message and line separator character(s).
    -  The second appender called <code>STDOUT</code> outputs to the
    -  console.  The encoder for this appender outputs only the message
    -  string followed by a line separator.
    -  </p>
    -
    -  <p>The appenders are attached to the root logger by referencing them
    -  by name within an <em>appender-ref</em> element. Note that each
    -  appender has its own encoder. Encoders are usually not designed to
    -  be shared by multiple appenders. The same is true for layouts. As
    -  such, logback configuration files do not provide any syntactical
    -  means for sharing encoders or layouts.
    -  </p>
    -
    -  <h4 class="doAnchor" name="cumulative">Appenders accumulate
    -  </h4>
    -
    -  <p>By default, <b>appenders are cumulative</b>: a logger will log to
    -  the appenders attached to itself (if any) as well as all the
    -  appenders attached to its ancestors.  Thus, attaching the same
    -  appender to multiple loggers will cause logging output to be
    -  duplicated.
    -  </p>
    -
    -  <p class="example">Example: Duplicate appender
    -  (logback-examples/src/main/resources/chapters/configuration/duplicate.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('duplicate');">View as .groovy</span>
    -  <pre id="duplicate" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;logger name="chapters.configuration">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/logger>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>Running <code>MyApp3</code> with <em>duplicate.xml</em> will
    -  yield the following output:
    -  </p>
    -
    -<p class="source">14:25:36.343 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -14:25:36.343 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    -14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    -14:25:36.359 [main] INFO  chapters.configuration.MyApp3 - Exiting application.
    -14:25:36.359 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</p>
    -
    -  <p>Notice the duplicated output. The appender named <em>STDOUT</em>
    -  is attached to two loggers, to root and to
    -  <em>chapters.configuration</em>. Since the root logger is the
    -  ancestor of all loggers and <em>chapters.configuration</em> is the
    -  parent of both <em>chapters.configuration.MyApp3</em> and
    -  <em>chapters.configuration.Foo</em>, each logging request made with
    -  these two loggers will be output twice, once because <em>STDOUT</em>
    -  is attached to <em>chapters.configuration</em> and once because it
    -  is attached to <em>root</em>.
    -  </p>
    -
    -  <p>Appender additivity is not intended as a trap for new users.  It
    -  is quite a convenient logback feature. For instance, you can
    -  configure logging such that log messages appear on the console (for
    -  all loggers in the system) while messages only from some specific
    -  set of loggers flow into a specific appender.
    -  </p>
    -
    -  <p class="example">Example: Multiple appender
    -  (logback-examples/src/main/resources/chapters/configuration/restricted.xml)</p>
    -  <span class="asGroovy" onclick="return asGroovy('restricted');">View as .groovy</span>
    -  <pre id="restricted" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>myApp.log&lt;/file>
    -    &lt;encoder>
    -      &lt;pattern>%date %level [%thread] %logger{10} [%file:%line] %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;logger name="chapters.configuration">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/logger>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>In this example, the console appender will log all the messages
    -  (for all loggers in the system) whereas only logging requests
    -  originating from the <em>chapters.configuration</em> logger and its
    -  children will go into the <em>myApp.log</em> file.
    -  </p>
    -	
    -  <h4 class="doAnchor" name="overrridingCumulativity">Overriding the
    -  default cumulative behaviour</h4>
    -
    -  <p>In case the default cumulative behavior turns out to be
    -  unsuitable for your needs, you can override it by setting the
    -  additivity flag to false.  Thus, a branch in your logger tree may
    -  direct output to a set of appenders different from those of the rest
    -  of the tree.
    -  </p>
    -
    -  <p class="example">Example: Additivity flag
    -  (logback-examples/src/main/resources/chapters/configuration/additivityFlag.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('additivityFlag');">View as .groovy</span>
    -  <pre id="additivityFlag" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>foo.log&lt;/file>
    -    &lt;encoder>
    -      &lt;pattern>%date %level [%thread] %logger{10} [%file : %line] %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;logger name="chapters.configuration.Foo" <b>additivity="false"</b>>
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/logger>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>This example, the appender named <em>FILE</em> is attached to the
    -  <em>chapters.configuration.Foo</em> logger. Moreover, the <em>chapters.configuration.Foo</em>
    -  logger has its additivity flag set to false such that its logging
    -  output will be sent to the appender named <em>FILE</em> but not to
    -  any appender attached higher in the hierarchy. Other loggers remain
    -  oblivious to the additivity setting of the <em>chapters.configuration.Foo</em>
    -  logger.  Running the <code>MyApp3</code> application with the
    -  <em>additivityFlag.xml</em> configuration file will output results
    -  on the console from the <em>chapters.configuration.MyApp3</em> logger.  However,
    -  output from the <em>chapters.configuration.Foo</em> logger will appear in the
    -  <em>foo.log</em> file and only in that file.
    -  </p>
    -
    -  <h3 class="doAnchor" name="contextName">Setting the context
    -  name</h3>
    -
    -  <p>As mentioned <a href="architecture.html#LoggerContext">in an
    -  earlier chapter</a>, every logger is attached to a logger
    -  context. By default, the logger context is called
    -  "default". However, you can set a different name with the help of
    -  the <code>&lt;contextName></code> configuration directive. Note that
    -  once set, the logger context name <a
    -  href="../apidocs/ch/qos/logback/core/ContextBase.html#setName(java.lang.String)">cannot
    -  be changed</a>. Setting the context name is a simple and
    -  straightforward method in order to distinguish between multiple
    -  applications logging to the same target.
    -  </p>
    -  
    -  <p class="example">Example: Set the context name and display it
    -  (logback-examples/src/main/resources/chapters/configuration/contextName.xml)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('contextName');">View as .groovy</span>
    -  <pre id="contextName" class="prettyprint source">&lt;configuration>
    -  <b>&lt;contextName>myAppName&lt;/contextName></b>
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d <b>%contextName</b> [%t] %level %logger{36} - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>This last example illustrates naming of the logger
    -  context. Adding the <a
    -  href="layouts.html#conversionWord">contextName conversion word</a>
    -  in layout's pattern will output the said name.</p>
    -  
    -  <!-- =============================================================== -->
    - 
    -  <h3 class="doAnchor" name="variableSubstitution">Variable
    -  substitution</h3>
    -
    -  <p><span class="label">Note</span> Earlier versions of this document
    -  used the term "property substitution" instead of the term
    -  "variable". Please consider both terms interchangeable although the
    -  latter term conveys a clearer meaning.
    -  </p>
    -
    -  <p>As in many scripting languages, logback configuration files
    -  support definition and substitution of variables. Variables have a
    -  <a href="#scopes">scope</a> (see below). Moreover, variables can be
    -  defined within the configuration file itself, in an external file,
    -  in an external resource or even computed and <a
    -  href="#definingPropsOnTheFly">defined on the fly</a>.
    -  </p>
    -
    -  <p class="highlight">Variable substitution can occur at any point in
    -  a configuration file where a value can be specified.</p>
    -
    -  <p>Variable substitution can occur at any point in a configuration
    -  file where a value can be specified. The syntax of variable
    -  substitution is similar to that of Unix shells. The string between
    -  an opening <em>${</em> and closing <em>}</em> is interpreted as a
    -  reference to the <em>value</em> of the property.  For property
    -  <em>aName</em>, the string "${aName}" will be replaced with the
    -  value held by the <em>aName</em> property.
    -  </p>
    -
    -  
    -
    -  <p>As they often come in handy, the HOSTNAME and CONTEXT_NAME
    -  variables are automatically defined and have context scope. Given
    -  that in some environments it may take some time to compute the
    -  hostname, its value is computed lazily (only when needed). Moreover,
    -  HOSTNAME can be set from within the <a
    -  href="#definingProps">configuration directly.</a>
    -  </p>
    -
    -   <h4 class="doAnchor" name="definingProps">Defining variables</h4>
    -
    -  <p>Variables can be defined one at a time in the configuration file
    -  itself or loaded wholesale from an external properties file or an
    -  external resource. For historical reasons, the XML element for
    -  defining variables is <code>&lt;property&gt;</code> although in
    -  logback 1.0.7 and later the element <code>&lt;variable></code> can
    -  be used interchangeably.</p>
    -
    -  <p>The next example shows a variable declared at the beginning of
    -  the configuration file. It is then used further down the file to
    -  specify the location of the output file.
    -  </p>
    -
    -  <p class="example">Example: Simple Variable substitution
    -  (logback-examples/src/main/resources/chapters/configuration/variableSubstitution1.xml)
    -  </p>
    -
    -  <span class="asGroovy" onclick="return asGroovy('variableSubstitution1');">View as .groovy</span>
    -  <pre id="variableSubstitution1" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;property name="USER_HOME" value="/home/sebastien" /></b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    <b>&lt;file>${USER_HOME}/myApp.log&lt;/file></b>
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>The next example shows the use of a System property to achieve
    -  the same result. The property is not declared in the configuration
    -  file, thus logback will look for it in the System properties. Java
    -  system properties can be set on the command line as shown next:
    -  </p>
    -  
    -  <p class="source">java -DUSER_HOME="/home/sebastien" MyApp2</p>
    -
    -  <p class="example">Example: System Variable substitution
    -  (logback-examples/src/main/resources/chapters/configuration/variableSubstitution2.xml)
    -  </p>
    -  <span class="asGroovy" onclick="return asGroovy('variableSubstitution2');">View as .groovy</span>
    -  <pre id="variableSubstitution2" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    <b>&lt;file>${USER_HOME}/myApp.log&lt;/file></b>
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  
    -  <p>When multiple variables are needed, it may be more convenient to
    -  create a separate file that will contain all the variables. Here is
    -  how one can do such a setup.
    -  </p>
    -
    -  <p class="example">Example: Variable substitution using a
    -  separate file
    -  (logback-examples/src/main/resources/chapters/configuration/variableSubstitution3.xml)
    -  </p>
    -  <span class="asGroovy" onclick="return asGroovy('variableSubstitution3');">View as .groovy</span>
    -  <pre id="variableSubstitution3" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;property file="src/main/java/chapters/configuration/variables1.properties" /></b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -     <b>&lt;file>${USER_HOME}/myApp.log&lt;/file></b>
    -     &lt;encoder>
    -       &lt;pattern>%msg%n&lt;/pattern>
    -     &lt;/encoder>
    -   &lt;/appender>
    -
    -   &lt;root level="debug">
    -     &lt;appender-ref ref="FILE" />
    -   &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p>This configuration file contains a reference to a file named
    -   <em>variables1.properties</em>.  The variables contained in that
    -   file will be read and then defined within local scope. Here is what
    -   the <em>variable.properties</em> file might look like.
    -   </p>
    -
    -   <em>Example: Variable file
    -   (logback-examples/src/main/resources/chapters/configuration/variables1.properties)</em>
    -
    -   <pre class="source">USER_HOME=/home/sebastien</pre>
    -
    -   <p>You may also reference a resource on the class path instead of a
    -   file.</p>
    -
    -  <pre class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;property resource="resource1.properties" /></b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -     <b>&lt;file>${USER_HOME}/myApp.log&lt;/file></b>
    -     &lt;encoder>
    -       &lt;pattern>%msg%n&lt;/pattern>
    -     &lt;/encoder>
    -   &lt;/appender>
    -
    -   &lt;root level="debug">
    -     &lt;appender-ref ref="FILE" />
    -   &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -   <h4 class="doAnchor" name="scopes">Scopes</h4>
    -
    -   <p>A property can be defined for insertion in <em>local scope</em>,
    -   in <em>context scope</em>, or in <em>system scope</em>. Local scope
    -   is the default. Although it is possible to read variables from the
    -   OS environment, it is not possible to write into the OS
    -   environment.</p>
    -
    -
    -   <p><span class="label">local scope</span> A property with local
    -   scope exists from the point of its definition in a configuration
    -   file until the end of interpretation/execution of said
    -   configuration file. As a corollary, each time a configuration file
    -   is parsed and executed, variables in local scope are defined
    -   anew.</p>
    -
    -   <p><span class="label">context scope</span> A property with context
    -   scope is inserted into the context and lasts as long as the context
    -   or until it is cleared.  Once defined, a property in context scope
    -   is part of the context. As such, it is available in all logging
    -   events, including those sent to remote hosts via serialization. 
    -   </p>
    -
    -   <p><span class="label">system scope</span> A property with system
    -   scope is inserted into the JVM's system properties and lasts as
    -   long as the JVM or until it is cleared.
    -   </p>
    - 
    -   <p class="highlight">Properties are looked up in the the local
    -   scope first, in the context scope second, in the system properties
    -   scope third, and in the OS environment last.
    -   </p>
    -
    -   <p>During substitution, properties are looked up in the local scope
    -   first, in the context scope second, in the system properties scope
    -   third, and in the <a
    -   href="http://docs.oracle.com/javase/tutorial/essential/environment/env.html">OS
    -   environment</a> fourth and last.
    -   </p>
    -
    -   <p>The <span class="attr">scope</span> attribute of the
    -   <code>&lt;property></code> element, <code>&lt;define></code>
    -   element or the <code>&lt;insertFromJNDI></code> element can be used
    -   to set the scope of a property. The <span class="attr">scope</span>
    -   attribute admits "local", "context" and "system" strings as
    -   possible values. If not specified, the scope is always assumed to
    -   be "local".
    -   </p>
    -
    -   <p class="example">Example: A variable defined in "context" scope
    -   (logback-examples/src/main/resources/chapters/configuration/contextScopedVariable.xml)
    -   </p>
    -
    -  <span class="asGroovy" onclick="return
    -  asGroovy('contextScopedVariable');">View as .groovy</span>
    -  <pre id="contextScopedVariable" class="prettyprint source">&lt;configuration>
    -
    -  &lt;property <b class="big">scope="context"</b> name="nodeId" value="firstNode" />
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    <b>&lt;file>/opt/${nodeId}/myApp.log&lt;/file></b>
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p>In the above example, given that the <em>nodeId</em> property is
    -   defined in the context scope, it will be available in every logging
    -   event, even those sent to remote hosts via serialization.</p>
    -
    -
    -  <h3 class="doAnchor" name="defaultValuesForVariables">Default values
    -  for variables</h3>
    -
    -  <p>Under certain circumstances, it may be desirable for a variable
    -  to have a default value if it is not declared or its value is
    -  null. As in the <a
    -  href="http://tldp.org/LDP/abs/html/parameter-substitution.html">Bash
    -  shell</a>, default values can be specified using the <b>":-"</b>
    -  operator. For example, assuming the variable named <em>aName</em> is
    -  not defined, <code>"${aName<b>:-golden</b>}"</code> will be
    -  interpreted as "golden".</p>
    -
    -   <h3 class="doAnchor" name="nestedSubst">Nested variables</h3>
    -
    -   <p>Variable nesting is fully supported. Both the name,
    -   default-value and value definition of a variable can reference
    -   other variables.</p>
    -
    -
    -   <h4>value nesting</h4>
    -   <p>The value definition of a variable can contain references to
    -   other variables. Suppose you wish to use variables to specify not
    -   only the destination directory but also the file name, and combine
    -   those two variables in a third variable called "destination". The
    -   properties file shown below gives an example.
    -   </p>
    -
    -   <p class="example">Example: Nested variable references
    -   (logback-examples/src/main/resources/chapters/configuration/variables2.properties)</p>
    -
    -   <pre class="source">USER_HOME=/home/sebastien
    -fileName=myApp.log
    -<b>destination=${USER_HOME}/${fileName}</b></pre>
    -
    -    <p>Note that in the properties file above, "destination" is
    -    composed from two other variables, namely "USER_HOME" and
    -    "fileName".
    -    </p>
    -    
    -    <em>Example: Variable substitution using
    -    a separate file
    -    (logback-examples/src/main/resources/chapters/configuration/variableSubstitution4.xml)</em>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -
    -  &lt;property file="variables2.properties" />
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    <b>&lt;file>${destination}&lt;/file></b>
    -    &lt;encoder>
    -      &lt;pattern>%msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -   <h4>name nesting</h4>
    -
    -   <p>When referencing a variable, the variable name may contain a
    -   reference to another variable. For example, if the variable named
    -   "userid" is assigned the value "alice", then
    -   "${${userid}.password}" references the variable with the name
    -   "alice.password".</p>
    -
    -   <h4>default value nesting</h4>
    -
    -   <p>The default value of a variable can reference a another
    -   variable.  For example, assuming the variable 'id' is unassigned
    -   and the variable 'userid' is assigned the value "alice", then the
    -   expression "${id<b>:-</b>${userid}}" will return "alice".
    -   </p>
    -
    -
    -  <!-- ==============================================================
    -       -->
    -  <h3 class="doAnchor" name="hostname">HOSTNAME property</h3>
    -
    -  <p>As it often comes in handy, the <code>HOSTNAME</code> property is
    -  defined automatically during configuration with context scope.</p>
    -
    -  <!-- ============================================================== -->
    -  <h3 class="doAnchor" name="context_name">CONTEXT_NAME property</h3>
    -
    -  <p>As its name indicates, the <code>CONTEXT_NAME</code> property
    -  corresponds to the name of the current logging context.</p>
    -
    -  <!-- ============================================================== -->
    -  
    -  <h3 class="doAnchor" name="timestamp">Setting a timestamp</h3>
    -
    -  <p>The <em>timestamp</em> element can define a property according to
    -  current date and time. The <em>timestamp</em> element is <a
    -  href="appenders.html#uniquelyNamed">explained in a subsequent
    -  chapter</a>.</p>
    -
    -  <!-- ============================================================== -->
    -  <h3 class="doAnchor" name="definingPropsOnTheFly">Defining
    -  properties on the fly</h3>
    -
    -  <p>You may define properties dynamically using the
    -  <code>&lt;define></code> element. The define element takes two
    -  mandatory attributes: <span class="attr">name</span> and <span
    -  class="attr">class</span>. The <span class="attr">name</span>
    -  attribute designates the name of the property to set whereas the
    -  <span class="attr">class</span> attribute designates any class
    -  implementing the <a
    -  href="../xref/ch/qos/logback/core/spi/PropertyDefiner.html">PropertyDefiner</a>
    -  interface. The value returned by the <code>getPropertyValue</code>()
    -  method of the <code>PropertyDefiner</code> instance will be the
    -  value of the named property. You may also specify a <a
    -  href="#scopes">scope</a> for the named property by specifying a
    -  <span class="attr">scope</span> attribute.
    -  </p>
    -
    -  <p>Here is an example.</p>
    -
    -  <pre class="prettyprint source">&lt;configuration>
    -
    -  &lt;define name="rootLevel" class="a.class.implementing.PropertyDefiner">
    -    &lt;shape>round&lt;/shape>
    -    &lt;color>brown&lt;/color>
    -    &lt;size>24&lt;/size>
    -  &lt;/define>
    - 
    -  &lt;root level="${rootLevel}"/>
    -&lt;/configuration></pre>
    -
    -  <p>In the above example, shape, color and size are properties of
    -  "a.class.implementing.PropertyDefiner". As long as there is a setter
    -  for a given property in your implementation of the
    -  <code>PropertyDefiner</code> instance, logback will inject the
    -  appropriate values as specified in the configuration file. </p>
    -
    - 
    -
    -  <p>At the present time, logback does ships with two fairly simple
    -  implementations of <code>PropertyDefiner</code>.  
    -  </p>
    -
    -  <table class="bodyTable striped">
    -    <tr>
    -      <th>Implementation name</th>
    -      <th>Description</th>
    -    </tr>
    -
    -    <tr>
    -      <td><a
    -      href="../apidocs/ch/qos/logback/core/property/CanonicalHostNamePropertyDefiner.html"><code>CanonicalHostNamePropertyDefiner</code></a>
    -      </td>
    -      <td>Set the named variable to the canonical host name of the
    -      local host. Note that obtaining the canonical host name may take several seconds.
    -      </td>
    -    </tr>
    -    <tr>
    -
    -    <tr>
    -      <td><a
    -      href="../apidocs/ch/qos/logback/core/property/FileExistsPropertyDefiner.html"><code>FileExistsPropertyDefiner</code></a>
    -      </td>
    -      <td>Set the named variable to "true" if the file specified by
    -      <span class="prop">path</span> property exists, to "false"
    -      otherwise.
    -      </td>
    -    </tr>
    -    <tr>
    -      <td><a
    -      href="../apidocs/ch/qos/logback/core/property/FileExistsPropertyDefiner.html"><code>ResourceExistsPropertyDefiner</code></a>
    -      </td>
    -      <td>Set the named variable to "true" if the <span
    -      class="prop">resource</span> specified by the user is available
    -      on the class path, to "false" otherwise.
    -      </td>
    -    </tr>
    -  </table>
    -
    -  <!-- ============================================================== -->
    -
    -  <h3 class="doAnchor" name="conditional">Conditional processing of
    -  configuration files</h3>
    -  
    -  <p>Developers often need to juggle between several logback
    -  configuration files targeting different environments such as
    -  development, testing and production. These configuration files have
    -  substantial parts in common differing only in a few places. To avoid
    -  duplication, logback supports conditional processing of
    -  configuration files with the help of <code>&lt;if></code>,
    -  <code>&lt;then></code> and <code>&lt;else></code> elements so that a
    -  single configuration file can adequately target several
    -  environments. Note that conditional processing requires the <a
    -  href="../setup.html#janino">Janino library</a>.
    -  </p>
    -
    -  <p>The general format for conditional statements is shown below.</p>
    -
    -  <pre class="prettyprint source">
    -   &lt;!-- if-then form -->
    -   &lt;if condition="some conditional expression">
    -    &lt;then>
    -      ...
    -    &lt;/then>
    -  &lt;/if>
    -  
    -  &lt;!-- if-then-else form -->
    -  &lt;if condition="some conditional expression">
    -    &lt;then>
    -      ...
    -    &lt;/then>
    -    &lt;else>
    -      ...
    -    &lt;/else>    
    -  &lt;/if></pre>  
    -
    -  <p>The condition is a Java expression in which only context
    -  properties or system properties are accessible. For a key passed as
    -  argument, the <code>property</code>() or its shorter equivalent
    -  <code>p</code>() methods return the String value of the property.
    -  For example, to access the value of a property with key "k", you
    -  would write <code>property("k")</code> or equivalently
    -  <code>p("k")</code>. If the property with key "k" is undefined, the
    -  property method will return the empty string and not null. This
    -  avoids the need to check for null values.</p>
    -
    -  <p>The <code>isDefined()</code> method can be used to check whether
    -  a property is defined. For example, to check whether the property
    -  "k" is defined you would write <code>isDefined("k")</code>
    -  Similarly, if you need to check whether a property is null, the
    -  <code>isNull()</code> method is provided. Example:
    -  <code>isNull("k")</code>.</p>
    -
    -  <pre class="prettyprint source">&lt;configuration debug="true">
    -
    -  <b>&lt;if condition='property("HOSTNAME").contains("torino")'></b>
    -    <b>&lt;then></b>
    -      &lt;appender name="CON" class="ch.qos.logback.core.ConsoleAppender">
    -        &lt;encoder>
    -          &lt;pattern>%d %-5level %logger{35} - %msg %n&lt;/pattern>
    -        &lt;/encoder>
    -      &lt;/appender>
    -      &lt;root>
    -        &lt;appender-ref ref="CON" />
    -      &lt;/root>
    -    <b>&lt;/then></b>
    -  <b>&lt;/if></b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>${randomOutputDir}/conditional.log&lt;/file>
    -    &lt;encoder>
    -      &lt;pattern>%d %-5level %logger{35} - %msg %n&lt;/pattern>
    -   &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="ERROR">
    -     &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  
    -  <p>Conditional processing is supported <em>anywhere</em> within the
    -  <code>&lt;configuration></code> element. Nested if-then-else
    -  statements are also supported. However, XML syntax is awfully
    -  cumbersome and is ill suited as the foundation of a general purpose
    -  programming language.  Consequently, too many conditionals will
    -  quickly render your configuration files incomprehensible to subsequent
    -  readers, including yourself.
    -  </p>
    -
    -
    -  <!-- ============================================================== -->
    -
    -   <h3 class="doAnchor" name="insertFromJNDI">Obtaining variables from
    -   JNDI</h3>
    -
    -   <p>Under certain circumstances, you may want to make use of
    -   env-entries stored in JNDI. The <code>&lt;insertFromJNDI></code>
    -   configuration directive extracts an env-entry stored in JNDI and
    -   inserts the property in local scope with key specified by the <span
    -   class="attr">as</span> attribute. As all properties, it is possible
    -   to insert the new property into a <a href="#scopes">different
    -   scope</a> with the help of the <em>scope</em> attribute.
    -   </p>
    -
    -   <p class="example">Example: Insert as properties env-entries
    -   obtained via JNDI
    -   (logback-examples/src/main/resources/chapters/configuration/insertFromJNDI.xml)</p>
    -
    -   <pre class="prettyprint source">&lt;configuration>
    -  <b>&lt;insertFromJNDI env-entry-name="java:comp/env/appName" as="<span class="green">appName"</span> /></b>
    -  <b>&lt;contextName><span class="green">${appName}</span>&lt;/contextName></b>
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d ${CONTEXT_NAME} %level %msg %logger{50}%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -  <p>In this last example, the "java:comp/env/appName" env-entry is
    -  inserted as the <span class="variable">appName</span> property. Note
    -  that the <code>&lt;contextName></code> directive sets the context
    -  name based on the value of the <span class="variable">appName</span>
    -  property inserted by the previous <code>&lt;insertFromJNDI></code>
    -  directive.
    -  </p>
    -
    -  <h3 class="doAnchor" name="fileInclusion">File inclusion</h3>
    -
    -  <p>Joran supports including parts of a configuration file from
    -  another file. This is done by declaring a <code>&lt;include></code>
    -  element, as shown below:
    -  </p>
    -
    -  <p class="example">Example: File include
    -  (logback-examples/src/main/resources/chapters/configuration/containingConfig.xml)</p>
    -
    -  <pre class="prettyprint source">&lt;configuration>
    -  <b>&lt;include file="src/main/java/chapters/configuration/includedConfig.xml"/></b>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="includedConsole" />
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -
    -  <p>The target file MUST have its elements nested inside an
    -  <code>&lt;included></code> element. For example, a
    -  <code>ConsoleAppender</code> could be declared as:
    -  </p>
    -
    -  <p class="example">Example: File include
    -  (logback-examples/src/main/resources/chapters/configuration/includedConfig.xml)</p>
    -
    -  <pre class="source"><b class="green big">&lt;included></b>
    -  &lt;appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>"%d - %m%n"&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -<b class="green big">&lt;/included></b></pre>
    -
    -
    -  <p>Again, please note the mandatory
    -  <code class="big green">&lt;included></code> element.</p>
    -
    -  <p>The contents to include can be referenced as a file, as a
    -  resource, or as a URL.</p>
    -
    -  <ul>
    -
    -    <li><b>As a file:</b><br/> To include a file use the <span
    -    class="attr">file</span> attribute. You can use relative paths but
    -    note that the current directory is defined by the application and
    -    is not necessarily related to the path of the configuration
    -    file.</li>
    -
    -    <li><p><b>As a resource:</b><br/> To include a resource, i.e a
    -    file found on the class path, use the <span
    -    class="attr">resource</span> attribute.</p>
    -
    -    <pre class="prettyprint source">&lt;include resource="includedConfig.xml"/></pre>
    -    
    -    </li>
    -
    -    <li><p><b>As a URL:</b><br/> To include the contents of a URL use
    -    the <span class="attr">url</span> attribute.</p>
    -
    -    <pre class="prettyprint source">&lt;include url="http://some.host.com/includedConfig.xml"/></pre>
    -
    -    </li>
    -  </ul>
    -
    -  <p>If it cannot find the file to be included, logback will complain
    -  by printing a status message.  In case the included file is
    -  optional, you can suppress the warning message by setting <span
    -  class="attr">optional</span> attribute to <code>true</code> in the
    -  <code>&lt;include></code> element.</p>
    -
    -
    -  <pre class="prettyprint source">&lt;include optional="true" ..../></pre>
    -
    -  <!-- ==================== ContextListener =================== -->
    -  <h2 class="doAnchor" name="contextListener">Adding a context
    -  listener</h2>
    -
    -  <p>Instances of the <a
    -  href="../xref/ch/qos/logback/classic/spi/LoggerContextListener.html">LoggerContextListener</a>
    -  interface listen to events pertaining to the lifecycle of a logger
    -  context. 
    -  </p>
    -
    -
    -  <p><code>JMXConfigurator</code> is one implementation of the
    -  <code>LoggerContextListener</code> interface. It is described in a <a
    -  href="jmxConfig.html">subsequent chapter</a>.
    -  </p>
    -
    -  <h3 class="doAnchor"
    -  name="LevelChangePropagator">LevelChangePropagator</h3>
    -
    -  <p>As of version 0.9.25, logback-classic ships with <a
    -  href="../xref/ch/qos/logback/classic/jul/LevelChangePropagator.html">LevelChangePropagator</a>,
    -  an implementation of <code>LoggerContextListener</code> which
    -  propagates changes made to the level of any logback-classic logger
    -  onto the java.util.logging framework. Such propagation eliminates
    -  the performance impact of disabled log statements. Instances of <a
    -  href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/logging/LogRecord.html?is-external=true">LogRecord</a>
    -  will be sent to logback (via SLF4J) only for enabled log
    -  statements. This makes it reasonable for real-world applications to
    -  use the <a
    -  href="http://www.slf4j.org/legacy.html#jul-to-slf4j">jul-to-slf4j</a>
    -  bridge.
    -  </p>
    -
    -
    -  <p>The contextListener element can be used to install <code>LevelChangePropagator</code> as shown next.</p>
    -  
    -  <pre class="prettyprint source">&lt;configuration debug="true">
    -  <b>&lt;contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/></b>
    -  .... 
    -&lt;/configuration></pre>
    -
    -  <p>Setting the <span class="option">resetJUL</span> property of
    -  LevelChangePropagator will reset all previous level configurations
    -  of all j.u.l. loggers. However, previously installed handlers will be
    -  left untouched.</p>
    -
    -  <pre class="prettyprint source">&lt;configuration debug="true">
    -  &lt;contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
    -    <b>&lt;resetJUL>true&lt;/resetJUL></b>
    -  &lt;/contextListener>
    -  ....
    -&lt;/configuration></pre>
    -  <p>
    -  </p>
    -  
    -
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/configuration_ja.html b/logback-site/src/site/pages/manual/configuration_ja.html
    deleted file mode 100644
    index 3986396850..0000000000
    --- a/logback-site/src/site/pages/manual/configuration_ja.html
    +++ /dev/null
    @@ -1,1273 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第3章 logbackの設定</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content" class="chapter">
    -      
    -      <h1>第3章 logbackの設定</h1>
    -      
    -    <div class="quote">
    -      <p><em>物事の本質を正確に表現するときは、記号を使うのが最も適している。また、記号によって表現されたものがあれば、思考に費やす労力が驚くほど軽減されるのだ。</em>
    -      </p>
    -      <p>—GOTTFRIED WILHELM LEIBNIZ</p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -
    -    <p>設定スクリプトの例を示しながら、logback の設定方法を説明していきます。logback は Joran という設定フレームワークを利用しています。Joran については<a href="./11-onJoran.html">後の章</a>で紹介します。
    -    </p>
    -
    -
    -    <h2 class="doAnchor" name="auto_configuration">logback の設定</h2>
    -    
    -    <p>アプリケーションコードにロギング要求を埋め込むには、かなりの計画と作業が必要です。調査したところ、だいたいコード全体の4%ほどがロギングのために使われていました。したがって、そこそこの規模のアプリケーションであっても、数千行のロギング行が含まれることになるのです。その数を考えれば、私たちにロギング式を管理するためのツールが必要となる理由が理解できるのではないでしょうか。
    -    </p>
    -
    -    <p>logback はプログラム的に設定することもできるし、XML や Groovy で記述された設定スクリプトで設定することもできます。なお、log4jユーザーのために、<em>log4j.propertiesファイル</em>を<em>logback.xml</em>に変換する<a href="http://logback.qos.ch/translator/">PropertiesTranslator</a>を用意しています。
    -
    -    </p>
    -
    -    <p>さて、logback が設定するところを順番に見ていきましょう。</p>
    -
    -    <ol>
    -      <li>
    -        <p>logback は<a href="./faq.html#configFileLocation">クラスパス</a>上で<em>logback.groovy</em>というファイルを探します。</p>
    -      </li>
    -
    -      <li>
    -        <p>見つからなかったら、今度は<a href="./faq.html#configFileLocation">クラスパス</a>上で<em>logback-test.xml</em>というファイルを探します。</p>
    -      </li>
    -
    -      <li><p>見つからなかったら、今度は<a href="./faq.html#configFileLocation">クラスパス</a>上で<em>logback.xml</em>というファイルを探します。</p>
    -      </li>
    -      
    -      <li><p>何も見つからなかったら、自動的に<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code></a>を使って設定します。ロギング出力は直接コンソールに出力されるようになります。
    -      </p> 
    -      </li> 
    -
    -    </ol>
    -
    -    <p>四番目のステップは、設定ファイルが見つからなかった場合デフォルトの(ごく基本的な)設定をする、ということです。
    -    </p>
    -
    -
    -    <p>もし Maven を使用しているなら、<em>src/test/resources</em> フォルダの下に <em>logback-test.xml</em>を置いている場合でも、それがアーティファクトに入り込まないことは Maven によって保証されます。したがって、テスト用と実際の環境用で、<em>logback-test.xml</em>と<em>logback.xml</em>を使い分けることができます。Antでも原則的に同じことができます。
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="automaticConf">logback の自動設定</h3>
    -
    -    <p>logback を設定する一番簡単な方法は、デフォルト設定を使わせることです。仮に<code>MyApp1</code>というアプリケーションがあるつもりで、これがどのように行われるのか詳しく見てみましょう。
    -    </p>
    -
    -    <p class="example">例:<code>BasicConfigurator</code>の使用例(<a href="http://logback.qos.ch/xref/chapters/configuration/MyApp1.html">logback-examples/src/main/java/chapters/configuration/MyApp1.java</a>)</p>
    -
    -    <pre class="prettyprint source">package manual.configuration;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -public class MyApp1 {
    -  final static Logger logger = LoggerFactory.getLogger(MyApp1.class);
    -
    -  public static void main(String[] args) {
    -    logger.info("Entering application.");
    -
    -    Foo foo = new Foo();
    -    foo.doIt();
    -    logger.info("Exiting application.");
    -  }
    -}</pre>
    -
    -  <p>このクラスでは、ロガーを静的クラスメンバ変数として定義しています。その後、<code>Foo</code>クラスのオブジェクトを生成します。<code>Foo</code>クラスの定義は次のとおりです。</p>
    -
    -  <p class="example">例:ロギングするクラスの例(<a href="http://logback.qos.ch/xref/chapters/configuration/Foo.html">logback-examples/src/main/java/chapters/configuration/Foo.java</a>)
    -  </p>
    -
    -  <pre class="prettyprint source">package chapters.configuration;
    -  
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -   
    -public class Foo {
    -  static final Logger logger = LoggerFactory.getLogger(Foo.class);
    -  
    -  public void doIt() {
    -    logger.debug("Did it again!");
    -  }
    -}</pre>
    -
    -
    -    <p>この章で紹介しているサンプルプログラムを実行するには、クラスパスに所定のjarファイルを置いておかなければなりません。詳しくは<a href="http://logback.qos.ch/setup.html">セットアップ</a>の説明をを参照してください。
    -    </p>
    -
    -    <p><em>logback-test.xml</em>も<em>logback.xml</em>も無い場合、logback は<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/BasicConfigurator.html"><code>BasicConfigurator</code></a>によって最小限の設定を行います。最小限の設定としてやることは、ルートロガーに<code>ConsoleAppender</code>を割り当てることだけです。出力は<code>PatternLayoutEncoder</code>によって書式化されます。<code>PatternLayoutEncoder</code>には、<em>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</em>という書式化パターンが設定されています。また、ルートロガーにはデフォルトで<code>DEBUG</code>レベルが割り当てられます。
    -    </p>
    -
    -    <p><em>java chapters.configuration.MyApp1</em>を実行すると次のような出力が得られます。</p>
    -
    -    <p class="source">16:06:09.031 [main] INFO  chapters.configuration.MyApp1 - Entering application.
    -16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!
    -16:06:09.046 [main] INFO  chapters.configuration.MyApp1 - Exiting application.</p>
    -
    -
    -   <p class="highlight">(あるとしたら)logback を設定するコード以外に、クライアントコードは一切logbackに依存しません。ロギングフレームワークとしてlogbackを使用するアプリケーションがコンパイル時に依存するのはSLF4Jであって、logbackではありません。
    -   </p>
    -
    -   <p><code>MyApp1</code>アプリケーションとlogbackのリンクは、アプリケーションが呼び出した<code>org.slf4j.LoggerFactory</code>クラスと<code>org.slf4j.Logger</code>クラスによって行われます。これらのクラスは、使用するロガーを(クラスローダーから?)取得し、つなぎあわせます。<code>Foo</code>クラスからlogbackへの依存は、<code>org.slf4j.LoggerFactory</code>と<code>org.slf4j.Logger</code>のimportを介した間接的なものであることに注意しましょう。SLF4Jはあらゆるロギングフレームワークから利用するための抽象層を備えています。おかで、ほとんどのコードをあるロギングフレームワークから別のロギングフレームワークに移行するのがとても簡単になっています。
    -   </p>
    -
    -   <h3><em>logback-test.xml</em>または<em>logback.xml</em>による自動設定</h3>
    -
    -   <p>前述したとおり、logback はクラスパス上で<em>logback-test.xml</em>や<em>logback.xml</em>を探して自分を設定しようとします。次に示す設定ファイルは、<code>BasicConfigurator</code>によって行われるのとまったく同じ内容の設定です。
    -   </p>
    -
    -   <p class="example">例:基本的な設定ファイル(<a href="http://logback.qos.ch/xref/chapters/configuration/sample0.xml">logback-examples/src/main/java/chapters/configuration/sample0.xml</a>)</p>
    -   <span class="asGroovy" onclick="return asGroovy(&#39;sample0&#39;);">Groovyで見る</span>
    -
    -
    -  <pre id="sample0" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p>ファイル名を<em>sample0.xml</em>から<em>logback.xml</em>(または<em>logback-test.xml</em>)に変更して、クラスパスに含まれるフォルダに置きましょう。そして<em>MyApp1</em>アプリケーションを実行すると、前の実行例と同じ出力結果が得られるはずです。</p>
    -
    -   <h4 class="doAnchor" name="automaticStatusPrinting">警告やエラーが発生した際、ステータスメッセージを自動的に出力する</h4>
    -
    -   <p class="highlight">設定ファイルを解析している間に警告やエラーが発生した場合、logback は内部状態を表すメッセージを自動的にコンソールに出力します。</p>
    - 
    -   <p>出力が重複することを避けるため、利用者が明示的にステータスリスナーを登録していた場合(後で例を示します)ステータスの自動出力は無効になります。</p>
    -
    -   <p>警告やエラーが起きていなくても logback の内部状態を出力させたければ、<code>StatusPrinter</code>クラスの<code>print()</code>メソッドを使えばよいです。次の<em>MyApp2</em>アプリケーションは、内部状態を出力するためのコードが二行追加されていることを除いて<em>MyApp1</em>とまったく同じです。</p>
    -
    -    <p class="example">例:logbackの内部状態を出力する(<a href="http://logback.qos.ch/xref/chapters/configuration/MyApp2.html">logback-examples/src/main/java/chapters/configuration/MyApp2.java</a>)</p>
    -
    -  
    -<pre class="prettyprint lang-java source">
    -  public static void main(String[] args) {
    -    // SLF4J が logback を使うようになっていると想定
    -    <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -    // logback の内部状態を出力
    -    <b>StatusPrinter.print(lc);</b>
    -    ...
    -  }</pre>
    -
    -  <p>何も問題が無ければ、コンソールに次のような出力が表示されます。</p>
    -
    -   <div class="source longline"><pre>17:44:58,578 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback-test.xml]
    -17:44:58,671 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - debug attribute not set
    -17:44:58,671 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
    -17:44:58,687 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
    -17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Popping appender named [STDOUT] from the object stack
    -17:44:58,812 |-INFO in ch.qos.logback.classic.joran.action.LevelAction - root level set to DEBUG
    -17:44:58,812 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[root]
    -
    -17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Entering application.
    -17:44:58.828 [main] DEBUG chapters.configuration.Foo - Did it again!
    -17:44:58.828 [main] INFO  chapters.configuration.MyApp2 - Exiting application.
    -</pre></div>
    -
    -  <p>出力結果の最後の方に、前の例で出力されたものと同じ行があることが分かるでしょう。logback の内部メッセージには気をつけるようにしてください。<code>Status</code>オブジェクトを使うと内部状態に簡単にアクセスできます。
    -  </p>
    -
    -   <p>コード中で<code>StatusPrinter</code>をプログラム的に呼び出す代わりに、設定ファイルで内部ステータスを出力するように指定することができます。警告やエラーが起きなくてもです。そのためには<em>configuration</em>要素の<span class="attr">debug</span>属性を設定します。この要素はXML形式の設定ファイルにおける最上位要素です。後で例を示します。<span class="attr">debug</span>属性はステータス情報にだけしか影響しないことは覚えておいてください。繰り返しますが、logback の他の設定には<em>影響しません</em>。ロガーレベルについても同様です。(たとえそうなっていて欲しくても、ルートロガーのログレベルは<code>DEBUG</code>に<em>なりません</em>。)
    -   </p>
    -
    -
    -   <p class="example">例:基本的な設定ファイルをデバッグモードにする(<a href="http://logback.qos.ch/xref/chapters/configuration/sample1.xml">logbackexamples/src/main/java/chapters/configuration/sample1.xml</a>)</p>
    -   <span class="asGroovy" onclick="return asGroovy(&#39;sample1&#39;);">Groovyで見る</span>
    -
    -   <pre id="sample1" class="prettyprint source">
    -&lt;configuration <span class="big bold">debug="true"</span>&gt; 
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt; 
    -    &lt;!-- encoders are  by default assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder --&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -  
    -   <p>configuration 要素の<code>debug</code>属性が設定されていると、次のような内部ステータスが出力されます。</p>
    -   <ol>
    -     <li>設定ファイルが見つかったかどうか</li>
    -     <li>設定ファイルのXML文法が適格かどうか</li>
    -   </ol>
    -     
    -   <p>これらのいずれかの条件が満たされなかった場合、設定ファイルを読み込むことができないので、Joran には<span class="attr">debug</span>属性を理解することができません。見つかった設定ファイルがXMLとして不適格な場合、logback はエラーと判断し、自動的に内部状態をコンソールに出力します。ですが、設定ファイルが見つからなかった場合、logback は内部状態を自動的に出力することはありません。それだけではエラーと見做すには不十分だからです。<a href="http://logback.qos.ch/xref/chapters/configuration/MyApp2.html"><em>MyApp2</em></a>アプリケーションの例で示したように、<code>StatusPrinter.print()</code>をプログラム的に呼び出していれば、どんな場合でも内部ステータス情報を確実に出力するようになります。</p>
    -
    - 
    -   <p>ステータスメッセージが無くても<span class="label">強制的に</span>内部ステータス情報を出力させていると、不正な<em>logback.xml</em>がどこにあるのか突き止めるのが難しくなります。アプリケーションのソースコードを簡単に変更できない商用環境においては特にそうです。不正な設定ファイルの場所を見つけやすくするには、システムプロパティの"logback.statusListenerClass"(<a href="./03-configuration.html#logback.statusLC">すぐ後で説明します</a>)に<code>StatusListener</code>を指定するとよいでしょう。そうすれば、強制的にステータスメッセージを出力させることができます。"logback.statusListenerClass" システムプロパティを使うと、エラーが発生したときに自動的に出力されるメッセージを抑止することもできます。
    -   </p>
    -
    -   <h3 class="doAnchor" name="configFileProperty">システムプロパティでデフォルトの設定ファイルの場所を指定する</h3>
    -
    -   <p>システムプロパティの<code>"logback.configurationFile"</code>を使えば、デフォルトの設定ファイルの場所を指定することができます。このプロパティにはURL形式の値を指定します。クラスパス上のリソースやアプリケーションの外部のファイルを指定することができます。
    -   </p>
    -
    -   <p class="source">java <b>-Dlogback.configurationFile=/path/to/config.xml</b> chapters.configuration.MyApp1</p>
    -
    -   <p>ファイルの拡張子は ".xml" か ".groovy" でなければならないので注意してください。他の拡張子の場合は無視します。<a href="./03-configuration.html#logback.statusLC">ステータスリスナーを明示的に登録</a>しておくと、設定ファイルの場所を突き止めるのに役立つでしょう。</p>
    -
    -
    -   <h3 class="doAnchor" name="autoScan">設定ファイルが変更されたら自動的に再読み込みする</h3>
    -
    -   <p class="highlight">logback-classic は設定ファイルの変更を監視することができます。そして、なんらか変更があった場合は自動的に再読み込みすることができます。</p>
    -
    -   <p><code>configuration</code>要素の<span class="attr">scan</span>属性を設定すると、設定ファイルの監視と自動的な再読み込みができるようになります。
    -
    -   </p>
    -  
    -   <p class="example">例:設定ファイルの変更の監視と自動的な再読み込み(<a href="http://logback.qos.ch/xref/chapters/configuration/scan1.xml">logback-examples/src/main/java/chapters/configuration/scan1.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;scan1&#39;);">Groovyで見る</span>
    -<pre id="scan1" class="prettyprint source">
    -&lt;configuration <b>scan="true"</b>&gt; 
    -  ... 
    -&lt;/configuration&gt; </pre>
    -
    -
    -   <p>デフォルトでは、設定ファイルを一分毎に監視します。<code>configuration</code>要素の<span class="attr">scanPeriod</span>属性に、監視周期を設定することができます。設定値はミリ秒、秒、分または時間単位で指定することができます。以下に例を示します。</p>
    -
    -  <p class="example">例:デフォルトとは異なる監視周期を指定する(<a href="http://logback.qos.ch/xref/chapters/configuration/scan2.xml">logback-examples/src/main/java/chapters/configuration/scan2.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;scan2&#39;);">Groovyで見る</span>
    -  <pre id="scan2" class="prettyprint source">
    -&lt;configuration scan="true" <b>scanPeriod="30 seconds"</b> &gt; 
    -  ...
    -&lt;/configuration&gt; </pre>
    -
    -   <p>時間の単位を指定しなかった場合ミリ秒として扱われますが、たいていの場合不適切だと思いますので<span class="label">注意</span>してください。時間単位を指定することを忘れないでください。
    -   </p>
    -
    -   <p>舞台裏で起きていることを説明しましょう。scan 属性に true を指定すると、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/turbo/ReconfigureOnChangeFilter.html">ReconfigureOnChangeFilter</a>という<code>TurboFilter</code>の派生クラスがインストールされます。TurboFiltersについては<a href="./filters.html#TurboFilter">後の章</a> で説明します。設定ファイルのスキャンは「内部スレッド」で行います。ロガーの出力メソッドが呼び出されるときは常にスキャンされるということです。(訳注:正確には、出力メソッドが呼び出されるたびにフィルタが評価されるので、その都度 logback のコンテキストに指定された ThreadExecutor にファイルをスキャンするタスクが投入されます。そして、ThreadExecutor の管理するスレッドによって実行されます。)例えば、scan 属性に true が設定されている場合、<code>myLogger</code>というロガーを使った「myLoger.debug("hello");」というロギング式があったら、このロギング式が実行されるたびに<code>ReconfigureOnChangeFilter</code>が呼び出されることになります。さらに言うと、<code>myLogger</code>でDEBUGレベルが無効になっているとしてもフィルターはその都度呼び出されることになります。
    -   </p>
    -
    -   <p class="highlight">設定ファイルを変更すると、何回かロギング処理を実行した後で、かつ、監視周期に基いて決定した遅延時間が経過したら、自動的に再読み込みします。
    -   </p>
    -
    -   <p><em>どんな</em>ロガーであれ、ロガーレベルがどうであれ、出力メソッドを呼び出すと<code>ReconfigureOnChangeFilter</code>も呼び出すことになるので、間違いなく致命的な性能影響があります。したがって、監視周期が経過していようともいまいとも、とてもコストの高い処理になります。性能を改善するため、実際に<code>ReconfigureOnChangeFilter</code>が有効になるのは、<em>N</em>回のロギングごとに一回だけです。あなたのアプリケーションがどれくらいの頻度でロギングするのかにもよるのですが、logback は<em>N</em>の値を実行している環境に合わせて調整します。Nのデフォルト値は16です。CPUを占有するアプリケーションの場合最大で2^16(=65536)にまで増えます。
    -   </p>
    -   
    -   <p>簡単に言うとこうなります。設定ファイルを変更すると、何回かロギング処理を実行した後で、かつ、監視周期に基いて決定した遅延時間が経過したら、自動的に再読み込みします。
    -   </p>
    -
    -   
    -
    -   <h3 class="doAnchor" name="joranDirectly"><code>JoranConfigurator</code>を直接呼び出す</h3>
    -
    -   <p>logback は、logbac-core に含まれる Joran という設定用ライブラリを利用しています。logback のデフォルトの設定の仕組みでは、<code>JoranConfigurator</code>を呼び出して、クラスパス上で見つけたデフォルトの設定ファイルを渡すようになっています。何か理由があってデフォルトの設定の仕組みを上書きしたい場合、<code>JoranConfigurator</code>を直接呼び出すことができます。次に示す<em>MyApp3</em>アプリケーションでは、設定ファイルを引数としてJoranConfiguratorを呼び出しています。</p>
    -   
    -   <p class="example">例:<code>JoranConfigurator</code>を直接呼び出す(<a href="http://logback.qos.ch/xref/chapters/configuration/MyApp3.html">logback-examples/src/mian/java/chapters/configuration/MyApp3.java</a>)</p>
    -
    -<pre class="prettyprint source">package chapters.configuration;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class MyApp3 {
    -  final static Logger logger = LoggerFactory.getLogger(MyApp3.class);
    -
    -  public static void main(String[] args) {
    -    // SLF4J が logback を使うようになっていると想定
    -    <b>LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -    
    -    <b>try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(context);
    -      // デフォルト設定を取り消すために context.reset() を呼び出す
    -      // 複数の設定を積み重ねる場合は呼ばないようにする
    -      context.reset(); 
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -      // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(context);</b>
    -
    -    logger.info("Entering application.");
    -
    -    Foo foo = new Foo();
    -    foo.doIt();
    -    logger.info("Exiting application.");
    -  }
    -}</pre>
    -
    -   <p>アプリケーションは、新しく生成した<code>JoranConfigurator</code>を、有効な<code>LoggerContext</code>に設定してから、ロガーコンテキストを初期化します。そして、アプリケーションの引数として渡された設定ファイルを使ってconfiguratorに設定させています。警告やエラーが発生した場合、内部ステータスが出力されます。複数の設定を積み重ねる場合は、<code>context.reset()</code>を呼び出さないようにしなければなりません。</p>
    -
    -   <h3 class="doAnchor" name="viewingStatusMessages">ステータスメッセージの内容</h3>
    -
    -   <p>logback は <code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/status/StatusManager.html">StatusManager</a></code>オブジェクトに内部ステータス情報を蓄積しています。このオブジェクトは<code>LoggerContext</code>からアクセスすることができます。
    -   </p>
    -
    -   <p><code>StatusManager</code>があれば、logbackのコンテキストに関連付けられた全てのステータス情報にアクセスすることができます。メモリ使用量を妥当なレベルで抑えるため、<code>StatusManager</code>のデフォルト実装ではステータスメッセージを先頭と末尾に分けて格納しています。先頭には最初の<em>H</em>個のステータスメッセージを格納し、末尾には最後の<em>T</em>個のステータスメッセージを格納します。今のところ<em>H</em>も<em>T</em>も150になっていますが、将来的にこの値は変更されるかもしれません。</p>
    -
    -   <p>logback-classic の配布物には、ViewStatusMessagesServlet というサーブレットが含まれています。このサーブレットは、<code>LoggerContext</code>に関連付けられた<code>StatusManager</code>の持つステータスメッセージを、HTML の表で描画します。出力サンプルは次のようになります。
    -   </p>
    -   
    -   <a href="./lbClassicStatus.jpg">
    -     <img src="images/chapters/configuration/lbClassicStatus.jpg" alt="クリックすると拡大します" width="90%">
    -   </a>
    -
    -   <p>Webアプリケーションにこのサーブレットを追加するには、<em>WEB-INF/web.xml</em>に次の行を追加します。</p>
    -
    -   <pre class="prettyprint source">  &lt;servlet&gt;
    -    &lt;servlet-name&gt;ViewStatusMessages&lt;/servlet-name&gt;
    -    &lt;servlet-class&gt;ch.qos.logback.classic.ViewStatusMessagesServlet&lt;/servlet-class&gt;
    -  &lt;/servlet&gt;
    -
    -  &lt;servlet-mapping&gt;
    -    &lt;servlet-name&gt;ViewStatusMessages&lt;/servlet-name&gt;
    -    &lt;url-pattern&gt;/lbClassicStatus&lt;/url-pattern&gt;
    -  &lt;/servlet-mapping&gt;</pre>
    -   
    -   <p><code>ViewStatusMessages</code>サーブレットにアクセスするためのURLは次のようになります。
    -<code>http://host/yourWebapp/lbClassicStatus</code>
    -   </p>
    -
    -   <h3 class="doAnchor" name="statusListener">ステータスメッセージの待ち受け</h3>
    -
    -   <p><code>StatusManager</code>に<code>StatusListener</code>を割り当てて、ステータスメッセージを受け取った応答として直ちに何らかの処理を実行させることができます。ただし、ステータスメッセージを受け取ることができるのは logback の設定が完了した後になります。ステータスリスナーを登録しておくと、人間が介入しなくてもlogbackの内部状態を監視できるようになるので便利です。
    -   </p>
    -
    -   <p>logback の配布物には<code>StatusListener</code>を実装した<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/status/OnConsoleStatusListener.html">OnConsoleStatusListener</a></code>というクラスが含まれています。名前が表すとおり、<em>到着した</em>ステータスメッセージを全てコンソールに出力します。
    -   </p>
    -
    -   <p>これは、StatusManager に<code>OnConsoleStatusListenerを登録する<a href="http://logback.qos.ch/xref/chapters/configuration/AddStatusListenerApp.html">サンプルコード</a>です。</code>
    -   </p>
    -
    -   <pre class="prettyprint source">   LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 
    -   StatusManager statusManager = lc.getStatusManager();
    -   OnConsoleStatusListener onConsoleListener = new OnConsoleStatusListener();
    -   statusManager.add(onConsoleListener);</pre>
    -
    -   <p>ステータスリスナーは、登録された後に発生したステータスメッセージだけしか受け取らないので気をつけてください。それより前のステータスメッセージは受け取りません。ですので、ステータスリスナーの登録を設定ファイルの一番最初の方に置いて、他のディレクティブよりも先に評価されるようにするとよいでしょう。</p>
    -
    -   <p>また、これは一つ以上のステータスリスナーを登録できるということでもあります。例を示します。</p>
    -
    -   <p class="example">例:ステータスリスナーの登録(<a href="http://logback.qos.ch/xref/chapters/configuration/onConsoleStatusListener.xml">logback-examples/src/main/java/chapters/configuration/onConsoleStatusListener.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;onConsoleStatusListener&#39;);">Groovyで見る</span>
    -   <pre id="onConsoleStatusListener" class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;</b>  
    -
    -  ... the rest of the configuration file  
    -&lt;/configuration&gt;</pre>
    -
    -   <h3 class="doAnchor" name="logback.statusLC">システムプロパティ "logback.statusListenerClass"</h3>
    -
    -   <p>ステータスリスナーを登録するには、システムプロパティの "logback.statusListenerClass" に、リスナークラス名を設定する方法もあります。例を示します。</p>
    -
    -   <p class="source">java <b>-Dlogback.statusListenerClass</b>=ch.qos.logback.core.status.OnConsoleStatusListener ...</p>
    -   
    -   <p>logback の配布物にはいくつかのステータスリスナー実装が含まれています。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/status/OnConsoleStatusListener.html">OnConsoleStatusListener</a> は、受け取ったステータスメッセージを、コンソール(例えばSystem.out)に出力します。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/status/OnErrorConsoleStatusListener.html">OnErrorConsoleStatusListener</a> は、受け取ったステータスメッセージをSystem.errに出力します。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/status/NopStatusListener.html">NopStatusListener</a>は、受け取ったステータスメッセージをそのまま捨ててしまいます。</p>
    -   
    -
    -   <p>設定の途中でステータスリスナーを登録したり、システムプロパティの"logback.statusListenerClass" でステータスリスナーを指定した場合は、<a href="./03-configuration.html#automaticStatusPrinting">メッセージステータスの自動出力(エラー発生時)</a>が無効になるので気をつけてください。つまり、<code>NopStatusListener</code>を使うように設定すると、内部ステータスの出力を完全に止めることが出来るのです。</p>
    -   
    -   <p class="source">java <b>-Dlogback.statusListenerClass</b>=ch.qos.logback.core.status.NopStatusListener ...</p>
    -
    -
    -   <h3 class="doAnchor" name="stopContext">logback-classic を止める</h3>
    -
    -   <p>logback-classic が使用しているリソースを解放するには、どんな場合でもlogbackコンテキストを停止することをお勧めします。コンテキストを停止すると、コンテキストに定義されたロガーに割り当てられている全てのアペンダーを閉じて、すべてのアクティブなスレッドを停止します。
    -   </p>
    -
    -<pre class="prettyprint lang-java source">
    -import org.sflf4j.LoggerFactory;
    -import ch.qos.logback.classic.LoggerContext;
    -...
    -
    -// SLF4J が logback を使うようになっていると想定
    -<b>LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -<b>loggerContext.stop();</b></pre>
    -
    -   <p>Webアプリケーションの場合、logback-classic を停止してリソースを開放するために、<code>ServletContextListener</code>の<a href="http://docs.oracle.com/javaee/6/api/javax/servlet/ServletContextListener.html#contextDestroyed(javax.servlet.ServletContextEvent)">contextDestroyed</a>メソッドから上記のようなコードを実行します。
    -
    -   <h2 class="doAnchor" name="syntax">設定ファイルの構文</h2>
    -
    -   <p>ここまでに、たくさんのわかりやすい例を見てきてお分かりのとおり、logback はコードを再コンパイルすること無く、ロギングの振る舞いを変えることができます。アプリケーションの特定の部分でロギングを無効にする、UNIX Syslog デーモンに直接出力する、データベースに出力する、ログの可視化ツールに出力する、別の logback サーバにロギングイベントを転送する、こういった色んな設定が実に簡単にできるようになっています。ロギングイベントの転送について補足すると、ログの転送はローカルサーバーのポリシーに従って行われます。例えば、ロギングイベントを二つ目のリモートlogbackサーバーに転送する、というようなものです。
    -   </p>
    -	
    -   <p>このセクションの残りの部分では、設定ファイルの構文を紹介します。
    -   </p>
    -
    -   <p>すでに何度か読んできたとおり、logback の設定ファイル構文はとても柔軟になっています。そのため、DTD や XML スキーマによって定義することができません。そうは言っても設定ファイルの基本的な構成要素について説明しないと始まらないでしょう。次のように定義されています。<code>configuration</code>要素は、0以上の<code>appender</code>要素、0以上の<code>logger</code>要素、1つの<code>root</code>要素によって構成されています。次の図は、この基本構造を図示したものです。</p>
    -
    -  
    -  <p align="left">
    -    <img src="images/chapters/configuration/basicSyntax.png" alt="基本的な構文" title="基本構成ファイルの構造">
    -  </p>
    -
    -
    -    <p class="highlight">タグ名に大文字と小文字のどちらを使うべきなのかわからないときは、<a href="http://en.wikipedia.org/wiki/CamelCase">キャメルケース規約</a>に従ってもらえれば、ほとんどの場合は合っていると思います。</p>
    -
    -    <h4 class="doAnchor" name="caseSensitivity">タグ名の大文字小文字の区別</h4>
    -   
    -    <p>logback 0.9.17 以降のリリースから、タグ名の大文字小文字は区別しないと明記されるようになりました。例えば、<code>&lt;logger&gt;</code>と<code>&lt;Logger&gt;</code>と<code>&lt;LOGGER&gt;</code>はどれもconfiguration要素の子要素として正しく、同じように解釈されます。XMLとしての適格性ルールは有効なままなので、開きタグを<code>&lt;xyz&gt;</code>として書いたら、閉じタグは<code>&lt;/xyz&gt;</code>でなければなりません。<code>&lt;/XyZ&gt;</code>ではダメです。<a href="./onJoran.html#implicit">暗黙的なルール</a>として、タグ名については最初の一文字以外は大文字小文字を区別するようになっています。したがって、<code>&lt;xyz&gt;</code>と<code>&lt;Xyz&gt;</code>は同じものとみなされますが、<code>&lt;xYz&gt;</code>はダメです。暗黙のルールとは言っても大体は<a href="http://en.wikipedia.org/wiki/CamelCase">キャメルケース</a>に従っています。Javaの世界では一般的な規則です。タグが明示的なアクションと暗黙的なアクションのどちらにも関連付けられていると、それを説明するのは決して簡単なことではありません。ですので、XML タグ名の最初の一文字が大文字小文字を区別するかどうかについて言及しておくことはとても重要なのです。
    -    </p>
    -
    -    <h4 class="doAnchor" name="loggerElement">ロガーの設定について、あるいは、<code>logger</code>要素について</h4>
    -
    -    <p>ここでは、少なくとも<a href="./architecture.html#effectiveLevel">レベルの継承</a>と<a href="./architecture.html#basic_selection">基本的な選択ルール</a>についてある程度理解しておかなければなりません。そうでなければ、あなたがエジブト語の学者でも無い限り、logback の設定ファイルはヒエログラフよりも意味の無いものでしかないでしょう。
    -    </p>
    -
    -    <p>ロガーは<code>logger</code>要素で設定します。<code>logger</code>要素には少なくとも一つの<span class="attr">name属性</span>が必要です。<span class="attr">level属性</span>、そして<em>true</em>または<em>false</em>を指定できる<span class="attr">additivity属性</span>は任意です。<span class="attr">level属性</span>にはTRACE、DEBUG、INFO、WARN、ERROR、ALLまたはOFFのいずれかの文字列を指定できます。大文字小文字は区別しません。大文字小文字を区別しない特別な値として<em>INHERITED</em>またはその同義語として<em>NULL</em>を指定すると、祖先から継承したロガーのレベルを強制的に使用することができます。ロガーのレベルを設定した後で、祖先ロガーのレベルを継承するべきだったことがわかった場合に便利です。
    -    </p>
    -
    -    <p class="highlight">
    -    log4jとは違って、logback-classic はロガーを設定した時に割り当てたどんなアペンダーもくクローズ<em>しない</em>し削除もしないので注意してください。
    -    </p>
    -
    -    <p><code>logger要素</code>には任意個の<code>appender-ref要素</code>を含めることができます。参照しているアペンダーがロガーに割り当てられます。log4jとは違って、logback-classic はロガーを設定した時に割り当てたどんなアペンダーもくクローズ<em>しない</em>し削除もしないので注意してください。
    -    </p>
    -
    -
    -
    -   <h4 class="doAnchor" name="rootElement">ルートロガー、あるいは、<code>root</code>要素について</h4>
    -
    -   <p>ルートロガーは<code>root</code>要素で設定します。たった一つ、<span class="attr">level属性</span>だけを指定できます。ルートロガーにはadditivity属性などの他の属性を指定することはできません。さらに、ルートロガーの名前は予め"ROOT"になっているので、name属性も指定することはできません。level属性にはTRACE、DEBUG、INFO、WARN、ERROR、ALLまたはOFFのいずれかの文字列を指定できます。大文字小文字は区別しません。ルートロガーにはINHERITEDまたはその同義語であるNULLを指定することはできません。</p>
    -
    -   <p class="highlight">log4jとは違って、logback-classic はルートロガーを設定した時に割り当てたどんなアペンダーもくクローズ<em>しない</em>し削除もしないので注意してください。</p>
    -
    -   <p><code>logger要素</code>と同じく、<code>root要素</code>には任意個の<code>appender-ref要素</code>を含めることができます。参照しているアペンダーがロガーに割り当てられます。log4jとは違って、logback-classic はルートロガーを設定した時に割り当てたどんなアペンダーもくクローズ<em>しない</em>し削除もしないので注意してください。
    -</p>
    -
    -   <h4>例</h4>
    -
    -   <p>ロガーあるいはルートロガーにレベルを設定するのは非常に簡単です。例を示します。"chapters.configuration" パッケージのコンポーネントから出力されていたDEBUGメッセージが不要になったとしましょう。次の設定ファイルはそれを実現するための例です。
    -   </p>
    -
    -   <p class="example">例:ロガーのレベルを設定する(<a href="http://logback.qos.ch/xref/chapters/configuration/sample2.xml">logback-examples/src/main/java/chapters/configuration/sample2.xml</a>)</p>
    -
    -   <span class="asGroovy" onclick="return asGroovy(&#39;sample2&#39;);">Groovyで見る</span>
    -   <pre id="sample2" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;!-- encoders are assigned the type
    -         ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO"/&gt;</b>
    -
    -  &lt;!-- 厳密にはここでルートロガーのlevel要素を設定する必要はありません --&gt;
    -  &lt;!-- デフォルトでDEBUGが指定されるからです       --&gt;
    -  &lt;root level="DEBUG"&gt;		
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;  
    -  
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この設定ファイルを<em>MyApp3</em>アプリケーションの引数として渡すと、次のような出力が得られます。</p>
    -
    -<pre class="source">17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -17:34:07.578 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</pre>
    -
    -  <p><a href="http://logback.qos.ch/xref/chapters/configuration/Foo.html">"chapters.configuration.Foo</a>"のロガーが出力していたDEBUGメッセージが抑止されていることに気づきましたか。気になるならFooクラスを見なおしてください。</p>
    -
    -  <p>あらゆるロガーのレベルを思った通りに設定することができます。次の設定ファイルでは、<em>chapters.configuration</em>ロガーのレベルをINFOに設定していますが、同時に<em>chapters.configuration.Foo</em>のレベルを<code>DEBUG</code>に設定しています。</p>
    -
    -  <p class="example">例:複数のロガーのレベルを設定する(<a href="http://logback.qos.ch/xref/chapters/configuration/sample3.xml">logback-examples/src/main/java/chapters/configuration/sample3.xml</a>)</p>
    -  
    -  <span class="asGroovy" onclick="return asGroovy(&#39;sample3&#39;);">Groovyで見る</span>
    -  <pre id="sample3" class="source prettyprint">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT"
    -    class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    -     &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO" /&gt;</b>
    -  <b>&lt;logger name="chapters.configuration.Foo" level="DEBUG" /&gt;</b>
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -  <p><code>MyApp3</code>アプリケーションの引数にこの設定ファイルを指定して実行すると、コンソールには次のように出力されます。</p>
    -
    -<p class="prettyprint source">17:39:27.593 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -17:39:27.593 [main] DEBUG chapters.configuration.Foo - Did it again!
    -17:39:27.593 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</p>
    -
    -   <p><code>JoranConfiguration</code>が<em>sample3.xml</em>を設定した後のロガーとそのレベルを表にまとめました。
    -   </p>
    -
    -   <table class="bodyTable">
    -     <tr>
    -       <th>ロガー名</th>
    -       <th>割り当てられたレベル</th>
    -       <th>有効レベル</th>
    -     </tr>
    -     <tr>
    -       <td>root</td>
    -       <td><code>DEBUG</code></td>
    -       <td><code>DEBUG</code></td>
    -     </tr>
    -     <tr class="alt">
    -       <td>chapters.configuration</td>
    -       <td><code>INFO</code></td>
    -       <td><code>INFO</code></td>
    -     </tr>
    -     <tr>
    -       <td>chapters.configuration.MyApp3</td>
    -       <td><code>null</code></td>
    -       <td><code>INFO</code></td>
    -     </tr>
    -     <tr class="alt">
    -       <td>chapters.configuration.Foo</td>
    -       <td><code>DEBUG</code></td>
    -       <td><code>DEBUG</code></td>
    -     </tr>
    -   </table>
    -
    -  <p>これは、<code>MyApp3</code>クラスの<code>INFO</code>レベルのロギング式2つと、<code>Foo</code>クラスのdoIt()メソッドにあるDEBUGレベルのロギング式がいずれも有効であるということです。ルートロガーのレベルは常に非null値が指定されること、デフォルトではDEBUGが指定されていることに注意しましょう。
    -  </p>
    -
    -  <p><a href="./architecture.html#basic_selection">基本的な選択ルール</a>についても考えてみましょう。ロギング要求が呼び出されるかどうかは、アペンダーを割り当てられたロガーに指定されたレベルそのものではなく、ロガーの有効レベルによって決定されるというものです。logback はまず最初にロギング式が有効かどうかを判定します。有効な場合は、ロガーのレベルに依らず、ロガー階層で見つかったアペンダーを呼び出します。設定ファイル<em>sample4.xml</em>でその点を確認しましょう。</p>
    -
    -  <p class="example">例:ロガーレベルのサンプル(<a href="http://logback.qos.ch/xref/chapters/configuration/sample4.xml">logback-examples/src/main/java/chapters/configuration/sample4.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;sample4&#39;);">Groovyで見る</span>
    -  <pre id="sample4" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT"
    -   class="ch.qos.logback.core.ConsoleAppender"&gt;
    -   &lt;encoder&gt;
    -     &lt;pattern&gt;
    -        %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  <b>&lt;logger name="chapters.configuration" level="INFO" /&gt;</b>
    -
    -  &lt;!-- turn OFF all logging (children can override) --&gt;
    -  &lt;root <b>level="OFF"</b>&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -  <p><em>sample4.xml</em>を設定した後のロガーとそのレベルを表にまとめました。
    -  </p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>ロガー名</th>
    -      <th>割り当てられたレベル</th>
    -      <th>有効レベル</th>
    -    </tr>
    -    <tr>
    -      <td>root</td>
    -      <td><code>OFF</code></td>
    -      <td><code>OFF</code></td>
    -    </tr>
    -    <tr class="alt">
    -      <td>chapters.configuration</td>
    -      <td><code>INFO</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -    <tr>
    -      <td>chapters.configuration.MyApp3</td>
    -      <td><code>null</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -    <tr class="alt">
    -      <td>chapters.configuration.Foo</td>
    -      <td><code>null</code></td>
    -      <td><code>INFO</code></td>
    -    </tr>
    -  </table>
    -
    -  <p><em>sample4.xml</em>では、ConsoleAppenderに<em>STDOUT</em>という名前を付けて、ルートロガーに割り当てています。ルートロガーのレベルは<code>OFF</code>です。しかし、<em>MyApp3</em>の引数として<em>sample4.xml</em>を指定して実行すると次のような出力になってしまいます。</p>
    -
    -  <div class="source"><pre>17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Entering application.
    -17:52:23.609 [main] INFO chapters.configuration.MyApp3 - Exiting application.</pre></div>
    -
    -  <p>つまり、<code>INFO</code>レベルが有効レベルとなっているロガーである<code>chapters.configuration.MyApp3</code>と<code>chapters.configuration.Foo</code>のどちらも、ルートロガーのレベルによる影響を受けていないのです。蛇足ですが、Java実装の中では<em>chapters.configuration</em>ロガーを直接参照しているところはありませんが、実際に存在しています。設定ファイルで宣言されいるからです。
    -  </p>
    -
    -  <h4 class="doAnchor" name="configuringAppenders">アペンダーの設定</h4>
    -
    -  <p>アペンダーの設定は<code>appender要素</code>で行います。この要素には<span class="attr">name属性</span>と<span class="attr">class属性</span>という二つの必須属性があります。<span class="attr">name属性</span>にはアペンダーの名前を、<span class="attr">class属性</span>にはアペンダーのクラスの完全名を指定します。<code>appender要素</code>には任意個の<code>layout要素</code>と<code>encoder要素</code>と<code>filter要素</code>を含めることができます。<code>appender要素</code>には、これらの一般的な要素とは別に、アペンダークラスのJavaBean規約に従ったプロパティを任意の数だけ含めることができます。logbackのコンポーネントのどんなプロパティでも違和感無く設定できるのは、<a href="./onJoran.html">Joran設定フレームワーク</a>の主な長所です。詳しくは後の章で説明します。一般的な構造を図示したのが次の図です。JavaBeanプロパティをサポートしているようには見えないことがわかるでしょう。
    -  </p>
    -
    -  <p align="left">
    -    <img src="images/chapters/configuration/appenderSyntax.png" alt="アペンダの構文" title="アペンダー要素構文">
    -  </p>
    -
    -  <p><code>layout要素</code>にはclass属性が必須です。レイアウトクラスの完全名を指定します。<code>appender要素</code>と同じく、<code>layout要素</code>にはレイアウトクラスのプロパティを含めることができます。レイアウトクラスとして<code>PatternLayout</code>を指定するのが一般的なので、その場合class属性は省略できるようになっています。これは<a href="./onJoran.html#defaultClassMapping">デフォルトのクラスマッピングルール</a>で定義されています。
    -  </p>
    -
    -  <p><code>encoder要素</code>にはclass属性が必須です。エンコーダクラスの完全名を指定します。エンコーダクラスとして<code>PaternLayoutEncoder</code>を指定するのが一般的なので、その場合class属性を省略できるようになっています。これは<a href="./onJoran.html#defaultClassMapping">デフォルトのクラスマッピングルール</a>で定義されています。
    -  </p>
    -
    -  <p>複数のアペンダーにログを出力するのも、さまざまなアペンダーを定義してロガーから参照するのも簡単です。設定ファイルの例を示しましょう。</p>
    -
    -  <p class="example">例:複数のロガー(<a href="http://logback.qos.ch/xref/chapters/configuration/multiple.xml">logback-examples/src/main/java/chapters/configuration/multiple.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;multiple&#39;);">Groovyで見る</span>
    -  <pre id="multiple" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="<b>FILE</b>" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;myApp.log&lt;/file&gt;
    -
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date %level [%thread] %logger{10} [%file:%line] %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender name="<b>STDOUT</b>" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    <b>&lt;appender-ref ref="FILE" /&gt;</b>
    -    <b>&lt;appender-ref ref="STDOUT" /&gt;</b>
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この設定スクリプトでは、<em>FILE</em>と<em>STDOUT</em>というアペンダーを定義しています。<em>FILE</em>は<em>myApp.log</em>というファイルへログを出力するアペンダーです。このアペンダーのエンコーダーは<code>PatternLayoutEncoder</code>で、日付、ログレベル、スレッド名、ロガー名、ソースコードファイル名、ロギング式の書かれたソースコードファイル中の行番号、メッセージ、改行文字を出力します。二つ目のアペンダーは<code></code>STDOUT[/0}という名前です。これはコンソールにログを出力するアペンダーです。このアペンダーのエンコーダーは、メッセージと改行文字だけを出力します。
    -  </p>
    -
    -  <p>二つのアペンダーはルートロガーに割り当てられています。それぞれ<em>appender-ref要素</em>から名前で参照されています。それぞれのアペンダーにエンコーダーを指定していることに気づきましたか。普通エンコーダーは複数のアペンダーから共有できるように設計されていません。レイアウトにも同じことが言えます。このように、logbackの設定ファイルは、エンコーダーやレイアウトを共有するためのいかなる構文上の手段も提供しません。
    -  </p>
    -
    -  <h4 class="doAnchor" name="cumulative">アペンダーの積み重ね</h4>
    -
    -  <p>デフォルトでは<b>アペンダーは積み重ねられていきます</b>。つまり、ロガーに割り当てられたアペンダーにログを出力するのとまったく同様に、祖先ロガーに割り当てられたアペンダーにもログが出力されます。従って、同じアペンダーを複数のロガーに割り当てると、ログ出力が重複することになります。
    -  </p>
    -
    -  <p class="example">例:アペンダーの重複(<a href="http://logback.qos.ch/xref/chapters/configuration/duplicate.xml">logback-examples/src/main/java/chapters/configuration/duplicate.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;duplicate&#39;);">Groovyで見る</span>
    -  <pre id="duplicate" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;logger name="chapters.configuration"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/logger&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p><code>MyApp3</code>アプリケーションの引数に<em>duplicate.xml</em>を指定して実行すると、次のような出力が得られます。</p>
    -
    -<p class="source">14:25:36.343 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -14:25:36.343 [main] INFO  chapters.configuration.MyApp3 - Entering application.
    -14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    -14:25:36.359 [main] DEBUG chapters.configuration.Foo - Did it again!
    -14:25:36.359 [main] INFO  chapters.configuration.MyApp3 - Exiting application.
    -14:25:36.359 [main] INFO  chapters.configuration.MyApp3 - Exiting application.</p>
    -
    -  <p>ログ出力の重複には気をつけましょう。<em>STDOUT</em>という名前のアペンダーは、ルートロガーと<em>chapters.configuration</em>ロガーに割り当てられています。ルートロガーは全てのロガーの祖先ロガーです。<em>chapters.configuration</em>ロガーは<em>chapters.configuration.MyApp3</em>ロガーと<em>chapters.configuration.Foo</em>ロガーの親ロガーです。子孫にあたるこれら二つのロガーによるロギング要求は二重に出力されます。なぜなら、<em>STDOUT</em>アペンダーが、<em>chapters.configuration</em>ロガーと<em>ルート</em>ロガーの両方に割り当てられているからです。
    -  </p>
    -
    -  <p>アペンダーが積み重ねられるようになっているのは、決して新しい利用者を罠にかけるためではありません。これは非常に便利なフィーチャなのです。例えば、システム中の全てのロガーによるメッセージをコンソールに出力させつつ、特定のロガーのメッセージだけを特別なアペンダーに出力させるように設定することができるのです。
    -  </p>
    -
    -  <p class="example">例:複数のアペンダー(<a href="http://logback.qos.ch/xref/chapters/configuration/restricted.xml">logback-examples/src/main/java/chapters/configuration/restricted.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;restricted&#39;);">Groovyで見る</span>
    -  <pre id="restricted" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;myApp.log&lt;/file&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date %level [%thread] %logger{10} [%file:%line] %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;logger name="chapters.configuration"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/logger&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この例では、システム中の全てのロガーのメッセージはコンソールアペンダーに出力されます。そして、<em>chapters.configuration</em>ロガーとその子孫ロガーからのロギング要求は<em>myApp.log</em>ファイルにも出力されます。
    -  </p>
    -	
    -  <h4 class="doAnchor" name="overrridingCumulativity">デフォルトの積み重ねを止める</h4>
    -
    -  <p>デフォルトの積み重ねがニーズに合わない場合は、aditivity フラグに false を設定して止めることができます。そうすれば、ロガーツリーのある枝から下の部分では、ツリーの他の部分と違って、ロガーに割り当てられたアペンダーにだけ出力するようになります。
    -  </p>
    -
    -  <p class="example">例:aditivity フラグ(<a href="http://logback.qos.ch/xref/chapters/configuration/aditivityFlag.xml">logback-examples/src/main/java/chapters/configuration/aditivityFlag.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;additivityFlag&#39;);">Groovyで見る</span>
    -  <pre id="additivityFlag" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;foo.log&lt;/file&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date %level [%thread] %logger{10} [%file : %line] %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;logger name="chapters.configuration.Foo" <b>additivity="false"</b>&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/logger&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この例では<em>chapters.configuration.Foo</em>ロガーに<em>FILE</em>というアペンダーが割り当てられています。また、<em>chapters.configuration.Foo</em>ロガーのaditivityフラグにfalseが指定されているので、ログは<em>FILE</em>アペンダーにだけ出力されて、ログ階層の先祖に割り当てられているアペンダーには出力されません。他のロガーが<em>chapters.configuration.Foo</em>ロガーのaditivityフラグの設定に気づくことはありません。<code>MyApp3</code>アプリケーションを<em>aditivityFlag.xml</em>を引数として実行すると、コンソールに<em>chapters.configuration.MyApp3</em>ロガーからの出力が表示されます。しかし、<em>chapters.configuration.Foo</em>ロガーの出力が<em>foo.log</em>以外に表れることはありません。
    -  </p>
    -
    -  <h3 class="doAnchor" name="contextName">コンテキスト名の設定</h3>
    -
    -  <p><a href="./architecture.html#LoggerContext">前の章</a>で述べたように、全てのロガーはロガーコンテキストに割り当てられます。ロガーコンテキストのデフォルトの名前は「default」です。しかし、<code>contextNameディレクティブ</code>を使えば別の名前を付けることが出来ます。ロガーコンテキストの名前は一度設定したら<a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/ContextBase.html#setName(java.lang.String)">変更できない</a>ので注意してください。複数のアプリケーションが同じ対象にロギングする場合、アプリケーションを区別しやすくするには、コンテキストに名前を付けるのが簡単でわかりやすい方法です。
    -  </p>
    -  
    -  <p class="example">例:コンテキスト名を設定して表示する(<a href="http://logback.qos.ch/xref/chapters/configuration/contextName.xml">logback-examples/src/main/java/chapters/configuration/contextName.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;contextName&#39;);">Groovyで見る</span>
    -  <pre id="contextName" class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;contextName&gt;myAppName&lt;/contextName&gt;</b>
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d <b>%contextName</b> [%t] %level %logger{36} - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>これはロガーコンテキストに名前を付ける例です。レイアウトのパターンに変換指示語として<a href="./layouts.html#conversionWord">contextName</a>を指定すると、コンテキスト名が出力されます。</p>
    -  
    -  <!-- =============================================================== -->
    - 
    -  <h3 class="doAnchor" name="variableSubstitution">変数の置換</h3>
    -
    -  <p>このドキュメントの以前のバージョンでは、「変数の置換」ではなく「プロパティの置換」と呼んでいました。<span class="label"></span>どちらも相互に置き換えられるくらい同じ意味の言葉ですが、前者のほうが明確に意味を表していると思います。
    -  </p>
    -
    -  <p>よくあるスクリプト言語のように、logbackの設定ファイルは変数を定義して、値と置き換えられるようになっています。変数は設定ファイルの中で定義することもできるし、外部のファイルで定義することもできます。そして外部リソースでも定義することもできるし、何らか計算することさえできます。また、<a href="./03-configuration.html#definingPropsOnTheFly">実行時に定義する</a>こともできます。
    -  </p>
    -
    -  <p class="highlight">値を指定できるところなら設定ファイル中のどこでも変数を置換することができます。</p>
    -
    -  <p>値を指定できるところなら設定ファイル中のどこでも変数を置換することができます。
    -変数の置換をする構文はUnixシェルとよく似ています。<em>${</em>から始まり<em>}</em>で終わる文字列は、<em>プロパティの値</em>への参照と見做されます。<em>aName</em>というプロパティの場合文字列の"${aName}"は、その値に置き換えられます。<em></em>
    -  </p>
    -
    -  <p>変数には<a href="./03-configuration.html#scopes">スコープ</a>があります(後で説明します)。</p>
    -
    -  <p>HOSTNAME変数とCONTEXT_NAME変数はよく使われるので、コンテキストスコープを持つ変数として自動的に定義されます。</p>
    -
    -   <h4 class="doAnchor" name="definingProps">変数の定義</h4>
    -
    -  <p>変数は、設定ファイルや外部のプロパティファイル、あるいは外部リソースを読み込むとき、それぞれで一度だけ定義することができます。歴史的な理由により、<code>property</code>XML要素を使って変数を定義することになっていますが、logback 1.0.7以降では<code>variable要素</code>を使うことも出来ます。これらの要素は完全に互換性があります。</p>
    -
    -  <p>設定ファイルの先頭で変数を定義する例を見てみましょう。定義された変数は、設定ファイルの後半のほうでログ出力用ファイルの位置を指定するために使われています。
    -  </p>
    -
    -  <p class="example">例:変数の置換(<a href="http://logback.qos.ch/xref/chapters/configuration/variableSubstitution1.xml">logback-examples/src/main/java/chapters/configuration/variableSubstitution1.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return asGroovy(&#39;variableSubstitution1&#39;);">Groovyで見る</span>
    -  <pre id="variableSubstitution1" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;property name="USER_HOME" value="/home/sebastien" /&gt;</b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    <b>&lt;file&gt;${USER_HOME}/myApp.log&lt;/file&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>次は、システムプロパティを使って同じことをしてみましょう。設定ファイル中にproperty要素が宣言されていないので、logback はそれをシステムプロパティから探します。Javaのシステムプロパティはコマンドラインで次のように指定します。</p>
    -  
    -  <p class="source">java -DUSER_HOME="/home/sebastien" MyApp2</p>
    -
    -  <p class="example">例:システム変数の置換(<a href="http://logback.qos.ch/xref/chapters/configuration/variableSubstitution2.xml">logback-examples/src/main/java/chapters/configuration/variableSubstitution2.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;variableSubstitution2&#39;);">Groovyで見る</span>
    -  <pre id="variableSubstitution2" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    <b>&lt;file&gt;${USER_HOME}/myApp.log&lt;/file&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  
    -  <p>複数の変数が必要なときは、変数の定義だけを含むファイルを別に用意したほうが便利な場合もあります。構成例を示します。
    -  </p>
    -
    -  <p class="example">例:別ファイルを使用する変数の置換(<a href="http://logback.qos.ch/xref/chapters/configuration/variableSubstitution3.xml">logback-examples/src/main/java/chapters/configuration/variableSubstitution3.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;variableSubstitution3&#39;);">Groovyで見る</span>
    -  <pre id="variableSubstitution3" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;property file="src/main/java/chapters/configuration/variables1.properties" /&gt;</b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -     <b>&lt;file&gt;${USER_HOME}/myApp.log&lt;/file&gt;</b>
    -     &lt;encoder&gt;
    -       &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -     &lt;/encoder&gt;
    -   &lt;/appender&gt;
    -
    -   &lt;root level="debug"&gt;
    -     &lt;appender-ref ref="FILE" /&gt;
    -   &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p>この設定ファイルでは<em>variables1.properties</em>という名前のファイルを参照しています。指定されたファイルで定義された変数がローカルスコープに定義されます。<em>variable.properties</em>の内容は次のようなものです。
    -   </p>
    -
    -   <em>例:変数定義ファイル(logback-examples/src/main/java/chapters/configuration/variables1.properties)</em>
    -
    -   <pre class="source">USER_HOME=/home/sebastien</pre>
    -
    -   <p>ファイルの代わりにクラスパス上のリソースを指定することもできます。</p>
    -
    -  <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;property resource="resource1.properties" /&gt;</b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -     <b>&lt;file&gt;${USER_HOME}/myApp.log&lt;/file&gt;</b>
    -     &lt;encoder&gt;
    -       &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -     &lt;/encoder&gt;
    -   &lt;/appender&gt;
    -
    -   &lt;root level="debug"&gt;
    -     &lt;appender-ref ref="FILE" /&gt;
    -   &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <h4 class="doAnchor" name="scopes">スコープ</h4>
    -
    -   <p><em>ローカルスコープ</em>、<em>コンテキストスコープ</em>、<em>システムスコープ</em>のそれぞれで変数を定義することができます。デフォルトのスコープはローカルスコープです。OSの提供する環境変数の読み込みはできますが、書き込みは出来ません。</p>
    -
    -
    -   <p><span class="label">ローカルスコープ</span>。ローカルスコープで定義された変数は、その変数が定義されている設定ファイルの解釈、実行が終了するまで有効です。当然ながら、設定ファイルを解釈、実行するたびに、ローカルスコープの変数は新しく定義されることになります。</p>
    -
    -   <p><span class="label">コンテキストスコープ</span>。コンテキストスコープで定義された変数は、コンテキストに登録されます。コンテキストが破棄されるまで、あるいは、コンテキストが初期化されるまで有効です。つまり、一度コンテキストスコープで定義された変数はコンテキストの一部となるのです。ですので、全てのロギングイベントから利用できますし、そのイベントをシリアライズして送信した先のリモートホストでも利用できます。
    -   </p>
    -
    -   <p><span class="label">システムスコープ</span>。システムスコープで定義された変数は、JVMのシステムプロパティに登録されます。JVMが停止するか、初期化されるまで有効です。
    -   </p>
    - 
    -   <p class="highlight">最初にローカルスコープで変数を検索します。そしてコンテキストスコープ、システムスコープの順に検索し、最後に<a href="http://docs.oracle.com/javase/tutorial/essential/environment/env.html">OSの環境変数</a>を検索します。
    -   </p>
    -
    -   <p>変数を置換する際、最初にローカルスコープで変数を検索します。そしてコンテキストスコープ、システムスコープの順に検索し、最後に<a href="http://docs.oracle.com/javase/tutorial/essential/environment/env.html">OSの環境変数</a>を検索します。
    -
    -   </p>
    -
    -   <p>変数のスコープは、<code>property要素</code>、<code>define要素</code>、<code>insertFromJNDI要素</code>の<span class="attr">scope属性</span>で指定します。<span class="attr">scope属性</span>に指定できるのは、"local"、"context"、"system" のいずれかの文字列です。scope属性を指定しなかった場合、スコープは常に"local"と見做されます。
    -   </p>
    -
    -   <p class="example">例:変数をコンテキストスコープで定義する(<a href="http://logback.qos.ch/xref/chapters/configuration/contextScopedVariable.xml">logback-examples/src/main/java/chapters/configuration/contextScopedVariable.xml</a>)</p>
    -
    -  <span class="asGroovy" onclick="return
    -  asGroovy(&#39;contextScopedVariable&#39;);">Groovyで見る</span>
    -  <pre id="contextScopedVariable" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;property <b class="big">scope="context"</b> name="nodeId" value="firstNode" /&gt;
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    <b>&lt;file&gt;/opt/${nodeId}/myApp.log&lt;/file&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p>この例では<em>nodeId変数</em>をコンテキストスコープで定義しています。この変数は全てのロギングイベントで有効であり、シリアライズ化して送信した先のリモートホストでも利用できます。</p>
    -
    -
    -  <h3 class="doAnchor" name="defaultValuesForVariables">変数のデフォルト値</h3>
    -
    -  <p>特定の状況では、変数が宣言されていなかったり、値がnullの場合、デフォルト値が使えるようになっているほうが望ましいことがあります。<a href="http://tldp.org/LDP/abs/html/parameter-substitution.html">Bash</a>と同じく、<b>":-"</b>演算子を使ってデフォルト値を指定することができます。例えば、 <em>aName</em>という名前の変数が定義されていない場合、<code>"${aName <b>:-golden</b> }"</code>という文字列を変数置換した結果は"goleden"になります。</p>
    -
    -   <h3 class="doAnchor" name="nestedSubst">変数のネスト</h3>
    -
    -   <p>変数はネストすることができます。変数の名前として、デフォルト値として、値として、他の変数を指定することができます。</p>
    -
    -
    -   <h4>値のネスト</h4>
    -   <p>変数の値を定義するとき、他の変数を指定することができます。例えば、宛先のディレクトリだけでなく、ファイル名も変数で指定したい場合、それぞれの変数をまとめた第三の変数"destination"を定義して、それを利用できるのです。プロパティファイルの例を示します。
    -   </p>
    -
    -   <p class="example">例:変数のネスト(<a href="http://logback.qos.ch/xref/chapters/configuration/variables2.properties">logback-examples/src/main/java/chapters/configuration/variables2.properties</a>)</p>
    -
    -   <pre class="source">USER_HOME=/home/sebastien
    -fileName=myApp.log
    -<b>destination=${USER_HOME}/${fileName}</b></pre>
    -
    -    <p>"destination"変数の定義で、"USER_HOME"変数と"fileName"変数を組み合わせていることが分かりますか。
    -    </p>
    -    
    -    <em>例:別のファイルを使用した変数の置換(logback-examples/src/main/java/chapters/configuration/variableSubstitution4.xml)</em>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;property file="variables2.properties" /&gt;
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    <b>&lt;file&gt;${destination}&lt;/file&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <h4>名前のネスト</h4>
    -
    -   <p>変数を参照するとき、変数の名前として、他の変数を指定することができます。たとえば、"uesrid"変数に"alice"という文字列が指定されていることにしましょう。そうすると、"${${userid}.password}"という文字列は、"alice.password"変数を参照することになるのです。</p>
    -
    -   <h4>デフォルト値のネスト</h4>
    -
    -   <p>変数のデフォルト値に他の変数を指定することができます。たとえば、'id'変数に値が割り当てられておらず、'userid'変数には"alice"という文字列が指定されていることにしましょう。そうすると、"${id<b>:-</b>${userid}}" という文字列は"alice"という文字列になるのです。
    -   </p>
    -
    -
    -  <!-- ==============================================================
    -       -->
    -  <h3 class="doAnchor" name="hostname">HOSTNAME変数</h3>
    -
    -  <p>あると便利なので、<code>HOSTNAME</code>変数は自動的にコンテキストスコープに定義されるようになっています。</p>
    -
    -  <!-- ============================================================== -->
    -  <h3 class="doAnchor" name="context_name">CONTEXT_NAME変数</h3>
    -
    -  <p>変数名のとおり、<code>CONTEXT_NAME</code>変数には、現在のロギングコンテキストの名前が設定されています。</p>
    -
    -  <!-- ============================================================== -->
    -  
    -  <h3 class="doAnchor" name="timestamp">タイムスタンプを設定する</h3>
    -
    -  <p><em>timestamp要素</em>を使うと、現在の日付と時刻に応じた値を持つプロパティを定義することができます。<em>timestamp要素</em>については<a href="./appenders.html#uniquelyNamed">後続の章</a>で説明します。</p>
    -
    -  <!-- ============================================================== -->
    -  <h3 class="doAnchor" name="definingPropsOnTheFly">実行時に変数を定義する</h3>
    -
    -  <p><code>define要素</code>を使うと動的に変数を定義できます。define要素には二つの必須属性<span class="attr">name属性</span>と<span class="attr">class属性</span>があります。<span class="attr">name属性</span>には変数の名前を指定します。<span class="attr">class属性</span>には<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/PropertyDefiner.html">PropertyDefiner</a>インターフェイスを実装したクラスの完全名を指定します。<code>PropertyDefiner</code>の<code>getPropertyValue()</code>メソッドの返す値が、変数の値になります。<span class="attr">scope属性</span>で<a href="./03-configuration.html#scopes">スコープ</a>を指定することもできます。
    -  </p>
    -
    -  <p>以下に例を示します。</p>
    -
    -  <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;define name="rootLevel" class="a.class.implementing.PropertyDefiner"&gt;
    -    &lt;shape&gt;round&lt;/shape&gt;
    -    &lt;color&gt;brown&lt;/color&gt;
    -    &lt;size&gt;24&lt;/size&gt;
    -  &lt;/define&gt;
    - 
    -  &lt;root level="${rootLevel}"/&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この例では、"a.class.implementing.PropertyDefiner"クラスのプロパティとして、shape、color、sizeを指定しています。指定したプロパティのセッターメソッドが<code>PropertyDefiner</code>クラスに定義されていれば、logback は設定ファイルで指定されたプロパティの値をインスタンスに設定することができます。</p>
    -
    - 
    -
    -  <p>logback の配布物には現在のところ非常に単純な二つの<code>PropertyDefiner</code>実装クラスが含まれてます。
    -  </p>
    -
    -  <table class="bodyTable striped">
    -    <tr>
    -      <th>実装クラス名</th>
    -      <th>説明</th>
    -    </tr>
    -
    -    <tr>
    -      <td><a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/property/FileExistsPropertyDefiner.html"><code>FileExistsPropertyDefiner</code></a>
    -      </td>
    -      <td><span class="prop">path</span>プロパティに指定したファイルが存在していれば"true"を、そうでなければ"false"を返します。
    -      </td>
    -    </tr>
    -    <tr>
    -      <td><a href="http://logback.qos.ch/apidocs/ch/qos/logback/core/property/FileExistsPropertyDefiner.html"><code>ResourceExistsPropertyDefiner</code></a>
    -      </td>
    -      <td>クラスパス上に指定された<span class="prop">リソース</span>が存在すれば"true"を、そうでなければ"false"を返します。
    -      </td>
    -    </tr>
    -  </table>
    -
    -  <!-- ============================================================== -->
    -
    -  <h3 class="doAnchor" name="conditional">設定ファイル内の条件分岐</h3>
    -  
    -  <p>開発環境、テスト環境、本番環境のような環境ごとに用意されたlogback設定ファイルと格闘しなければならないことがよくあります。これらの設定ファイルの内容はほとんど同じですが、少しだけ異なる箇所があります。同じような設定ファイルの重複を避けるため、logback は設定ファイル中で条件分岐できるようになっています。<code>if要素</code>、<code>then要素</code>、<code>else要素</code>を使えば、さまざまな環境用のファイルを一つにまとめることができるのです。条件分岐できるようにするため、<a href="http://logback.qos.ch/setup.html#janino">Jainoライブラリ</a>を使用しています。
    -  </p>
    -
    -  <p>条件分岐式の一般的な形式を次に示します。</p>
    -
    -  <pre class="prettyprint source">
    -   &lt;!-- if-then form --&gt;
    -   &lt;if condition="some conditional expression"&gt;
    -    &lt;then&gt;
    -      ...
    -    &lt;/then&gt;
    -  &lt;/if&gt;
    -  
    -  &lt;!-- if-then-else form --&gt;
    -  &lt;if condition="some conditional expression"&gt;
    -    &lt;then&gt;
    -      ...
    -    &lt;/then&gt;
    -    &lt;else&gt;
    -      ...
    -    &lt;/else&gt;    
    -  &lt;/if&gt;</pre>  
    -
    -  <p>condition属性に指定するのはJavaの条件式です。コンテキストスコープとシステムスコープの変数が利用できます。<code>property()</code>メソッドか、その省略形である<code>p()</code>メソッドの引数としてヘンス名を渡すと、その値を文字列として返します。たとえば、"k"という値の変数の値にアクセスするには、<code>property("k")</code>あるいは<code>p("k")</code>という書き方をします。変数"k"が未定義ならproperty()メソッドは空文字列を返します。nullではありません。つまり、nullチェックは不要です。</p>
    -
    -  <p><code>isDefined()</code>メソッドを使うと、変数が定義されているかどうかを確かめることが出来ます。たとえば、変数"k"が定義されているかどうかをチェックするには<code>isDefined("k")</code>と書けばよいです。他にも、nullチェックをするための<code>isNull()</code>メソッドが用意されています。
    -
    -例: <code>isNull("k")</code></p>
    -
    -  <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -
    -  <b>&lt;if condition='property("HOSTNAME").contains("torino")'&gt;</b>
    -    <b>&lt;then&gt;</b>
    -      &lt;appender name="CON" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -        &lt;encoder&gt;
    -          &lt;pattern&gt;%d %-5level %logger{35} - %msg %n&lt;/pattern&gt;
    -        &lt;/encoder&gt;
    -      &lt;/appender&gt;
    -      &lt;root&gt;
    -        &lt;appender-ref ref="CON" /&gt;
    -      &lt;/root&gt;
    -    <b>&lt;/then&gt;</b>
    -  <b>&lt;/if&gt;</b>
    -
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;${randomOutputDir}/conditional.log&lt;/file&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d %-5level %logger{35} - %msg %n&lt;/pattern&gt;
    -   &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="ERROR"&gt;
    -     &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  
    -  <p><code>configuratoin要素</code>の中なら<em>どこにでも</em>条件分岐を置くことが出来ます。if-then-else式をネストすることもできます。しかし、XML文法は非常に面倒ですし、汎用プログラミング言語の基盤には不適切です。したがって、条件分岐が多すぎると、設定ファイルはあっという間に他の人には理解できないものになってしまいます。ましてや、後で自分が読み返しても理解できないでしょう。
    -  </p>
    -
    -
    -  <!-- ============================================================== -->
    -
    -   <h3 class="doAnchor" name="insertFromJNDI">JNDIから変数を取得する</h3>
    -
    -   <p>特定の状況下では、JNDIに格納されたenvの内容を参照したいこともあるでしょう。<code>insertFromJNDIディレクティブ</code>を使うと、JNDIに格納されたenvを取得して、ローカルスコープの変数<span class="attr">として</span>取り込むことができます。他の変数と同じく、<em>scope属性</em>に指定した<a href="./03-configuration.html#scopes">別のスコープ</a>に新しい変数を登録することができます。
    -   </p>
    -
    -   <p class="example">例:JNDI経由で取得したenvを変数として登録する(<a href="http://logback.qos.ch/xref/chapters/configuration/insertFromJNDI.xml">logback-examples/src/main/java/chapters/configuration/insertFromJNDI.xml</a>)</p>
    -
    -   <pre class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;insertFromJNDI env-entry-name="java:comp/env/appName" as="<span class="green">appName"</span> /&gt;</b>
    -  <b>&lt;contextName&gt;<span class="green">${appName}</span>&lt;/contextName&gt;</b>
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d ${CONTEXT_NAME} %level %msg %logger{50}%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -  <p>この例は、env の "java:comp/env/appName"を<span class="variable">appName</span>という名前の変数として登録するものです。<code>contextNameディレクティブ</code>で指定しているコンテキスト名には、その前に定義されている<code>insertFromJNDIディレクティブ</code>で登録した変数を使用していることがわかりますか。
    -  </p>
    -
    -  <h3 class="doAnchor" name="fileInclusion">ファイルの取り込み</h3>
    -
    -  <p>Joranは、設定ファイルの一部として別のファイルを読み込むことができます。そのためには、<code>include要素</code>として宣言します。</p>
    -
    -  <p class="example">例:ファイルの取り込み(<a href="http://logback.qos.ch/xref/chapters/configuration/containingConfig.xml">logback-examples/src/main/java/chapters/configuration/containingConfig.xml</a>)</p>
    -
    -  <pre class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;include file="src/main/java/chapters/configuration/includedConfig.xml"/&gt;</b>
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="includedConsole" /&gt;
    -  &lt;/root&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -  <p>取り込まれるファイルでは、全ての要素が<code>included要素</code>の中に入っていなければなりません。<code>ConsoleAppender</code>を宣言する例を示します。</p>
    -
    -  <p class="example">例:ファイルの取り込み(<a href="http://logback.qos.ch/xref/chapters/configuration/includedConfig.xml">logback-examples/src/main/java/chapters/configuration/includedConfig.xml</a>)</p>
    -
    -  <pre class="source"><b class="green big">&lt;included&gt;</b>
    -  &lt;appender name="includedConsole" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;"%d - %m%n"&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -<b class="green big">&lt;/included&gt;</b></pre>
    -
    -
    -  <p>繰り返しになりますが、取り込まれるファイルでは、<code class="big green">included要素</code>が必須です。</p>
    -
    -  <p>include する対象として指定するのは、外部ファイルだけではなく、クラスパス上のリソースでも、URLでもよいです。</p>
    -
    -  <ul>
    -
    -    <li><b>ファイルの場合:</b><br>ファイルを取り込むには<span class="attr">file属性</span>を使用します。相対パスを指定できますが、カレントディレクトリとして使われるのはアプリケーションが設定したものになります。設定ファイルのパスとは無関係なので注意してください。</li>
    -
    -    <li><p><b>リソースの場合:</b><br>クラスパス上のファイルなどのリソースを取り込むには<span class="attr">resource属性</span>を使用します。</p>
    -
    -    <pre class="prettyprint source">&lt;include resource="includedConfig.xml"/&gt;</pre>
    -    
    -    </li>
    -
    -    <li><p><b>URLの場合:</b><br>URLから取得できるコンテンツを取り込むには<span class="attr">url属性</span>を使用します。</p>
    -
    -    <pre class="prettyprint source">&lt;include url="http://some.host.com/includedConfig.xml"/&gt;</pre>
    -
    -    </li>
    -  </ul>
    -
    -  <p>指定されたファイルが存在しなかった場合、logback はステータスメッセージを出力してそのことを報告します。取り込もうとしているファイルが<span class="attr">任意の</span>ものである場合、<code>include要素のoptional属性に<code>true</code>を指定しておけば、エラーメッセージを抑止することができます。</code></p>
    -
    -
    -  <pre class="prettyprint source">&lt;include optional="true" ..../&gt;</pre>
    -
    -  <!-- ==================== ContextListener =================== -->
    -  <h2 class="doAnchor" name="contextListener">コンテキストリスナーを追加する</h2>
    -
    -  <p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/spi/LoggerContextListener.html">LoggerContextListener</a>インターフェイスのインスタンスは、ロガーコンテキストのライフサイクルに関連するイベントを待ち受けます。
    -  </p>
    -
    -
    -  <p><code>JMXConfigurator</code>は<code>LoggerContextListener</code>インターフェイスの実装の一つです。詳しくは<a href="./jmxConfig.html">後の章</a>で説明します。
    -  </p>
    -
    -  <h3 class="doAnchor" name="LevelChangePropagator">LevelChangePropagator</h3>
    -
    -  <p>バージョン0.9.25から、logback-classic の配布物には<code>LoggerContextListener</code>インターフェイスの実装である<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/jul/LevelChangePropagator.html">LevelChangePropagator</a>が含まれるようになりました。これは、logback-classic のあらゆるロガーのレベルの変更を捉えて、java.util.logging フレームワークに伝播します。ログレベルの変化を伝播するのはコストが高いので、ロギング式を無効にすることで改善されたはずの性能が台無しになってしまいます。<a href="http://download.oracle.com/javase/1.5.0/docs/api/java/util/logging/LogRecord.html?is-external=true">LogRecord</a>のインスタンスはロギング式が有効な場合にだけ、SLF4Jを介してlogbackに渡されます。したがって、実際のアプリケーションでは<a href="http://www.slf4j.org/legacy.html#jul-to-slf4j">jul-to-slf4j</a>ブリッジを使うのが合理的です。
    -  </p>
    -
    -
    -  <p>contextListener要素に<code>LevelChangePropagator</code>を登録するには次のようにします。</p>
    -  
    -  <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -  <b>&lt;contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"/&gt;</b>
    -  .... 
    -&lt;/configuration&gt;</pre>
    -
    -  <p>LevelChangePropagatorのプロパティ<span class="option">resetJUL</span>を指定すると、j.u.l.loggers に指定されていたレベルがすべて初期化されます。ただし、ハンドラーはそのまま残ります。</p>
    -
    -  <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator"&gt;
    -    <b>&lt;resetJUL&gt;true&lt;/resetJUL&gt;</b>
    -  &lt;/contextListener&gt;
    -  ....
    -&lt;/configuration&gt;</pre>
    -  <p>
    -  </p>
    -  
    -
    -
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/encoders.html b/logback-site/src/site/pages/manual/encoders.html
    deleted file mode 100755
    index fcb44f51d3..0000000000
    --- a/logback-site/src/site/pages/manual/encoders.html
    +++ /dev/null
    @@ -1,226 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 5: Encoders</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js" ></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">      
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>    
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -
    -    <div id="content">
    -
    -    <h1>Chapter 5: Encoders</h1>
    -
    -    <a href="encoders_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <div class="quote">
    -      <p><b>ACTION THIS DAY</b> Make sure they have all they want on
    -      extreme priority and report to me that this has been done.
    -      </p>
    -      <p>&mdash;CHURCHILL on October 1941 to General Hastings Ismay in
    -      response to a request for more resources signed by Alan Turing
    -      and his cryptanalyst colleagues at Bletchley Park</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -
    -    <h2 class="doAnchor">What is an encoder</h2>
    -
    -    <p>Encoders are responsible for transforming an event into a byte
    -    array as well as writing out that byte array into an
    -    <code>OutputStream</code>. Encoders were introduced in logback
    -    version 0.9.19. In previous versions, most appenders relied on a
    -    layout to transform an event into a string and write it out using
    -    a <code>java.io.Writer</code>. In previous versions of logback,
    -    users would nest a <code>PatternLayout</code> within
    -    <code>FileAppender</code>. Since logback 0.9.19,
    -    <code>FileAppender</code> and sub-classes <a
    -    href="../codes.html#layoutInsteadOfEncoder">expect an encoder and no
    -    longer take a layout</a>.
    -    </p>
    -
    -    <p>Why the breaking change?</p>
    -
    -    <p>Layouts, as discussed in detail in the next chapter, are only
    -    able to transform an event into a String. Moreover, given that a
    -    layout has no control over when events get written out, layouts
    -    cannot aggregate events into batches. Contrast this with encoders
    -    which not only have total control over the format of the bytes
    -    written out, but also control when (and if) those bytes get written
    -    out.
    -    </p>
    -
    -    <p>At the present time, <code>PatternLayoutEncoder</code> is the
    -    only really useful encoder. It merely wraps a
    -    <code>PatternLayout</code> which does most of the work. Thus, it
    -    may seem that encoders do not bring much to the table except
    -    needless complexity. However, we hope that with the advent of new
    -    and powerful encoders this impression will change.</p>
    -
    -    <h2 class="doAnchor" name="interface">Encoder interface</h2>
    -
    -    <p>Encoders are responsible for transforming an incoming event
    -    into a byte array <b>and</b> writing out the resulting byte array
    -    onto the appropriate <code>OutputStream</code>.  Thus, encoders
    -    have total control of what and when bytes gets written to the
    -    <code>OutputStream</code> maintained by the owning appender. Here
    -    is the <a
    -    href="../xref/ch/qos/logback/core/encoder/Encoder.html">Encoder
    -    interface:</a>
    -   
    -    </p>
    -    <pre class="prettyprint source">package ch.qos.logback.core.encoder;
    -
    -public interface Encoder&lt;E> extends ContextAware, LifeCycle {
    -
    -   /**
    -   * This method is called when the owning appender starts or whenever output
    -   * needs to be directed to a new OutputStream, for instance as a result of a
    -   * rollover.
    -   */
    -  void init(OutputStream os) throws IOException;
    -
    -  /**
    -   * Encode and write an event to the appropriate {@link OutputStream}.
    -   * Implementations are free to defer writing out of the encoded event and
    -   * instead write in batches.
    -   */
    -  void doEncode(E event) throws IOException;
    -
    -
    -  /**
    -   * This method is called prior to the closing of the underling
    -   * {@link OutputStream}. Implementations MUST not close the underlying
    -   * {@link OutputStream} which is the responsibility of the owning appender.
    -   */
    -  void close() throws IOException;
    -}</pre>
    -
    -    <p>As you can see, the <code>Encoder</code> interface consists of
    -    few methods, but surprisingly many useful things can be
    -    accomplished with these methods.
    -    </p>
    -
    -
    -    <h2 class="doAnchor">LayoutWrappingEncoder</h2>
    -
    -    <p>Until logback version 0.9.19, many appenders relied on the
    -    Layout instances to control the format of log output. As there
    -    exists substantial amount of code based on the layout interface,
    -    we needed a way for encoders to inter-operate with layouts. <a
    -    href="../xref/ch/qos/logback/core/encoder/LayoutWrappingEncoder.html">LayoutWrappingEncoder</a>
    -    bridges the gap between encoders and layouts. It implements the
    -    encoder interface and wraps a layout to which it delegates the
    -    work of transforming an event into string.
    -    </p>
    -
    -    <p>Below is an excerpt from the <code>LayoutWrappingEncoder</code>
    -    class illustrating how delegation to the wrapped layout instance
    -    is done.</p>
    -
    - <pre class="prettyprint source">package ch.qos.logback.core.encoder;
    -
    -public class LayoutWrappingEncoder&lt;E> extends EncoderBase&lt;E> {
    -
    -  protected Layout&lt;E> layout;
    -  private Charset charset;
    - 
    -   // encode a given event as a byte[]
    -   public byte[] encode(E event) {
    -     String txt = layout.doLayout(event);
    -     return convertToBytes(txt);
    -  }
    -
    -  private byte[] convertToBytes(String s) {
    -    if (charset == null) {
    -      return s.getBytes();
    -    } else {
    -      return s.getBytes(charset);
    -    }
    -  } 
    -}</pre>
    -
    -    <p>The <code>doEncode</code>() method starts by having the wrapped
    -    layout convert the incoming event into string. The resulting text
    -    string is converted to bytes according to the charset encoding
    -    chosen by the user.</p>
    -    
    -
    -    <h2 class="doAnchor">PatternLayoutEncoder</h2>
    -
    -    <p>Given that <code>PatternLayout</code> is the most commonly used
    -    layout, logback caters for this common use-case with
    -    <code>PatternLayoutEncoder</code>, an extension of
    -    <code>LayoutWrappingEncoder</code> restricted to wrapping
    -    instances of <code>PatternLayout</code>.
    -    </p>
    -
    -    <p>As of logback version 0.9.19, whenever a
    -    <code>FileAppender</code> or one of its sub-classes was configured
    -    with a <code>PatternLayout</code>, a
    -    <code>PatternLayoutEncoder</code> must be used instead. This is
    -    explained in the <a
    -    href="../codes.html#layoutInsteadOfEncoder">relevant entry in the
    -    logback error codes</a>.
    -    </p>
    -
    -     <h4 class="doAnchor" name="immediateFlush"><span class="prop">immediateFlush</span> property</h4>
    -
    -     <p>As of <span class="label notice">logback 1.2.0</span>, the
    -     <span class="prop">immediateFlush</span> property is part of the
    -     enclosing Appender.</p>
    -
    -    <h4 class="doAnchor" name="outputPatternAsHeader">Output pattern
    -    string as header</h4>
    -
    -    <p>In order to facilitate parsing of log files, logback can insert
    -    the pattern used for the log output at the top of log files.  This
    -    feature is <b>disabled</b> by default. It can be enabled by
    -    setting the <span class="prop">outputPatternAsHeader</span>
    -    property to 'true' for relevant
    -    <code>PatternLayoutEncoder</code>. Here is an example:</p>
    -
    -<pre class="prettyprint">&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"> 
    -  &lt;file>foo.log&lt;/file>
    -  &lt;encoder>
    -    &lt;pattern>%d %-5level [%thread] %logger{0}: %msg%n&lt;/pattern>
    -    <b>&lt;outputPatternAsHeader>true&lt;/outputPatternAsHeader></b>
    -  &lt;/encoder> 
    -&lt;/appender></pre>
    -    
    -    <p>This will result output akin to the following in the log file:</p>
    -
    -    <pre>#logback.classic pattern: %d [%thread] %-5level %logger{36} - %msg%n
    -2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hello world
    -2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hi again
    -...</pre>
    -
    -     <p>The line starting with "#logback.classic pattern" is newly
    -     inserted pattern line.</p>
    -
    -    
    -     
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </div>
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/encoders_ja.html b/logback-site/src/site/pages/manual/encoders_ja.html
    deleted file mode 100644
    index d4a61c6faa..0000000000
    --- a/logback-site/src/site/pages/manual/encoders_ja.html
    +++ /dev/null
    @@ -1,175 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"></meta>
    -    <title>第5章 エンコーダ</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -    <h1>第5章 エンコーダー</h1>
    -
    -    <div class="quote">
    -      <p><b>ACTION THIS DAY</b>
    -彼らの要求するものを全て最優先にして、それが終わったら報告してくれ。
    -      </p>
    -      <p>アラン・チューリングと彼の同僚の暗号解読者が署名した破格の予算請求書について、1941年10月、チャーチル首相がヘイスティングス・イスメイ将軍に伝えた言葉</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -
    -    <h2 class="doAnchor">エンコーダーとは何か</h2>
    -
    -    <p>エンコーダーの役割は、ロギングイベントをバイト配列に変換するだけでなく、そのバイト配列を<code>OutputStream</code>に書き込むことです。エンコーダーはlogback 0.9.19から導入されました。以前のバージョンでは、ほとんどのアペンダーはロギングイベントを文字列に変換して<code>java.io.Writer</code>に書き込むのをレイアウトに任せていました。また、以前のバージョンでは、利用者は<code>FileAppender</code>に<code>PatternLayout</code>をネストしていました。logback 0.9.19になってから、<code>FileAppender</code>もその派生クラスも、<a href="http://logback.qos.ch/codes.html#layoutInsteadOfEncoder">エンコーダーを利用するようになり、レイアウトを使わなくなりました</a>。
    -    </p>
    -
    -    <p>どうしてこんな破壊的な変更をしたのか?</p>
    -
    -    <p>詳細は次章で説明しますが、レイアウトにできるのはロギングイベントを文字列に変換することだけです。また、レイアウトはロギングイベントを書き込む間何もできません。ロギングイベントをまとめてバッチ的に処理することができないのです。対照的にエンコーダーはバイト配列を書式化している間だけでなく、書式化されたバイト配列を書き込んでいる間も完全に主導権を握っています。
    -    </p>
    -
    -    <p>現時点では、利便性のあるエンコーダーは<code>PatternLayoutEncoder</code>だけです。単に<code>PatternLayout</code>をラップしただけで、ほとんどの仕事は任せてしまいます。一見すると、エンコーダーは不必要な複雑さだけをもたらしているようにも見えます。しかし、私たちは新しく強力なエンコーダーが登場することによってこういった印象を上書きしてくれることに期待しています。</p>
    -
    -    <h2 class="doAnchor" name="interface">エンコーダーインターフェイス</h2>
    -
    -    <p>エンコーダーの役割は、到着したロギングイベントをバイト配列に変換すること<b>と</b>変換されたバイト配列を適切な<code>OutputStream</code>に書き込むことです。前述したように、エンコーダーは親のアペンダーが管理している<code>OutputStream</code>に対して、いつ、どんなバイト配列を書き込むのか完全に制御することができます。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/Encoder.html">エンコーダのインターフェイス</a>を見てみましょう。
    -
    -    </p>
    -    <pre class="prettyprint source">package ch.qos.logback.core.encoder;
    -
    -public interface Encoder&lt;E&gt; extends ContextAware, LifeCycle {
    -
    -   /**
    -   * This method is called when the owning appender starts or whenever output
    -   * needs to be directed to a new OutputStream, for instance as a result of a
    -   * rollover.
    -   */
    -  void init(OutputStream os) throws IOException;
    -
    -  /**
    -   * Encode and write an event to the appropriate {@link OutputStream}.
    -   * Implementations are free to defer writing out of the encoded event and
    -   * instead write in batches.
    -   */
    -  void doEncode(E event) throws IOException;
    -
    -
    -  /**
    -   * This method is called prior to the closing of the underling
    -   * {@link OutputStream}. Implementations MUST not close the underlying
    -   * {@link OutputStream} which is the responsibility of the owning appender.
    -   */
    -  void close() throws IOException;
    -}</pre>
    -
    -    <p>見ての通り、<code>Encoder</code>インターフェイスには少ししかメソッドが宣言されていません。ですが、これらのメソッドだけで驚くほどたくさんの仕事ができるのです。
    -    </p>
    -
    -
    -    <h2 class="doAnchor">LayoutWrappingEncoder</h2>
    -
    -    <p>logback0.9.19より前は、ほとんどのアペンダーがログの出力形式の面倒をレイアウトにまかせていました。レイアウトインターフェイスを使用するコードが大量に残っているので、レイアウトとエンコーダーを仲介する方法が必要でした。それをするのが<a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/LayoutWrappingEncoder.html">LayoutWrappingEncoder</a>です。LayoutWrappingEncoder はエンコーダーインターフェイスを実装しています。そして、ラップしたレイアウトにロギングイベントを文字列に変換する仕事を委譲します。
    -    </p>
    -
    -    <p><code>LayoutWrappingEncoder</code>のコードを一部抜粋して紹介します。レイアウトインスタンスにどうやって委譲しているのかがわかるでしょうか。。</p>
    -
    - <pre class="prettyprint source">package ch.qos.logback.core.encoder;
    -
    -public class LayoutWrappingEncoder&lt;E&gt; extends EncoderBase&lt;E&gt; {
    -
    -  protected Layout&lt;E&gt; layout;
    -  private Charset charset;
    -  private boolean immediateFlush = true;
    -
    -  public void doEncode(E event) throws IOException {
    -    String txt = layout.doLayout(event);
    -    outputStream.write(convertToBytes(txt));
    -    if (immediateFlush)
    -      outputStream.flush();
    -  }
    -
    -  private byte[] convertToBytes(String s) {
    -    if (charset == null) {
    -      return s.getBytes();
    -    } else {
    -      return s.getBytes(charset);
    -    }
    -  }
    -}</pre>
    -
    -    <p><code>doEncode()</code>メソッドは、まず最初に受け取ったロギングイベントをラップしたレイアウトで文字列に変換します。そして変換結果の文字列を、使用者が指定した文字セットで符号化してバイト配列に変換します。次に、変換されたバイト配列を親アペンダーの<code>OutputStream</code>に書き込みます。デフォルトでは<code>OutputStream</code>をすぐにフラッシュします。ただし、<span class="prop">immediateFlush</span>プロパティに明示的にfalseが指定されているときはフラッシュしません。<span class="prop">immediateFlush</span>プロパティにfalseを指定しておくと、ロギングのスループットが大幅に向上します。設定例については、次の<code>PatternLayoutEncoder</code>のところで紹介します。
    -    </p>
    -
    -
    -    <h2 class="doAnchor">PatternLayoutEncoder</h2>
    -
    -    <p><code>PatternLayout</code>は最も広く使われているレイアウトです。この一般的なユースケースに対応するため、logbackは<code>PatternLayoutEncoder</code>を提供しています。これは、<code>PatternLayout</code>のインスタンスだけをラップするようにした<code>LayoutWrappingEncoder</code>の派生クラスです。
    -    </p>
    -
    -    <p>logback0.9.19の頃は、<code>FileAppender</code>やその派生クラスの設定には必ず<code>PatternLayout</code>が使われていました。今は代わりに<code>PatternLayoutEncoder</code>を使わなければなりません。これについては、<a href="http://logback.qos.ch/codes.html#layoutInsteadOfEncoder">logbackのエラーコード</a>でも説明しています。
    -    </p>
    -
    -     <h4 class="doAnchor" name="immediateFlush"><span class="prop">immediateFlush</span>プロパティ</h4>
    -
    -     <p><code>PatternLayoutEncoder</code> は<a href="http://logback.qos.ch/xref/ch/qos/logback/core/encoder/LayoutWrappingEncoder.html"><code>LayoutWrappingEncoder</code></a>の派生クラスなので、<span class="prop">immediateFlush</span>プロパティを設定することができます。<span class="prop">immediateFlush</span>のデフォルト値は"true"です。出力ストリームをすぐにフラッシュすることで、ロギングイベントがディスクに書き込まれること、アプリケーションが終了するときにちゃんとアペンダーを閉じなかったときでも、ロギングイベントが失われないことを保証することができます。一方、このプロパティに"false"を指定すると、(それが必要なのかどうかはわかりませんが)ロギングのスループットが5倍にまで向上する可能性があります。前述したとおり、<span class="prop">immediateFlush</span>にfalseを指定した場合、アプリケーションが終了するときにちゃんとアペンダーを閉じなかったとき、ディスクに書き込まれていないロギングイベントが失われる可能性があります。
    -     </p>
    -
    -     <p><code>FileAppender</code>に<code>PatternLayoutEncoder</code>を指定した設定例を見てみましょう。<span class="prop">immediateFlush</span>プロパティにはfalseを指定しています。</p>
    -
    -<pre class="prettyprint">&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -  &lt;file&gt;foo.log&lt;/file&gt;
    -  &lt;encoder&gt;
    -    &lt;pattern&gt;%d %-5level [%thread] %logger{0}: %msg%n&lt;/pattern&gt;
    -    &lt;!-- this quadruples logging throughput --&gt;
    -    <b>&lt;immediateFlush&gt;false&lt;/immediateFlush&gt;</b>
    -  &lt;/encoder&gt;
    -&lt;/appender&gt;</pre>
    -
    -
    -    <h4 class="doAnchor" name="outputPatternAsHeader">ヘッダに出力形式を入れる</h4>
    -
    -    <p>ログファイルの解析を容易にするため、logbackはログファイルの先頭にログの出力形式を出力することができます。この機能はデフォルトでは<b>無効</b>になっています。<code>PatternLayoutEncoder</code>の<span class="prop">outputPatternAsHeader</span>プロパティにtrueを指定すれば、有効化することができます。以下に例を示します。</p>
    -
    -<pre class="prettyprint">&lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -  &lt;file&gt;foo.log&lt;/file&gt;
    -  &lt;encoder&gt;
    -    &lt;pattern&gt;%d %-5level [%thread] %logger{0}: %msg%n&lt;/pattern&gt;
    -    <b>&lt;outputPatternAsHeader&gt;true&lt;/outputPatternAsHeader&gt;</b>
    -  &lt;/encoder&gt;
    -&lt;/appender&gt;</pre>
    -
    -    <p>この設定を使うと次のように出力されます。</p>
    -
    -    <pre>#logback.classic pattern: %d [%thread] %-5level %logger{36} - %msg%n
    -2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hello world
    -2012-04-26 14:54:38,461 [main] DEBUG com.foo.App - Hi again
    -...</pre>
    -
    -     <p>先頭行の"#logback.classic pattern"が出力形式として出力されたヘッダです。</p>
    -
    -
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/filters.html b/logback-site/src/site/pages/manual/filters.html
    deleted file mode 100755
    index 01d1a11609..0000000000
    --- a/logback-site/src/site/pages/manual/filters.html
    +++ /dev/null
    @@ -1,1200 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 7: Filters</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" /> 
    -
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -    <h1>Chapter 7: Filters</h1>
    -
    -   <a href="filters_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <div class="quote">
    -      <p><em>Have lots of ideas and throw away the bad ones. You aren't
    -      going to have good ideas unless you have lots of ideas and some
    -      sort of principle of selection.</em></p>
    -      
    -      <p>&mdash;LINUS PAULING</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -		
    -		<p>In the preceding chapters, the <a
    -		href="architecture.html#basic_selection">basic selection rule</a>,
    -		which lies at the heart of logback-classic, has been presented. In
    -		this chapter, additional filtering methods will be introduced.
    -    </p>
    -	
    -
    -    <p>Logback filters are based on ternary logic allowing them to be
    -    assembled or chained together to compose an arbitrarily complex
    -    filtering policy.  They are largely inspired by Linux's iptables.
    -		</p>
    -
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -		<h2>In logback-classic</h2>
    -
    -
    -		<p>Logback-classic offers two types of filters, regular filters
    -		and turbo filters.
    -		</p>
    -		
    -    <h3 class="doAnchor" name="filter">Regular filters</h3>
    -
    -		<p>Regular logback-classic filters extend the <a
    -		href="../xref/ch/qos/logback/core/filter/Filter.html"><code>Filter</code></a>
    -		abstract class which essentially consists of a single
    -		<code>decide()</code> method taking an <code>ILoggingEvent</code>
    -		instance as its parameter.
    -		</p>
    -		
    -
    -		<p>Filters are organized as an ordered list and are based on
    -		ternary logic. The <code>decide(ILoggingEvent event)</code> method
    -		of each filter is called in sequence.  This method returns one of
    -		the <a
    -		href="../xref/ch/qos/logback/core/spi/FilterReply.html"><code>FilterReply</code></a>
    -		enumeration values, i.e. one of <code>DENY</code>,
    -		<code>NEUTRAL</code> or <code>ACCEPT</code>.  If the value
    -		returned by <code>decide</code>() is <code>DENY</code>, then the
    -		log event is dropped immediately without consulting the remaining
    -		filters. If the value returned is <code>NEUTRAL</code>, then the
    -		next filter in the list is consulted. If there are no further
    -		filters to consult, then the logging event is processed normally.
    -		If the returned value is <code>ACCEPT</code>, then the logging
    -		event is processed immediately skipping the invocation of the
    -		remaining filters.
    -    </p>
    -    
    -    <p>In logback-classic, filters can be added to
    -    <code>Appender</code> instances. By adding one or more filters to
    -    an appender, you can filter events by arbitrary criteria, such as
    -    the contents of the log message, the contents of the MDC, the time
    -    of day or any other part of the logging event.
    -    </p>
    -    
    -		<h3 class="doAnchor" name="yourOwnFilter">Implementing your own
    -		Filter</h3>
    -		
    -		<p>Creating your own filter is easy. All you have to do is extend
    -		the <code>Filter</code> abstract class and implement the
    -		<code>decide()</code> method.
    -		</p>
    -		
    -		<p>The SampleFilter class shown below provides an example. Its
    -		<code>decide</code> method returns ACCEPT for logging events
    -		containing the string "sample" in its message field. For other
    -		events, the value NEUTRAL is returned.
    -		</p>
    -		
    -    <em>Example: Basic custom filter (<a
    -    href="../xref/chapters/filters/SampleFilter.html">logback-examples/src/main/java/chapters/filters/SampleFilter.java</a>)</em>
    -    <pre class="prettyprint source">package chapters.filters;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.filter.Filter;
    -import ch.qos.logback.core.spi.FilterReply;
    -
    -public class SampleFilter extends Filter&lt;ILoggingEvent> {
    -
    -  @Override
    -  public FilterReply decide(ILoggingEvent event) {    
    -    if (event.getMessage().contains("sample")) {
    -      return FilterReply.ACCEPT;
    -    } else {
    -      return FilterReply.NEUTRAL;
    -    }
    -  }
    -}</pre>
    -
    -		<p>The configuration files shown next attaches a
    -		<code>SampleFilter</code> to a <code>ConsoleAppender</code>.
    -		</p>
    -
    -    <em>Example: SampleFilter configuration
    -    (logback-examples/src/main/resources/chapters/filters/SampleFilterConfig.xml)</em>
    -    <span class="asGroovy" onclick="return asGroovy('SampleFilterConfig');">View as .groovy</span>
    -    <pre id="SampleFilterConfig" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -
    -    <b>&lt;filter class="chapters.filters.SampleFilter" /></b>
    -
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -	
    -  &lt;root>
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>With the help of Joran, logback's configuration framework,
    -		specifying properties or sub-components to filters is also
    -		easy. After adding the corresponding setter method in the filter
    -		class, specify the value of the property in an xml element named
    -		after the property, nesting it within a <code>&lt;filter></code>
    -		element. 
    -		</p>
    -		
    -		<p>Often, the desired filter logic consists of two
    -		orthogonal parts, a match/mismatch test and a response depending
    -		on the match/mismatch. For example, for a given test, e.g. message
    -		equals "foobar", one filter might respond ACCEPT on match and
    -		NEUTRAL on mismatch, and another filter might respond NEUTRAL on
    -		match and DENY on mismatch. 
    -    </p>
    -
    -    <p>Taking notice of this orthogonality, logback ships with the <a
    -    href="../xref/ch/qos/logback/core/filter/AbstractMatcherFilter.html">
    -    <code>AbstractMatcherFilter</code></a> class which provides a
    -    useful skeleton for specifying the appropriate response on match
    -    and on mismatch, with the help of two properties, named
    -    <em>OnMatch</em> and <em>OnMismatch</em>. Most of the regular
    -    filters included in logback are derived from
    -    <code>AbstractMatcherFilter</code>.
    -    </p>
    -		
    -		<h3 class="doAnchor" name="levelFilter">LevelFilter</h3>
    -		
    -		<p><a href="../xref/ch/qos/logback/classic/filter/LevelFilter.html">
    -		<code>LevelFilter</code></a> filters events based on exact level
    -		matching. If the event's level is equal to the configured level,
    -		the filter accepts or denies the event, depending on the
    -		configuration of the <span class="option">onMatch</span> and <span
    -		class="option">onMismatch</span> properties. Here is a sample
    -		configuration file.
    -		</p>
    -		
    -    <em>Example: Sample LevelFilter configuration
    -    (logback-examples/src/main/resources/chapters/filters/levelFilterConfig.xml)</em>
    -    <span class="asGroovy" onclick="return asGroovy('levelFilterConfig');">View as .groovy</span>
    -    <pre id="levelFilterConfig" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    <b>&lt;filter class="ch.qos.logback.classic.filter.LevelFilter">
    -      &lt;level>INFO&lt;/level>
    -      &lt;onMatch>ACCEPT&lt;/onMatch>
    -      &lt;onMismatch>DENY&lt;/onMismatch>
    -    &lt;/filter></b>
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger{30} - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -    <h3 class="doAnchor" name="thresholdFilter">ThresholdFilter</h3>
    -
    -		<p>The <a
    -		href="../xref/ch/qos/logback/classic/filter/ThresholdFilter.html">
    -		<code>ThresholdFilter</code></a> filters events below the
    -		specified threshold. For events of level equal or above the
    -		threshold, <code>ThresholdFilter</code> will respond NEUTRAL when
    -		its <code>decide</code>() method is invoked. However, events with
    -		a level below the threshold will be denied. Here is a sample
    -		configuration file.
    -		</p>
    -
    -    <em>Example: Sample ThresholdFilter configuration
    -    (logback-examples/src/main/resources/chapters/filters/thresholdFilterConfig.xml)</em>
    -    <span class="asGroovy" onclick="return asGroovy('thresholdFilterConfig');">View as .groovy</span>
    -    <pre id="thresholdFilterConfig"  class="prettyprint source">&lt;configuration>
    -  &lt;appender name="CONSOLE"
    -    class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;!-- deny all events with a level below INFO, that is TRACE and DEBUG -->
    -    <b>&lt;filter class="ch.qos.logback.classic.filter.ThresholdFilter">
    -      &lt;level>INFO&lt;/level>
    -    &lt;/filter></b>
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger{30} - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -    <h2 class="doAnchor" name="evalutatorFilter">EvaluatorFilter</h2>
    -
    -    <p><a
    -    href="../xref/ch/qos/logback/core/filter/EvaluatorFilter.html"><code>EvaluatorFilter</code></a>
    -    is a generic filter encapsulating an
    -    <code>EventEvaluator</code>. As the name suggests, an
    -    <a
    -    href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html">
    -    <code>EventEvaluator</code></a> evaluates whether a given criteria
    -    is met for a given event. On match and on mismatch,
    -    the hosting <code>EvaluatorFilter</code> will return the value
    -    specified by the <span class="option">onMatch</span>
    -    or <span class="option">onMismatch</span> properties respectively.
    -    </p>
    -
    -
    -    <p>Note that <code>EventEvaluator</code> is an abstract class. You
    -    can implement your own event evaluation logic by sub-classing
    -    <code>EventEvaluator</code>.
    -    </p>
    -    
    -
    -    <!-- ======================== GEventEvaluator ========================= -->
    -
    -    <h3 class="doAnchor" name="GEventEvaluator">GEventEvaluator</h3>
    -    
    -    <p><a
    -    href="../xref/ch/qos/logback/classic/boolex/GEventEvaluator.html">GEventEvaluator</a>
    -    is a concrete <a
    -    href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html"><code>EventEvaluator</code></a>
    -    implementation taking arbitrary Groovy language boolean
    -    expressions as the evaluation criteria.  We refer such Groovy
    -    language boolean expressions as "groovy evaluation
    -    expressions". Groovy evaluation expressions enable hitherto
    -    unprecedented flexibility in event
    -    filtering. <code>GEventEvaluator</code> requires the Groovy
    -    runtime. Please see the <a
    -    href="../setup.html#groovy">corresponding section</a> of
    -    the setup document on adding the Groovy runtime to your class
    -    path.
    -    </p>
    -
    -    <p>Evaluation expressions are compiled on-the-fly during the
    -    interpretation of the configuration file. As a user, you do not
    -    need to worry about the actual plumbing. However, it is your
    -    responsibility to ensure that the groovy-language expression is
    -    valid.
    -    </p>
    -
    -    <p>The evaluation expression acts on the current logging event.
    -    Logback automatically inserts the current logging event of type <a
    -    href="../apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvent</a>
    -    as a variable referred to as '<em>event</em>' as well as its
    -    shorthand referred to as '<em>e</em>'. The variables TRACE, DEBUG,
    -    INFO, WARN and ERROR are also exported into the scope of the
    -    expression. Thus, "event.level == DEBUG" and "e.level == DEBUG"
    -    are equivalent and valid groovy expressions which will return
    -    <code>true</code> only if the current logging event's level is
    -    identical to DEBUG. For other comparison operators on levels, the
    -    level field should be converted to integer with the
    -    <code>toInt()</code> operator.
    -    </p>
    -
    -    <p>Here is a more complete example.</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy('GEventEvaluator');">View as .groovy</span>
    -    <pre id="GEventEvaluator"  class="prettyprint source">&lt;configuration>
    -    
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
    -      &lt;evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"> 
    -        &lt;expression>
    -           e.level.toInt() >= WARN.toInt() &amp;amp;&amp;amp;  &lt;!-- Stands for &amp;&amp; in XML -->
    -           !(e.mdc?.get("req.userAgent") =~ /Googlebot|msnbot|Yahoo/ )
    -        &lt;/expression>
    -      &lt;/evaluator>
    -      &lt;OnMismatch>DENY&lt;/OnMismatch>
    -      &lt;OnMatch>NEUTRAL&lt;/OnMatch>
    -    &lt;/filter></b>
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -    <p>The above filter will let events of level WARN and higher go
    -    through onto the console unless the error is generated by Web
    -    crawlers associated with Google, MSN or Yahoo. It does so by
    -    checking whether the MDC associated with the event contains a
    -    value for "req.userAgent" matching the
    -    <code>/Googlebot|msnbot|Yahoo/</code> regular expression. Note
    -    that since the MDC map can be null, we are also using Groovy's <a
    -    href="http://groovy.codehaus.org/Null+Object+Pattern">safe
    -    dereferencing operator</a>, that is the ?. operator. The equivalent
    -    logic would have been much longer if expressed in Java.
    -    </p>
    -    
    -    <p>If you are wondering how the identifier for the user agent was
    -    inserted into the MDC under the 'req.userAgent' key, it behooves
    -    us to mention that logback ships with a servlet filter named <a
    -    href="mdc.html#mis"><code>MDCInsertingServletFilter</code></a>
    -    designed for this purpose. It will be described in a later
    -    chapter.
    -    </p>
    -
    -    <!-- ==================== JaninoEventEvaluator ======================== -->
    -    
    -    <h3 class="doAnchor"
    -    name="JaninoEventEvaluator">JaninoEventEvaluator</h3>
    -    
    -
    -    <p>Logback-classic ships with another concrete
    -    <code>EventEvaluator</code> implementation called <a
    -    href="../xref/ch/qos/logback/classic/boolex/JaninoEventEvaluator.html">JaninoEventEvaluator</a>
    -    taking an arbitrary Java language block returning a boolean value
    -    as the evaluation criteria. We refer to such Java language boolean
    -    expressions as "<em>evaluation expressions</em>". Evaluation
    -    expressions enable great flexibility in event
    -    filtering. <code>JaninoEventEvaluator</code> requires the <a
    -    href="http://docs.codehaus.org/display/JANINO/Home">Janino
    -    library</a>. Please see the <a
    -    href="../setup.html#janino">corresponding section</a> of the setup
    -    document.  Compared to <code>JaninoEventEvaluator</code>,
    -    <code>GEventEvaluator</code>, by virtue of the Groovy language, is
    -    significantly more convenient to use, but
    -    <code>JaninoEventEvaluator</code> will usually run (much) faster
    -    for equivalent expressions.
    -    </p>
    -
    -    <p>Evaluation expressions are compiled on-the-fly during the
    -    interpretation of the configuration file. As a user, you do not
    -    need to worry about the actual plumbing. However, it is your
    -    responsibility to ensure that the Java language expression returns
    -    a boolean, i.e. that it evaluates to true or false. </p>
    -
    -
    -    <p>The evaluation expression is evaluated on the current logging
    -    event. Logback-classic automatically exports various fields of the
    -    logging event as variables accessible from the evaluation
    -    expression. The case-sensitive names of these exported variables
    -    are listed below.
    -    </p>
    -
    -		<table class="bodyTable">
    -      <tr>
    -        <th>Name</th>
    -        <th>Type</th>
    -        <th>Description</th>
    -			</tr>
    -      <tr>
    -				<td>event</td>
    -				<td><code>LoggingEvent</code></td>
    -
    -        <td>The raw logging event associated with the logging
    -        request. All of the following variables are also available
    -        from the event. For example, <code>event.getMessage()</code>
    -        returns the same String value as the <em>message</em> variable
    -        described next.
    -        </td>
    -			</tr>
    -
    -      <tr class="alt">
    -				<td>message</td>
    -        <td><code>String</code></td>
    -        <td>The raw message of the logging request. For some logger
    -        <em>l</em>, when you write l.info("Hello {}", name); where
    -        name is assigned the value "Alice", then "Hello {}" is the
    -        message.</td> </tr>
    -		
    -      <tr>
    -				<td>formattedMessage</td>
    -        <td><code>String</code></td>
    -        <td>The formatted message in the logging request. For some
    -        logger <em>l</em>, when you write l.info("Hello {}", name);
    -        where name is assigned the value "Alice", then "Hello Alice"
    -        is the formatted message.</td>
    -			</tr>
    -		
    -      <tr class="alt">
    -				<td>logger</td>
    -				<td><code>String</code></td>
    -				<td>The name of the logger.
    -        </td>
    -			</tr>
    -
    -      <tr>
    -        <td>loggerContext</td>
    -				<td><a
    -				href="../xref/ch/qos/logback/classic/spi/LoggerContextVO.html"><code>LoggerContextVO</code></a></td>
    -				<td>A restricted (value object) view of the logger context to which the logging event belongs to.
    -        </td>
    -			</tr>
    -
    -
    -			<tr class="alt">
    -				<td>level</td>
    -				<td><code>int</code></td>
    -				<td>The int value corresponding to the level. To help create
    -				easily expressions involving levels, the default value
    -				<em>DEBUG</em>, <em>INFO</em>, <em>WARN</em> and
    -				<em>ERROR</em> are also available. Thus, using <em>level &gt;
    -				INFO</em> is a correct expression.
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td>timeStamp
    -				</td>
    -				<td><code>long</code></td>
    -				<td>The timestamp corresponding to the logging event's
    -				creation.
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>marker</td>
    -				<td><code>Marker</code></td>
    -        <td>The <code>Marker</code> object associated with the logging
    -        request. Note that marker can be null and it is your
    -        responsibility to check for this condition in order to avoid
    -        <code>NullPointerException</code>.
    -				</td>
    -			</tr>
    -			<tr>
    -				<td>mdc</td>
    -				<td><code>Map</code></td>
    -				<td>A map containing all the MDC values at the time of the
    -				creation of the logging event. A value can be accessed by
    -				using the following expression: <em>mdc.get("myKey")</em>. As
    -				of logback-classic version 0.9.30, the 'mdc' variable will
    -				never be null.
    -
    -        <p>The <code>java.util.Map</code> type is non-parameterized
    -        because Janino does not support generics. It follows that the
    -        type returned by <code>mdc.get()</code> is <code>Object</code>
    -        and not <code>String</code>. To invoke <code>String</code>
    -        methods on the returned value, it must be cast as
    -        <code>String</code>. For example,
    -        <code>((String)&nbsp;mdc.get("k")).contains("val")</code>.
    -        </p>
    -				</td>
    -			</tr>
    -
    -      <tr class="alt">
    -				<td>throwable</td>
    -        <td>java.lang.Throwable</td>
    -				<td>If no exception is associated with the event, then the
    -				value of the "throwable" variable will be null. Unfortunately,
    -				"throwable" does not survive serialization. Thus, on remote
    -				systems, its value will always be null. For location
    -				independent expressions, use the <code>throwableProxy</code>
    -				variable described next.
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td>throwableProxy</td>
    -				<td><a
    -				href="../xref/ch/qos/logback/classic/spi/IThrowableProxy.html"><code>IThrowableProxy</code></a></td>
    -				<td>A proxy for the exception associated with the logging
    -				event. If no exception is associated with the event, then the
    -				value of the "throwableProxy" variable will be null. In
    -				contrast to "throwable", when an exception is associated with
    -				an event, the value of "throwableProxy" will be non-null even
    -				on remote systems, that is even after serialization.
    -				</td>
    -			</tr>
    -
    -    
    -
    -		</table>
    -
    -    <p>Here is a concrete example.</p>
    -
    -    <em>Example: Basic event evaluator usage
    -    (logback-examples/src/main/resources/chapters/filters/basicEventEvaluator.xml)</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('basicEventEvaluator');">View as .groovy</span>
    -    <pre id="basicEventEvaluator" class="prettyprint source longline">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">      
    -      &lt;evaluator> &lt;!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator -->
    -        &lt;expression><span class="green">return message.contains("billing");</span>&lt;/expression>
    -      &lt;/evaluator>
    -      &lt;OnMismatch>NEUTRAL&lt;/OnMismatch>
    -      &lt;OnMatch>DENY&lt;/OnMatch>
    -    &lt;/filter></b>
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="INFO">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>The bold part in the above configuration file adds an
    -		<code>EvaluatorFilter</code> to a <code>ConsoleAppender</code>. An
    -		evaluator of type <code>JaninoEventEvaluator</code> is then
    -		injected into the <code>EvaluatorFilter</code>. In the absence of
    -		<span class="attr">class</span> attribute in the
    -		<code>&lt;evaluator></code> element specified by the user, Joran
    -		will infer a default type of <code>JaninoEventEvaluator</code>
    -		for the evaluator. This is one of the <a
    -		href="onJoran.html#defaultClassMapping">few occurrences</a> where
    -		Joran implicitly infers the type of a component.
    -    </p>
    -
    -    <p>The <em>expression</em> element corresponds to the evaluation
    -    expression just discussed. The expression
    -    <code>return message.contains("billing");</code> returns a boolean
    -    value. Notice that the <em>message</em> variable is exported
    -    automatically by <code>JaninoEventEvaluator</code>.
    -    </p>
    -
    -		<p>Given that the <span class="option">OnMismatch</span> property is
    -		set to NEUTRAL and the <span class="option">OnMatch</span>
    -		property set to DENY, this evaluator filter will drop all logging
    -		events whose message contains the string "billing".
    -    </p>
    -
    -    <p>The <a
    -    href="../xref/chapters/filters/FilterEvents.html"><code>FilterEvents</code></a>
    -    application issues ten logging requests, numbered 0 to 9. Let us
    -    first run <code>FilterEvents</code> class without any filters:
    -		</p>
    -		
    -<div class="source"><pre>
    -java chapters.filters.FilterEvents src/main/java/chapters/filters/basicConfiguration.xml
    -</pre></div>
    -		
    -		<p>All requests will be displayed, as shown below:</p>
    -
    -<div class="source"><pre>0    [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -0    [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -0    [main] ERROR chapters.filters.FilterEvents - <b>billing statement 6</b>
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 9</pre></div>
    -
    -
    -
    -		<p>Suppose that we want to get rid of the "billing statement".
    -		The <em>basicEventEvaluator.xml</em> configuration file listed
    -		above filters messages containing the string "billing" which is
    -		precisely the desired outcome.</p>
    -
    -    <p>Running with <em>basicEventEvaluator.xml</em>:</p>
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/basicEventEvaluator.xml</p>
    -    <p>we obtain:
    -		</p>
    -		
    -    <p class="source">0    [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -0    [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 9</p>
    -		
    -
    -    <p>Evaluation expressions can be Java blocks. For example, the
    -    following is a valid expression.</p>
    -
    -    <pre class="prettyprint source">&lt;evaluator>
    -  &lt;expression>
    -    if(logger.startsWith("org.apache.http"))
    -      return true;
    -
    -    if(mdc == null || mdc.get("entity") == null)
    -      return false;
    -
    -    String payee = (String) mdc.get("entity");
    -
    -    if(logger.equals("org.apache.http.wire") &amp;amp;&amp;amp; &lt;!-- &amp; encoded as &amp;amp; -->
    -        payee.contains("someSpecialValue") &amp;amp;&amp;amp;
    -        !message.contains("someSecret")) {
    -      return true;
    -    }
    -
    -    return false;
    -  &lt;/expression>
    -&lt;/evaluator></pre>
    -
    -
    - 	  <h2 class="doAnchor" name="matcher">Matchers</h2>
    -
    -    <p>While it is possible to do pattern matching by invoking the <a
    -    href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#matches%28java.lang.String%29">matches()</a>
    -    method in the <code>String</code> class, this incurs the cost of
    -    compiling of a brand new <code>Pattern</code> object each time the
    -    filter is invoked. To eliminate this overhead, you can predefine
    -    one or more <a
    -    href="../xref/ch/qos/logback/core/boolex/Matcher.html">Matcher</a>
    -    objects. Once a matcher is defined, it can be repeatedly
    -    referenced by name in the evaluator expression.</p>
    -
    -    <p>An example should clarify the point:</p>
    -
    -    <em>Example: Defining matchers in an event evaluator (logback-examples/src/main/resources/chapters/filters/evaluatorWithMatcher.xml)</em>
    -    <span class="asGroovy" onclick="return asGroovy('evaluatorWithMatcher');">View as .groovy</span>
    -
    -    <pre id="evaluatorWithMatcher" class="prettyprint source">&lt;configuration debug="true">
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    -      &lt;evaluator>        
    -        <b>&lt;matcher>
    -          &lt;Name>odd&lt;/Name>
    -          &lt;!-- filter out odd numbered statements -->
    -          &lt;regex>statement [13579]&lt;/regex>
    -        &lt;/matcher>
    -        
    -        &lt;expression>odd.matches(formattedMessage)&lt;/expression></b>
    -      &lt;/evaluator>
    -      &lt;OnMismatch>NEUTRAL&lt;/OnMismatch>
    -      &lt;OnMatch>DENY&lt;/OnMatch>
    -    &lt;/filter>
    -    &lt;encoder>
    -      &lt;pattern>%-4relative [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -    <p>Running with <em>evaluatorWithMatcher.xml</em>:</p>
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/evaluatorWithMatcher.xml</p>
    -    <p>we obtain:
    -		</p>
    -		
    -    <p class="source">260  [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -264  [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -264  [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -266  [main] ERROR chapters.filters.FilterEvents - billing statement 6
    -266  [main] INFO  chapters.filters.FilterEvents - logging statement 8</p>
    -
    -    <p>If you need to define additional matchers, you can do so by
    -    adding further <code>&lt;matcher></code> elements.</p>
    -
    -
    -
    -
    -
    -    <!-- ================================================================ -->
    -    <!-- ===================== TURBO FILTER ============================= -->
    -    <!-- ================================================================ -->
    -
    -    <h2 class="doAnchor" name="TurboFilter">TurboFilters</h2>
    -    
    -    <p><code>TurboFilter</code> objects all extend the
    -    	<a href="../xref/ch/qos/logback/classic/turbo/TurboFilter.html">
    -    	<code>TurboFilter</code></a> abstract class. Like the regular
    -    	filters, they use ternary logic to return their evaluation of
    -    	the logging event.
    -    </p>
    -    
    -    <p>Overall, they work much like the previously mentioned
    -    filters. However, there are two main differences between
    -    <code>Filter</code> and <code>TurboFilter</code> objects.
    -    </p>
    -    
    -   	<p><code>TurboFilter</code> objects are tied to the logging
    -   	context. Hence, they are called not only when a given appender is
    -   	used, but each and every time a logging request is issued. Their
    -   	scope is wider than appender-attached filters.
    -   	</p>
    -   	
    -   	<p>More importantly, they are called before the
    -   	<code>LoggingEvent</code> object creation.
    -   	<code>TurboFilter</code> objects do not require the instantiation
    -   	of a logging event to filter a logging request. As such, turbo
    -   	filters are intended for high performance filtering of logging
    -	events, even before the events are created.
    -    </p>
    -
    -   	
    -   	<h3 class="doAnchor" name="yourOwnTurboFilter">Implementing your
    -   	own TurboFilter</h3>
    -    
    -    <p>To create your own <code>TurboFilter</code> component, just
    -    extend the <code>TurboFilter</code> abstract class. As previously,
    -    when implementing a customized filter object, developing a custom
    -    <code>TurboFilter</code> only asks that one implement the
    -    <code>decide()</code> method. In the next example, we create a
    -    slightly more complex filter:
    -    </p>
    -    
    -<em>Example: Basic custom <code>TurboFilter</code> (<a href="../xref/chapters/filters/SampleTurboFilter.html">logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java</a>)</em>		
    -<pre class="prettyprint source">package chapters.filters;
    -
    -import org.slf4j.Marker;
    -import org.slf4j.MarkerFactory;
    -
    -import ch.qos.logback.classic.Level;
    -import ch.qos.logback.classic.Logger;
    -import ch.qos.logback.classic.turbo.TurboFilter;
    -import ch.qos.logback.core.spi.FilterReply;
    -
    -public class SampleTurboFilter extends TurboFilter {
    -
    -  String marker;
    -  Marker markerToAccept;
    -
    -  @Override
    -  public FilterReply decide(Marker marker, Logger logger, Level level,
    -      String format, Object[] params, Throwable t) {
    -
    -    if (!isStarted()) {
    -      return FilterReply.NEUTRAL;
    -    }
    -
    -    if ((markerToAccept.equals(marker))) {
    -      return FilterReply.ACCEPT;
    -    } else {
    -      return FilterReply.NEUTRAL;
    -    }
    -  }
    -
    -  public String getMarker() {
    -    return marker;
    -  }
    -
    -  public void setMarker(String markerStr) {
    -    this.marker = markerStr;
    -  }
    -
    -  @Override
    -  public void start() {
    -    if (marker != null &amp;&amp; marker.trim().length() > 0) {
    -      markerToAccept = MarkerFactory.getMarker(marker);
    -      super.start(); 
    -    }
    -  }
    -}
    -</pre>
    -
    -		<p>The <code>TurboFilter</code> above accepts events that contain
    -		a specific marker.  If said marker is not found, then the filter
    -		passes the responsibility to the next filter in the chain.
    -		</p>
    -		
    -		<p>To allow more flexibility, the marker that will be tested can
    -		be specified in the configuration file, hence the getter and
    -		setter methods. We also implemented the <code>start()</code>
    -		method, to check that the option has been specified during the
    -		configuration process.
    -		</p>
    -		
    -		<p>Here is a sample configuration that makes use of our newly
    -		created <code>TurboFilter</code>.
    -		</p>
    -		
    -    <em>Example: Basic custom <code>TurboFilter</code> configuration
    -    (logback-examples/src/main/resources/chapters/filters/sampleTurboFilterConfig.xml)</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('sampleTurboFilterConfig');">View as .groovy</span>
    -
    -    <pre id="sampleTurboFilterConfig"  class="prettyprint source">&lt;configuration>
    -  <b>&lt;turboFilter class="chapters.filters.SampleTurboFilter">
    -    &lt;Marker>sample&lt;/Marker>
    -  &lt;/turboFilter></b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root>
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>  
    -
    -   	<p>Logback classic ships with several <code>TurboFilter</code>
    -   	classes ready for use.  The <a
    -   	href="../xref/ch/qos/logback/classic/turbo/MDCFilter.html"><code>MDCFilter</code></a>
    -   	checks the presence of a given value in the MDC whereas <a
    -   	href="../apidocs/ch/qos/logback/classic/turbo/DynamicThresholdFilter.html"><code>DynamicThresholdFilter</code></a>
    -   	allows filtering based on MDC key/level threshold associations. On
    -   	the other hand, <a
    -   	href="../xref/ch/qos/logback/classic/turbo/MarkerFilter.html"><code>MarkerFilter</code></a>
    -   	checks for the presence of a specific marker associated with the
    -   	logging request.
    -   	</p>
    -   	
    -   	<p>Here is a sample configuration, using both
    -   	<code>MDCFilter</code> and <code>MarkerFilter</code>.
    -   	</p>
    -   	
    -    <em>Example: <code>MDCFilter</code> and <code>MarkerFilter</code>
    -    configuration
    -    (logback-examples/src/main/resources/chapters/filters/turboFilters.xml)</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('turboFilters');">View as .groovy</span>
    -    <pre id="turboFilters" class="prettyprint source">&lt;configuration>
    -
    -  &lt;turboFilter class="ch.qos.logback.classic.turbo.MDCFilter">
    -    &lt;MDCKey>username&lt;/MDCKey>
    -    &lt;Value>sebastien&lt;/Value>
    -    &lt;OnMatch>ACCEPT&lt;/OnMatch>
    -  &lt;/turboFilter>
    -	
    -  &lt;turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter">
    -    &lt;Marker>billing&lt;/Marker>
    -    &lt;OnMatch>DENY&lt;/OnMatch>
    -  &lt;/turboFilter>
    -
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%date [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="INFO">
    -    &lt;appender-ref ref="console" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -		<p>You can see this configuration in action by issuing the
    -		following command:
    -		</p>
    -    
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/turboFilters.xml</p>
    -
    -		<p>As we've seen previously, the <a
    -		href="../xref/chapters/filters/FilterEvents.html"><code>FilterEvents</code></a>
    -		application issues 10 logging requests, numbered 0 to 9. Except
    -		for requests 3 and 6, all of the requests are of level
    -		<em>INFO</em>, the same level as the one assigned to the root
    -		logger. The 3rd request, is issued at the the <em>DEBUG</em>
    -		level, which is below the effective level. However, since the MDC
    -		key "username" is set to "sebastien" just before the 3rd request
    -		and removed just afterwards, the <code>MDCFilter</code>
    -		specifically accepts the request (and only that request). The 6th
    -		request, issued at the <em>ERROR</em> level, is marked as
    -		"billing". As such, it is denied by the MarkerFilter (the second
    -		turbo filter in the configuration).
    -		</p>
    -		
    -		<p>Thus, the output of <code>FilterEvents</code> application
    -		configured with <em>turboFilters.xml</em> file shown above is:
    -		</p>
    -
    -    <p class="source">2006-12-04 15:17:22,859 [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -2006-12-04 15:17:22,875 [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 9</p>
    -			
    -			
    -		<p>One can see that the 3rd request, which should not be displayed
    -		if we only followed the overall <em>INFO</em> level, appears
    -		anyway, because it matched the first <code>TurboFilter</code>
    -		requirements and was accepted.
    -		</p>    
    -		
    -		<p>On the other hand, the 6th request, that is an <em>ERROR</em>
    -		level request should have been displayed. But it satisfied the
    -		second <code>TurboFilter</code> whose <span
    -		class="option">OnMatch</span> option is set to <em>DENY</em>.
    -		Thus, the 6th request was not displayed.
    -		</p>
    -		
    -
    -
    -		  
    -    <h3 class="doAnchor"
    -    name="DuplicateMessageFilter">DuplicateMessageFilter</h3>
    -
    -    <p>The <code>DuplicateMessageFilter</code> merits a separate
    -    presentation.  This filter detects duplicate messages, and beyond
    -    a certain number of repetitions, drops repeated messages.
    -    </p>
    -
    -    <p>To detect repetition, this filter uses simple String equality
    -    between messages. It does not detect messages which are very
    -    similar, varying only by few characters. For example, if you
    -    write:
    -    </p>
    -
    -    <pre class="prettyprint source">logger.debug("Hello "+name0);
    -logger.debug("Hello "+name1);</pre>
    -  
    -    <p>Assuming <code>name0</code> and <code>name1</code> have
    -    different values, the two "Hello" messages will be considered as
    -    unrelated. Depending on user demand, future releases may check for
    -    string similarity, eliminating repetitions of similar but not
    -    identical messages.
    -    </p>
    -
    -    <p>Note that in case of parameterized logging, only the raw
    -    message is taken into consideration. For example, in the next two
    -    requests, the raw messages, i.e. "Hello {}.", are identical, and
    -    thus considered as repetitions.
    -    </p>
    -
    -    <pre class="prettyprint source">logger.debug("Hello {}.", name0);
    -logger.debug("Hello {}.", name1);</pre>
    -  
    -    <p>The number of allowed repetitions can be specified by the <span
    -    class="option">AllowedRepetitions</span> property. For example, if
    -    the property is set to 1, then the 2nd and subsequent
    -    occurrences of the same message will be dropped. Similarly, if the
    -    property is set to 2, then the 3rd and subsequent occurrences
    -    of the same message will be dropped. By default, the <span
    -    class="option">AllowedRepetitions</span> property is set to 5.
    -    </p>
    -
    -    <p>In order to detect repetitions, this filter needs to keep
    -    references to old messages in an internal cache. The size of this
    -    cache is determined by the <span class="option">CacheSize</span>
    -    property. By the default, this is set to 100.
    -    </p>
    -
    -    
    -    <em>Example: <code>DuplicateMessageFilter</code> 
    -    configuration (logback-examples/src/main/resources/chapters/filters/duplicateMessage.xml)</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('duplicateMessage');">View as .groovy</span>
    -    <pre id="duplicateMessage" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter"/></b>
    -
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%date [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="INFO">
    -    &lt;appender-ref ref="console" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -  <p>Thus, the output for <code>FilterEvents</code> application
    -  configured with <em>duplicateMessage.xml</em> is:
    -  </p>
    -
    -    <p class="source">2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -2008-12-19 15:04:26,171 [main] ERROR chapters.filters.FilterEvents - billing statement 6</p>
    -
    -    <p>"logging statement 0" is the first <em>occurrence</em> of the
    -    message "logging statement {}". "logging statement 1" is the first
    -    <em>repetition</em>, "logging statement 2" is the second
    -    repetition. Interestingly enough, "logging statement 3" of level
    -    DEBUG, is the <em>third</em> repetition, even though it is later
    -    dropped by virtue of the <a
    -    href="architecture.html#basic_selection">basic selection
    -    rule</a>. This can be explained by the fact that turbo filters are
    -    invoked before other types of filters, including the basic
    -    selection rule. Thus, <code>DuplicateMessageFilter</code>
    -    considers "logging statement 3" as a repetition, oblivious to the
    -    fact that it will be dropped further down in the processing
    -    chain. "logging statement 4" is the fourth repetition and "logging
    -    statement 5" the fifth. Statements 6 and beyond are dropped
    -    because only 5 repetitions are allowed by default.
    -    </p>
    -
    -    <h1 class="doAnchor" name="logbac-access">In logback-access</h1>
    -    
    -    <p>Logback-access offers most of the features available with
    -    logback-classic. In particular, <code>Filter</code> objects are
    -    available and work in the same way as their logback-classic
    -    counterparts, with one notable difference. Instead of
    -    <code>LoggingEvent</code> instances logback-access filters act
    -    upon <a
    -    href="../xref/ch/qos/logback/access/spi/AccessEvent.html"><code>AccessEvent</code></a>
    -    instances. At present time, logback-access ships with a limited
    -    number of filters described below. If you would like to suggest
    -    additional filters, please contact the logback-dev mailing list.
    -    </p>
    -
    -		<h2 class="doAnchor"
    -		name="countingFilter"><code>CountingFilter</code></h2>
    -		
    -		<p>With the help of <a
    -		href="xref/ch/qos/logback/access/filter/CountingFilter.html"><code>CountingFilter</code></a>
    -		class, logback-access can provide statistical data about access to
    -		the web-server. Upon initialization, <code>CountingFilter</code>
    -		registers itself as an MBean onto the platform's JMX server. You
    -		can then interrogate that MBean for statistical data,
    -		e.g. averages by minute, hour, day, week, or month. Other
    -		statistics such the count for the preceding week, day, hour or
    -		month as well as the total count are also available.
    -		</p>
    -		
    -		<p>The following <em>logback-access.xml</em> configuration file
    -		declares a <code>CountingFilter</code>.  </p>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -
    -  <b>&lt;filter class="ch.qos.logback.access.filter.CountingFilter">
    -    &lt;name>countingFilter&lt;/name>
    -  &lt;/filter></b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%h %l %u %t \"%r\" %s %b&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender-ref ref="STDOUT" />
    -&lt;/configuration></pre>
    -
    -    <p>You can examine the various statistics maintained by
    -    <code>CountingFilter</code> on your platform's JMX server via the
    -    <code>jconsole</code> application. </p>
    -
    -
    -    <img alt="CountingFilter via jconsole" src="images/chapters/filters/countingFilter.png" />
    -	
    -
    -    <h3 class="doAnchor" name="access_EvalutorFilter">EvaluatorFilter</h3>
    -
    -    
    -    <p><a
    -    href="../xref/ch/qos/logback/core/filter/EvaluatorFilter.html"><code>EvaluatorFilter</code></a>
    -    is a generic filter encapsulating an
    -    <code>EventEvaluator</code>. As the name suggests, an
    -    <a
    -    href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html">
    -    <code>EventEvaluator</code></a> evaluates whether a given criteria
    -    is met for a given event. On match and on mismatch,
    -    the hosting <code>EvaluatorFilter</code> will return the value
    -    specified by the <span class="option">onMatch</span> or
    -    <span class="option">onMismatch</span> properties respectively. Note that
    -    <code>EvaluatorFilter</code> has been previously discussed in the
    -    context of logback-classic (<a href="#evalutatorFilter">see
    -    above</a>). The present text is mostly a repetition of the
    -    previous discussion.</p>
    -
    -
    -    <p>Note that <code>EventEvaluator</code> is an abstract class. You
    -    can implement your own event evaluation logic by sub-classing
    -    <code>EventEvaluator</code>. Logback-access ships with a concrete
    -    implementation named <a
    -    href="../xref/ch/qos/logback/access/boolex/JaninoEventEvaluator.html">JaninoEventEvaluator</a>.
    -    It takes arbitrary Java language boolean expressions as the
    -    evaluation criteria. We refer to such Java language blocks as
    -    "<em>evaluation expressions</em>". Evaluation expressions enable
    -    great flexibility in event
    -    filtering. <code>JaninoEventEvaluator</code> requires the <a
    -    href="http://docs.codehaus.org/display/JANINO/Home">Janino
    -    library</a>. Please see the <a
    -    href="../setup.html#janino">corresponding section</a> of the setup
    -    document.
    -    </p>
    -
    -    <p>Evaluation expressions are compiled on-the-fly during the
    -    interpretation of the configuration file. As a user, you do not
    -    need to worry about the actual plumbing. However, it is your
    -    responsibility to ensure that the Java language expression returns
    -    a boolean, i.e. that it evaluates to true or false. </p>
    -
    -
    -    <p>The evaluation expression is evaluated on the current access
    -    event. Logback-access automatically exports the current
    -    <code>AccessEvent</code> instance under the variable name
    -    <b><code>event</code></b>. You can read the various data
    -    associated with the HTTP request as well as the HTTP response via
    -    the <code>event</code> variable. Please refer to the <a
    -    href="../xref/ch/qos/logback/access/spi/AccessEvent.html"><code>AccessEvent</code>
    -    class source code</a> for the exact list.
    -    </p>
    -    
    -    <p>The next logback-access configuration file illustrates
    -    filtering based on the <a
    -    href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404
    -    (Not Found)</a> HTTP response code. Every request resulting in a
    -    404 will be printed on the console.</p>
    -   	
    -<em>Example: Access Evaluator (logback-examples/src/main/resources/chapters/filters/accessEventEvaluator.xml)</em>
    -<pre class="prettyprint source">&lt;configuration>
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    -      &lt;evaluator>
    -        &lt;expression>event.getStatusCode() == 404&lt;/expression>
    -      &lt;/evaluator>
    -      &lt;onMismatch>DENY&lt;/onMismatch>
    -    &lt;/filter></b>
    -   &lt;encoder>&lt;pattern>%h %l %u %t %r %s %b&lt;/pattern>&lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender-ref ref="STDOUT" />
    -&lt;/configuration></pre>
    -
    -		<p>In the next example, we still log requests resulting in 404 errors,
    -		except those requests asking for CSS files.
    -		</p>	
    -
    -
    -    <em>Example 6.10: Access Evaluator (logback-examples/src/main/resources/chapters/filters/accessEventEvaluator2.xml)</em>
    -    <pre class="prettyprint source">&lt;configuration>
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    -      &lt;evaluator name="Eval404">
    -        &lt;expression>
    -         <b>(event.getStatusCode() == 404)</b>
    -           <b>&amp;amp;&amp;amp;</b>  &lt;!-- ampersand characters need to be escaped -->
    -         <b>!(event.getRequestURI().contains(".css"))</b>
    -        &lt;/expression>
    -      &lt;/evaluator>
    -      &lt;onMismatch>DENY&lt;/onMismatch>
    -    &lt;/filter>
    -
    -   &lt;encoder>&lt;pattern>%h %l %u %t %r %s %b&lt;/pattern>&lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender-ref ref="STDOUT" />
    -&lt;/configuration>
    -    </pre>
    -	
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -  </div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/filters_ja.html b/logback-site/src/site/pages/manual/filters_ja.html
    deleted file mode 100644
    index db2c87c6e3..0000000000
    --- a/logback-site/src/site/pages/manual/filters_ja.html
    +++ /dev/null
    @@ -1,818 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第7章 フィルター</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -    <h1>第7章 フィルター</h1>
    -
    -    <div class="quote">
    -      <p><em>たくさんのアイデアを集めて悪いものを捨てていこう。たくさんのアイデアと、自分なりの基準が無ければ、いいアイデアに巡り会えることはない。</em></p>
    -      
    -      <p>—LINUS PAULING</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    
    -    <p>ここまでに、<a href="http://logback.qos.ch/manual/architecture.html#basic_selection">基本的な選択ルール</a>がlogback-classicモジュールの中核を担っていることを説明してきました。本章では、それに加えてフィルタリングの方法を紹介します。
    -    </p>
    -  
    -
    -    <p>logbackのフィルターは、三値論理に基づいて合成や連結を駆使して、任意に複雑な条件を実現することができます。主に Linux の iptables から着想を得たものです。
    -    </p>
    -
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -		<h2>logback-classicモジュール</h2>
    -
    -
    -		<p>logback-classic モジュールには二種類のフィルターがあります。通常フィルターとターボフィルターです。
    -		</p>
    -		
    -    <h3 class="doAnchor" name="filter">通常フィルター</h3>
    -
    -		<p>通常フィルターとは、<a href="http://logback.qos.ch/xref/ch/qos/logback/core/filter/Filter.html"><code>Filter</code></a>抽象クラスを継承したものです。本質的には<code>ILoggingEvent</code>を引数にとる<code>decide()</code>メソッドを実装することが目的です。
    -		</p>
    -		
    -
    -		<p>フィルターは順序付きリストにまとめて扱われます。また、三値論理に基づいて扱われます。それぞれのフィルターの<code>decide(ILoggingEvent event)</code>メソッドが順番に呼び出されます。このメソッドは<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/FilterReply.html"><code>FilterReply</code></a>列挙型の値である、<code>DENY</code> 、 <code>NEUTRAL</code>または<code>ACCEPT</code>を返します。<code>decide()</code>メソッドが<code>DENY</code>を返したら、そのロギングイベントは残りのフィルターに渡されることなく、ただちに破棄されます。<code>NEUTRAL</code>を返したら、リスト内の次のフィルターに渡されます。リストの末尾に到達したら、そのロギングイベントは通常通りに処理されることになります。<code>ACCEPT</code>を返したら、残りのフィルターはスキップして、そのロギングイベントはただちに処理されます。
    -    </p>
    -    
    -    <p>logback-classicモジュールでは、<code>Appender</code>にフィルターを追加することが出来ます。アペンダーに複数のフィルターを登録すれば、ロギングイベントをさまざまな条件で篩にかけられるようになります。ロギングメッセージの内容やMDCの内容、時刻や日付などロギングイベントのあらゆる内容を判定できるのです。
    -    </p>
    -    
    -		<h3 class="doAnchor" name="yourOwnFilter">フィルターを自作する</h3>
    -		
    -		<p>フィルターを自作するのは簡単です。<code>Filter</code>抽象クラスを継承して<code>decide()</code>メソッドを実装するだけです。
    -		</p>
    -		
    -		<p>例としてSampleFilterクラスを見てください。<code>decide()</code>メソッドがACCEPTを返すのは、ロギングイベントのメッセージに "sample" という文字列が含まれる場合だけです。その他の場合はNEUTRALを返すようになっています。
    -		</p>
    -		
    -    <p class="example">例:基本的な自作フィルター(<a href="http://logback.qos.ch/xref/chapters/filters/SampleFilter.html">logback-examples/src/main/java/chapters/filters/SampleFilter.java</a>)</p>
    -
    -    <pre class="prettyprint source">package chapters.filters;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.filter.Filter;
    -import ch.qos.logback.core.spi.FilterReply;
    -
    -public class SampleFilter extends Filter&lt;ILoggingEvent&gt; {
    -
    -  @Override
    -  public FilterReply decide(ILoggingEvent event) {    
    -    if (event.getMessage().contains("sample")) {
    -      return FilterReply.ACCEPT;
    -    } else {
    -      return FilterReply.NEUTRAL;
    -    }
    -  }
    -}</pre>
    -
    -		<p>次の設定ファイルでは、<code>ConsoleAppender</code>に<code>SampleFilter</code>を割り当てています。
    -		</p>
    -
    -    <p class="example">例:SampleFilterの設定(<a href="http://logback.qos.ch/xref/chapters/filters/SampleFilterConfig.xml">logback-examples/src/main/java/chapters/filters/SampleFilterConfig.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;SampleFilterConfig&#39;);">Groovyとして表示</span>
    -    <pre id="SampleFilterConfig" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -
    -    <b>&lt;filter class="chapters.filters.SampleFilter" /&gt;</b>
    -
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -	
    -  &lt;root&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>設定フレームワークの Joran を使えばフィルターのプロパティやサブコンポーネントを指定するのも簡単です。フィルタークラスにフィールドのセッターメソッドを追加すれば、<code>filter要素</code>にネストしてプロパティの値を指定することができます。
    -		</p>
    -		
    -		<p>たいていの場合フィルタリング条件には2つの直行する条件が含まれています。マッチするかどうかの条件と、何を返すのかの条件です。たとえば、メッセージが"foobar"だったらACCEPTを返し、そうでなければNEUTRALを返すフィルターもありますし、メッセージが"foobar"だったらNEUTRALを返し、そうでなければDENYを返すフィルターもあります。
    -    </p>
    -
    -    <p>logback の配布物には、この直交性に焦点を当てた<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/filter/AbstractMatcherFilter.html">AbstractMatcherFilter</a></code>が含まれています。このクラスはフィルター条件の判定結果に基づいて何らかの値を返すスケルトンです。判定結果が真の時に返す値を<em>OnMatch</em>プロパティに、偽の時に返す値を<em>OnMismatch</em>プロパティに指定することができます。logbackの配布物に含まれるほとんどの通常フィルターは<code>AbstractMatcherFilter</code>を継承しています。
    -    </p>
    -		
    -		<h3 class="doAnchor" name="levelFilter">LevelFilter</h3>
    -		
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/filter/LevelFilter.html">LevelFilter</a></code>は、ロギングイベントのログレベルの正確なマッチングに基づいたフィルタリングをします。ログレベルが設定されたレベルと等しければ、<span class="option">omMatch</span>プロパティあるいは<span class="option">omMismach</span>プロパティに設定された値に応じて、ロギングイベントを受け入れるか拒否するかが決まります。設定ファイルを見てみましょう。
    -		</p>
    -		
    -    <p class="example">例:LevelFilterの設定例(<a href="http://logback.qos.ch/xref/chapters/filters/levelFilterConfig.xml">logback-examples/src/main/java/chapters/filters/levelFilterConfig.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;levelFilterConfig&#39;);">Groovyとして表示</span>
    -    <pre id="levelFilterConfig" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    <b>&lt;filter class="ch.qos.logback.classic.filter.LevelFilter"&gt;
    -      &lt;level&gt;INFO&lt;/level&gt;
    -      &lt;onMatch&gt;ACCEPT&lt;/onMatch&gt;
    -      &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;
    -    &lt;/filter&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger{30} - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <h3 class="doAnchor" name="thresholdFilter">ThresholdFilter</h3>
    -
    -		<p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/filter/ThresholdFilter.html">ThresholdFilter</a></code>は、ログレベルが指定されたしきい値より低いロギングイベントをフィルタリングします。ログレベルがしきい値と同じかより高い場合、<code>ThresholdFilter</code>の<code>decide()</code>はNEUTRALを返します。一方、しきい値より低いログレベルのロギングイベントは拒否します。設定ファイルを見てみましょう。
    -		</p>
    -
    -    <p class="example">例:ThresholdFilterの設定例(<a href="http://logback.qos.ch/xref/chapters/filters/thresholdFilterConfig.xml">logback-examples/src/main/java/chapters/filters/thresholdFilterConfig.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;thresholdFilterConfig&#39;);">Groovyとして表示</span>
    -    <pre id="thresholdFilterConfig" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="CONSOLE"
    -    class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;!-- deny all events with a level below INFO, that is TRACE and DEBUG --&gt;
    -    <b>&lt;filter class="ch.qos.logback.classic.filter.ThresholdFilter"&gt;
    -      &lt;level&gt;INFO&lt;/level&gt;
    -    &lt;/filter&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger{30} - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <h2 class="doAnchor" name="evalutatorFilter">EvaluatorFilter</h2>
    -
    -    <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/filter/EvaluatorFilter.html"><code>EvaluatorFilter</code></a>は内部で<code>EventEvaluator</code>を使用する汎用的なフィルターです。名前のとおり、ロギングイベントが<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/boolex/EventEvaluator.html">EventEvaluator</a></code>に指定された条件を満たすかどうかを評価します。評価結果がなんであれ、<code>EvaluatorFilter</code>に設定された<span class="option">onMatch</span>プロパティまたは<span class="option">onMismatch</span>プロパティの値を返します。
    -    </p>
    -
    -
    -    <p><code>EventEvaluator</code>は抽象クラスです。つまり、<code>EventEvaluator</code>を継承すれば、独自のイベント評価ロジックを実装することができます。
    -    </p>
    -    
    -
    -    <!-- ======================== GEventEvaluator ========================= -->
    -
    -    <h3 class="doAnchor" name="GEventEvaluator">GEventEvaluator</h3>
    -    
    -    <p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/GEventEvaluator.html">GEventEvaluator</a>は<a href="http://logback.qos.ch/xref/ch/qos/logback/core/boolex/EventEvaluator.html"><code>EventEvaluator</code></a>の派生クラスで、評価条件に結果が真偽値になるGroovy言語で書かれた任意の式を指定することができます。私たちはこのGroovy言語で書かれた式のことを "Groovy評価式" と呼んでいます。Groovy評価式を使うと、ロギングイベントをこれまでにないくらい柔軟にフィルタリングできるようになります。<code>GEventEvaluator</code> を使うにはGroovyのランタイムが必要です。設定ドキュメントの<a href="http://logback.qos.ch/setup.html#groovy">対応するセクション</a>には、クラスパスにGroovyのランタイムを指定する方法が記載されています。
    -    </p>
    -
    -    <p>Groovy評価式は設定ファイルを解釈する際にコンパイルされます。どのように実行させるのかか、利用者が考える必要はありません。しかし、Groovy言語として間違いが無いことを保証するのは使用者の責任です。
    -    </p>
    -
    -    <p>Groovy評価式は一度に1つのロギングイベントを扱います。logbackは、ロギングイベントを<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/spi/ILoggingEvent.html">ILoggingEvnet</a>型の変数'<em>event</em>'あるいは'<em>e</em>'として用意します。また、ログレベルのTRACE、DBUG、INFO、WARN、ERROR は、Groovy評価式から同じ名前の変数として使用することが出来ます。したがって、"event.level == DEBUG" と "e.level == DEBUG" は同じ意味のGroovy評価式ということになります。ロギングイベントのログレベルがDEBUGの場合、式の値は<code>true</code>になります。他の比較演算子を使うときは、ログレベルの変数に<code>toInt()</code>演算子を適用して、整数値として評価しなければなりません。
    -    </p>
    -
    -    <p>具体的な例を見てみましょう。</p>
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;GEventEvaluator&#39;);">Groovyとして表示</span>
    -    <pre id="GEventEvaluator" class="prettyprint source">&lt;configuration&gt;
    -    
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter"&gt;      
    -      &lt;evaluator class="ch.qos.logback.classic.boolex.GEventEvaluator"&gt; 
    -        &lt;expression&gt;
    -           e.level.toInt() &gt;= WARN.toInt() &amp;amp;&amp;amp;  &lt;!-- Stands for &amp;&amp; in XML --&gt;
    -           !(e.mdc?.get("req.userAgent") =~ /Googlebot|msnbot|Yahoo/ )
    -        &lt;/expression&gt;
    -      &lt;/evaluator&gt;
    -      &lt;OnMismatch&gt;DENY&lt;/OnMismatch&gt;
    -      &lt;OnMatch&gt;NEUTRAL&lt;/OnMatch&gt;
    -    &lt;/filter&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -    <p>この設定は、ログレベルがWARN以上で、ユーザーエージェントがクローラー(GooglebotやmsnbotやYahoo)以外のロギングイベントのメッセージをコンソールに出力します。ロギングイベントに関連付けられた MDC の "req.userAgent" の値を正規表現<code>/Googlebot|msnbot|Yahoo/</code>で評価して、ユーザーエージェントを判定しています。MDCがnullになることもあるので、Groovyの<a href="http://groovy.codehaus.org/Null+Object+Pattern">安全なデリファレンス演算子</a>(?.)を使っています。同じことをJava言語で実装するともっと長くなってしまうでしょう。
    -    </p>
    -    
    -    <p>ユーザーエージェント文字列がいつMDCに登録されたのか疑問に思うかもしれません。説明しておくべきでしたが、logbackの配布物に含まれている<a href="http://logback.qos.ch/manual/mdc.html#mis"><code>MDCInsertingServletFilter</code></a>を使っています。詳しくは後の章で説明します。
    -    </p>
    -
    -    <!-- ==================== JaninoEventEvaluator ======================== -->
    -    
    -    <h3 class="doAnchor" name="JaninoEventEvaluator">JaninoEventEvaluator</h3>
    -    
    -
    -    <p>logback-classicの配布物には、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/boolex/JaninoEventEvaluator.html">JaninoEventEvaluator</a>という<code>EventEvaluator</code>の別の実装クラスが含まれています。これは、booleanを返す任意のJava言語のブロックを評価するものです。私たちはこのJava言語で書かれた式のことを"<em>Java評価式</em>”と呼んでいます。Java評価式を使うとロギングイベントを柔軟にフィルタリングできるようになります。<code>JaninoEventEvaluator</code>を使用するには<a href="http://docs.codehaus.org/display/JANINO/Home">Janinoライブラリ</a>が必要です。設定方法は設定ドキュメントの<a href="http://logback.qos.ch/setup.html#janino">対応するセクション</a>を参照してください。<code>JaninoEventEvaluator</code>と比べると、<code>GEventEvaluator</code>はGroovy言語のおかげで非常に使いやすいです。ですが、<code>JaninoEventEvaluator</code>のほうが同じ評価式をより高速に実行することができます。
    -    </p>
    -
    -    <p>Java評価式は設定ファイルを解釈する間にコンパイルされます。どのように呼び出すのか、利用者は気にすることはありません。ですが、Java言語の式が真偽値を返すものであることを保証するのは利用者の責任です。</p>
    -
    -
    -    <p>Java評価式は一度に1つのロギングイベントを扱います。logback-classicは、ロギングイベントのいろいろなフィールドをJava評価式から参照できる変数として自動的に公開します。公開する変数の名前は大文字小文字を区別するものです。表にまとめました。
    -    </p>
    -
    -		<table class="bodyTable">
    -      <tr>
    -        <th>変数名</th>
    -        <th>型</th>
    -        <th>説明</th>
    -			</tr>
    -      <tr>
    -				<td>event</td>
    -				<td><code>LoggingEvent</code></td>
    -
    -        <td>ロギング要求に関連付けられたロギングイベントそのもの。以下の変数はロギングイベントから参照することができます。たとえば、 <code>event.getMessage()</code>は<em>message</em>変数と同じ文字列を返します。
    -        </td>
    -			</tr>
    -
    -      <tr class="alt">
    -				<td>message</td>
    -        <td><code>String</code></td>
    -        <td>ロギング要求に指定されたメッセージそのものです。ロガー<em>l</em>について l.info("Hello {}", name); というロギング式があったとき、"Hello {}" がメッセージとなります。</td> </tr>
    -		
    -      <tr>
    -				<td>formattedMessage</td>
    -        <td><code>String</code></td>
    -        <td>ロギング要求の書式化されたメッセージ。ロガー<em>l</em>について l.info("Hello {}", name); というロギング式があったとき、nameの値が"Alice"なら、"Hello Alice" が書式化されたメッセージになります。</td>
    -			</tr>
    -		
    -      <tr class="alt">
    -				<td>logger</td>
    -				<td><code>String</code></td>
    -				<td>ロガーの名前。
    -        </td>
    -			</tr>
    -
    -      <tr>
    -        <td>loggerContext</td>
    -				<td><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/spi/LoggerContextVO.html"><code>LoggerContextVO</code></a></td>
    -				<td>ロギングイベントが割り当てられたロガーコンテキストの、値オブジェクトとしてのビュー。
    -        </td>
    -			</tr>
    -
    -
    -			<tr class="alt">
    -				<td>level</td>
    -				<td><code>int</code></td>
    -				<td>ログレベルに対応する整数値。ログレベルを含む評価式を簡潔にするため、<em>DEBUG</em>、<em>INFO
    -</em>、<em>WARN</em>、<em>ERROR</em>が利用できるようになっています。たとえば、<em> level &gt; INFO </em> は正しい評価式です。
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td>timeStamp
    -				</td>
    -				<td><code>long</code></td>
    -				<td>ロギングイベントの作成時のタイムスタンプ。
    -				</td>
    -			</tr>
    -			<tr class="alt">
    -				<td>marker</td>
    -				<td><code>Marker</code></td>
    -        <td>ロギング要求に関連付けられた<code>Marker</code>オブジェクト。マーカーオブジェクトがnullの場合もあるので、<code>NullPointerException</code>を避けるためにnullチェックをするのは使用者の責任です。
    -				</td>
    -			</tr>
    -			<tr>
    -				<td>mdc</td>
    -				<td><code>Map</code></td>
    -				<td>ロギングイベントの作成時に関連付けられたMDC。<em>mdc.get("MYKEY")</em>とすると値を参照できます。logback-classic 0.9.30以降では、'mdc'変数は決してnullになりません。
    -
    -        <p>Janino はジェネリクスをサポートしていないので、<code>java.util.Map</code>には型パラメータがありません。つまり、<code>mdc.get()</code>の返り値の型は<code>Object</code>であって<code>String</code>ではないのです。戻り値で<code>String</code>のメソッドを実行するには、<code>String</code>にキャストしなければなりません。こんな感じです。
    - <code>((String) mdc.get("k")).contains("val")</code> 
    -        </p>
    -				</td>
    -			</tr>
    -
    -      <tr class="alt">
    -				<td>throwable</td>
    -        <td>java.lang.Throwable</td>
    -				<td>ロギングイベントに例外オブジェクトが関連付けられていないときは、"throwable"変数はnullになります。"throwable"変数はシリアライズすると失われてしまいます。したがって、リモートサーバ側ではこの値は常にnullになります。ローカルとリモートで同じ評価式を使いたい場合は、次項の<code>throwableProxy</code>変数を使用してください。
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td>throwableProxy</td>
    -				<td><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/spi/IThrowableProxy.html"><code>IThrowableProxy</code></a></td>
    -				<td>ロギングイベント関連付けられた例外オブジェクトのプロキシオブジェクト。例外オブジェクトが関連付けられていないとき、"throwableProxy"変数はnullになります。"throwable"変数と違って、例外オブジェクトがロギングイベントに関連付けられているときは、シリアライズされてリモートサーバに渡された後でも "throwableProxy"変数の値はnullになりません。
    -				</td>
    -			</tr>
    -
    -    
    -
    -		</table>
    -
    -    <p>具体的な例を見てみましょう。</p>
    -
    -    <p class="example">例:評価式の基本的な使い方(<a href="http://logback.qos.ch/xref/chapters/filters/basicEventEvaluator.xml">logback-examples/src/main/java/chapters/filters/basicEventEvaluator.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;basicEventEvaluator&#39;);">Groovyとして表示</span>
    -    <pre id="basicEventEvaluator" class="prettyprint source longline">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter"&gt;      
    -      &lt;evaluator&gt; &lt;!-- defaults to type ch.qos.logback.classic.boolex.JaninoEventEvaluator --&gt;
    -        &lt;expression&gt;<span class="green">return message.contains("billing");</span>&lt;/expression&gt;
    -      &lt;/evaluator&gt;
    -      &lt;OnMismatch&gt;NEUTRAL&lt;/OnMismatch&gt;
    -      &lt;OnMatch&gt;DENY&lt;/OnMatch&gt;
    -    &lt;/filter&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="INFO"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>設定ファイル中の太字部分で、<code>ConsoleAppender</code>に<code>EvaluatorFilter</code>を追加しています。<code>EvaluationFilter</code>に追加されたのは<code>JaninoEventEvaluator</code>です。<code>evaluator要素</code>の<span class="attr">class属性</span>を省略すると、Joranはデフォルトの<code>JaninoEventEvaluator</code>を使用します。これはJoranが暗黙的にコンポーネントの型を推測する<a href="http://logback.qos.ch/manual/onJoran.html#defaultClassMapping">珍しいケース</a>の1つです。
    -    </p>
    -
    -    <p><em>expression要素</em>に指定されているのは評価式です。<code>return message.contains("billing");</code>という式の値は真偽値です。<em>message変数</em>は、<code>JaninoEventEvaluator</code>が自動的に公開した変数です。
    -    </p>
    -
    -		<p><span class="option">OnMismatch</span>プロパティにNEUTRALが、<span class="option">OnMatch</span>プロパティにDENYが指定されているので、このフィルターはメッセージに"billing"という文字列の含まれているロギングイベントをすべて拒否することになります。
    -    </p>
    -
    -    <p><a href="http://logback.qos.ch/xref/chapters/filters/FilterEvents.html"><code>FilterEvents</code></a>アプリケーションでは、0〜9までの連番を付けられた10個のロギング要求を生成します。まずはフィルター無しで<code>FilterEvents</code>を実行してみましょう。</p>
    -		
    -<div class="source"><pre>
    -java chapters.filters.FilterEvents src/main/java/chapters/filters/basicConfiguration.xml
    -</pre></div>
    -		
    -		<p>次のように、全てのロギング要求が出力されます。</p>
    -
    -<div class="source"><pre>0    [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -0    [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -0    [main] ERROR chapters.filters.FilterEvents - <b>billing statement 6</b>
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 9</pre></div>
    -
    -
    -
    -		<p>この中から"billing statement"を取り除きたいものとします。上記の<em>basicEventEvaluator.xml</em>では、メッセージに"billing"を含むロギングイベントをフィルタリングするので、まさに今欲しいものです。</p>
    -
    -    <p><em>basicEventEvaluator.xml</em>を使って実行してみましょう。</p>
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/basicEventEvaluator.xml</p>
    -    <p>次のような出力になります。</p>
    -		
    -    <p class="source">0    [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -0    [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -0    [main] INFO  chapters.filters.FilterEvents - logging statement 9</p>
    -		
    -
    -    <p>Java評価式にはJavaのコードブロックを指定できます。つまり次のようなものでも正しい式なのです。</p>
    -
    -    <pre class="prettyprint source">&lt;evaluator&gt;
    -  &lt;expression&gt;
    -    if(logger.startsWith("org.apache.http"))
    -      return true;
    -
    -    if(mdc == null || mdc.get("entity") == null)
    -      return false;
    -
    -    String payee = (String) mdc.get("entity");
    -
    -    if(logger.equals("org.apache.http.wire") &amp;amp;&amp;amp; &lt;!-- &amp; encoded as &amp;amp; --&gt;
    -        payee.contains("someSpecialValue") &amp;amp;&amp;amp;
    -        !message.contains("someSecret")) {
    -      return true;
    -    }
    -
    -    return false;
    -  &lt;/expression&gt;
    -&lt;/evaluator&gt;</pre>
    -
    -
    - 	  <h2 class="doAnchor" name="matcher">マッチャー</h2>
    -
    -    <p><code>String</code>の<a href="http://java.sun.com/j2se/1.5.0/docs/api/java/lang/String.html#matches%28java.lang.String%29">matchs()</a>メソッドを使えば文字列のパターンマッチをすることができます。ですが、毎回<code>Pattern</code>(正規表現)オブジェクトをコンパイルするコストがかかるので、つまりフィルターが呼び出されるたびにコストがかかることになってしまいます。このオーバーヘッドを無くすため、<a href="http://logback.qos.ch/xref/ch/qos/logback/core/boolex/Matcher.html">Matcher</a>オブジェクトを事前に複数用意することができます。定義したマッチャーオブジェクトは評価式の中から名前で参照できるようになります。</p>
    -
    -    <p>マッチャーの使用例を見てみましょう。</p>
    -
    -    <p class="example">例:マッチャーの定義(<a href="http://logback.qos.ch/xref/chapters/filters/evaluatorWithMatcher.xml">logback-examples/src/main/java/chapters/filters/evaluatorWithMatcher.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;evaluatorWithMatcher&#39;);">Groovyとして表示</span>
    -
    -    <pre id="evaluatorWithMatcher" class="prettyprint source">&lt;configuration debug="true"&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter"&gt;
    -      &lt;evaluator&gt;        
    -        <b>&lt;matcher&gt;
    -          &lt;Name&gt;odd&lt;/Name&gt;
    -          &lt;!-- filter out odd numbered statements --&gt;
    -          &lt;regex&gt;statement [13579]&lt;/regex&gt;
    -        &lt;/matcher&gt;
    -        
    -        &lt;expression&gt;odd.matches(formattedMessage)&lt;/expression&gt;</b>
    -      &lt;/evaluator&gt;
    -      &lt;OnMismatch&gt;NEUTRAL&lt;/OnMismatch&gt;
    -      &lt;OnMatch&gt;DENY&lt;/OnMatch&gt;
    -    &lt;/filter&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%-4relative [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p><em>evaluatorWithMatcher.xml</em>の設定を使ってみましょう。</p>
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/evaluatorWithMatcher.xml</p>
    -    <p>コンソールには次のように出力されます。</p>
    -		
    -    <p class="source">260  [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -264  [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -264  [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -266  [main] ERROR chapters.filters.FilterEvents - billing statement 6
    -266  [main] INFO  chapters.filters.FilterEvents - logging statement 8</p>
    -
    -    <p>マッチャーを追加したければ、<code>matcher要素</code>を追加すればよいでしょう。</p>
    -
    -
    -
    -
    -
    -    <!-- ================================================================ -->
    -    <!-- ===================== TURBO FILTER ============================= -->
    -    <!-- ================================================================ -->
    -
    -    <h2 class="doAnchor" name="TurboFilter">TurboFilters</h2>
    -    
    -    <p><code>ターボフィルター</code>とは、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/turbo/TurboFilter.html"><code>TurboFilter</code></a>抽象クラスを継承したオブジェクトのことです。通常フィルターと同様に、三値論理でロギングイベントを評価します。
    -    </p>
    -    
    -    <p>全体的に前に説明したフィルターと同じように動作します。ただし、<code>Filter</code>と<code>TurboFilter</code>には大きな違いが2つあります。
    -    </p>
    -    
    -   	<p><code>TurboFilter</code>はロギングコンテキストに紐付けられています。したがって、アペンダーが使用されたときにだけ呼ばれるのではなく、ロギング要求が発生するたびに呼ばれることになります。つまり、ターボフィルターの有効範囲はアペンダーに割り当てられたフィルターよりも広いのです。
    -   	</p>
    -   	
    -   	<p>さらに重要なのは、ターボフィルターが呼ばれるのは<code>LoggingEvent</code>オブジェクトが作成される前だということです。
    -   	<code>TurboFilter</code>オブジェクトは、ロギング要求をフィルタリングするのにロギングイベントを必要としません。つまり、ターボフィルターはロギングイベントの高速なフィルタリングを意図したものなのです。
    -    </p>
    -
    -   	
    -   	<h3 class="doAnchor" name="yourOwnTurboFilter">ターボフィルターを自作する</h3>
    -    
    -    <p><code>ターボフィルター</code>を自作するには、<code>TurboFilter</code>抽象クラスを継承するだけです。前述のとおり、フィルターを自作するには<code>decide()</code>メソッドを実装するだけでいいのです。少し複雑なフィルターの実装例を見てみましょう。</p>
    -    
    -    <p class="example">例:基本的な自作<code>TurboFilter</code>(<a href="http://logback.qos.ch/xref/chapters/filters/SampleTurboFilter.html">logback-examples/src/main/java/chapters/filters/SampleTurboFilter.java</a>)</p>
    -
    -<pre class="prettyprint source">package chapters.filters;
    -
    -import org.slf4j.Marker;
    -import org.slf4j.MarkerFactory;
    -
    -import ch.qos.logback.classic.Level;
    -import ch.qos.logback.classic.Logger;
    -import ch.qos.logback.classic.turbo.TurboFilter;
    -import ch.qos.logback.core.spi.FilterReply;
    -
    -public class SampleTurboFilter extends TurboFilter {
    -
    -  String marker;
    -  Marker markerToAccept;
    -
    -  @Override
    -  public FilterReply decide(Marker marker, Logger logger, Level level,
    -      String format, Object[] params, Throwable t) {
    -
    -    if (!isStarted()) {
    -      return FilterReply.NEUTRAL;
    -    }
    -
    -    if ((markerToAccept.equals(marker))) {
    -      return FilterReply.ACCEPT;
    -    } else {
    -      return FilterReply.NEUTRAL;
    -    }
    -  }
    -
    -  public String getMarker() {
    -    return marker;
    -  }
    -
    -  public void setMarker(String markerStr) {
    -    this.marker = markerStr;
    -  }
    -
    -  @Override
    -  public void start() {
    -    if (marker != null &amp;&amp; marker.trim().length() &gt; 0) {
    -      markerToAccept = MarkerFactory.getMarker(marker);
    -      super.start(); 
    -    }
    -  }
    -}
    -</pre>
    -
    -		<p>この<code>ターボフィルター</code>は、特定のマーカーが含まれているロギングイベントを受け付けます。マーカーが見つからなかったら、チェーン内の次のフィルターに引き継ぎます。
    -		</p>
    -		
    -		<p>柔軟性を考慮して、チェックするマーカーを設定ファイルで指定できるよう、アクセサメソッドが定義されています。他にも、設定ファイルの解釈中に、指定されたオプションをチェックするため、<code>start()</code>メソッドを実装しています。
    -		</p>
    -		
    -		<p>自作した<code>ターボフィルター</code>を使う設定ファイルは次のとおりです。
    -		</p>
    -		
    -    <p class="example">例:基本的な自作<code>TurboFilter</code>の設定(<a href="http://logback.qos.ch/xref/chapters/filters/sampleTurboFilterConfig.xml">logback-examples/src/main/java/chapters/filters/sampleTurboFilterConfig.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;sampleTurboFilterConfig&#39;);">Groovyとして表示</span>
    -
    -    <pre id="sampleTurboFilterConfig" class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;turboFilter class="chapters.filters.SampleTurboFilter"&gt;
    -    &lt;Marker&gt;sample&lt;/Marker&gt;
    -  &lt;/turboFilter&gt;</b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level %logger - %msg%n
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>  
    -
    -   	<p>logback-classicの配布物にはいくつか<code>TurboFilter</code>の実装クラスが含まれています。<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/turbo/MDCFilter.html"><code>MDCFilter</code></a>を使うとMDC内の指定された値の存在をチェックすることができますし、<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/turbo/DynamicThresholdFilter.html"><code>DynamicThresholdFilter</code></a>を使うとMDCのキーまたはレベルをしきい値でフィルタリングすることができます。また、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/turbo/MarkerFilter.html"><code>MarkerFilter</code></a>を使うとロギング要求に関連付けられた特定のマーカーの存在をチェックすることができます。</p>
    -   	
    -   	<p><code>MDCFilter</code>と<code>MarkerFilter</code>の両方を使う設定を見てみましょう。
    -   	</p>
    -   	
    -    <p class="example">例:<code>MDCFilter</code>と<code>MarkerFilter</code>の設定例(<a href="http://logback.qos.ch/xref/chapters/filters/turboFilters.xml">logback-examples/src/main/java/chapters/filters/turboFilters.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;turboFilters&#39;);">Groovyとして表示</span>
    -    <pre id="turboFilters" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;turboFilter class="ch.qos.logback.classic.turbo.MDCFilter"&gt;
    -    &lt;MDCKey&gt;username&lt;/MDCKey&gt;
    -    &lt;Value&gt;sebastien&lt;/Value&gt;
    -    &lt;OnMatch&gt;ACCEPT&lt;/OnMatch&gt;
    -  &lt;/turboFilter&gt;
    -	
    -  &lt;turboFilter class="ch.qos.logback.classic.turbo.MarkerFilter"&gt;
    -    &lt;Marker&gt;billing&lt;/Marker&gt;
    -    &lt;OnMatch&gt;DENY&lt;/OnMatch&gt;
    -  &lt;/turboFilter&gt;
    -
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="INFO"&gt;
    -    &lt;appender-ref ref="console" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -		<p>次のコマンドを実行してみましょう。</p>
    -    
    -    <p class="source">java chapters.filters.FilterEvents src/main/java/chapters/filters/turboFilters.xml</p>
    -
    -		<p>前に見たように、<a href="http://logback.qos.ch/xref/chapters/filters/FilterEvents.html"><code>FilterEvents</code></a>アプリケーションは0〜9の連番を付けて10個のロギング要求を生成します。3番目と6番目を除く他のロギング要求のログレベルは<em>INFO</em>です。これはルートロガーに割り当てたログレベルと同じです。3番目のロギング要求のログレベルは<em>DEBUGレベル</em>で、これは有効レベルを下回っています。ですが、3番目のロギング要求を生成する直前に、MDCのキー"username"には値"sebastien"が設定され、直後に取り除かれています。そして、<code>MDCFIlter</code>はこのロギング要求だけを受け入れるようになっています。6番目のロギング要求はログレベル<em>ERROR</em>で、かつ、"billing" というマーカーが指定されています。このロギング要求はMarkerFilter(二つ目のターボフィルター)によって拒否されます。
    -		</p>
    -		
    -		<p>結果として、<code>FilterEvents</code>アプリケーションに<em>turboFilters.xml</em>を指定した場合は次のように出力されます。</p>
    -
    -    <p class="source">2006-12-04 15:17:22,859 [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -2006-12-04 15:17:22,875 [main] DEBUG chapters.filters.FilterEvents - logging statement 3
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 7
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 8
    -2006-12-04 15:17:22,875 [main] INFO  chapters.filters.FilterEvents - logging statement 9</p>
    -			
    -			
    -		<p>有効レベルは<em>INFO</em>なのに、3番目のロギング要求、つまり、ログレベルがDEBUGのロギング要求が出力されています。これは最初の<code>TurboFilter</code>が受け入れたからです。
    -		</p>    
    -		
    -		<p>また、6番目のロギング要求はログレベルが<em>ERROR</em>なのに出力されていません。二つ目の<code>TurboFilter</code>の<span class="option">OnMatch</span>プロパティに<em>DENY</em>が指定されていたからです。
    -		</p>
    -		
    -
    -
    -		  
    -    <h3 class="doAnchor" name="DuplicateMessageFilter">DuplicateMessageFilter</h3>
    -
    -    <p><code>DuplicateMessageFilter</code>の利点は異なる見え方をします。メッセージの重複を検出し、一定回数以上繰り返す場合は、メッセージを破棄します。
    -    </p>
    -
    -    <p>繰り返しの検出は、単純に文字列が一致するかどうかを見ています。数文字違うだけでそれは別のメッセージとして扱われるので、重複メッセージとしては検出しません。たとえばこんな風に書いたとしましょう。</p>
    -
    -    <pre class="prettyprint source">logger.debug("Hello "+name0);
    -logger.debug("Hello "+name1);</pre>
    -  
    -    <p><code>name0</code>と<code>name1</code>が別の値だとしたら、これらのメッセージは別のものであるとみなされます。利用者のニーズによりますが、将来のリリースでは文字列の類似度をチェックすることになりそうです。完全に同一ではないけどよく似ているメッセージの繰り返しを排除するたmです。
    -    </p>
    -
    -    <p>ロギングメッセージに引数を指定している場合、書式化される前のメッセージが判定対象になるので注意してください。たとえば次の二つのロギング式のメッセージ部分はどちらも同じ "Hello {}." なので、これは重複メッセージと判定されます。
    -    </p>
    -
    -    <pre class="prettyprint source">logger.debug("Hello {}.", name0);
    -logger.debug("Hello {}.", name1);</pre>
    -  
    -    <p>繰り返しを許容する回数は<span class="option">AllowedRepetitions</span>プロパティで指定します。allowedRepetitionsプロパティに1を指定した場合、最初のメッセージは出力されて、2番目のメッセージは破棄されます。同様に、2を指定したら、1番目、2番目のメッセージは出力されて、三番目以降のメッセージは破棄されます。デフォルトは5が設定されています。
    -    </p>
    -
    -    <p>繰り返しを検出するには、内部的に古いメッセージへの参照をキャッシュしておかなければなりません。このキャッシュのサイズは<span class="option">CacheSize</span>プロパティによって決まります。デフォルトは100(個)が設定されています。
    -    </p>
    -
    -    
    -    <p class="example">例:<code>DuplicateMessageFilter</code>の設定例(<a href="http://logback.qos.ch/xref/chapters/filters/duplicateMessage.xml">logback-examples/src/main/java/chapters/filters/duplicateMessage.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;duplicateMessage&#39;);">Groovyとして表示</span>
    -    <pre id="duplicateMessage" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;turboFilter class="ch.qos.logback.classic.turbo.DuplicateMessageFilter"/&gt;</b>
    -
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="INFO"&gt;
    -    &lt;appender-ref ref="console" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -  <p><code>FilterEvents</code>アプリケーションに<em>duplicateMessage.xml</em>を指定した場合の出力は次のようになります。</p>
    -
    -    <p class="source">2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 0
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 1
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 2
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 4
    -2008-12-19 15:04:26,156 [main] INFO  chapters.filters.FilterEvents - logging statement 5
    -2008-12-19 15:04:26,171 [main] ERROR chapters.filters.FilterEvents - billing statement 6</p>
    -
    -    <p>"logging statement 0" は、書式化する前のメッセージ"logging statement {}" によって出力された<em>最初</em>のメッセージです。"logging statement 1"が1回目の<em>繰り返し</em>、"logging statement 2"が2回目の繰り返しとなります。<em>3回目</em>の繰り返しとなる"logging statement 3"はログレベルがDEBUGのはずなので、<a href="http://logback.qos.ch/manual/architecture.html#basic_selection">基本的な選択ルール</a>によって破棄されました。つまり、ターボフィルターは基本的な選択ルールを含む他のフィルターに先駆けて呼び出されるということなのです。したがって、後続の処理チェインの中で破棄されてしまうのですが、<code>DuplicateMessageFilter</code>は"logging statement 3"を繰り返しメッセージだと判断したはずです。したがって "logging statement 4" は4回目の繰り返し、"logging statement 5" は5回目の繰り返しになります。デフォルトで許されている繰り返しは5回なので、"logging statement 5"より後は出てきませんでした。
    -    </p>
    -
    -    <h1 class="doAnchor" name="logbac-access">logback-access モジュール</h1>
    -    
    -    <p>logback-access モジュールは logback-classic モジュールとほとんど同じ機能を提供します。具体的には、<code>Filter</code>オブジェクトはlogback-classic と同じように利用可能できますし、同じように動作します。一点だけ大きく違うころがあって、それは<code>LoggingEvent</code>のインスタンスではなく <a href="http://logback.qos.ch/xref/ch/qos/logback/access/spi/AccessEvent.html"><code>AccessEvent</code></a>のインスタンスを使うということです。現時点では、logback-access の配布物に含まれているフィルターの数はそれほど多くありません。追加のフィルターを提案したいときは、logback-dev メーリングリスト宛に連絡してください。
    -    </p>
    -
    -		<h2 class="doAnchor" name="countingFilter"><code>CountingFilter</code></h2>
    -		
    -		<p>logback-access では、<a href="http://logback.qos.ch/manual/xref/ch/qos/logback/access/filter/CountingFilter.html"><code>CountingFilter</code></a>を使ってWebサーバへのアクセス統計情報を集めることができます。<code>CountingFilter</code>は、初期化時に実行プラットフォームの JMX サーバーに自身を MBean として登録します。その後は、MBean に統計情報を問い合わせることができるようになります。分平均、時間平均、日平均、週平均、月平均などです。他にも、集計単位の一つ前の情報と全体の合計を参照することができます。
    -		</p>
    -		
    -		<p><code>CountingFilter</code>を使用する設定ファイルを見てみましょう。</p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;
    -
    -  <b>&lt;filter class="ch.qos.logback.access.filter.CountingFilter"&gt;
    -    &lt;name&gt;countingFilter&lt;/name&gt;
    -  &lt;/filter&gt;</b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%h %l %u %t \"%r\" %s %b&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender-ref ref="STDOUT" /&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p><code>CountingFilter</code>の収集する統計情報は、例えば<code>jconsole</code>から JMX サーバにアクセスして参照することができます。</p>
    -
    -
    -    <img alt="jconsoleを経由してCountingFilterにアクセス" src="images/chapters/filters/countingFilter.png">
    -	
    -
    -    <h3 class="doAnchor" name="access_EvalutorFilter">EvaluatorFilter</h3>
    -
    -    
    -    <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/filter/EvaluatorFilter.html"><code>EvaluatorFilter</code></a>は<code>EventEvaluator</code>をカプセル化した汎用的なフィルターです。名前が示すように、 <code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/boolex/EventEvaluator.html">EventEvaluator</a></code>は指定された条件を評価して、イベントがその条件を満たすかどうかを判定します。条件を満たす場合もそうでない場合も、<code>EvaluatorFilter</code>の<span class="option">onMatch</span>プロパティ、または、<span class="option">onMismatch</span>プロパティに指定された値を返します。<code>EvaluatorFilter</code>についてはlogback-classicモジュールの章で説明してあるので思い出してください(<a href="http://logback.qos.ch/manual/filters.html#evalutatorFilter">上記参照</a>)。ここの説明は前の章に記載した内容の繰り返しです。</p>
    -
    -
    -    <p><code>EventEvaluator</code>は抽象クラスです。
    -つまり、<code>EventEvaluator</code>を継承すれば、独自のイベント評価ロジックを実装することができます。
    -logback-accessの配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/access/boolex/JaninoEventEvaluator.html">JaninoEventEvaluator</a>という実装クラスが含まれています。これは、booleanを返す任意のJava言語のブロックを評価するものです。私たちはこのJava言語で書かれた式のことを"<em>Java評価式</em>”と呼んでいます。
    -Java評価式を使うとロギングイベントを柔軟にフィルタリングできるようになります。
    -<code>JaninoEventEvaluator</code>を使用するには<a href="http://docs.codehaus.org/display/JANINO/Home">Janinoライブラリ</a>が必要です。
    -設定方法は設定ドキュメントの<a href="http://logback.qos.ch/setup.html#janino">対応するセクション</a>を参照してください。
    -
    -    </p>
    -
    -    <p>Java評価式は設定ファイルを解釈する間にコンパイルされます。
    -どのように呼び出すのか、利用者は気にすることはありません。
    -ですが、Java言語の式が真偽値を返すものであることを保証するのは利用者の責任です。
    -</p>
    -
    -
    -    <p>Java評価式は一度に1つのイベントを扱います。logback-accessは、<code>AccessEvent</code>を<b><code>event</code></b>という名前の変数として公開します。<code>event</code>変数を介して、HTTPリクエストやHTTP応答に関連付けられたさまざまなデータを参照することができます。正確なところは<a href="http://logback.qos.ch/xref/ch/qos/logback/access/spi/AccessEvent.html">AccessEvent</a>クラスの<code>ソースコード</code>を読んでください。
    -    </p>
    -    
    -    <p>次の設定ファイルでは、応答コード<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">404(Not Found)</a>をひっかけます。つまり、応答コードが404になったHTTPリクエストをすべてコンソールに出力するのです。</p>
    -   	
    -    <p class="example">例:Access Evaluator(<a href="http://logback.qos.ch/xref/chapters/filters/accessEventEvaluator.xml">logback-examples/src/main/java/chapters/filters/accessEventEvaluator.xml</a>)</p>
    -
    -<pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    <b>&lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter"&gt;
    -      &lt;evaluator&gt;
    -        &lt;expression&gt;event.getStatusCode() == 404&lt;/expression&gt;
    -      &lt;/evaluator&gt;
    -      &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;
    -    &lt;/filter&gt;</b>
    -   &lt;encoder&gt;&lt;pattern&gt;%h %l %u %t %r %s %b&lt;/pattern&gt;&lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender-ref ref="STDOUT" /&gt;
    -&lt;/configuration&gt;</pre>
    -
    -		<p>次の設定ファイルでは、やはり404エラーをひっかけているのですが、CSSファイルを要求したものだけをひっかけています。
    -		</p>	
    -
    -
    -    <p class="example">例6.10:Access Evaluator(<a href="http://logback.qos.ch/xref/chapters/filters/accessEventEvaluator2.xml">logback-examples/src/main/java/chapters/filters/accessEventEvaluator2.xml</a>)</p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter"&gt;
    -      &lt;evaluator name="Eval404"&gt;
    -        &lt;expression&gt;
    -         <b>(event.getStatusCode() == 404)</b>
    -           <b>&amp;amp;&amp;amp;</b>  &lt;!-- ampersand characters need to be escaped --&gt;
    -         <b>!(event.getRequestURI().contains(".css"))</b>
    -        &lt;/expression&gt;
    -      &lt;/evaluator&gt;
    -      &lt;onMismatch&gt;DENY&lt;/onMismatch&gt;
    -    &lt;/filter&gt;
    -
    -   &lt;encoder&gt;&lt;pattern&gt;%h %l %u %t %r %s %b&lt;/pattern&gt;&lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;appender-ref ref="STDOUT" /&gt;
    -&lt;/configuration&gt;
    -    </pre>
    -	
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -  </div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/groovy.html b/logback-site/src/site/pages/manual/groovy.html
    deleted file mode 100755
    index d82c101fa3..0000000000
    --- a/logback-site/src/site/pages/manual/groovy.html
    +++ /dev/null
    @@ -1,547 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter12: Groovy configuration</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -
    -    <script type="text/javascript">prefix='../'</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content" class="chapter">
    -      
    -    <h1>Chapter 12: Groovy Configuration</h1>
    -
    -    <a href="groovy_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -      
    -
    -    <div class="quote">
    -      <p><em>It is better to be a human being dissatisfied than a pig
    -      satisfied; better to be a Socrates dissatisfied than a fool
    -      satisfied. And if the fool or the pig thinks otherwise, it is
    -      because they have no experience of the better part.
    -      </em>
    -      </p>
    -      <p>&mdash;JOHN STUART MILL, <em>Utilitarianism</em></p>
    -    </div>
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -
    -    <p>Domain-specific languages or DSLs are rather pervasive. The
    -    XML-based logback configuration can be viewed as a DSL
    -    instance. By the very nature of XML, XML-based configuration files
    -    are quite verbose and rather bulky. Moreover, a relatively large
    -    body of code in logback, namely Joran, is dedicated to processing
    -    these XML-based configuration files. Joran supports nifty features
    -    such as variable substitution, conditional processing and
    -    on-the-fly extensibility. However, not only is Joran a complex
    -    beast, the user-experience it provides can be described as
    -    unsatisfactory or at the very least unintuitive.
    -    </p>
    -
    -    <p>The Groovy-based DSL described in this chapter aims to be
    -    consistent, intuitive, and powerful. Everything you can do using XML in
    -    configuration files, you can do in Groovy with a much shorter
    -    syntax. To help you migrate to Groovy style configuration, we have
    -    developed a <a
    -    href="http://logback.qos.ch/translator/asGroovy.html">tool to
    -    automatically migrate your existing <em>logback.xml</em> files to
    -    <em>logback.groovy</em></a>.
    -    </p>
    -
    -
    -    <h2 class="doAnchor">General philosophy</h2>
    -    
    -    <p>As a general rule, <em>logback.groovy</em> files are Groovy
    -    programs. And since Groovy is a super-set of Java, whatever
    -    configuration actions you can perform in Java, you can do the same
    -    within a <em>logback.groovy</em> file. However, since configuring
    -    logback programmatically using Java syntax can be cumbersome, we
    -    added a few logback-specific extensions to make your life
    -    easier. We try hard to limit the number of logback-specific
    -    syntactic extensions to an absolute minimum. If you are already
    -    familiar with Groovy, you should be able to read, understand and
    -    even write your own <em>logback.groovy</em> files with great
    -    ease. Those unfamiliar with Groovy should still find
    -    <em>logback.groovy</em> syntax much more comfortable to use than
    -    <em>logback.xml</em>.
    -    </p>
    -
    -    <p>Given that <em>logback.groovy</em> files are Groovy programs
    -    with minimal logback-specific extensions, <em>all</em> the usual
    -    groovy constructs such as class imports, variable definitions,
    -    evaluation of $&#123;..&#125; expressions contained in strings
    -    (GStrings), and if-else statements are available in
    -    <em>logback.groovy</em> files.</p>
    -
    -    <h2 class="doAnchor">Automatic imports</h2>
    -
    -    <p><span class="label">Since 1.0.10</span> In order to reduce
    -    unnecessary boilerplate several common types and packages are
    -    imported automatically.  Thus, as long as you are only configuring
    -    built-in appenders, layouts etc. you do not need to add the
    -    corresponding import statement into your script. You will need
    -    them for types not covered by the default imports, of course.</p>
    -
    -    <p>Here is the list of default imports:</p>
    -
    -    <ul>
    -      <li><span class="code">import ch.qos.logback.core.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.encoder.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.read.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.rolling.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.status.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.classic.net.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.classic.encoder.PatternLayoutEncoder;</span></li>
    -    </ul>
    -
    -    <p>In addition, all constants in <span
    -    class="code">ch.qos.logback.classic.Level</span> are statically
    -    imported as is (uppercase) and as lowercased aliases. It follows
    -    that your scripts can reference both <em>INFO</em> or
    -    <em>info</em> without a static import statement.</p>
    -
    -
    -    <h2 class="doAnchor" name="sift">SiftingAppender no longer supported</h2>
    -
    -    <p><span class="label">Since version 1.0.12</span>
    -    <code>SiftingAppender</code> is no longer supported within groovy
    -    configuration files. However, in case there is demand, it may be
    -    re-introduced.</p>
    -
    -    <h2 class="doAnchor" name="entensions">Extensions specific to
    -    <em>logback.groovy</em></h2>
    -
    -    <p><span class="green">Essentially, <em>Logback.groovy</em> syntax
    -    consists of half a dozen methods described next; in the reverse
    -    order of their customary appearance. </span>Strictly speaking, the
    -    order of invocation of these methods does NOT matter, with one
    -    exception: appenders MUST be defined before they can be attached
    -    to a logger.</p>
    -
    -    
    -
    -    <!-- ========================================================== -->
    -
    -    <h3> &#8226; <span class="code">root(Level level, List&lt;String> appenderNames = [])</span></h3>
    -
    -    <p>The <code>root</code> method can be used to set the level of
    -    the root logger. As an optional second argument of type
    -    <code>List&lt;String></code>, can be used to attach previously
    -    defined appenders by name. If you do not specify the list of
    -    appender names, then an empty list is assumed. In Groovy, an empty
    -    list is denoted by <code>[]</code>.</p>
    -
    -    <p>To set the level of the root logger to WARN, you would write:</p>
    -
    -    <pre class="prettyprint source">root(WARN)</pre>
    -
    -    <p>To set the level of the root logger to INFO, and attach 
    -    appenders named "CONSOLE" and "FILE" to root, you would write:</p>
    -
    -    <pre class="prettyprint source">root(INFO, ["CONSOLE", "FILE"])</pre>
    -
    -    <p>In the previous example, it is assumed that the appenders named
    -    "CONSOLE" and "FILE" were already defined. Defining appenders will
    -    be discussed shortly.
    -    </p>
    -
    -    <!-- ========================================================== -->
    -
    -    <h3>&#8226; <span class="code">logger(String name, Level level, List&lt;String> appenderNames = [], <br/>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Boolean additivity = null)</span></h3>
    -
    -    <p>The <code>logger()</code> method takes four arguments, of which
    -    the last two are optional. The first argument is the name of the
    -    logger to configure. The second argument is the level of the
    -    designated logger. Setting the level of a logger to
    -    <code>null</code> forces it to <a
    -    href="architecture.html#effectiveLevel">inherit its level</a> from
    -    its nearest ancestor with an assigned level. The third argument of
    -    type <code>List&lt;String></code> is optional and defaults to an
    -    empty list if omitted. The appender names in the list are attached
    -    to the designated logger. The fourth argument of type
    -    <code>Boolean</code> is also optional and controls the <a
    -    href="architecture.html#additivity">additivity flag</a>. If
    -    omitted, it defaults to <code>null</code>.
    -    </p> 
    -
    -    <p>For example, the following script sets the level of the
    -    "com.foo" logger to INFO.</p>
    -
    -       <pre class="prettyprint source">logger("com.foo", INFO)</pre>
    -
    -    <p>The next script sets the level of the "com.foo" logger to
    -    DEBUG, and attaches the appender named "CONSOLE" to it.</p>
    -
    -  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"])</pre>
    -    
    -   <p>The next script is similar to the previous one, except that it
    -   also sets the the additivity flag of the "com.foo" logger to
    -   false.</p>
    -
    -  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"], false)</pre>
    -
    -
    -    <!-- ========================================================== -->
    -    <h3>&#8226; <span class="code">appender(String name, Class clazz, Closure closure = null)</span></h3>
    -
    -    <p>The appender method takes the name of the appender being
    -    configured as its first argument. The second mandatory argument is
    -    the class of the appender to instantiate. The third argument is a
    -    closure containing further configuration instructions. If omitted,
    -    it defaults to null.</p>
    -
    -    <p>Most appenders require properties to be set and sub-components
    -    to be injected to function properly. Properties are set using the
    -    '=' operator (assignment). Sub-components are injected by invoking
    -    a method named after the property and passing that method the
    -    class to instantiate as an argument. This convention can be
    -    applied recursively to configure properties as well as
    -    sub-components of any appender sub-component. This approach is at
    -    the heart of <em>logback.groovy</em> scripts and is probably the
    -    only convention that needs learning.</p>
    -    
    -    <p>For example, the following script instantiates a
    -    <code>FileAppender</code> named "FILE", setting its <span
    -    class="option">file</span> property to "testFile.log" and its
    -    <span class="option">append</span> property to false. An encoder
    -    of type <code>PatternLayoutEncoder</code> is injected into the
    -    appender. The pattern property of the encoder is set to "%level
    -    %logger - %msg%n". The appender is then attached to the root
    -    logger.</p>
    -
    -    <pre class="prettyprint source">appender("FILE", FileAppender) {
    -  file = "testFile.log"
    -  append = true
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%level %logger - %msg%n"
    -  }
    -}
    -
    -root(DEBUG, ["FILE"])</pre>
    -
    -    <p>
    -    </p>
    -
    -    
    -    <!-- ========================================================== -->        
    -    <h3>&#8226; <span class="code">timestamp(String datePattern, long timeReference = -1)</span></h3>
    -
    -    <p>The <code>timestamp()</code> method method returns a string
    -    corresponding to the <code>timeReference</code> parameter
    -    formatted according to the <code>datePattern</code> parameter. The
    -    <code>datePattern</code> parameter should follow the conventions
    -    defined by <a
    -    href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>. If
    -    the <code>timeReference</code> value is unspecified, it defaults
    -    to -1, in which case current time, that is time when the
    -    configuration file is parsed, is used as the time
    -    reference. Depending on the circumstances, occasion, you might
    -    wish to use <code>context.birthTime</code> as the time reference.
    -    </p>
    -
    -    <p>In the next example, the <code>bySecond</code> variable is
    -    assigned the current time in the "yyyyMMdd'T'HHmmss" format. The
    -    "bySecond" variable is then used to define the value of the <span
    -    class="option">file</span> property.
    -    </p>
    -
    -<pre class="prettyprint source"><b>def bySecond = timestamp("yyyyMMdd'T'HHmmss")</b>
    -
    -appender("FILE", FileAppender) {
    -  <b>file = "log-${bySecond}.txt"</b>
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%logger{35} - %msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -    <!-- ========================================================== -->        
    -    <h3>&#8226; <span class="code">conversionRule(String conversionWord, Class converterClass)</span></h3>
    -
    -    <p>After creating your own <a
    -    href="layouts.html#customConversionSpecifier">conversion
    -    specifier</a>, you need to inform logback of its existence. Here
    -    is a sample logback.groovy file which instructs logback to use
    -    MySampleConverter whenever the <code>%sample</code> conversion
    -    word is encountered.
    -    </p>
    -
    -    <pre class="prettyprint source">
    -import chapters.layouts.MySampleConverter
    -
    -conversionRule("sample", MySampleConverter)
    -appender("STDOUT", ConsoleAppender) {
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%-4relative [%thread] %<b>sample</b> - %msg%n"
    -  }
    -}
    -root(DEBUG, ["STDOUT"])</pre>
    -
    -   <!-- ========================================================== -->
    -   <h3>&#8226; <span class="code">scan(String scanPeriod = null)</span></h3>
    -
    -    <p>Invoking the scan() method instructs logback to periodically
    -    scan the logback.groovy file for changes. Whenever a change is
    -    detected, the <em>logback.groovy</em> file is reloaded.</p>
    -
    -    <pre class="prettyprint source">scan()</pre>
    -
    -    <p>By default, the configuration file will be scanned for changes
    -    once every minute. You can specify a different scanning period by
    -    passing a "scanPeriod" string value. Values can be specified in
    -    units of milliseconds, seconds, minutes or hours. Here is an
    -    example:
    -    </p>
    -
    -    <pre class="prettyprint source">scan("30 seconds")</pre>
    -    
    -    <p>If no unit of time is specified, then the unit of time is
    -    assumed to be milliseconds, which is usually inappropriate. If you
    -    change the default scanning period, do not forget to specify a
    -    time unit. For additional details on how scanning works, please
    -    refer to the <a href="configuration.html#autoScan">section on
    -    automatic reloading</a>.
    -    </p>
    -    
    -    <!-- ========================================================== -->
    -  
    -    <h3>&#8226; <span class="code">statusListener(Class listenerClass)</span></h3>
    -
    -    <p>You can add a status listener by invoking the
    -    <code>statusListener</code> method and passing a listener class as
    -    an argument. Here is an example:</p>
    -
    -    <pre class="prettyprint source">import chapters.layouts.MySampleConverter
    -
    -<b>// We highly recommended that you always add a status listener just</b>
    -<b>// after the last import statement and before all other statements</b>
    -<b>statusListener(OnConsoleStatusListener)</b></pre>
    - 
    -    <p><a href="configuration.html#statusListener">Status listeners</a> were described in an earlier
    -    chapter.</p>
    -
    -    <h3>&#8226; <span class="code">jmxConfigurator(String name)</span></h3>
    -
    -    <p>You can register a <a href="jmxConfig.html"><code>JMXConfigurator</code></a>
    -    MBean with this method. Invoke it without any parameters to use Logback's
    -    default ObjectName
    -    (<code>ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator</code>)
    -    for the registered MBean:</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator()</pre>
    -
    -    <p>To change the value of the <code>Name</code> key to something other than "default",
    -    simply pass in a different name as the parameter for the <code>jmxConfigurator</code>
    -    method:</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator('MyName')</pre>
    -
    -    <p>If you want define the ObjectName completely, use the same syntax but
    -    pass in a valid ObjectName string representation as the parameter:</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator('myApp:type=LoggerManager')</pre>
    -
    -    <p>The method will first attempt to use the parameter as an ObjectName,
    -    and falls back to treating it as the value for the "Name" key if it doesn't
    -    represent a valid ObjectName.</p>
    -
    -    <!-- ========================================================== -->
    -
    -    <h2 class="doAnchor" name="internalDSL">Internal DSL, i.e. it's
    -    all groovy baby!</h2>
    -
    -    <p>The <em>logback.groovy</em> is an internal DSL meaning that its
    -    contents are executed as a Groovy script. Thus, all the usual
    -    Groovy constructs such as class imports, GString, variable
    -    definitions, evaluation of $&#123;..&#125; expressions contained
    -    within strings (GStrings), if-else statements are all available in
    -    logback.groovy files. In the following discussion, we will present
    -    typical uses of these Groovy constructs in <em>logback.groovy</em>
    -    files.
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="varedef">Variable definitions and
    -    GStrings</h3>
    -
    -    <p>You can define variables anywhere within a
    -    <em>logback.groovy</em> file, then use the variable within a
    -    GString. Here is an example.</p>
    -
    -    <pre class="prettyprint source">// define the USER_HOME variable setting its value 
    -// to that of the "user.home" system property
    -<b>def USER_HOME = System.getProperty("user.home")</b>
    -
    -appender("FILE", FileAppender) {
    -  // make use of the USER_HOME variable
    -  <b>file = "${USER_HOME}/myApp.log"</b>
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -    <h3 class="doAnchor" name="printing">Printing on the console</h3>
    -
    -    <p>You can invoke Groovy's <code>println</code> method to print on
    -    the console. Here is an example.</p>
    -
    -    <pre class="prettyprint source">def USER_HOME = System.getProperty("user.home");
    -<b>println "USER_HOME=${USER_HOME}"</b>
    -
    -appender("FILE", FileAppender) {
    -  <b>println "Setting [file] property to [${USER_HOME}/myApp.log]"</b>
    -  file = "${USER_HOME}/myApp.log"  
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -   <h3 class="doAnchor" name="automaticallyExported">Automatically
    -   exported fields</h3>
    -
    -   <h4 class="doAnchor" name="hostname">'hostname' variable</h4>
    -
    -   <p>The 'hostname' variable contains the name of the current
    -   host. However, due to scoping rules that the authors cannot fully
    -   explain, the 'hostname' variable is available only at the topmost
    -   scope but not in nested scopes. The next example should get the
    -   point across.
    -   </p>
    -
    - <pre class="prettyprint source">// will print "hostname is x" where x is the current host's name
    -println "Hostname is ${hostname}"
    -
    -appender("STDOUT", ConsoleAppender) {
    -  <b>// will print "hostname is null"</b>
    -  <b>println "Hostname is ${hostname}" </b>
    -}</pre>
    -
    -   <p>If you wish to have the hostname variable be seen in all scopes,
    -   you need to define another variable and assign it the value of
    -   'hostname' as shown next.</p>
    -
    - <pre class="prettyprint source">// define HOSTNAME by assigning it hostname
    -def HOSTNAME=hostname
    -// will print "hostname is x" where x is the current host's name
    -println "Hostname is ${HOSTNAME}"
    -
    -appender("STDOUT", ConsoleAppender) {
    -  // will print "hostname is x" where x is the current host's name
    -  println "Hostname is ${HOSTNAME}" 
    -}</pre>
    -
    -
    -   <h3 class="doAnchor" name="everythingIsContext">Everything is
    -   context aware with a reference to the current context</h3>
    -
    -   <p>The execution of the <em>logback.groovy</em> script is done
    -   within the scope of a <a
    -   href="../xref/ch/qos/logback/core/spi/ContextAware.html">ContextAware</a>
    -   object. Thus, the current context is always accessible using the
    -   '<code>context</code>' variable and you can invoke
    -   <code>addInfo</code>(), <code>addWarn</code>() and
    -   <code>addError</code>() methods to send status messages to the
    -   context's <code>StatusManager</code>.</p>
    -
    -   <pre class="prettyprint source">// always a good idea to add an on console status listener
    -statusListener(OnConsoleStatusListener)
    -
    -// set the context's name to wombat
    -<b>context.name = "wombat"</b>
    -// add a status message regarding context's name
    -<b>addInfo("Context name has been set to ${context.name}")</b>
    -
    -def USER_HOME = System.getProperty("user.home");
    -// add a status message regarding USER_HOME
    -<b>addInfo("USER_HOME=${USER_HOME}")</b>
    -
    -appender("FILE", FileAppender) {
    -  // add a status message regarding the file property
    -  <b>addInfo("Setting [file] property to [${USER_HOME}/myApp.log]")</b>
    -  file = "${USER_HOME}/myApp.log"  
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -   <h3 class="doAnchor">Conditional configuration</h3>
    -   
    -   <p>Given that Groovy is a fully-fledged programming language,
    -   conditional statements allow for a single <em>logback.groovy</em>
    -   file to adapt to various environments such as development, testing
    -   or production.</p>
    -
    -   <p>In the next script, a console appender is activated on hosts
    -   other than pixie or orion, our production machines. Note that the
    -   output directory of the rolling file appender also depends on the
    -   host.</p>
    -   
    -   <pre class="prettyprint source">// always a good idea to add an on console status listener
    -statusListener(OnConsoleStatusListener)
    -
    -def appenderList = ["ROLLING"]
    -def WEBAPP_DIR = "."
    -def consoleAppender = true;
    -
    -// does hostname match pixie or orion?
    -if (hostname =~ /pixie|orion/) {
    -  WEBAPP_DIR = "/opt/myapp"     
    -  consoleAppender = false   
    -} else {
    -  appenderList.add("CONSOLE")
    -}
    -
    -if (consoleAppender) {
    -  appender("CONSOLE", ConsoleAppender) {
    -    encoder(PatternLayoutEncoder) {
    -      pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    -    }
    -  }
    -}
    -
    -appender("ROLLING", RollingFileAppender) {
    -  encoder(PatternLayoutEncoder) {
    -    Pattern = "%d %level %thread %mdc %logger - %m%n"
    -  }
    -  rollingPolicy(TimeBasedRollingPolicy) {
    -    FileNamePattern = "${WEBAPP_DIR}/log/translator-%d{yyyy-MM}.zip"
    -  }
    -}
    -
    -root(INFO, appenderList)</pre>
    -
    -
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </div>
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/groovy_ja.html b/logback-site/src/site/pages/manual/groovy_ja.html
    deleted file mode 100644
    index 0d42614097..0000000000
    --- a/logback-site/src/site/pages/manual/groovy_ja.html
    +++ /dev/null
    @@ -1,364 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第12章 Groovyによる設定</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content" class="chapter">
    -      
    -    <h1>第12章 Groovyによる設定</h1>
    -      
    -      <div class="quote">
    -      <p><em>満足した豚になるより不満を抱えた人間になるほうがずっと良い。ましてや、満足気な愚か者になるより不満だらけのソクラテス派になるほうがずっと良い。豚や愚か者がこれとは異なる主張をしたとしても、それは彼らに良いとされる側の経験が無いからなのだ。
    -      </em>
    -      </p>
    -      <p>-ジョン·スチュアート·ミル、 <em>功利主義</em></p>
    -    </div>
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -
    -    <p>ドメイン固有言語やDSLはかなり普及しています。XMLベースのlogbackの設定は、DSLのインスタンスとみなすことができます。XMLの性質上、設定ファイルは非常に冗長でかさばるものになります。さらに、logbackのコードの大部分はXMLベースの設定ファイル処理専用のJoranと呼ばれるものです。Joranは、変数置換や条件分岐、および実行時の拡張など、気の利いた機能をサポートしています。しかし、Joranの問題は複雑さだけではありません。ユーザーエクスペリエンスは不十分ですし、直感とはかけ離れています。
    -    </p>
    -
    -    <p>この章で説明するGroovyベースのDSLは、一貫性があり、直感的で、かつ、強力であることを目指しています。XMLの設定ファイルでできることはすべて、それもより短い行数で実現することができます。Groovyスタイルの設定ファイルへの移行を支援するために、<a href="http://logback.qos.ch/translator/asGroovy.html">既存の<em>logback.xml</em>を自動的に<em>logback.groovy</em>へ変換するツールを用意しました</a>。
    -    </p>
    -
    -
    -    <h2 class="doAnchor">基本的な哲学</h2>
    -    
    -    <p>基本的なルールを説明します。<em>logback.groovy</em>はGroovyのプログラムです。Groovy言語はJava言語のスーパーセットなので、Javaにできるあらゆる設定アクションと同じことを<em>logback.groovy</em>で実行することができます。ですが、logbackをJavaの構文でプログラム的に設定するのはかなり厄介なので、logback専用の拡張構文を追加しました。logback専用の拡張構文はできるだけ少なくなるようにしましたし、実際のところほんのわずかしかありません。Groovyに慣れているとしても本章に目を通してもらって、<em>logback.groovy</em>を書くのは非常に簡単であることを理解してください。Groovyに不慣れな方であっても、<em>logback.xml</em>を使い続けるより<em>logback.groovy</em>の記法のほうがずっとわかりやすいと思えるようになるはずです。
    -    </p>
    -
    -    <p>改めて整理します。<em>logback.groovy</em>は最小限のlogback専用の拡張がなされたGroovyプログラムです。<em>logback.groovy</em>の中では、クラスのimportや変数定義、変数評価、GString の${..}記法、if-else構文などのGroovyの機能は<em>全て</em>利用可能です。</p>
    -
    -    <h2 class="doAnchor">自動import</h2>
    -
    -    <p><span class="label">logback1.0.10以降</span>決まりきったものになる共通するクラスやパッケージのimportをしなくてもすむように、自動的にimportします。したがって、組み込みのアペンダーやレイアウトについてはわざわざimport文を書かなくても設定だけでよいのです。もちろん、デフォルトのimportでカバーされていないクラスやパッケージがあるなら、それは自分でやらなければなりません。</p>
    -
    -    <p>デフォルトのimport対象は次のとおりです。</p>
    -
    -    <ul>
    -      <li><span class="code">import ch.qos.logback.core.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.encoder.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.read.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.rolling.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.core.status.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.classic.net.*;</span></li>
    -      <li><span class="code">import ch.qos.logback.classic.encoder.PatternLayoutEncoder;</span></li>
    -    </ul>
    -
    -    <p>さらに、<span class="code">ch.qos.logback.classic.Level</span>の全ての定数は、大文字バージョンと小文字バージョンのそれぞれでstatic importされます。つまり、スクリプトでは<em>INFO</em>と<em>info</em>のどちらでも利用できます。</p>
    -
    -
    -    <h2 class="doAnchor" name="sift">SiftingAppenderはサポートされなくなりました</h2>
    -
    -    <p><span class="label">logback1.0.12以降</span>Groovy設定ファイルでは<code>SiftingAppender</code>はサポートされなくなりました。需要がありそうなら復活するかもしれません。</p>
    -
    -    <h2 class="doAnchor" name="entensions"><em>logback.groovy</em>用の拡張構文</h2>
    -
    -    <p><span class="green">基本的に<em>logback.groovyの構文</em>は、次に説明する半ダースほどのメソッドで構成されています。これらは実際に定義する順番とは逆順に並んでいます。</span>厳密に言えば、これらのメソッドの呼び出し順序は1つの例外(アペンダーはそれを割り当てるロガーの前に定義しなければならない)を除いて重要ではありません。</p>
    -
    -    
    -
    -    <!-- ========================================================== -->
    -
    -    <h3> • <span class="code">root(Level level, List&lt;String&gt; appenderNames = [])</span></h3>
    -
    -    <p><code>root</code>メソッドはルートロガーのログレベルを設定するために使用します。第二引数のappenderName(<code>List&lt;String&gt;</code>)は任意で、ルートロガーに割り当てるアペンダーを名前で指定します。引数に値を指定しなければ、空のリストが指定されたものとして扱います。。Groovyでは空のリストを<code>[]</code>で記述します。</p>
    -
    -    <p>ルートロガーのログレベルにWARNを設定するには次のように記述します。</p>
    -
    -    <pre class="prettyprint source">root(WARN)</pre>
    -
    -    <p>ルートロガーのログレベルにINFOを設定し、"CONSOLE"アペンダーと"FILE"アペンダーを割り当てるには次のように記述します。</p>
    -
    -    <pre class="prettyprint source">root(INFO, ["CONSOLE", "FILE"])</pre>
    -
    -    <p>"CONSOLE"と"FILE"という名前のアペンダーはすでに定義されているものとします。アペンダーの定義の仕方はすぐ後で説明します。
    -    </p>
    -
    -    <!-- ========================================================== -->
    -
    -    <h3>• <span class="code">logger(String name, Level level, List&lt;String&gt; appenderNames = [], <br>         Boolean additivity = null)</span></h3>
    -
    -    <p><code>logger()</code>メソッドは引数を四つとります。後ろの二つは任意です。第一引数にはロガーの名前を指定します。第二引数にはロガーのログレベルを指定します。ログレベルに<code>null</code>を指定すると、直近の祖先ロガーに指定されたログレベルを<a href="./01-architecture.html#effectiveLevel">継承</a>するという意味になります。第三引数は<code>List&lt;String&gt;</code>で任意です。省略した場合は空のリストを指定したものとして扱います。リストにはロガーに割り当てるアペンダーの名前を並べます。第四引数は<code>Boolean</code>でこちらも任意です。<a href="./01-architecture.html#additivity">additivityフラグ</a>として使われます。省略した場合は<code>null</code>が指定されたものとして扱います。
    -    </p> 
    -
    -    <p>たとえば、次のスクリプトはロガー名として"com.foo"、ログレベルとしてINFOを設定します。</p>
    -
    -       <pre class="prettyprint source">logger("com.foo", INFO)</pre>
    -
    -    <p>次のスクリプトは、ロガー名として"com.foo"、ログレベルとしてDEBUG、そしてアペンダーに"CONSOLE"を割り当てます。</p>
    -
    -  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"])</pre>
    -    
    -   <p>次のスクリプトは前のスクリプトとほとんど同じですが、additivityフラグにfalseを設定します。</p>
    -
    -  <pre class="prettyprint source">logger("com.foo", DEBUG, ["CONSOLE"], false)</pre>
    -
    -
    -    <!-- ========================================================== -->
    -    <h3>• <span class="code">appender(String name, Class clazz, Closure closure = null)</span></h3>
    -
    -    <p>appender()メソッドでは、第一引数にアペンダーの名前を指定します。第二引数は必須で、インスタンス化するアペンダーのクラスを指定します。第三引数には、そのほかの設定をするクロージャーを指定します。省略した場合はnullになります。</p>
    -
    -    <p>ほとんどのアペンダーは、ちゃんと動作するためにプロパティを設定したりサブコンポーネントを注入しなければなりません。プロパティを設定するには '='演算子(代入)を使用します。サブコンポーネントを注入するには、プロパティ名をメソッド名のように記述して、引数にインスタンス化するクラスを指定します。このコーディング規約は、アペンダーのあらゆるサブコンポーネントについても同じように再帰的に適用されるものです。このアプローチは<em>logback.groovy</em>の中核を為すもので、覚えなければならない唯一の規約となるでしょう。</p>
    -    
    -    <p>例を見てみましょう。次のスクリプトは"FILE"という名前の<code>FileAppender</code>をインスタンス化して、<span class="option">file</span>プロパティに"testFile.log"を設定し、<span class="option">append</span>プロパティにfalseを設定しています。encoderには<code>PatternLayoutEncoder</code>を注入しています。encoderのpatternプロパティには" - %level %logger - %msg%n"を設定しています。そしてこのアペンダーをルートロガーに割り当てます。</p>
    -
    -    <pre class="prettyprint source">appender("FILE", FileAppender) {
    -  file = "testFile.log"
    -  append = true
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%level %logger - %msg%n"
    -  }
    -}
    -
    -root(DEBUG, ["FILE"])</pre>
    -
    -    <p>
    -    </p>
    -
    -    
    -    <!-- ========================================================== -->        
    -    <h3>• <span class="code">timestamp(String datePattern, long timeReference = -1)</span></h3>
    -
    -    <p><code>timestamp()</code>メソッドは、<code>datePattern</code>に指定された書式文字列で<code>timeReference</code>に指定されたlong値の時間を書式化した文字列を返します。第一引数の<code>datePattern</code>に指定する書式文字列は、<a href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html">SimpleDateFormat</a>で定義されている規則に従わなければなりません。第二引数の<code>timeReference</code>が省略された場合-1が指定されたものとして扱います。これは設定ファイルを解析しているときの現在日時を表す値です。状況によりますが、基準時間として<code>context.birthTime</code>を使うこともあるでしょう。
    -    </p>
    -
    -    <p>次の例では、 <code>bySecond</code>変数に"yyyyMMdd'T'HHmmss"という書式で文字列化した現在日時を代入していますそして、"bySecond" 変数を<span class="option">file</span>プロパティの値として使っています。
    -    </p>
    -
    -<pre class="prettyprint source"><b>def bySecond = timestamp("yyyyMMdd'T'HHmmss")</b>
    -
    -appender("FILE", FileAppender) {
    -  <b>file = "log-${bySecond}.txt"</b>
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%logger{35} - %msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -    <!-- ========================================================== -->        
    -    <h3>• <span class="code">conversionRule(String conversionWord, Class converterClass)</span></h3>
    -
    -    <p><a href="./06-layouts.html#customConversionSpecifier">変換指定子</a>を自作しても、logbackに教えてあげなければ利用できません。このlogback.groovyでは、logbackが<code>%sample</code>という変換指定子に対してMySampleConverterを呼び出すようにしています。
    -    </p>
    -
    -    <pre class="prettyprint source">
    -import chapters.layouts.MySampleConverter
    -
    -conversionRule("sample", MySampleConverter)
    -appender("STDOUT", ConsoleAppender) {
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%-4relative [%thread] %<b>sample</b> - %msg%n"
    -  }
    -}
    -root(DEBUG, ["STDOUT"])</pre>
    -
    -   <!-- ========================================================== -->
    -   <h3>• <span class="code">scan(String scanPeriod = null)</span></h3>
    -
    -    <p>scan()メソッドを使うと、logbackが定期的にlogback.groovyの変更を監視するようになります。logbackは変更を検出するたびに<em>logback.groovy</em>を再読み込みします。</p>
    -
    -    <pre class="prettyprint source">scan()</pre>
    -
    -    <p>デフォルトでは、一分ごとに設定ファイルの変更を監視します。監視周期を指定するには、"scanPeriod" 文字列引数を指定します。"scanPeriod" に指定する文字列には、時間単位としてミリ秒、秒、分または時間を含めることができます。例をみてください。</p>
    -
    -    <pre class="prettyprint source">scan("30 seconds")</pre>
    -    
    -    <p>時間単位がない場合ミリ秒が指定されたものとして扱いますが、ほとんどの場合これは不適切な単位です。デフォルトの監視周期を変更する場合は、時間単位を指定することを忘れないでください。どのように変更を監視するのかについて詳しくは<a href="./03-configuration.html#autoScan">自動再読み込みのセクション</a>を参照してください。
    -    </p>
    -    
    -    <!-- ========================================================== -->
    -  
    -    <h3>• <span class="code">statusListener(Class listenerClass)</span></h3>
    -
    -    <p><code>statusListener()</code>メソッドは、指定したリスナークラスをステータスリスナーとして追加します。例を見てください。</p>
    -
    -    <pre class="prettyprint source">import chapters.layouts.MySampleConverter
    -
    -<b>// statusListener()メソッドの呼び出しはimport文の直後、他の式よりも前に置くことを強く推奨します</b>
    -<b>statusListener(OnConsoleStatusListener)</b></pre>
    - 
    -    <p><a href="./03-configuration.html#statusListener">ステータスリスナー</a>については前の章で説明しました。</p>
    -
    -    <h3>• <span class="code">jmxConfigurator(String name)</span></h3>
    -
    -    <p><a href="./10-jmxConfig.html"><code>JMXConfigurator</code></a>のMBeanを登録します。MBean名としてデフォルトのオブジェクト名(<code>ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator</code>)を使うには引数を指定せずに呼び出してください。</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator()</pre>
    -
    -    <p><code>Name</code>キーに"default"以外の値を指定するには、<code>jmxConfigurator()</code>メソッドの引数として指定するだけです。</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator('MyName')</pre>
    -
    -    <p>オブジェクト名全体を指定したい場合は、正確なオブジェクト名文字列を引数に指定してください。</p>
    -
    -    <pre class="prettyprint source">jmxConfigurator('myApp:type=LoggerManager')</pre>
    -
    -    <p>このメソッドは、指定された文字列をオブジェクト名として使おうとしてから、それが有効なオブジェクト名ではなかったら、フォールバックとして"Name"キーの値にします。</p>
    -
    -    <!-- ========================================================== -->
    -
    -    <h2 class="doAnchor" name="internalDSL">内部DSL、すべてはGroovyの賜物だ!</h2>
    -
    -    <p><em>logback.groovy</em>は内部DSLです。つまり、内容自体が実行可能なGroovyスクリプトなのです。したがって、logback.groovyの中ではGroovy言語に備わっているクラスimport、GString、変数定義、GString文字列中の${..}記法の評価、if-else文などのあらゆる機能を利用することができるのです。以降の説明では<em>logback.groovy</em>における典型的なGroovy言語の使用例を紹介します。
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="varedef">変数定義、そしてGString</h3>
    -
    -    <p><em>logback.groovy</em>の中ならどこでも変数を定義することができますし、その変数をGString文字列の中で評価することができます。例を見てください。</p>
    -
    -    <pre class="prettyprint source">// USER_HOME 変数にシステムプロパティの "user.home" の値を代入します
    -<b>def USER_HOME = System.getProperty("user.home")</b>
    -
    -appender("FILE", FileAppender) {
    -  // USER_HOME 変数を使います
    -  <b>file = "${USER_HOME}/myApp.log"</b>
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -    <h3 class="doAnchor" name="printing">コンソールへの出力</h3>
    -
    -    <p>Groovyの<code>println()</code>メソッドを使ってコンソールに出力することができます。例を見てください。</p>
    -
    -    <pre class="prettyprint source">def USER_HOME = System.getProperty("user.home");
    -<b>println "USER_HOME=${USER_HOME}"</b>
    -
    -appender("FILE", FileAppender) {
    -  <b>println "Setting [file] property to [${USER_HOME}/myApp.log]"</b>
    -  file = "${USER_HOME}/myApp.log"  
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -   <h3 class="doAnchor" name="automaticallyExported">自動的に公開されるフィールド</h3>
    -
    -   <h4 class="doAnchor" name="hostname">'hostname' 変数</h4>
    -
    -   <p>'hostname' 変数にはスクリプトを実行しているホスト名が設定されています。このドキュメントの著者には正確な説明はできませんが、可視範囲のルールがあるため、'hostname'変数が利用できるのは最上位のスコープだけで、ネストされたスコープからは参照できません。次の例を見ればどういうことかわかるでしょう。
    -   </p>
    -
    - <pre class="prettyprint source">// will print "hostname is x" where x is the current host's name
    -println "Hostname is ${hostname}"
    -
    -appender("STDOUT", ConsoleAppender) {
    -  <b>// will print "hostname is null"</b>
    -  <b>println "Hostname is ${hostname}" </b>
    -}</pre>
    -
    -   <p>すべてのスコープでhostname変数を使いたいなら、次のように別の変数に代入して参照しなければなりません。</p>
    -
    - <pre class="prettyprint source">// define HOSTNAME by assigning it hostname
    -def HOSTNAME=hostname
    -// will print "hostname is x" where x is the current host's name
    -println "Hostname is ${HOSTNAME}"
    -
    -appender("STDOUT", ConsoleAppender) {
    -  // will print "hostname is x" where x is the current host's name
    -  println "Hostname is ${HOSTNAME}" 
    -}</pre>
    -
    -
    -   <h3 class="doAnchor" name="everythingIsContext">現在のコンテキストを参照するContextAwareが全ての土台になっている</h3>
    -
    -   <p><em>logback.groovy</em>スクリプトは<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/ContextAware.html">ContextAware</a>オブジェクト上で実行されます。したがって、<code>context</code>変数からいつでも現在のコンテキストにアクセスすることができます。それに、<code>addInfo()</code>メソッドや<code>addWarn()</code>メソッド、<code>addError()</code>メソッドで、<code>StatusManager</code>にステータスメッセージを伝えることができます。</p>
    -
    -   <pre class="prettyprint source">// always a good idea to add an on console status listener
    -statusListener(OnConsoleStatusListener)
    -
    -// set the context's name to wombat
    -<b>context.name = "wombat"</b>
    -// add a status message regarding context's name
    -<b>addInfo("Context name has been set to ${context.name}")</b>
    -
    -def USER_HOME = System.getProperty("user.home");
    -// add a status message regarding USER_HOME
    -<b>addInfo("USER_HOME=${USER_HOME}")</b>
    -
    -appender("FILE", FileAppender) {
    -  // add a status message regarding the file property
    -  <b>addInfo("Setting [file] property to [${USER_HOME}/myApp.log]")</b>
    -  file = "${USER_HOME}/myApp.log"  
    -  encoder(PatternLayoutEncoder) {
    -    pattern = "%msg%n"
    -  }
    -}
    -root(DEBUG, ["FILE"])</pre>
    -
    -
    -   <h3 class="doAnchor">条件付き設定</h3>
    -   
    -   <p>Groovyは本格的なプログラミング言語なので、条件分岐を使うと1つの<em>logback.groovy</em>をdevelopment、testing、productionといったいろいろな環境で使い回すことができます。</p>
    -
    -   <p>次のスクリプトでは、ホスト名が本番環境のホスト名である pixie あるいは orion 以外の場合コンソールアペンダーが有効になるようにしています。ローリングファイルアペンダーの出力ディレクトリがホスト名に依存していることにも気をつけてください。</p>
    -   
    -   <pre class="prettyprint source">// always a good idea to add an on console status listener
    -statusListener(OnConsoleStatusListener)
    -
    -def appenderList = ["ROLLING"]
    -def WEBAPP_DIR = "."
    -def consoleAppender = true;
    -
    -// does hostname match pixie or orion?
    -if (hostname =~ /pixie|orion/) {
    -  WEBAPP_DIR = "/opt/myapp"     
    -  consoleAppender = false   
    -} else {
    -  appenderList.add("CONSOLE")
    -}
    -
    -if (consoleAppender) {
    -  appender("CONSOLE", ConsoleAppender) {
    -    encoder(PatternLayoutEncoder) {
    -      pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
    -    }
    -  }
    -}
    -
    -appender("ROLLING", RollingFileAppender) {
    -  encoder(PatternLayoutEncoder) {
    -    Pattern = "%d %level %thread %mdc %logger - %m%n"
    -  }
    -  rollingPolicy(TimeBasedRollingPolicy) {
    -    FileNamePattern = "${WEBAPP_DIR}/log/translator-%d{yyyy-MM}.zip"
    -  }
    -}
    -
    -root(INFO, appenderList)</pre>
    -
    -
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -    </div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/index.html b/logback-site/src/site/pages/manual/index.html
    deleted file mode 100755
    index 5c7dd54c4c..0000000000
    --- a/logback-site/src/site/pages/manual/index.html
    +++ /dev/null
    @@ -1,130 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Logback Manual</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    
    -  </head>
    -  <body>
    -    <script type="text/javascript">prefix='../';</script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h2>The logback manual</h2>
    -
    -    <a href="index_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <p>The complete logback manual documents the latest version of
    -    logback framework. In over 150 pages and dozens of concrete
    -    examples, it covers both basic and advanced logback features, including:
    -    </p>
    -
    -
    -    <div>
    -      <ul>
    -        <li><p>the overall logback architecture</p></li>
    -        <li><p>discussion of best logback practices and anti-patterns</p></li>
    -        <li><p>logback configuration scripts in XML format</p></li>
    -        <li><p>appenders</p></li>
    -        <li><p>encoders</p></li>
    -        <li><p>layouts</p></li>
    -        <li><p>filters</p></li>
    -        <li><p>mapped diagnostic contexts</p></li>
    -        <li><p>Joran, logback's configuration system</p></li>
    -      </ul>
    -    </div>
    -
    -  
    -    <p>The logback manual describes the logback API in considerable
    -    detail, including its features and design rationale. Authored by
    -    Ceki G&#252;lc&#252; and S&#233;bastien Pennec, the main
    -    contributors to the logback project, the logback manual is
    -    intended for developers already familiar with the Java language
    -    but new to logback, as much as for experienced logback users. With
    -    the aid of introductory material and many examples, new users
    -    should quickly come up to speed.
    -    </p>
    -
    -  
    -    
    -    <div>
    -      <p>Without further ado, here are the contents of the manual:</p>
    -      
    -      <ul>
    -        <li><p>
    -          <a href="introduction.html"><b>Chapter 1: Introduction to logback</b></a>
    -        </p></li>
    -        <li><p>
    -          <a href="architecture.html"><b>Chapter 2: Architecture</b></a>
    -        </p></li>
    -        <li><p>
    -          <a href="configuration.html"><b>Chapter 3: Configuration</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="appenders.html"><b>Chapter 4: Appenders</b></a>
    -        </p></li>
    -      
    -        <li><p><a href="encoders.html"><b>Chapter 5:
    -        Encoders</b></a></p>
    -        </li>
    -  
    -        <li><p>
    -          <a href="layouts.html"><b>Chapter 6: Layouts</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="filters.html"><b>Chapter 7: Filters</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="mdc.html"><b>Chapter 8: Mapped Diagnostic Contexts</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="loggingSeparation.html"><b>Chapter 9: Logging Separation</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="jmxConfig.html"><b>Chapter 10: JMX Configurator</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="onJoran.html"><b>Chapter 11: Joran</b></a>
    -        </p></li>
    -
    -        <li><p><a href="groovy.html"><b>Chapter 12: Groovy
    -        Configuration</b></a></p></li>
    -
    -        <li><p>
    -          <a href="migrationFromLog4j.html"><b>Chapter 13: Migration from log4j</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="receivers.html"><b>Chapter 14: Receivers</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="usingSSL.html"><b>Chapter 15: Using SSL</b></a>
    -        </p></li>
    -
    -      </ul>
    -    </div>
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -    </div>
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/index_ja.html b/logback-site/src/site/pages/manual/index_ja.html
    deleted file mode 100644
    index 997afdce20..0000000000
    --- a/logback-site/src/site/pages/manual/index_ja.html
    +++ /dev/null
    @@ -1,115 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>Logbackマニュアル</title>
    -    
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -   
    -  </head>
    -  <body dir="ltr">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h2>logback マニュアル</h2>
    -
    -
    -    <p>このマニュアルでは、logbackフレームワークの最新バージョンについて説明しています。百五十ページを超える説明、および、数十もの具体例によって、logback のフィーチャの基本的な使い方と応用的な使い方を説明します。大きく次のような内容が含まれています。</p>
    -
    -
    -    <div>
    -      <ul>
    -        <li><p>logbackの全体的なアーキテクチャ</p></li>
    -        <li><p>最高のプラクティスとアンチパターンについての議論</p></li>
    -        <li><p>XML形式のlogback設定</p></li>
    -        <li><p>アペンダー</p></li>
    -        <li><p>エンコーダー</p></li>
    -        <li><p>レイアウト</p></li>
    -        <li><p>フィルター</p></li>
    -        <li><p>診断コンテキスト(MDC)</p></li>
    -        <li><p>logbackの設定システム Joran の解説</p></li>
    -      </ul>
    -    </div>
    -
    -  
    -    <p>このマニュアルは、logback の API をかなり詳細に説明したもので、フィーチャや設計の理論的な根拠も含まれます。執筆したのは logback プロジェクトの主要な貢献者である CekiGülcü と Sébastien Pennec です。
    -このマニュアルの対象者としては、Javaによる開発の経験はあるけど logback を使ったことがない人から、logback について経験豊富な人までを想定しています。入門資料と多くの具体例を手がかりにすれば、例え未経験であってもすぐに慣れることができるはずです。
    -    </p>
    -
    -  
    -    
    -    <div>
    -      <p>ここまでで特に疑問に思うことがなければ、早速ここから読み進めてください。</p>
    -      
    -      <ul>
    -        <li><p>
    -          <a href="introduction_ja.html"><b>第1章:はじめに</b></a>
    -        </p></li>
    -        <li><p>
    -          <a href="architecture_ja.html"><b>第2章:アーキテクチャ</b></a>
    -        </p></li>
    -        <li><p>
    -          <a href="configuration_ja.html"><b>第3章:設定</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="appenders_ja.html"><b>第4章:アペンダー</b></a>
    -        </p></li>
    -      
    -        <li><p><a href="encoders_ja.html"><b>第5章:エンコーダー</b></a></p>
    -        </li>
    -  
    -        <li><p>
    -          <a href="layouts_ja.html"><b>第6章:レイアウト</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="filters_ja.html"><b>第7章:フィルタ</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="mdc_ja.html"><b>第8章:診断コンテキスト(MDC)</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="loggingSeparation_ja.html"><b>第9章:ログの分離</b></a>
    -        </p></li>
    -        
    -        <li><p>
    -          <a href="jmxConfig_ja.html"><b>第10章:JMXコンフィギュレーター</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="onJoran_ja.html"><b>第11章:Joran</b></a>
    -        </p></li>
    -
    -        <li><p><a href="groovy_ja.html"><b>第12章:Groovyによる設定</b></a></p></li>
    -
    -        <li><p>
    -          <a href="migrationFromLog4j_ja.html"><b>第13章:log4jからの移行</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="receivers_ja.html"><b>第14章:レシーバー</b></a>
    -        </p></li>
    -
    -        <li><p>
    -          <a href="usingSSL_ja.html"><b>第15章:SSLの使用</b></a>
    -        </p></li>
    -
    -      </ul>
    -    </div>
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -    </div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/introduction.html b/logback-site/src/site/pages/manual/introduction.html
    deleted file mode 100755
    index 2af6d77a59..0000000000
    --- a/logback-site/src/site/pages/manual/introduction.html
    +++ /dev/null
    @@ -1,255 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 1: Introduction</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />    
    -
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"> </script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -    <h1>Chapter 1: Introduction</h1>
    -
    -    <div class="quote">
    -      <p><em> The morale effects are startling. Enthusiasm jumps when
    -      there is a running system, even a simple one. Efforts redouble when
    -      the first picture from a new graphics software system appears on the
    -      screen, even if it is only a rectangle. One always has, at every
    -      stage in the process, a working system. I find that teams can grow
    -      much more complex entities in four months than they can
    -      build.</em></p>
    -      
    -      <p>&mdash;FREDERICK P. BROOKS, JR., <em>The Mythical Man-Month</em></p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -  
    -    <h2>What is logback?</h2>
    -
    -    <a href="introduction_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -  
    -    <p>Logback is intended as a successor to the popular log4j
    -    project.  It was designed by Ceki G&#252;lc&#252;, log4j's
    -    founder.  It builds upon a decade of experience gained in
    -    designing industrial-strength logging systems. The resulting
    -    product, i.e. logback, is faster and has a smaller footprint than
    -    all existing logging systems, sometimes by a wide margin. Just as
    -    importantly, logback offers <a
    -    href="../reasonsToSwitch.html">unique and rather useful
    -    features</a> missing in other logging systems.
    -    </p>
    -
    -    <h2>First Baby Step</h2>
    -
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -    
    -    <a name="Requirements"></a>
    -    <h3>Requirements</h3>
    -
    -    <p>Logback-classic module requires the presence of
    -    <em>slf4j-api.jar</em> and <em>logback-core.jar</em> in addition to
    -    <em>logback-classic.jar</em> on the classpath.
    -    </p>
    -
    -    <p>The <em>logback-*.jar</em> files are part of the logback
    -    distribution whereas <em>slf4j-api-${slf4j.version}.jar</em> ships
    -    with <a href="http://www.slf4j.org">SLF4J</a>, a separate project.
    -    </p>
    -
    -    <p>Let us now begin experimenting with logback.</p>
    -
    -<em>Example 1.1: Basic template for logging (<a href="../xref/chapters/introduction/HelloWorld1.html">logback-examples/src/main/java/chapters/introduction/HelloWorld1.java</a>)</em>
    -<pre class="prettyprint source">package chapters.introduction;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -public class HelloWorld1 {
    -
    -  public static void main(String[] args) {
    -
    -    Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
    -    logger.debug("Hello world.");
    -
    -  }
    -}</pre>
    -
    -    <p><code>HelloWorld1</code> class is defined in the
    -    <code>chapters.introduction</code> package. It starts by importing the <a
    -    href="http://slf4j.org/api/org/slf4j/Logger.html"><code>Logger</code></a>
    -    and <a
    -    href="http://slf4j.org/api/org/slf4j/LoggerFactory.html"><code>LoggerFactory</code></a>
    -    classes defined in the SLF4J API, specifically within the
    -     <code>org.slf4j</code> package.
    -    </p>
    -
    -
    -    <p>On the first line of the main() method, the variable named
    -    <code>logger</code> is assigned a <code>Logger</code> instance
    -    retrieved by invoking the static <code>getLogger</code> method
    -    from the <code>LoggerFactory</code> class.  This logger is named
    -    "chapters.introduction.HelloWorld1". The main method proceeds to call the
    -    <code>debug</code> method of this logger passing "Hello World" as
    -    an argument.  We say that the main method contains a logging
    -    statement of level DEBUG with the message "Hello world".
    -    </p>
    -
    -    <p>Note that the above example does not reference any logback
    -    classes. In most cases, as far as logging is concerned, your
    -    classes will only need to import SLF4J classes. Thus, the vast
    -    majority, if not all, of your classes will use the SLF4J
    -    API and will be oblivious to the existence of logback.
    -    </p>
    -
    -
    -    <p>You can launch the first
    -    sample application, <em>chapters.introduction.HelloWorld1</em> with the command:
    -    </p>
    -    <div class="source"><pre>java chapters.introduction.HelloWorld1</pre></div>
    -
    -    <p>Launching the <code>HelloWorld1</code> application will output
    -    a single line on the console. By virtue of logback's default
    -    configuration policy, when no default configuration file is found,
    -    logback will add a <code>ConsoleAppender</code> to the root
    -    logger.
    -    </p>
    -
    -    <p class="source">20:49:07.962 [main] DEBUG chapters.introduction.HelloWorld1 - Hello world.</p>
    -
    -    <p>Logback can report information about its internal state using a
    -    built-in status system. Important events occurring during logback's
    -    lifetime can be accessed through a component called
    -    <code>StatusManager</code>. For the time being, let us instruct
    -    logback to print its internal state by invoking the static
    -    <code>print()</code> method of the <code>StatusPrinter</code>
    -    class.
    -    </p>
    -
    -<em>Example: Printing Logger Status (<a href="../xref/chapters/introduction/HelloWorld2.html">logback-examples/src/main/java/chapters/introduction/HelloWorld2.java</a>)</em>
    -<pre class="prettyprint source">package chapters.introduction;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -<b>import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.core.util.StatusPrinter;</b>
    -
    -public class HelloWorld2 {
    -
    -  public static void main(String[] args) {
    -    Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");
    -    logger.debug("Hello world.");
    -
    -    // print internal state
    -    <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -    StatusPrinter.print(lc);</b>
    -  }
    -}</pre>
    -
    -
    -   <p>Running the <code>HelloWorld2</code> application will produce
    -   the following output:</p>
    -
    -<div class="source longline"><pre>12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
    -12:49:22,076 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
    -12:49:22,078 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
    -12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
    -12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Setting up default configuration.
    -</pre></div>
    -
    -
    -  <p>Logback explains that having failed to find the
    -  <em>logback-test.xml</em> and <em>logback.xml</em> configuration
    -  files (discussed later), it configured itself using its default
    -  policy, which is a basic <code>ConsoleAppender</code>.  An
    -  <code>Appender</code> is a class that can be seen as an output
    -  destination. Appenders exist for many different destinations
    -  including the console, files, Syslog, TCP Sockets, JMS and many
    -  more. Users can also easily create their own Appenders as
    -  appropriate for their specific situation.
    -  </p>
    -
    -  <p>Note that in case of errors, logback will automatically print its
    -  internal state on the console.</p>
    -
    -  <p>The previous examples are rather simple. Actual logging in a
    -  larger application would not be that different. The general pattern
    -  for logging statements would not change. Only the configuration
    -  process would be different. However, you would probably want to
    -  customize or configure logback according to your needs. Logback
    -  configuration will be covered in subsequent chapters.
    -  </p>
    -
    -  <p>Note that in the above example we have instructed logback to
    -  print its internal state by invoking the
    -  <code>StatusPrinter.print()</code> method. Logback's internal status
    -  information can be very useful in diagnosing logback-related
    -  problems.
    -  </p>
    -
    -  <p>Here is a list of the three required steps in order to enable
    -  logging in your application.
    -  </p>
    -
    -  <ol> 
    -    <li>Configure the logback environment. You can do so in several
    -    more or less sophisticated ways. More on this later.</li>
    -
    -    <li>In every class where you wish to perform logging, retrieve a
    -    <code>Logger</code> instance by invoking the
    -    <code>org.slf4j.LoggerFactory</code> class'
    -    <code>getLogger()</code> method, passing the current class name
    -    or the class itself as a parameter.</li>
    -    
    -    <li>Use this logger instance by invoking its printing methods,
    -    namely the debug(), info(), warn() and error() methods. This will
    -    produce logging output on the configured appenders.</li>
    -  </ol>
    - 
    -  
    -  <h2 class="doAnchor" name="building">Building logback</h2>
    -  
    -  <p>As its build tool, logback relies on <a
    -  href="http://maven.apache.org">Maven</a>, a widely-used open-source
    -  build tool.
    -  </p>
    -
    -  <p>Once you have installed Maven, building the logback project,
    -  including all its modules, should be as easy as issuing a <code>mvn
    -  install</code> command from within the directory where you
    -  unarchived the logback distribution. Maven will automatically
    -  download the required external libraries.
    -  </p>
    -
    -  <p>Logback distributions contain complete source code such that you
    -  can modify parts of logback library and build your own version of
    -  it. You may even redistribute the modified version, as long as you
    -  adhere to the conditions of the LGPL license or the EPL license.
    -  </p>
    -
    -  <p>For building logback under an IDE, please see the <a
    -  href="../setup.html#ide">relevant section on the class path setup
    -  page</a>.</p>
    -  
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/introduction_ja.html b/logback-site/src/site/pages/manual/introduction_ja.html
    deleted file mode 100644
    index 23af9e23c5..0000000000
    --- a/logback-site/src/site/pages/manual/introduction_ja.html
    +++ /dev/null
    @@ -1,165 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第1章:はじめに</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -    
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -    <h1>第1章 はじめに</h1>
    -
    -    <div class="quote">
    -      <p><em>やる気のもたらす効果には驚くべきものがある。例え簡単なシステムであろうとも、一つでも稼働しているシステムがあれば熱意は燃え上がる。新しいグラフィカルソフトウェアを使って、スクリーンに画像が表示されたなら、たとえそれが単純な四角形であったとしても、その効果は倍増する。システムを稼働させるまでのプロセス一つ一つにそういう瞬間があるものだ。チームが四ヶ月かけて何かを成し遂げたなら、きっとそれ以上に複雑なことが出来るほどに成長できることに気付かされたものだ。</em></p>
    -      
    -      <p>—FREDERICK P. BROOKS, JR., <em>The Mythical Man-Month</em></p>
    -    </div>
    -
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -
    -    <h2>logbackとは何か?</h2>
    -
    -    <p>Logback は巷で大人気の log4j プロジェクトの後継プロジェクトです。log4j の創始者であるCekiGülcü によって設計されました。強固で頑健なロギングシステムの十年間に及ぶ経験に基いて設計、構築されています。おかげで、logback は既存のロギングシステムと比べてより高速で、より小さなフットプリントを持つようになりました。場合によってはその差は非常に大きなこともあります。同じくらい重要なのが、logback は他のロギングシステムには無い<a href="http://logback.qos.ch/reasonsToSwitch.html">固有の便利なフィーチャ</a>を提供していることです。
    -    </p>
    -
    -    <h2>最初のステップ</h2>
    -
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -    
    -    <a name="Requirements"></a>
    -    <h3>必要条件</h3>
    -
    -    <p>logback-classic モジュールを動かす際は、クラスパス上に <em>slf4j-api.jar</em> と <em>logback-core.jar</em>  と <em>logback-classic.jar</em> が配置されている必要があります。
    -    </p>
    -
    -    <p><em>logback-*.jar</em> ファイルは logback の配布物に含まれていますが、 {0]slf4j-api-1.7.6.jar は別のプロジェクトである <a href="http://www.slf4j.org">SLF4J</a> の配布物です。
    -    </p>
    -
    -    <p>さあ、logback を使ってみましょう。</p>
    -
    -<em>例1.1:ロギングの基本的なテンプレート( <a href="http://logback.qos.ch/xref/chapters/introduction/HelloWorld1.html">logback-examples/src/main/java/chapters/introduction/HelloWorld1.java</a> )</em>
    -<pre class="prettyprint source">package chapters.introduction;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -public class HelloWorld1 {
    -
    -  public static void main(String[] args) {
    -
    -    Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
    -    logger.debug("Hello world.");
    -
    -  }
    -}</pre>
    -
    -    <p><code>HelloWorld1</code>クラスは <code>chapters.introduction</code> パッケージに定義されています。最初に SLF4J の API である <a href="http://slf4j.org/api/org/slf4j/Logger.html"><code>Logger</code></a> と <a href="http://slf4j.org/api/org/slf4j/LoggerFactory.html"><code>LoggerFactory</code></a> を <code>org.slf4j</code> パッケージからインポートしています。
    -    </p>
    -
    -
    -    <p>main()メソッドの最初の行では、<code>LoggerFactory</code>クラスの static メソッドである <code>getLogger</code> メソッドの返す<code>Logger</code> クラスのインスタンスを、<code>logger</code>変数に代入します。logger には "chapters.introduction.HelloWorld1" という名前が付けられています。次の処理では、引数として "Hello World" を渡して、logger の<code>debug</code>メソッドを呼び出しています。これを一言で表すと、main メソッドでは、"Hello World" というメッセージの DEBUG レベルのロギングがある、と言えます。
    -    </p>
    -
    -    <p>上記の例では、logbackのクラスを一つも参照していないことに注意してください。ほとんどの場合、ロギングは本来の仕事ではないので、あなたのクラスでは SLF4J のクラスをインポートするだけでよいのです。このように、全てではないにしても、あなたのクラスの大半はSLF4J APIを使用するだけで、logbackの存在には気づかないでしょう。
    -    </p>
    -
    -
    -    <p>このサンプルアプリケーション(<em>chapters.introduction.HelloWorld1</em>)は次のコマンドで実行することができます。</p>
    -    <div class="source"><pre>java chapters.introduction.HelloWorld1</pre></div>
    -
    -    <p><code>HelloWorld1</code>アプリケーションを実行すると、コンソールに何かが一行出力されます。logbackのデフォルトの設定ポリシーにより、デフォルトの設定ファイルが見つからない場合はルートロガーに<code>ConsoleAppender</code>を割り当てるようになっています。
    -    </p>
    -
    -    <p class="source">20:49:07.962 [main] DEBUG chapters.introduction.HelloWorld1 - Hello world.</p>
    -
    -    <p>logback は組み込みの状態管理システムによって、内部状態をレポートすることができます。<code>StatusManager</code>と呼ばれるコンポーネントを通じて、logback が有効になってから発生した重要なイベントの回数にアクセスすることができます。しばらくは、<code>StatusPrinter</code> クラスの<code>print()</code>メソッドによって logback の内部状態を出力することにします。
    -    </p>
    -
    -<em>例:ロガーの内部状態を出力する( <a href="http://logback.qos.ch/xref/chapters/introduction/HelloWorld2.html">logback-examples/src/main/java/chapters/introduction/HelloWorld2.java</a> )</em>
    -<pre class="prettyprint source">package chapters.introduction;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -<b>import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.core.util.StatusPrinter;</b>
    -
    -public class HelloWorld2 {
    -
    -  public static void main(String[] args) {
    -    Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld2");
    -    logger.debug("Hello world.");
    -
    -    // print internal state
    -    <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -    StatusPrinter.print(lc);</b>
    -  }
    -}</pre>
    -
    -
    -   <p><code>HelloWorld2</code>アプリケーションを実行すると次のように出力されます。</p>
    -
    -<div class="source longline"><pre>12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
    -12:49:22,076 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
    -12:49:22,078 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
    -12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.xml]
    -12:49:22,093 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Setting up default configuration.
    -</pre></div>
    -
    -
    -  <p>これは、logback が設定ファイルである <em>logback-test.xml</em> と <em>logback.xml</em> を見つけられなかったことを意味しています(詳しくは後述します)。そして、デフォルトポリシー(基本的な<code>ConsoleAppender</code>を使います)を使うように自分自身を設定しています。<code>Appender</code>とは、出力先として使用されるクラスのことです。既存のアペンダーには、コンソール、ファイル、Syslog、TCPソケット、JMSなどをいろいろな出力先のクラスが存在します。ユーザーは自分たちの状況に応じたアペンダーを簡単に作ることができます。
    -  </p>
    -
    -  <p>異常が起きた場合、内部状態を自動的にコンソールに出力するようになっています。</p>
    -
    -  <p>前の例はかなり単純なものでした。とはいえ、大きなアプリケーションで行われている実際のロギングもそんなに大きく変わるものではありません。一般的なロギング文の形式は変わらないでしょう。違うとしたら設定処理ですね。しかし、必要に応じたカスタマイズや設定をしたくなるでしょう。logbackを設定する方法については、以降の章で説明します。
    -  </p>
    -
    -  <p>前述した例では、<code>StatusPrinter.print()</code>メソッドによって内部状態を出力するように命令していることに注意してください。logback の内部状態は、logback に起因する問題を調査するために非常に役立ちます。
    -  </p>
    -
    -  <p>あなたのアプリケーションでロギングを有効にするための手順は次の三つです。
    -  </p>
    -
    -  <ol> 
    -    <li>logback の環境を準備します。やり方はさまざまです。詳細は後述します。</li>
    -
    -    <li>ロギングをしたい全てのクラスで、<code>org.slf4j.LoggerFactory</code>の<code>getLogger()</code>メソッドによって<code>Logger</code>のインスタンスを取得しましょう。<code>getLogger()</code>メソッドにはクラス名やクラスオブジェクト自体を引数にしましょう。</li>
    -    
    -    <li>logger インスタンスのロギング用メソッドを呼び出しましょう。debug() info() warn() error() といったものがあります。そうすれば、アペンダーとして設定した出力先にログが出力されます。</li>
    -  </ol>
    - 
    -  
    -  <h2 class="doAnchor" name="building">logbackをビルドする</h2>
    -  
    -  <p>logback はビルドツールとして Maven を使用しています。<a href="http://maven.apache.org">Maven</a> はオープンソースのビルドツールで、広く利用されているものです。
    -  </p>
    -
    -  <p>Maven をインストールしたら、logback の配布物を展開して、展開先のディレクトリに移動します。そして <code>mvn install</code> コマンドを実行すれば、全てのモジュールがビルドされます。Mavenは必要な外部ライブラリを自動的にダウンロードします。
    -  </p>
    -
    -  <p>logback の配布物には完全なソースコードが含まれているので、編集して自分だけのバージョンのライブラリをビルドすることができます。あなたが修正したバージョンの logback は、LGPL ライセンス、もしくは、EPL ライセンスに従って再配布することもできます。
    -  </p>
    -
    -  <p>IDE から logback をビルドする場合は<a href="http://logback.qos.ch/setup.html#ide">クラスパスの設定について説明したページ</a>を参照してください。</p>
    -  
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/jmxConfig.html b/logback-site/src/site/pages/manual/jmxConfig.html
    deleted file mode 100755
    index 0d90fd9e63..0000000000
    --- a/logback-site/src/site/pages/manual/jmxConfig.html
    +++ /dev/null
    @@ -1,405 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -
    -    <title>Chapter 10: JMX Configuration</title>
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -    <h1>Chapter 10: JMX Configurator</h1>
    -
    -    <a href="jmxConfig_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -    
    -		<p>As its name indicates, <code>JMXConfigurator</code> allows
    -		configuration of logback via JMX. In a nutshell, it lets you
    -		reconfigure logback from the default configuration file, from a
    -		designated file or URL, list loggers and modify logger levels.
    -		</p>
    -		
    -		<h3>Using the JMX Configurator</h3>
    -    
    -    
    -		<p>If your server run on JDK 1.6 or later, then you can just
    -		invoke the <code>jconsole</code> application on the command line and
    -		then connect to your server's MBeanServer. If you are running an
    -		older JVM, then you should read the section on <a
    -		href="#jmxEnablingServer">JMX enabling your server</a>.
    -    </p>
    -
    -    <p><code>JMXConfigurator</code> is enabled by a single line in
    -    your logback configuration file, as shown below:
    -		</p>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -  <b>&lt;jmxConfigurator /></b>
    -  
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout">
    -      &lt;Pattern>%date [%thread] %-5level %logger{25} - %msg%n&lt;/Pattern>
    -    &lt;/layout>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="console" />
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -		
    -    <p>After you connect to your server with <em>jconsole</em>, on the
    -    MBeans panel, under "ch.qos.logback.classic.jmx.Configurator"
    -    folder you should see several operations to choose from, as shown
    -    in the figure below:
    -    </p>
    -    
    -    <h3 class="doAnchor" name="jmxConfigurator">Screen-shot of
    -    <code>JMXConfigurator</code> viewed in <code>jconsole</code></h3>
    -
    -    <img src="images/chapters/jmxConfigurator/jmxConfigurator.gif" alt="jmxConfigurator"/>   
    -
    -		<p>Thus, you can</p>
    -		
    -		<ul>
    -      <li>Reload logback configuration using the default configuration
    -      file.</li>
    -
    -      <li>Reload the configuration with the specified URL.</li>
    -      <li>Reload the configuration with the specified file.</li>
    -
    -			<li>Set the level of a specified logger. To set to null, pass
    -			the string "null" as value.</li>
    -      <li>Get the level of a specified logger. The returned value can be
    -      null.</li>
    -			<li>Get the <a href="architecture.html#effectiveLevel">effective
    -			level</a> of a specified logger.</li>
    -		</ul>
    -		
    -    <p><code>JMXConfigurator</code> exposes the list of existing
    -    loggers and a status list as attributes.</p>
    -    
    -    <p>The status list can help you diagnose logback's internal
    -    state.</p>
    -
    -    <img src="images/chapters/jmxConfigurator/statusList.gif" alt="statusList.gif"/>   
    -
    -    <h3 class="doAnchor" name="leak">Avoiding memory leaks</h3>
    -
    -    <p>If your application is deployed in a web-server or an
    -    application server, the registration of an
    -    <code>JMXConfigurator</code> instance creates a reference from the
    -    system class loader into your application which will prevent it
    -    from being garbage collected when it is stopped or re-deployed,
    -    resulting in a severe memory leak.</p>
    -
    -    <p>Thus, unless your application is a standalone Java application,
    -    you MUST unregister the <code>JMXConfigurator</code> instance from
    -    the JVM's Mbeans server. Invoking the <code>reset</code>() method
    -    of the appropriate <code>LoggerContext</code> will automatically
    -    unregister any JMXConfigurator instance. A good place to reset the
    -    logger context is in the <code>contextDestroyed</code>()
    -    method of a
    -    <code>javax.servlet.ServletContextListener</code>. Here is sample
    -    code:</p>
    -
    -    <pre class="prettyprint source">import javax.servlet.ServletContextEvent;
    -import javax.servlet.ServletContextListener;
    -
    -import org.slf4j.LoggerFactory;
    -import ch.qos.logback.classic.LoggerContext;
    -
    -public class MyContextListener implements ServletContextListener {
    -
    -  public void contextDestroyed(ServletContextEvent sce) {
    -    <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -    <b>lc.stop();</b>
    -  }
    -
    -  public void contextInitialized(ServletContextEvent sce) {
    -  }
    -} </pre>
    -
    -
    -    <!-- ============ Multiple web-applications  ================== -->
    -
    -    
    -    <h2 class="doAnchor" name="multiple"><code>JMXConfigurator</code>
    -    with multiple web-applications</h2>
    -
    -    <p>If you deploy multiple web-applications in the same server,
    -    and if you have not overridden the default <a
    -    href="contextSelector.html">context selector</a>, and if you
    -    have placed a copy of <em>logback-*.jar</em> and
    -    <em>slf4j-api.jar</em> under the <em>WEB-INF/lib</em> folder of
    -    each web-application, then by default each <code>JMXConfigurator</code>
    -    instance will be registered under the same name, that is,
    -    "ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator". In
    -    other words, by default the various <code>JMXConfigurator</code>
    -    instances associated with the logger contexts in each of your
    -    web-applications will collide.
    -    </p>
    -    
    -    <p>To avoid such undesirable collisions, you simply <a
    -    href="configuration.html#contextName">set the name of your
    -    application's logging context</a> and <code>JMXConfigurator</code>
    -    will automatically use the name you have set.
    -    </p>
    -
    -    <p>For example, if you deploy two web-applications named "Koala"
    -    and "Wombat", then you would write in Koala's logback
    -    configuration
    -    </p>
    -    
    -    <pre class="prettyprint source">&lt;configuration>
    -  <b>&lt;contextName>Koala&lt;/contextName></b>
    -  &lt;jmxConfigurator/>
    -  ...
    -&lt;configuration></pre>
    -
    -    <p>and in Wombat logback configuration file, you would write:</p>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -  <b>&lt;contextName>Wombat&lt;/contextName></b>x
    -  &lt;jmxConfigurator/>
    -  ...
    -&lt;configuration></pre>
    -
    -    <p>In jconsole's MBeans panel, you would see two distinct
    -    <code>JMXConfigurator</code> instances:</p>
    -
    -    <img src="images/chapters/jmxConfigurator/multiple.gif" alt="multiple.gif"/>   
    -
    -    <p>You may fully control the name under which JMXConfigurator is
    -    registered with an MBeans server with the help of the "objectName"
    -    attribute of the <code>&lt;jmxConfigurator></code> element.</p>
    -    
    -    <!-- ============ JMX enabling your  server ================== -->
    -
    -    <h3 class="doAnchor" name="jmxEnablingServer">JMX enabling your
    -    server</h3>
    -
    -    <p>If your server runs with JDK 1.6 or later, your server should
    -    be JMX enabled by default.</p>
    -
    -		<p>For older JVMs, we suggest that you refer to the JMX-related
    -		documentation of your web-server. Such documentation is available
    -		for both <a
    -		href="http://tomcat.apache.org/tomcat-6.0-doc/monitoring.html">Tomcat</a>
    -		and <a
    -		href="http://docs.codehaus.org/display/JETTY/JMX">Jetty</a>. In
    -		this document, we briefly describe the required configuration
    -		steps for Tomcat and Jetty.
    -		</p>
    -
    -    <!-- ================ Configuring Jetty ================== -->
    -
    -		<h4>Enabling JMX in Jetty (tested under JDK 1.5 and JDK 1.6)</h4>
    -		
    -		<p>The following has been tested under JDK 1.5 and 1.6.  Under JDK
    -		1.6 and later, your server is JMX enabled by default and you can,
    -		but do not need to, follow the steps discussed below.  Under JDK
    -		1.5, adding JMX support in Jetty requires a number of additions to
    -		the <em>$JETTY_HOME/etc/jetty.xml</em> configuration file. Here
    -		are the elements that need to be added:
    -		</p>
    -
    -    <pre class="prettyprint source">&lt;Call id="MBeanServer" class="java.lang.management.ManagementFactory" 
    -      name="getPlatformMBeanServer"/>
    -
    -&lt;Get id="Container" name="container">
    -  &lt;Call name="addEventListener">
    -    &lt;Arg>
    -      &lt;New class="org.mortbay.management.MBeanContainer">
    -        &lt;Arg>&lt;Ref id="MBeanServer"/>&lt;/Arg>
    -        &lt;Call name="start" />
    -      &lt;/New>
    -    &lt;/Arg>
    -  &lt;/Call>
    -&lt;/Get> </pre>
    -
    -    <p>If you wish to access the MBeans exposed by Jetty via the
    -    <code>jconsole</code> application, then you need to start Jetty after
    -    having set the "com.sun.management.jmxremote" Java system
    -    property.
    -    </p>
    -
    -    <p>For a standalone version of Jetty, this translates to: </p>
    -
    -    
    -    <p class="source">java <b>-Dcom.sun.management.jmxremote</b> -jar start.jar [config files]</p>
    -
    -    <p>And if you wish to launch Jetty as a Maven plugin, then you
    -    need to set the "com.sun.management.jmxremote" system property via
    -    the <code>MAVEN_OPTS</code> shell variable:
    -    </p>
    -
    -    <p class="source"><b>MAVEN_OPTS="-Dcom.sun.management.jmxremote</b>"
    -mvn jetty:run</p>
    -
    -    <p>You can then access the MBeans exposed by Jetty as well as
    -    logback's <code>JMXConfigurator</code> via
    -    <code>jconsole</code>.</p>
    -
    -    <img src="images/chapters/jmxConfigurator/jconsole15_jetty.gif" alt="jconsole15_jetty.gif"/>   
    -
    -    <p>After you are connected, you should be able to access
    -    <code>JMXXConfigurator</code> as shown in the <a
    -    href="#jmxConfigurator">screenshot</a> above.</p>
    -
    -    <h4>MX4J with Jetty (tested under JDK 1.5 and 1.6)</h4>
    -
    -    <p>If you wish to access <code>JMXConfigurator</code> via MX4J's
    -    HTTP interface and assuming you have already downloaded <a
    -    href="http://mx4j.sourceforge.net/">MX4J</a>, you then need to
    -    modify the Jetty configuration file discussed previously by adding
    -    an instruction to set the management port.
    -    </p>
    -    
    -    <pre class="prettyprint source">&lt;Call id="MBeanServer"
    -    class="java.lang.management.ManagementFactory"
    -    name="getPlatformMBeanServer"/>
    -
    -&lt;Get id="Container" name="container">
    -  &lt;Call name="addEventListener">
    -    &lt;Arg>
    -      &lt;New class="org.mortbay.management.MBeanContainer">
    -        &lt;Arg>&lt;Ref id="MBeanServer"/>&lt;/Arg>
    -        <b>&lt;Set name="managementPort">8082&lt;/Set></b>
    -        &lt;Call name="start" />
    -      &lt;/New>
    -    &lt;/Arg>
    -  &lt;/Call>
    -&lt;/Get> 
    -    </pre>
    -    
    -    <p>Moreover, <em>mx4j-tools.jar</em> needs to be added to Jetty's
    -    class path.
    -    </p>
    -
    -    <p>If you are running Jetty as a Maven plug-in, then you need to add
    -    <em>mx4j-tools</em> as a dependency.</p>
    -
    -    <pre class="prettyprint source">&lt;plugin>
    -  &lt;groupId>org.mortbay.jetty&lt;/groupId>
    -  &lt;artifactId>maven-jetty-plugin&lt;/artifactId>
    -  &lt;configuration>
    -    &lt;jettyConfig>path/to/jetty.xml&lt;/jettyConfig>
    -    ...
    -  &lt;/configuration>
    -  <b>&lt;dependencies>
    -    &lt;dependency>
    -      &lt;groupId>mx4j&lt;/groupId>
    -      &lt;artifactId>mx4j-tools&lt;/artifactId>
    -      &lt;version>3.0.1&lt;/version>
    -    &lt;/dependency>
    -  &lt;/dependencies></b>
    -&lt;/plugin></pre>
    -
    -		<p>After Jetty is started with the above configuration,
    -		<code>JMXConfigurator</code> will be available at the following
    -		URL (search for "ch.qos.logback.classic"):
    -		</p>
    -
    -    <p class="source"><a href="http://localhost:8082/">http://localhost:8082/</a></p>
    -
    -    <p>Below is a screen shot view of the MX4J interface.</p>
    -
    -    <img src="images/chapters/jmxConfigurator/mx4j_jetty.gif" alt="mx4j_jetty.gif"/>   
    -
    -
    -    <!-- ================ Tomcat ================== -->
    -		
    -		<h4>Configuring JMX for Tomcat (tested under JDK 1.5 and 1.6)</h4>
    -    
    -    <p>If you are using JDK 1.6 and later, your server is already JMX
    -    enabled by default and you can, but do not need to, follow the
    -    steps discussed below. Under JDK 1.5, Tomcat requires the addition
    -    of the following lines to the
    -    <em>$TOMCAT_HOME/bin/catalina.bat/sh</em> shell script:
    -		</p>
    -		
    -    <p class="source">CATALINA_OPTS="-Dcom.sun.management.jmxremote"</p>
    -
    -		<p>Once started with these options, MBeans exposed by Tomcat as
    -		well logback's <code>JMXConfigurator</code> can be accessed with
    -		<code>jconsole</code> by issuing the following command in a shell:
    -		</p>
    -    
    -    <p class="source">jconsole</p>
    -
    -    <img src="images/chapters/jmxConfigurator/jconsole15_tomcat.gif" alt="jconsole15_tomcat.gif"/>   
    -
    -    <p>After you are connected, you should be able to access
    -    <code>JMXXConfigurator</code> as shown in the <a
    -    href="#jmxConfigurator">screenshot</a> above.</p>
    -
    -
    -    <h4>MX4J with Tomcat (tested under JDK 1.5 and 1.6)</h4>
    -    
    -		<p>You might prefer to access JMX components via a web-based
    -		interface provided by MX4J.  In that case, here are the required
    -		steps:
    -		</p>
    -		
    -    <p>Assuming you have already downloaded <a
    -    href="http://mx4j.sourceforge.net/">MX4J</a>, place the
    -    <em>mx4j-tools.jar</em> file under the <em>$TOMCAT_HOME/bin/</em>
    -    directory. Then, add the following lines to the
    -    <em>$TOMCAT_HOME/bin/catalina.sh</em> configuration file:
    -		</p>
    -
    -    <p class="source">&lt;!-- at the beginning of the file -->
    -CATALINA_OPTS="-Dcom.sun.management.jmxremote"
    -
    -&lt;!-- in the "Add on extra jar files to CLASSPATH" section -->
    -CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/mx4j-tools.jar</p>
    -
    -		<p>Finally, declare a new <code>Connector</code> in the
    -		<em>$TOMCAT_HOME/conf/server.xml</em> file:
    -		</p>
    -
    -		
    -    <pre class="prettyprint source">&lt;Connector port="0" 
    -  handler.list="mx"
    -  mx.enabled="true" 
    -  mx.httpHost="localhost" 
    -  mx.httpPort="8082" 
    -  protocol="AJP/1.3" /></pre>
    -  
    -  	<p>Once Tomcat is started, you should be able to find
    -  	JMXConfigurator by pointing your browser at the following URL
    -  	(search for "ch.qos.logback.classic"):
    -  	</p>
    -
    -    <p class="source"><a href="http://localhost:8082">http://localhost:8082/</a></p>
    -
    -    <p>Below is a screen shot view of the MX4J interface.</p>
    -
    -    <img src="images/chapters/jmxConfigurator/mx4j_tomcat.gif" alt="mx4j_tomcat.gif"/>   
    -
    -		
    -	
    -	
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -  </div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/jmxConfig_ja.html b/logback-site/src/site/pages/manual/jmxConfig_ja.html
    deleted file mode 100644
    index 7dbc829f80..0000000000
    --- a/logback-site/src/site/pages/manual/jmxConfig_ja.html
    +++ /dev/null
    @@ -1,290 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第10章 JMXコンフィギュレーター</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -    <h1>第10章 JMXコンフィギュレーター</h1>
    -    
    -		<p>名前のとおり、<code>JMXConfigurator</code>を使うとJMX経由でlogbackを設定することができます。つまり、デフォルトの設定ファイルで設定されたlogbackを、指定したパスやURLに配置した設定ファイルの内容で再設定したり、ロガーの一覧を取得したり、ロガーのレベルを変更することができるのです。
    -		</p>
    -		
    -		<h3>JMXコンフィギュレーターを使用する</h3>
    -    
    -    
    -		<p>サーバーを実行しているJVMがJDK1.6以降なら、コマンドラインから<code>jconsole</code>コマンドを実行するだけで、実行中のサーバーのMBeanServerにアクセスすることができます。古いJVMで実行している場合は<a href="./10-jmxConfig.html#jmxEnablingServer">サーバーでJMXを有効化する</a>のセクションを読んでおいてください。
    -    </p>
    -
    -    <p>次のように設定ファイルに1行追加するだけで<code>JMXConfigurator</code>が有効になります。</p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;jmxConfigurator /&gt;</b>
    -  
    -  &lt;appender name="console" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;layout class="ch.qos.logback.classic.PatternLayout"&gt;
    -      &lt;Pattern&gt;%date [%thread] %-5level %logger{25} - %msg%n&lt;/Pattern&gt;
    -    &lt;/layout&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="console" /&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -		
    -    <p><em>jconsole</em>でサーバーに接続すれば、MBeans タブに表示される "ch.qos.logback.classic.jmx.Configurator" フォルダの下にいろいろなコマンドがぶら下がっているのが確認できます。スクリーンショットを見てください。</p>
    -    
    -    <h3 class="doAnchor" name="jmxConfigurator"><code>jconsole</code>で<code>JMXConfigurator</code>を表示している様子</h3>
    -
    -    <img src="images/chapters/jmxConfigurator/jmxConfigurator.gif" alt="jmxConfigurator">   
    -
    -		<p>次のような操作を実行することができます。</p>
    -		
    -		<ul>
    -      <li>デフォルトの設定ファイルを使用してlogbackを再設定します。</li>
    -
    -      <li>URLに配置された設定ファイルを使用して再設定します。</li>
    -      <li>ファイルパスに配置された設定ファイルを使用して再設定します。</li>
    -
    -			<li>指定したロガーのレベルを設定します。nullを設定するには文字列"null"を指定します。</li>
    -      <li>指定したロガーのレベルを取得します。nullになることがあります。</li>
    -			<li>指定したロガーの<a href="./01-architecture.html#effectiveLevel">有効レベル</a>を取得します。</li>
    -		</ul>
    -		
    -    <p><code>JMXConfigurator</code>はAttributesとして存在しているロガーの一覧と、ステータスの一覧を公開します。</p>
    -    
    -    <p>ステータスの一覧は、logbackの内部状態を診断するのに役立ちます。</p>
    -
    -    <img src="images/chapters/jmxConfigurator/statusList.gif" alt="statusList.gif">   
    -
    -    <h3 class="doAnchor" name="leak">メモリリークを避ける</h3>
    -
    -    <p>アプリケーションがWebサーバーやアプリケーションサーバにデプロイされているときは、<code>JMXConfigurator</code>のインスタンスをJVMのMBeanサーバーに登録すると、システムクラスローダーの参照をアプリケーションが持つようになってしまいます。これはアプリケーションが停止したり再デプロイされたときにJMXConfiguratorがガベージコレクションされてしまうのを防ぐためですが、そのせいで深刻なメモリリークが生じてしまいます。</p>
    -
    -    <p>したがって、スタンドアローンなJavaアプリケーションではないときは、必ず<code>JMXConfigurator</code>のインスタンスの登録をJVMのMBeanサーバーから解除しなければなりません。適切な<code>LoggerContetext</code>の<code>reset()</code>メソッドを呼べば、すべてのJMXConfiguratorのインスタンスは自動的に解除されるはずです。ロガーコンテキストを初期化するなら、<code>javax.servlet.ServletContextListener</code>の<code>contextDestroyed()</code>メソッドがちょうど良い場所です。サンプルコードを見てみましょう。</p>
    -
    -    <pre class="prettyprint source">import javax.servlet.ServletContextEvent;
    -import javax.servlet.ServletContextListener;
    -
    -import org.slf4j.LoggerFactory;
    -import ch.qos.logback.classic.LoggerContext;
    -
    -public class MyContextListener implements ServletContextListener {
    -
    -  public void contextDestroyed(ServletContextEvent sce) {
    -    <b>LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();</b>
    -    <b>lc.stop();</b>
    -  }
    -
    -  public void contextInitialized(ServletContextEvent sce) {
    -  }
    -} </pre>
    -
    -
    -    <!-- ============ Multiple web-applications  ================== -->
    -
    -    
    -    <h2 class="doAnchor" name="multiple">複数のWebアプリケーションで<code>JMXConfigurator</code>を使用する</h2>
    -
    -    <p>複数のアプリケーションを同じサーバーにデプロイしている、<a href="./09-loggingSeparation.html#contextSelectors">コンテキストセレクタ</a>を上書きせずデフォルトのまま使っている、それぞれのアプリケーションの<em>WEB-INF/lib</em>フォルダに<em>logback-*.jar</em>と<em>slf4j-api.jar</em>を置いている、これらが全て当てはまるなら、それぞれのアプリケーションの<code>JMXConfigurator</code>のインスタンスは同じ名前("ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator")で登録されてしまうでしょう。言い換えると、何もしなければそれぞれのアプリケーションのロガーコンテキストに関連付けられた<code>JMXConfigurator</code>のインスタンスは衝突してしまうのです。
    -    </p>
    -    
    -    <p>衝突を避けるには、単に<a href="./03-configuration.html#contextName">アプリケーションのロガーコンテキストに名前を付ける</a>だけでよいのです。そうすれば、<code>JMXConfigurator</code>には自動的にその名前が設定されます。
    -    </p>
    -
    -    <p>たとえば、それぞれ"コアラ"と"コウモリ"という名前のアプリケーションをデプロイしているものとします。そして、コアラのlogback.xmlには次のような設定がされているとしましょう。</p>
    -    
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;contextName&gt;Koala&lt;/contextName&gt;</b>
    -  &lt;jmxConfigurator/&gt;
    -  ...
    -&lt;configuration&gt;</pre>
    -
    -    <p>また、コウモリのlogback.xmlには次のように設定がされていることにします。</p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;contextName&gt;Wombat&lt;/contextName&gt;</b>x
    -  &lt;jmxConfigurator/&gt;
    -  ...
    -&lt;configuration&gt;</pre>
    -
    -    <p>そうすると、jconsoleのMBeanタブには、次のように二つの独立した<code>JMXConfigurator</code>のインスタンスが表示されるはずです。</p>
    -
    -    <img src="images/chapters/jmxConfigurator/multiple.gif" alt="multiple.gif">   
    -
    -    <p>MBeanサーバーに登録されるJMXConfiguratorの名前は、<code>jmxConfigurator要素</code>の"objectName"属性で指定することができます。</p>
    -    
    -    <!-- ============ JMX enabling your  server ================== -->
    -
    -    <h3 class="doAnchor" name="jmxEnablingServer">JMXを有効化する</h3>
    -
    -    <p>JDK1.6以降のJVMでサーバーを実行している場合はデフォルトでJMXが有効になっています。</p>
    -
    -		<p>JDK1.6以前の古いJVMを使っている場合は、サーバーのJMX関連のドキュメントを確認することをおすすめします。たとえば<a href="http://tomcat.apache.org/tomcat-6.0-doc/monitoring.html">Tomcat</a>や<a href="http://docs.codehaus.org/display/JETTY/JMX">Jetty</a>にはドキュメントがあります。それはさておき、このドキュメントではTomcatとJettyの設定方法を簡単に説明していきます。
    -		</p>
    -
    -    <!-- ================ Configuring Jetty ================== -->
    -
    -		<h4>JettyでJMXを有効にする(JDK 1.5およびJDK 1.6で動作確認済み)</h4>
    -		
    -		<p>以降の設定はJDK1.5とJDK1.6で動作確認しています。JDK1.6以降のJVMでは、デフォルトでJMXが有効になっています。以降の設定をしてもよいですが、特に何も変わりません。JDK1.5のJVMで実行するJettyでJMXを有効にするには、設定ファイル<em>$JETTY_HOME/etc/jetty.xml</em>にいくつか設定を追加する必要があります。追加するのは次の設定です。</p>
    -
    -    <pre class="prettyprint source">&lt;Call id="MBeanServer" class="java.lang.management.ManagementFactory" 
    -      name="getPlatformMBeanServer"/&gt;
    -
    -&lt;Get id="Container" name="container"&gt;
    -  &lt;Call name="addEventListener"&gt;
    -    &lt;Arg&gt;
    -      &lt;New class="org.mortbay.management.MBeanContainer"&gt;
    -        &lt;Arg&gt;&lt;Ref id="MBeanServer"/&gt;&lt;/Arg&gt;
    -        &lt;Call name="start" /&gt;
    -      &lt;/New&gt;
    -    &lt;/Arg&gt;
    -  &lt;/Call&gt;
    -&lt;/Get&gt; </pre>
    -
    -    <p>Jettyの公開するMBeanに<code>jconsole</code>でアクセスしたいときは、Jettyを実行するJVMにシステムプロパティ"com.sun.management.jmxremote"を指定しておかなければなりません。
    -    </p>
    -
    -    <p>スタンドアローン版のJettyなら次のように実行します。</p>
    -
    -    
    -    <p class="source">java <b>-Dcom.sun.management.jmxremote</b> -jar start.jar [config files]</p>
    -
    -    <p>MavenのプラグインからJettyを実行する場合は、システムプロパティ"com.sun.management.jmxremote"をシェル変数<code>MAVEN_OPTS</code>で指定しなければなりません。</p>
    -
    -    <p class="source"><b>MAVEN_OPTS="-Dcom.sun.management.jmxremote</b>" mvn jetty:run</p>
    -
    -    <p>そうすれば、<code>jconsole</code>でJettyの公開するMBeanとして<code>JMXConfigurator</code>にアクセスすることができます。</p>
    -
    -    <img src="images/chapters/jmxConfigurator/jconsole15_jetty.gif" alt="jconsole15_jetty.gif">   
    -
    -    <p>接続したら、<a href="./10-jmxConfig.html#jmxConfigurator">前のスクリーンショット</a>のように<code>JMXConfigurator</code>にアクセスできるはずです。</p>
    -
    -    <h4>JettyにMX4Jを入れる(JDK 1.5およびJDK 1.6で動作確認済み)</h4>
    -
    -    <p>MX4JのHTTPインターフェイスを経由して<code>JMXConfigurator</code>にアクセスしたいときは、前に説明した設定ファイルに管理ポート番号の設定を追加します。<a href="http://mx4j.sourceforge.net/">MX4J</a>はすでにダウンロード済みであることにします。
    -    </p>
    -    
    -    <pre class="prettyprint source">&lt;Call id="MBeanServer"
    -    class="java.lang.management.ManagementFactory"
    -    name="getPlatformMBeanServer"/&gt;
    -
    -&lt;Get id="Container" name="container"&gt;
    -  &lt;Call name="addEventListener"&gt;
    -    &lt;Arg&gt;
    -      &lt;New class="org.mortbay.management.MBeanContainer"&gt;
    -        &lt;Arg&gt;&lt;Ref id="MBeanServer"/&gt;&lt;/Arg&gt;
    -        <b>&lt;Set name="managementPort"&gt;8082&lt;/Set&gt;</b>
    -        &lt;Call name="start" /&gt;
    -      &lt;/New&gt;
    -    &lt;/Arg&gt;
    -  &lt;/Call&gt;
    -&lt;/Get&gt; 
    -    </pre>
    -    
    -    <p>なお、<em>mx4j-tools.jar</em>はJettyのクラスパス上に配置しておいてください。
    -    </p>
    -
    -    <p>MavenのプラグインからJettyを実行する場合は、依存関係に<em>mx4j-tools</em>を追加してください。</p>
    -
    -    <pre class="prettyprint source">&lt;plugin&gt;
    -  &lt;groupId&gt;org.mortbay.jetty&lt;/groupId&gt;
    -  &lt;artifactId&gt;maven-jetty-plugin&lt;/artifactId&gt;
    -  &lt;configuration&gt;
    -    &lt;jettyConfig&gt;path/to/jetty.xml&lt;/jettyConfig&gt;
    -    ...
    -  &lt;/configuration&gt;
    -  <b>&lt;dependencies&gt;
    -    &lt;dependency&gt;
    -      &lt;groupId&gt;mx4j&lt;/groupId&gt;
    -      &lt;artifactId&gt;mx4j-tools&lt;/artifactId&gt;
    -      &lt;version&gt;3.0.1&lt;/version&gt;
    -    &lt;/dependency&gt;
    -  &lt;/dependencies&gt;</b>
    -&lt;/plugin&gt;</pre>
    -
    -		<p>この設定でJettyを起動すると、ブラウザで次のURLから<code>JMXConfigurator</code>にアクセスできるようになります。アクセスした後は "ch.qos.logback.classic" を探してください。</p>
    -
    -    <p class="source"><a href="http://localhost:8082/">http://localhost:8082/</a></p>
    -
    -    <p>MX4Jにアクセスしているところのスクリーンショットです。</p>
    -
    -    <img src="images/chapters/jmxConfigurator/mx4j_jetty.gif" alt="mx4j_jetty.gif">   
    -
    -
    -    <!-- ================ Tomcat ================== -->
    -		
    -		<h4>Tomcat用のJMXの設定(JDK 1.5およびJDK 1.6で動作確認済み)</h4>
    -    
    -    <p>JDK1.6以降のJVMを使用しているならJMXはデフォルトで有効になっているので以降の設定をする必要はありません。JDK1.5の場合は<em>$TOMCAT_HOME/bin/catalina.sh(Windowsの場合はcatalina.bat)</em>の適切な場所に次の設定を追加する必要があります。</p>
    -		
    -    <p class="source">CATALINA_OPTS="-Dcom.sun.management.jmxremote"</p>
    -
    -		<p>そうすれば、<code>jconsole</code>を使ってTomcatの公開するMBeanとして<code>JMXConfigurator</code>にアクセスできるようになります。</p>
    -    
    -    <p class="source"></p>
    -
    -    <img src="images/chapters/jmxConfigurator/jconsole15_tomcat.gif" alt="jconsole15_tomcat.gif">   
    -
    -    <p>接続したら、<a href="./10-jmxConfig.html#jmxConfigurator">前のスクリーンショット</a>のように<code>JMXConfigurator</code>にアクセスできるはずです。
    -</p>
    -
    -
    -    <h4>TomcatにMX4Jを入れる(JDK 1.5およびJDK 1.6で動作確認済み)</h4>
    -    
    -		<p>MX4JのWebインターフェイスを使ってJMXのコンポーネントにアクセスしたくなるかもしれません。そうするために必要な設定方法を説明します。</p>
    -		
    -    <p><a href="http://mx4j.sourceforge.net/">MX4J</a>はすでにダウンロードしてあることにしましょう。<em>mx4j-tools.jar</em>を<em>$TOMCAT_HOME/bin</em>フォルダーにおいてください。それから、<em>$TOMCAT_HOME/bin/catalina.sh(Windowsの場合はcatalina.bat)</em>の適切な場所に次の設定を追加します。</p>
    -
    -    <p class="source">&lt;!-- at the beginning of the file --&gt;
    -CATALINA_OPTS="-Dcom.sun.management.jmxremote"
    -
    -&lt;!-- in the "Add on extra jar files to CLASSPATH" section --&gt;
    -CLASSPATH="$CLASSPATH":"$CATALINA_HOME"/bin/mx4j-tools.jar</p>
    -
    -		<p>最後に、<em>$TOMCAT_HOME/conf/server.xmlで新しい<code>Connector要素</code>を宣言します。</em></p>
    -
    -		
    -    <pre class="prettyprint source">&lt;Connector port="0" 
    -  handler.list="mx"
    -  mx.enabled="true" 
    -  mx.httpHost="localhost" 
    -  mx.httpPort="8082" 
    -  protocol="AJP/1.3" /&gt;</pre>
    -  
    -  	<p>Tomcatを起動したら、ブラウザで次のURLからJMXConfiguratorにアクセスできるようになります。アクセスした後は "ch.qos.logback.classic" を探してください。</p>
    -
    -    <p class="source"><a href="http://localhost:8082">http://localhost:8082/</a></p>
    -
    -    <p>MX4Jにアクセスしているところのスクリーンショットです。
    -</p>
    -
    -    <img src="images/chapters/jmxConfigurator/mx4j_tomcat.gif" alt="mx4j_tomcat.gif">   
    -
    -		
    -	
    -	
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -   
    -  </div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/layouts.html b/logback-site/src/site/pages/manual/layouts.html
    deleted file mode 100755
    index 943a8588cd..0000000000
    --- a/logback-site/src/site/pages/manual/layouts.html
    +++ /dev/null
    @@ -1,2315 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 6: Layouts</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -    
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -    
    -    <h1>Chapter 6: Layouts</h1>
    -
    -    <a href="layouts_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -	
    -    <div class="quote">
    -      <p>TCP implementations will follow a general principle of
    -      robustness: be conservative in what you do, be liberal in what
    -      you accept from others.
    -      </p>
    -      <p>&mdash;JON POSTEL, RFC 793</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -     
    -		<h2 class="doAnchor">What is a layout?</h2>
    -
    -		<p>In case you were wondering, layouts have nothing to do with
    -		large estates in Florida.  Layouts are logback components
    -		responsible for transforming an incoming event into a String.  The
    -		<code>format()</code> method in the <a
    -		href="../xref/ch/qos/logback/core/Layout.html"><code>Layout</code></a>
    -		interface takes an object that represents an event (of any type)
    -		and returns a String. A synopsis of the <code>Layout</code>
    -		interface is shown below.
    -		</p>
    -
    -		<pre class="prettyprint source">public interface Layout&lt;E> extends ContextAware, LifeCycle {
    -
    -  String doLayout(E event);
    -  String getFileHeader();
    -  String getPresentationHeader();
    -  String getFileFooter();
    -  String getPresentationFooter();
    -  String getContentType();
    -}</pre>
    -
    -		<p>This interface is rather simple and yet is sufficient for many
    -		formatting needs. The Texan developer from Texas, whom you might
    -		know from Joseph Heller's <em>Catch-22</em>, might exclaim: it
    -		just takes five methods to implement a layout!!?
    -		</p>
    -
    -		<h2>Logback-classic</h2>
    -
    -		<p>Logback-classic is wired to process only events of type
    -		<a href="../xref/ch/qos/logback/classic/spi/ILoggingEvent.html">
    -      <code>ch.qos.logback.classic.spi.ILoggingEvent</code></a>.  This
    -			fact will be apparent throughout this section.</p>
    -
    -		<h2 class="doAnchor" name="writingYourOwnLayout">Writing your own
    -		custom Layout</h2>
    -
    -		<p>Let us implement a simple yet functional layout for the
    -			logback-classic module that prints the time elapsed since the
    -			start of the application, the level of the logging event, the
    -			caller thread between brackets, its logger name, a dash followed
    -			by the event message and a new line.
    -		</p>
    -
    -		<p>Sample output might look like:</p>
    -
    -		<div class="source">10489 DEBUG [main] com.marsupial.Pouch - Hello world.</div>
    -
    -		<p>Here is a possible implementation, authored by the Texan developer:</p>
    -		<em>Example: Sample implementation of a Layout
    -			<a href="../xref/chapters/layouts/MySampleLayout.html">
    -			(logback-examples/src/main/java/chapters/layouts/MySampleLayout.java)</a></em>
    -
    -		<pre class="prettyprint source">package chapters.layouts;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class MySampleLayout extends LayoutBase&lt;ILoggingEvent> {
    -
    -  public String doLayout(ILoggingEvent event) {
    -    StringBuffer sbuf = new StringBuffer(128);
    -    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    -    sbuf.append(" ");
    -    sbuf.append(event.getLevel());
    -    sbuf.append(" [");
    -    sbuf.append(event.getThreadName());
    -    sbuf.append("] ");
    -    sbuf.append(event.getLoggerName();
    -    sbuf.append(" - ");
    -    sbuf.append(event.getFormattedMessage());
    -    sbuf.append(CoreConstants.LINE_SEP);
    -    return sbuf.toString();
    -  }
    -}</pre>
    -
    -		<p>Note that <code>MySampleLayout</code> extends <a
    -		href="../xref/ch/qos/logback/core/LayoutBase.html">
    -		<code>LayoutBase</code></a>.  This class manages state common to
    -		all layout instances, such as whether the layout is started or
    -		stopped, header, footer and content type data. It allows the
    -		developer to concentrate on the formatting expected from his/her
    -		<code>Layout</code>. Note that the <code>LayoutBase</code> class
    -		is generic. In its class declaration, <code>MySampleLayout</code>
    -		extends <code>LayoutBase&lt;ILoggingEvent&gt;</code>.
    -		</p>
    -		
    -		<p>The <code>doLayout(ILoggingEvent event)</code> method, i.e. the
    -		only method in <code>MySampleLayout</code>, begins by
    -		instantiating a <code>StringBuffer</code>. It proceeds by adding
    -		various fields of the event parameter. The Texan from Texas was
    -		careful to print the formatted form of the message. This is
    -		significant if one or more parameters were passed along with
    -		the logging request.
    -		</p>
    -		
    -    <p>After adding these various characters to the string buffer, the
    -    <code>doLayout()</code> method converts the buffer into a
    -    <code>String</code> and returns the resulting value.
    -		</p>
    -
    -		<p>In the above example, the <code>doLayout</code> method ignores
    -		any eventual exceptions contained in the event. In a real world
    -		layout implementation, you would most probably want to print the
    -		contents of exceptions as well.
    -		</p>
    -
    -    <h3 class="doAnchor" name="configuringYourOwnLayout">Configuring
    -    your custom layout</h3>
    -
    -		<p>Custom layouts are configured as any other component. As
    -		mentioned earlier, <code>FileAppender</code> and its sub-classes
    -		expect an encoder. In order to fulfill this requirement, we pass
    -		to <code>FileAppender</code> an instance of
    -		<code>LayoutWrappingEncoder</code> which wraps our
    -		<code>MySampleLayout</code>. Here is the configuration file:</p>
    -		
    -		<em>Example: Configuration of MySampleLayout
    -		(logback-examples/src/main/resources/chapters/layouts/sampleLayoutConfig.xml)</em>
    -    <span class="asGroovy" onclick="return asGroovy('sampleLayoutConfig');">View as .groovy</span>
    -<pre id="sampleLayoutConfig" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    <b>&lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"></b>
    -      <b>&lt;layout class="chapters.layouts.MySampleLayout" /></b>
    -    <b>&lt;/encoder></b>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -   
    -		<p>The sample application <a
    -		href="../xref/chapters/layouts/SampleLogging.html">
    -		<code>chapters.layouts.SampleLogging</code></a> configures logback
    -		with the configuration script passed as its first argument and
    -		then logs a debug message, followed by an error message. </p>
    -		
    -		<p>To run this example issue the following command from within the
    -		<em>logback-examples</em> directory.
    -    </p>
    -    
    -    <p class="command">java chapters.layouts.SampleLogging src/main/java/chapters/layouts/sampleLayoutConfig.xml</p>
    -    
    -    <p> This will produce:</p>
    -		
    -<div class="source"><pre>0 DEBUG [main] chapters.layouts.SampleLogging - Everything's going well
    -0 ERROR [main] chapters.layouts.SampleLogging - maybe not quite...</pre></div>
    -
    -		<p>That was simple enough.  The skeptic Pyrrho of Elea, who
    -		insists that nothing is certain except perhaps uncertainty itself,
    -		which is by no means certain either, might ask: how about a layout
    -		with options?  The reader shall find a slightly modified version
    -		of our custom layout in <a
    -		href="../xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2.java</code></a>. As
    -		mentioned throughout this manual, adding a property to a layout or
    -		any other logback component is as simple as declaring a setter
    -		method for the property.
    -		</p>
    -
    -		<p>The <a
    -		href="../xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2</code></a>
    -		class contains two properties. The first one is a prefix that can
    -		be added to the output. The second property is used to choose
    -		whether to display the name of the thread from which the logging
    -		request was sent.
    -    </p>
    -		
    -    <p>Here is a copy of the <a
    -    href="../xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2</code></a>
    -    class :</p>
    -
    -    <pre class="prettyprint source">package chapters.layouts;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class MySampleLayout2 extends LayoutBase&lt;ILoggingEvent> {
    -
    -  String prefix = null;
    -  boolean printThreadName = true;
    -
    -  <b>public void setPrefix(String prefix) {
    -    this.prefix = prefix;
    -  }
    -
    -  public void setPrintThreadName(boolean printThreadName) {
    -    this.printThreadName = printThreadName;
    -  }</b>
    -
    -  public String doLayout(ILoggingEvent event) {
    -    StringBuffer sbuf = new StringBuffer(128);
    -    <b>if (prefix != null) {
    -      sbuf.append(prefix + ": ");
    -    }</b>
    -    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    -    sbuf.append(" ");
    -    sbuf.append(event.getLevel());
    -    <b>if (printThreadName) {
    -      sbuf.append(" [");
    -      sbuf.append(event.getThreadName());
    -      sbuf.append("] ");
    -    } else {
    -      sbuf.append(" ");
    -    }</b>
    -    sbuf.append(event.getLoggerName());
    -    sbuf.append(" - ");
    -    sbuf.append(event.getFormattedMessage());
    -    sbuf.append(LINE_SEP);
    -    return sbuf.toString();
    -  }
    -}</pre>
    -
    -
    -    <p>The addition of the corresponding setter method is all that is
    -    needed to enable the configuration of a property.  Note that the
    -    <code>PrintThreadName</code> property is a boolean and not a
    -    <code>String</code>. Configuration of logback components was
    -    covered in detail in the <a
    -    href="configuration.html">chapter on configuration</a>. The <a
    -    href="onJoran.html">chapter on Joran</a> provides further detail. Here is
    -    the configuration file tailor made for
    -    <code>MySampleLayout2</code>.
    -    </p> 
    -
    -
    -    <span class="asGroovy" onclick="return asGroovy('MySampleLayout2');">View as .groovy</span>
    -    <pre id="MySampleLayout2" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
    -      &lt;layout class="chapters.layouts.MySampleLayout2"> 
    -        <b>&lt;prefix&gt;MyPrefix&lt;/prefix&gt;</b>
    -        <b>&lt;printThreadName&gt;false&lt;/printThreadName&gt;</b>
    -      &lt;/layout>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -   <p></p>
    -
    -
    -		<h2 class="doAnchor" name="ClassicPatternLayout">PatternLayout</h2>
    -
    -		<p>Logback classic ships with a flexible layout called <a
    -		href="../xref/ch/qos/logback/classic/PatternLayout.html">
    -		<code>PatternLayout</code></a>.  As all layouts,
    -		<code>PatternLayout</code> takes a logging event and returns a
    -		<code>String</code>. However, this <code>String</code> can be
    -		customized by tweaking <code>PatternLayout</code>'s
    -		conversion pattern.
    -		</p>   
    -
    -    <p>The conversion pattern of <code>PatternLayout</code> is closely
    -    related to the conversion pattern of the <code>printf()</code>
    -    function in the C programming language. A conversion pattern is
    -    composed of literal text and format control expressions called
    -    <em>conversion specifiers</em>. You are free to insert any literal
    -    text within the conversion pattern. Each conversion specifier
    -    starts with a percent sign '%' and is followed by optional
    -    <em>format modifiers</em>, a <em>conversion word</em> and optional
    -    parameters between braces. The conversion word controls the data
    -    field to convert, e.g. logger name, level, date or thread
    -    name. The format modifiers control field width, padding, and left
    -    or right justification.
    -		</p>
    -
    -    <p>As already mentioned on several occasions,
    -    <code>FileAppender</code> and sub-classes expect an
    -    encoder. Consequently, when used in conjunction with
    -    <code>FileAppender</code> or its subclasses a
    -    <code>PatternLayout</code> must be wrapped within an
    -    encoder. Given that the
    -    <code>FileAppender</code>/<code>PatternLayout</code> combination
    -    is so common, logback ships with an encoder named
    -    <code>PatternLayoutEncoder</code>, designed solely for the purpose
    -    of wrapping a <code>PatternLayout</code> instance so that it can
    -    be seen as encoder. Below is an example which programmatically
    -    configures a <code>ConsoleAppender</code> with a
    -    <code>PatternLayoutEncoder</code>:</p>
    -
    - 
    -		<em>
    -			Example: Sample usage of a PatternLayout
    -			<a href="../xref/chapters/layouts/PatternSample.html">
    -			(logback-examples/src/main/java/chapters/layouts/PatternSample.java)</a>
    -		</em>
    -		<pre class="prettyprint source">package chapters.layouts;
    -
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.Logger;
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.ConsoleAppender;
    -
    -public class PatternSample {
    -
    -  static public void main(String[] args) throws Exception {
    -    Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    -    LoggerContext loggerContext = rootLogger.getLoggerContext();
    -    // we are not interested in auto-configuration
    -    loggerContext.reset();
    -
    -    <b>PatternLayoutEncoder encoder = new PatternLayoutEncoder();</b>
    -    <b>encoder.setContext(loggerContext);</b>
    -    <b>encoder.setPattern("%-5level [%thread]: %message%n");</b>
    -    <b>encoder.start();</b>
    -
    -    ConsoleAppender&lt;ILoggingEvent> appender = new ConsoleAppender&lt;ILoggingEvent>();
    -    appender.setContext(loggerContext);
    -    appender.setEncoder(encoder); 
    -    appender.start();
    -
    -    rootLogger.addAppender(appender);
    -
    -    rootLogger.debug("Message 1"); 
    -    rootLogger.warn("Message 2");
    -  } 
    -}</pre>
    -
    -		<p>In the above example, the conversion pattern is set to be
    -		<b>"%-5level [%thread]: %message%n"</b>. A synopsis of conversion
    -		word included in logback will be given shortly. Running
    -		<code>PatternSample</code> application as:
    -		</p>
    -
    -    <p class="source">java java chapters.layouts.PatternSample</p>
    -
    -    <p>will yield the following		output on the console.</p>
    -
    -		<p class="source">DEBUG [main]: Message 1 
    -WARN  [main]: Message 2</p>
    -
    -    <p>Note that in the conversion pattern <b>"%-5level [%thread]:
    -    %message%n"</b> there is no explicit separator between literal
    -    text and conversion specifiers. When parsing a conversion pattern,
    -    <code>PatternLayout</code> is capable of differentiating between
    -    literal text (space characters, the brackets, colon character) and
    -    conversion specifiers. In the example above, the conversion
    -    specifier %-5level means the level of the logging event should be
    -    left justified to a width of five characters. Format specifiers
    -    will be explained below.
    -		</p>
    -
    -		<p>In <code>PatternLayout</code>, parenthesis can be used to group
    -		conversion patterns. <b>It follows that the '(' and ')' carry
    -		special meaning and need to be escaped if intended to be used as
    -		literals. </b> The special nature of parenthesis is further <a
    -		href="#Parentheses">explained below</a>.
    -		</p>
    -
    -		<p>As mentioned previously, certain conversion specifiers may
    -		include optional parameters passed between braces. A sample
    -		conversion specifier with options could be
    -		<code>%logger{10}</code>. Here "logger" is the conversion word,
    -		and 10 is the option. Options are <a href="#cwOptions">further
    -		discussed below</a>.
    -		</p>
    -		
    -		<p>The recognized conversions words along with their options are
    -		described in the table below. When multiple conversion words are
    -		listed in the same table cell, they are considered as aliases.
    -		</p>
    -
    -		<table class="bodyTable properties striped" border="0">
    -      <tr>
    -        <th><a name="conversionWord" href="#conversionWord">Conversion Word</a></th>
    -        <th>Effect</th>
    -      </tr>
    -
    -			<tr>
    -				<td class="word" name="logger">
    -          <a name="logger" href="#logger"><span class="anchor"/></a>
    -					<b>c</b>{<em>length</em>} <br /> 				
    -					<b>lo</b>{<em>length</em>} <br />
    -					<b>logger</b>{<em>length</em>} <br />
    -				</td>
    -
    -				<td>
    -          Outputs the name of the logger at the origin of the logging
    -          event.
    -
    -					<p>This conversion word takes an integer as its first and
    -					only option. The converter's abbreviation algorithm will
    -					shorten the logger name, usually without significant loss of
    -					meaning. Setting the value of length option to zero
    -					constitutes an exception. It will cause the conversion word
    -					to return the sub-string right to the rightmost dot
    -					character in the logger name. The next table provides
    -					examples of the abbreviation algorithm in action.
    -          </p>
    -
    -					<table class="bodyTable dark" border="0" cellpadding="8">
    -						<tr>
    -							<th>Conversion specifier</th>
    -							<th>Logger name</th>
    -							<th>Result</th>
    -						</tr>
    -						<tr>
    -							<td>%logger</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -						</tr>
    -
    -            <tr>
    -							<td>%logger{0}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>Bar</td>
    -						</tr>
    -
    -						<tr>
    -							<td>%logger{5}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>m.s.s.Bar</td>
    -						</tr>
    -
    -						<tr>
    -							<td>%logger{10}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>m.s.s.Bar</td>
    -						</tr>
    -
    -						<tr>
    -							<td>%logger{15}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>m.s.sample.Bar</td>
    -						</tr>
    -
    -						<tr>
    -							<td>%logger{16}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>m.sub.sample.Bar</td>
    -						</tr>
    -
    -						<tr>
    -							<td>%logger{26}</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -							<td>mainPackage.sub.sample.Bar</td>
    -						</tr>
    -					</table>
    -
    -          <p>Please note that the rightmost segment in a logger name
    -          is never abbreviated, even if its length is longer than the
    -          <em>length</em> option. Other segments may be shortened to
    -          at most a single character but are never removed.</p>
    -
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="class">
    -					<b>C</b>{<em>length</em>} <br /> 
    -					<b>class</b>{<em>length</em>} <br />
    -				</td>
    -
    -				<td>
    -					<p>Outputs the fully-qualified class name of the caller
    -					issuing the logging request.
    -					</p>
    -
    -					<p>Just like the <em>%logger</em> conversion word above,
    -					this conversion takes an integer as an option to shorten
    -					the class name. Zero carries special meaning and will cause
    -					the simple class name to be printed without the package name
    -					prefix. By default the class name is printed in full.
    -					</p>
    -
    -          <p>Generating the caller class information is not
    -          particularly fast.  Thus, its use should be avoided unless
    -          execution speed is not an issue.
    -					</p>
    -				</td>
    -			</tr>
    -
    -      <tr>
    -        <td class="word" name="contextName">
    -          <b>contextName</b><br/>
    -          <b>cn</b><br/></td>
    -          <td>Outputs the name of the logger context to which the
    -          logger at the origin of the event was attached to. </td>
    -      </tr>
    -			<tr>
    -        <td class="word" name="date">
    -          <b>d</b>{<em>pattern</em>} <br /> 
    -          <b>date</b>{<em>pattern</em>} <br />
    -          <b>d</b>{<em>pattern</em>, <em>timezone</em>} <br />
    -          <b>date</b>{<em>pattern</em>,&nbsp;<em>timezone</em>} <br />
    -        </td>
    -        <td>
    -         <p>Used to output the date of the logging event.  The date
    -         conversion word admits a pattern string as a parameter. The
    -         pattern syntax is compatible with the format accepted by <a
    -         href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html"><code>java.text.SimpleDateFormat</code></a>.</p>
    -
    -         <p>You can specify the string <em>"ISO8601"</em> for the
    -         ISO8601 date format. Note that the %date conversion word
    -         defaults to the <a
    -         href="http://en.wikipedia.org/wiki/ISO_8601">ISO 8601 date
    -         format</a> in the absence of a pattern parameter.</p>
    -
    -         <p>Here are some sample parameter values. They assume that
    -         the actual date is Friday 20th of October, 2006 and that the
    -         author has returned to working on this document just after
    -         lunch.</p>
    -					
    -         <table class="bodyTable dark" cellpadding="8">
    -           <tr>
    -             <th>Conversion Pattern</th>
    -            <th>Result</th>
    -           </tr>
    -           <tr>
    -             <td>%d</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>
    -           <tr>
    -             <td>%date</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>
    -           <tr>
    -             <td>%date{ISO8601}</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>			
    -           <tr>
    -             <td>%date{HH:mm:ss.SSS}</td>
    -             <td>14:06:49.812</td>
    -           </tr>
    -           <tr>
    -             <td>%date{dd&nbsp;MMM&nbsp;yyyy;HH:mm:ss.SSS}</td>
    -             <td>20 oct. 2006;14:06:49.812	</td>
    -           </tr>
    -         </table>
    -         
    -          <p>The second parameter specifies a timezone. For example,
    -          the '%date{HH:mm:ss.SSS,&nbsp;Australia/Perth} would print
    -          the time in the time zone of Perth, Australia, the world's
    -          most isolated city.  Note that in the absence of the
    -          timezone parameter, the default timezone of the host Java
    -          platform is used.  If the specified timezone identifier is
    -          unknown or misspelled, the GMT timezone is assumed as
    -          dictated by the <a
    -          href="http://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)">TimeZone.getTimeZone(String)</a>
    -          method specification.
    -          </p>
    -
    -          <p><span class="label">common error</span> Given that the
    -          comma ',' character is interpreted as the parameter
    -          separator, the pattern <code>HH:mm:ss,SSS</code> will be
    -          interpreted as the pattern <code>HM:mm:ss</code> and the
    -          timezone <code>SSS</code>. If you wish to include a comma in
    -          your date pattern, then simply enclose the pattern between
    -          quotes. For example, %date{<b>"</b>HH:mm:ss,SSS<b>"</b>}.
    -          </p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="file">
    -					<b>F / file</b>
    -				</td>
    -
    -				<td>
    -					<p>Outputs the file name of the Java source file where the
    -					logging request was issued.
    -					</p>
    -
    -					<p>Generating the file information is not particularly fast.
    -					Thus, its use should be avoided unless execution speed is
    -					not an issue.
    -					</p>
    -				</td>
    -			</tr>
    -
    -			<tr >
    -				<td class="word" name="caller">
    -					<b>caller{depth}</b>
    -					<b>caller{depthStart..depthEnd}</b>
    -					<b>caller{depth, evaluator-1, ... evaluator-n}</b>
    -					<b>caller{depthStart..depthEnd, evaluator-1, ... evaluator-n}</b>
    -				</td>
    -
    -				<td>
    -					<p>Outputs location information of the caller which
    -					generated the logging event.
    -					</p>
    -
    -					<p>The location information depends on the JVM
    -					implementation but usually consists of the fully qualified
    -					name of the calling method followed by the caller's source,
    -					the file name and line number between parentheses.
    -					</p>
    -
    -					<p>A integer can be added to the <em>caller</em> conversion
    -					specifier's options to configure the depth of the
    -					information to be displayed.
    -					</p>
    -
    -          <p>For example, <b>%caller{2}</b> would display the
    -          following excerpt:</p>
    -					
    -<pre class="source white_bg">0    [main] DEBUG - logging statement 
    -Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
    -Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre>
    -
    -					<p>And <b>%caller{3}</b> would display this other excerpt:</p>
    -
    -<pre class="source white_bg">16   [main] DEBUG - logging statement 
    -Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
    -Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)
    -Caller+2   at mainPackage.ConfigTester.main(ConfigTester.java:38)</pre>
    -
    -                    <p>A range specifier can be added to the <em>caller</em> conversion specifier's
    -                    options to configure the depth range of the information to be displayed.
    -                    </p>
    -
    -                    <p>For example, <b>%caller{1..2}</b> would display the following excerpt:</p>
    -
    -<pre class="source white_bg">0    [main] DEBUG - logging statement
    -Caller+0   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre>
    -
    -					<p>This conversion word can also use evaluators to test
    -					logging events against a given criterion before computing
    -					caller data. For example, using <b>%caller{3,
    -					CALLER_DISPLAY_EVAL}</b> will display three lines of
    -					stacktrace, only if the evaluator called
    -					<em>CALLER_DISPLAY_EVAL</em> returns a <b>positive</b>
    -					answer.
    -				</p>
    -
    -				 <p>Evaluators are described below.</p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="line">
    -					<b>L / line</b>
    -				</td>
    -
    -				<td><p>Outputs the line number from where the logging
    -					request was issued.</p>
    -
    -					<p>Generating the line number information is not
    -					particularly fast.  Thus, its use should be avoided unless
    -					execution speed is not an issue.
    -					</p>
    -				</td>
    -			</tr>
    -
    -
    -			<tr>
    -				<td class="word" name="message">
    -					<b>m / msg / message</b>
    -				</td>
    -				<td>
    -          <p>Outputs the application-supplied message associated with
    -          the logging event.
    -          </p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="method">
    -					<b>M / method</b>
    -				</td>
    -
    -				<td>
    -					<p>Outputs the method name where the logging request was
    -					issued.</p>
    -					<p>Generating the method name is not particularly fast.
    -					Thus, its use should be avoided unless execution speed is
    -					not an issue.</p>
    -				</td>
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="newline">
    -					<b>n</b>
    -				</td>
    -
    -				<td>
    -					<p>Outputs the platform dependent line separator
    -						character or characters.</p>
    -					<p>This conversion word offers practically the same
    -					performance as using non-portable line separator strings
    -					such as "\n", or "\r\n". Thus, it is the preferred way of
    -					specifying a line separator.
    -					</p>
    -				</td>
    -
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="level">
    -					<b>p / le / level</b>
    -				</td>
    -				<td>Outputs the level of the logging event.</td>
    -			</tr>
    -
    -			<tr>
    -
    -				<td class="word" name="relative">
    -					<b>r / relative</b>
    -				</td>
    -
    -				<td>Outputs the number of milliseconds elapsed since the start
    -				of the application until the creation of the logging event.
    -				</td>
    -			</tr>
    -
    -
    -			<tr>
    -				<td class="word" name="relative">
    -					<b>t / thread</b>
    -				</td>
    -
    -				<td>Outputs the name of the thread that generated the logging
    -				event.
    -				</td>
    -
    -			</tr>
    -
    -			<tr>
    -				<td class="word" name="mdc">
    -					<b>X</b>{<em>key:-defaultVal</em>} <br /> 
    -					<b>mdc</b>{<em>key:-defaultVal</em>} <br />
    -				</td>
    -
    -				<td>
    -
    -					<p>Outputs the MDC (mapped diagnostic context) associated
    -					with the thread that generated the logging event.
    -					</p>
    -
    -					<p>If the <b>mdc</b> conversion word is followed by a key
    -					between braces, as in <b>%mdc{userid}</b>, then the MDC
    -					value corresponding to the key 'userid' will be output. If
    -					the value is null, then the <a
    -					href="configuration.html#defaultValuesForVariables">default
    -					value</a> specified after the <b>:-</b> operator is
    -					output. If no default value is specified than the empty
    -					string is output.
    -					</p>
    -
    -					<p>If no key is given, then the entire content of the MDC
    -					will be output in the format "key1=val1, key2=val2".
    -					</p>
    -
    -					<p>See the <a href="mdc.html">chapter on MDC</a> for more
    -					details on the subject.</p>
    -
    -				</td>
    -			</tr>
    -			<tr>
    -				<td class="word" name="ex">
    -					<b>ex</b>{<em>depth</em>} <br /> 
    -          	<b>exception</b>{<em>depth</em>} <br /> 
    -					<b>throwable</b>{<em>depth</em>} <br />
    -          <br />
    -					<b>ex</b>{depth, evaluator-1, ..., evaluator-n} <br />
    -					<b>exception</b>{depth, evaluator-1, ..., evaluator-n} <br />
    -					<b>throwable</b>{depth, evaluator-1, ..., evaluator-n}
    -				</td>
    -
    -				<td>
    -					<p>Outputs the stack trace of the exception
    -					associated with the logging event, if any. By default the
    -					full stack trace will be output.
    -				 </p>
    -         
    -
    -  			 <p>The <em>throwable</em> conversion word can followed by one of
    -						the following options:
    -				 </p>
    -				 <ul>
    -				   <li><em>short</em>: prints the first line of the stack trace</li>
    -				   <li><em>full</em>: prints the full stack trace</li>
    -				   <li>Any integer: prints the given number of lines of the stack trace</li>
    -				 </ul>
    -				 
    -				 <p>Here are some examples:</p>
    -				 
    -				 <table  class="bodyTable">
    -						<tr class="a">
    -							<th>Conversion Pattern</th>
    -							<th>Result</th>
    -						</tr>
    -						<tr class="b">
    -							<td>%ex</td>
    -							<td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
    -  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td>
    -						</tr>
    -						<tr class="a">
    -							<td>%ex{short}</td>
    -							<td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)</pre></td>
    -						</tr>
    -						<tr class="b">
    -							<td>%ex{full}</td>
    -							<td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
    -  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td>
    -						</tr>
    -						<tr class="a">
    -							<td>%ex{2}</td>
    -							<td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)</pre></td>
    -						</tr>
    -				 </table>
    -					
    -					<p>This conversion word can also use evaluators to test
    -					logging events against a given criterion before creating the
    -					output. For example, using <b>%ex{full, EX_DISPLAY_EVAL}</b>
    -					will display the full stack trace of the exception only if
    -					the evaluator called <em>EX_DISPLAY_EVAL</em> returns a
    -					<b>negative</b> answer. Evaluators are described further
    -					down in this document.
    -					</p>
    -
    -         <p>If you do not specify %throwable or another
    -          throwable-related conversion word in the conversion pattern,
    -          <code>PatternLayout</code> will automatically add it as the
    -          last conversion word, on account of the importance of stack
    -          trace information. The $nopex conversion word can be
    -          substituted for %throwable, if you do not wish stack trace
    -          information to be displayed. See also the %nopex conversion
    -          word.
    -         </p>
    -
    -				</td>
    -			</tr>
    -      
    -      <tr>
    -				<td class="word" name="xThrowable">
    -					<b>xEx</b>{<em>depth</em>} <br /> 
    -          <b>xException</b>{<em>depth</em>} <br /> 
    -					<b>xThrowable</b>{<em>depth</em>} <br />
    -          <br />
    -					<b>xEx</b>{depth, evaluator-1, ..., evaluator-n} <br />
    -					<b>xException</b>{depth, evaluator-1, ..., evaluator-n} <br />
    -					<b>xThrowable</b>{depth, evaluator-1, ..., evaluator-n}
    -				</td>
    -
    -				<td>
    -					<p>Same as the %throwable conversion word above with the
    -					addition of class packaging information.</p>
    -
    -          <p>At the end of each stack frame of the exception, a string
    -          consisting of the jar file containing the relevant class
    -          followed by the "Implementation-Version" as found in that
    -          jar's manifest will be added. This innovative technique was
    -          <a
    -          href="http://macstrac.blogspot.com/2008/09/better-stack-traces-in-java-with-log4j.html">originally suggested
    -          by James Strachan</a>. If the information is uncertain, then
    -          the class packaging data will be preceded by a tilde, i.e.
    -          the '~' character.
    -          </p>
    -
    -          <p>Here is an example:</p>
    -
    -          <p class="source small">java.lang.NullPointerException
    -  at com.xyz.Wombat(Wombat.java:57) <b><span class="red">~</span>[wombat-1.3.jar:1.3]</b>
    -  at  com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
    -  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06]
    -  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06]
    -  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06]
    -  at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06]
    -  at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na]
    -  at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na]
    -  ...etc </p>
    -
    -          <p>Logback goes to great lengths to ensure that the class
    -          packaging information it displays is correct, even in
    -          arbitrarily complex class loader hierarchies.  However, when
    -          it is unable to guarantee the absolute correctness of the
    -          information, then it will prefix the data with a tilde, i.e.
    -          the '~' character. Thus, it is theoretically possible for
    -          the printed class packaging information to differ from the
    -          real class packaging information. So, in the above example,
    -          given that packaging data for the Wombat class is preceded
    -          by a tilde, it is possible that the correct packaging data is
    -          in reality [wombat.jar:1.7].
    -          </p>
    -
    -          <p>Please note that given its potential cost, computation of
    -          <a href="configuration.html#packagingData"><b>packaging data
    -          is disabled by default</b></a>. When computation of
    -          packaging data is enabled, <code>PatternLayout</code> will
    -          automatically assume the %xThrowable suffix instead of
    -          %throwable suffix at the end of the pattern string.</p>
    -
    -          <p><a
    -          href="http://jira.qos.ch/browse/LBCLASSIC-212">Feedback from
    -          users</a> indicates that Netbeans chokes on packaging
    -          information.
    -          </p>
    -        </td>
    -
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="nopex">
    -          <b>nopex</b> <br />
    -          <b>nopexception</b>
    -        </td>
    -
    -        <td>
    -          <p>Although it <em>pretends</em> to handle stack trace data,
    -          this conversion word does not output any data, thus,
    -          effectively ignoring exceptions.
    -          </p>
    -
    -          <p>The %nopex conversion word allows the user to override
    -          <code>PatternLayout</code>'s internal safety mechanism which
    -          silently adds the %xThrowable conversion keyword in the absence of
    -          another conversion word handling exceptions.
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr >
    -        <td class="word" name="marker">
    -          <b>marker</b>
    -        </td>
    -
    -        <td>
    -          <p>Outputs the marker associated with the logger 
    -         request.</p>
    -
    -          <p>In case the marker contains children markers, the
    -          converter displays the parent as well as childrens' names
    -          according to the format shown below.
    -          </p>
    -          <p>
    -            <em>parentName [ child1, child2 ]</em>
    -          </p>
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="property">
    -          <b>property{key}</b>
    -        </td>
    -        
    -        <td><p>Outputs the value associated with a property named
    -        <em>key</em>. The the relevant docs on how to define ion
    -        entitled <a
    -        href="configuration.html#variableSubstitution">define
    -        variables</a> and <a href="configuration.html#scopes">variable
    -        scopes</a>. 
    -
    -        <!-- XXXXXXXXXXXX -->
    -
    -        If <em>key</em> is not a property of
    -        the logger context, then <em>key</em> will be looked up in the
    -        System properties.</p>
    -
    -
    -         <p>There is no default value for <em>key</em>. If it is
    -         omitted, the returned value will be "Property_HAS_NO_KEY",
    -         expliciting the error condition.</p>
    -          
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="replace">
    -          <b>replace(<em>p</em>){r, t}</b>
    -        </td>
    -        
    -        <td>
    -          <p>Replaces occurrences of 'r', a regex, with its
    -          replacement 't' in the string produces by the sub-pattern
    -          'p'. For example, "%replace(%msg){'\s', ''}" will remove all
    -          spaces contained in the event message. 
    -          </p>
    -
    -          <p>The pattern 'p' can be arbitrarily complex and in
    -          particular can contain multiple conversion keywords. For
    -          instance, "%replace(%logger %msg){'\.', '/'}" will replace
    -          all dots in the logger or the message of the event with a
    -          forward slash.
    -          </p>
    -          
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="rootException">
    -          <b>rEx</b>{<em>depth</em>} <br /> 
    -          <b>rootException</b>{<em>depth</em>} <br /> 
    -          <br />
    -					<b>rEx</b>{depth, evaluator-1, ..., evaluator-n} <br />
    -					<b>rootException</b>{depth, evaluator-1, ..., evaluator-n}
    -        </td>
    -        
    -        <td>
    -          <p>Outputs the stack trace of the exception associated with
    -          the logging event, if any. The root cause will be output
    -          first instead of the standard "root cause last". Here is a
    -          sample output (edited for space):
    -          </p>
    -
    -         <pre class="small">java.lang.NullPointerException
    -  at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]
    -  at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
    -Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat': 
    -  at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) [spring-2.0.jar:2.0]
    -  at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) [spring-2.0.jar:2.0]
    -  at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) [tomcat-6.0.26.jar:6.0.26]
    -</pre>          
    -
    -         <p>The %rootException converter admits the same optional
    -         parameters as the %xException converter described above,
    -         including depth and evaluators. It outputs also packaging
    -         information. In short, %rootException is very similar to
    -         %xException, only the order of exception output is reversed.
    -         </p>
    -
    -         <p>Tomasz Nurkiewicz, the author of %rootException converter,
    -         documents his contribution in a blog entry entitled <a
    -         href="http://nurkiewicz.blogspot.com/2011/09/logging-exceptions-root-cause-first.html">"Logging
    -         exceptions root cause first"</a>.</p>
    -        </td>
    -      </tr>
    -
    -		</table>
    -
    -
    -    <h4 class="doAnchor" name="percentIsSpecial">% character has special meaning</h4>
    -
    -    <p>Given that in the context of conversion patterns the percent
    -    sign carries special meaning, in order to include it as a literal,
    -    it needs to be escaped with a backslash, e.g. "%d %p <b>\%</b>
    -    %m%n".
    -    </p>
    -
    -    <h4 class="doAnchor" name="restrictionsOnLiterals">Restrictions on
    -    literals immediately following conversion words</h4>
    -
    -    <p>In most cases literals naturally contain spaces or other
    -    delimiting characters so that they are not confused with
    -    conversion words. For example, the pattern
    -    "%level&nbsp;[%thread]&nbsp;-&nbsp;%message%n" contains the string
    -    literals <code>"&nbsp;["</code> and
    -    <code>"]&nbsp;-&nbsp;"</code>. However, if a character which can
    -    be part of a java identifier immediately follows a conversion
    -    word, logback's pattern parser will be fooled into thinking that
    -    the literal is part of the conversion word. For example, the
    -    pattern "%date<b>%nHello</b>" will be interpreted as two
    -    conversion words %date and %nHello and since %nHello is not a
    -    known conversion word, logback will output %PARSER_ERROR[nHello]
    -    for %nHello. If you wish the string literal "Hello" to immediately
    -    separate %n and Hello, pass an empty argument list to %n. For
    -    example, "%date<b>%n{}</b>Hello" will be interpreted as %date
    -    followed by %n followed by the literal "Hello".
    -
    -    </p>
    -
    -    <h2 class="doAnchor" name="formatModifiers">Format modifiers</h2>
    -
    -		<p>By default the relevant information is output as-is.  However,
    -		with the aid of format modifiers it is possible to change the
    -		minimum and maximum width and the justifications of each data
    -		field.
    -		</p>
    -
    -		<p>The optional format modifier is placed between the percent sign
    -		and the conversion character or word.
    -		</p>
    -
    -		<p>The first optional format modifier is the <em>left
    -		justification flag</em> which is just the minus (-)
    -		character. Then comes the optional <em>minimum field width</em>
    -		modifier. This is a decimal constant that represents the minimum
    -		number of characters to output. If the data item contains fewer
    -		characters, it is padded on either the left or the right until the
    -		minimum width is reached. The default is to pad on the left (right
    -		justify) but you can specify right padding with the left
    -		justification flag. The padding character is space. If the data
    -		item is larger than the minimum field width, the field is expanded
    -		to accommodate the data. The value is never truncated.
    -		</p>
    -
    -		<p>This behavior can be changed using the <em>maximum field
    -		width</em> modifier which is designated by a period followed by a
    -		decimal constant. If the data item is longer than the maximum
    -		field, then the extra characters are removed from the
    -		<em>beginning</em> of the data item. For example, if the maximum
    -		field width is eight and the data item is ten characters long,
    -		then the first two characters of the data item are dropped. This
    -		behavior deviates from the printf function in C where truncation
    -		is done from the end.
    -		</p>
    -
    -		<p>Truncation from the end is possible by appending a minus
    -		character right after the period. In that case, if the maximum
    -		field width is eight and the data item is ten characters long,
    -		then the last two characters of the data item are dropped.
    -		</p>
    -
    -		<p>Below are various format modifier examples for the logger
    -		conversion specifier.
    -		</p>
    -
    -		<table class="bodyTable" border="0" cellpadding="8">
    -      <tr>
    -        <th>Format modifier</th>
    -        <th>Left justify</th>
    -        <th>Minimum width</th>
    -        <th>Maximum width</th>
    -        <th>Comment</th>
    -      </tr>
    -			<tr class="a">
    -				<td align="center">%20logger</td>
    -				<td align="center">false</td>
    -				<td align="center">20</td>
    -				<td align="center">none</td>
    -				<td>
    -					Left pad with spaces if the logger name is less
    -					than 20 characters long.
    -				</td>
    -			</tr>
    -			<tr class="b">
    -				<td align="center">%-20logger</td>
    -				<td align="center">true</td>
    -				<td align="center">20</td>
    -				<td align="center">none</td>
    -				<td>
    -					Right pad with spaces if the logger name is less
    -					than 20 characters long.
    -				</td>
    -			</tr>
    -			<tr class="a">
    -				<td align="center">%.30logger</td>
    -				<td align="center">NA</td>
    -				<td align="center">none</td>
    -				<td align="center">30</td>
    -				<td>
    -					Truncate from the beginning if the logger name is
    -					longer than 30 characters.
    -				</td>
    -			</tr>
    -			<tr class="b">
    -				<td align="center">%20.30logger</td>
    -				<td align="center">false</td>
    -				<td align="center">20</td>
    -				<td align="center">30</td>
    -				<td>
    -					Left pad with spaces if the logger name is shorter
    -					than 20 characters. However, if logger name is
    -					longer than 30 characters, then truncate from the
    -					beginning.
    -				</td>
    -			</tr>
    -			<tr class="a">
    -				<td align="center">%-20.30logger</td>
    -				<td align="center">true</td>
    -				<td align="center">20</td>
    -				<td align="center">30</td>
    -				<td>
    -					Right pad with spaces if the logger name is shorter
    -					than 20 characters. However, if logger name is
    -					longer than 30 characters, then truncate from the
    -					<em>beginning</em>.
    -				</td>
    -			</tr>
    -			<tr class="b">
    -				<td align="center">%.-30logger</td>
    -				<td align="center">NA</td>
    -				<td align="center">none</td>
    -				<td align="center">30</td>
    -				<td>
    -					Truncate from the <em>end</em> if the logger name is
    -					longer than 30 characters.
    -				</td>
    -			</tr>
    -		</table>
    -				
    -		<p>The table below list examples for format modifier
    -		truncation. Please note that the square brackets, i.e the pair of "[]"
    -		characters, are not part of the output. They are used to delimit
    -		the width of output.</p>
    -
    -
    -		<table  class="bodyTable" border="0" cellpadding="8">
    -      <tr>
    -        <th>Format modifier</th>
    -        <th>Logger name</th>
    -        <th>Result</th>		
    -      </tr>
    -			<tr class="b">
    -				<td align="center">[%20.20logger]</td>
    -				<td align="center">main.Name</td>
    -				<td align="center"><pre>[           main.Name]</pre></td>
    -			</tr>
    -      <tr class="a">
    -				<td align="center">[%-20.20logger]</td>
    -				<td align="center">main.Name</td>
    -				<td align="center"><pre>[main.Name           ]</pre></td>
    -			</tr>
    -		  <tr class="a">
    -				<td align="center">[%10.10logger]</td>
    -				<td align="center">main.foo.foo.bar.Name</td>
    -				<td align="center"><pre>[o.bar.Name]</pre></td>
    -			</tr>
    -			<tr class="b">
    -				<td align="center">[%10.-10logger]</td>
    -				<td align="center">main.foo.foo.bar.Name</td>
    -				<td align="center"><pre>[main.foo.f]</pre></td>
    -			</tr>
    -		</table>
    -
    -    <h3 class="doAnchor" name="oneLetterLevel">Output just one letter
    -    for the level</h3>
    -
    -    <p>Instead of printing TRACE, DEBUG, WARN, INFO or ERROR for the
    -    level, you may want to print just T, D, W, I and E. You could
    -    write a <a href="#customConversionSpecifier">custom converter</a>
    -    for this purpose, or simply make use of format modifiers (just
    -    discussed) to shorten the level value to a single character. The
    -    appropriate conversion specifier would be
    -    "<code>%.-1level</code>".
    -    </p>
    -    
    -		<h2 class="doAnchor" name="cwOptions">Conversion word options</h2>
    -
    -		<p>A conversion specifier can be followed by options. The are
    -		always declared between braces. We have already seen some of the
    -		possibilities offered by options, for instance in conjunction with
    -		the MDC conversion specifier, as in: <em>%mdc{someKey}</em>.
    -		</p>
    -
    -    <p>A conversion specifier might have more than one option. For
    -    example, a conversion specifier that makes use of evaluators,
    -    which will be covered soon, may add evaluator names to the option
    -    list, as shown below:</p>
    -
    -		<pre class="prettyprint source">&lt;pattern>%-4relative [%thread] %-5level - %msg%n \
    -  <b>%caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</b>&lt;/pattern></pre>
    -    
    -    <p>If the option includes special characters such as a braces, spaces or
    -    commas, you can enclose it between single or double quotes. For
    -    example, consider the next pattern.</p>
    -
    -		<pre class="prettyprint source">&lt;pattern>%-5level - %replace(%msg)<b>{'\d{14,16}', 'XXXX'}</b>%n&lt;/pattern></pre>
    -
    -
    -    <p>We pass the options <code>\d{16}</code> and <code>XXXX</code>
    -    to the <code>replace</code> conversion word. It replaces any
    -    sequence of 14, 15 or 16 digits contained in the message with XXXX
    -    effectively obfuscating credit card numbers. Note that "\d" which
    -    is a shorthand for a single digit in regular expressions. The
    -    "{14,16\}" is interpreted as "{14, 16}", that is, repeat the
    -    previous item at least 14 but at most 16 times.
    -    </p>
    -
    -		<h2 class="doAnchor" name="Parentheses">Parentheses are
    -		special</h2>
    -
    -    <p>In logback, parentheses within the pattern string are treated
    -    as grouping tokens. Thus, it is possible to group a sub-pattern
    -    and apply formatting directives on that sub-pattern. As of version
    -    0.9.27, logback supports composite conversion words such as <a
    -    href="#replace">%replace</a> which can transform sub-patterns.
    -    </p>
    -
    -    <p>For example, the pattern</p> 
    -
    -    <p class="source"><b>%-30(</b>%d{HH:mm:ss.SSS} [%thread]<b>)</b> %-5level %logger{32} - %msg%n</p> 
    -
    -    <p>will group the output generated by the sub-pattern
    -    "%d{HH:mm:ss.SSS} [%thread]" so that it is right-padded if less
    -    than 30 characters.
    -    </p>
    -
    -    <p>If without the grouping the output was</p>
    -
    -    <p class="source">13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
    -13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
    -13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
    -13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
    -13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
    -13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
    -13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
    -13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
    -    </p>
    -
    -    <p>with the "%-30()" grouping it would be</p>
    -
    -    <p class="source">13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
    -13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
    -13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
    -13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
    -13:09:38 [btpool0-7]       INFO  c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
    -13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
    -13:09:40 [btpool0-7]       DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
    -13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Found factor 2
    -    </p>
    -
    -    
    -    <p>The latter form is more comfortable to read.</p>
    -    
    -    <p>If you need to treat the parenthesis character as a literal, it
    -    needs to be escaped by preceding each parenthesis with a
    -    backslash. As in, <b>\(</b>%d{HH:mm:ss.SSS}
    -    [%thread]<b>\)</b>. 
    -    </p>
    -
    -    <h2 class="doAnchor" name="coloring">Coloring</h2>
    -
    -    <p>Grouping by <a href="#Parentheses">parentheses</a> as explained
    -    above allows coloring of sub-patterns. As of version 1.0.5,
    -    <code>PatternLayout</code> recognizes "%black", "%red",
    -    "%green","%yellow","%blue", "%magenta","%cyan", "%white", "%gray",
    -    "%boldRed","%boldGreen", "%boldYellow", "%boldBlue",
    -    "%boldMagenta""%boldCyan", "%boldWhite" and "%highlight" as
    -    conversion words. These conversion words are intended to contain a
    -    sub-pattern. Any sub-pattern enclosed by a coloring word will be
    -    output in the specified color.
    -    </p>
    -
    -    <p>Below is a configuration file illustrating coloring. Note the
    -    %cyan conversion specifier enclosing "%logger{15}". This will
    -    output the logger name abbreviated to 15 characters in cyan. The
    -    %highlight conversion specifier prints its sub-pattern in bold-red
    -    for events of level ERROR, in red for WARN, in BLUE for INFO, and
    -    in the default color for other levels.</p>
    -
    -		<em>
    -			Example: Highlighting levels
    -			(logback-examples/src/main/resources/chapters/layouts/highlighted.xml)
    -		</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('highlighted');">View as .groovy</span>
    -
    -
    -<pre id="highlighted" class="prettyprint">&lt;configuration debug="true">
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;!-- On Windows machines setting withJansi to true enables ANSI
    -         color code interpretation by the Jansi library. This requires
    -         org.fusesource.jansi:jansi:1.8 on the class path.  Note that
    -         Unix-based operating systems such as Linux and Mac OS X
    -         support ANSI color codes by default. --&gt;
    -    <b>&lt;withJansi>true&lt;/withJansi></b>
    -    &lt;encoder>
    -      &lt;pattern>[%thread] <b>%highlight(%-5level)</b> <b>%cyan(%logger{15})</b> - %msg %n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -     <p>Here is the corresponding output:</p>
    -
    -<pre class="source">[main] <span style="color:#611">WARN</span>  <span style="color:#2bd">c.l.TrivialMain</span> - a warning message 0
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number1
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number2
    -[main] <span style="color:#00F">INFO</span>  <span style="color:#2bd">c.l.TrivialMain</span> - hello world number3
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number4
    -[main] <span style="color:#611">WARN</span>  <span style="color:#2bd">c.l.TrivialMain</span> - a warning message 5
    -[main] <span style="color:#F00">ERROR</span> <span style="color:#2bd">c.l.TrivialMain</span> - Finish off with fireworks</pre>
    -
    -    <p>It takes very few lines of code to create a coloring conversion
    -    word. The section entitled <a
    -    href="#customConversionSpecifier">creating a custom conversion
    -    specifier</a> discusses the steps necessary for registering a
    -    conversion word in your configuration file.</p>
    -   
    -		<h2 class="doAnchor" name="Evaluators">Evaluators</h2>
    -
    -		<p>As mentioned above, option lists come in handy when a
    -		conversion specifier is required to behave dynamically based on
    -		one or more
    -		<a href="../xref/ch/qos/logback/core/boolex/EventEvaluator.html">
    -		<code>EventEvaluator</code></a> objects.
    -		<code>EventEvaluator</code> objects have the responsibility to
    -		determine whether a given logging event matches the criteria of the
    -		evaluator.
    -		</p>
    -		
    -    <p>Let us review an example involving a
    -    <code>EventEvaluator</code>.  The next configuration file outputs
    -    the logging events to the console, displaying date, thread, level,
    -    message and caller data. Given that extracting the caller data of
    -    a logging event is on the expensive side, we will do so only when
    -    the logging request originates from a specific logger, and when
    -    the message contains a certain string. Thus, we make sure that only
    -    specific logging requests will have their caller information
    -    generated and displayed. In other cases, where the caller data is
    -    superfluous, we will not penalize application performance.
    -		</p>
    -
    -    <p>Evaluators and in particular <em>evaluation expressions</em>
    -    are presented in a <a
    -    href="filters.html#evalutatorFilter">dedicated section of the
    -    chapter on filters</a> which you MUST read if you want to use
    -    evaluators in any meaningful way. Also note that the examples below
    -    are implicitly based on <code>JaninoEventEvaluator</code> which
    -    requires the <a
    -    href="http://docs.codehaus.org/display/JANINO/Home">Janino
    -    library</a>. Please see the <a
    -    href="../setup.html#janino">corresponding section</a> of the setup
    -    document.</p>
    -
    -		<em>
    -			Example: Sample usage of EventEvaluators
    -			(logback-examples/src/main/resources/chapters/layouts/callerEvaluatorConfig.xml)
    -		</em>
    -
    -    <span class="asGroovy" onclick="return asGroovy('callerEvaluatorConfig');">View as .groovy</span>
    -
    -
    -		<pre id="callerEvaluatorConfig" class="prettyprint source">&lt;configuration>
    -  <b>&lt;evaluator name="DISP_CALLER_EVAL">
    -    &lt;expression>logger.contains("chapters.layouts") &amp;amp;&amp;amp; \
    -      message.contains("who calls thee")&lt;/expression>
    -  &lt;/evaluator></b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
    -    &lt;encoder>
    -      &lt;pattern>
    -        %-4relative [%thread] %-5level - %msg%n<b>%caller{2, DISP_CALLER_EVAL}</b>
    -      &lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG"> 
    -    &lt;appender-ref ref="STDOUT" /> 
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>The above evaluation expression matches events which emanate
    -		from a logger with a name containing the string "chapters.layouts"
    -		and the message contains the string "who calls thee". Due to XML
    -		encoding rules, the &amp; character cannot be written as is, and
    -		needs to be escaped as &amp;amp;.</p>
    -
    -    <p>The following class makes use of some of the characteristics
    -    mentioned in above configuration file.</p>
    -		
    -    <p><em>
    -			Example: Sample usage of EventEvaluators
    -			<a href="../xref/chapters/layouts/CallerEvaluatorExample.html">
    -			(logback-examples/src/main/java/chapters/layouts/CallerEvaluatorExample.java)</a>
    -		</em>
    -    </p>
    -		<pre class="prettyprint source">package <b>chapters.layouts</b>;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class CallerEvaluatorExample {
    -
    -  public static void main(String[] args)  {
    -    Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class);
    -    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -
    -    try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(lc);
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -      // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
    -
    -    for (int i = 0; i &lt; 5; i++) {
    -      if (i == 3) {
    -        logger.debug(<b>"who calls thee</b>?");
    -      } else {
    -        logger.debug("I know me " + i);
    -      }
    -    }
    -  }
    -}</pre>
    -
    -		<p>The above application does nothing particularly fancy. Five
    -		logging requests are issued, the third one emitting the message
    -		"who calls thee?"
    -		</p>
    -
    -		<p>The command</p>
    -
    -    <p class="source">java chapters.layouts.CallerEvaluatorExample src/main/java/chapters/layouts/callerEvaluatorConfig.xml</p>
    -
    -    <p>will yield</p>
    -
    -		<div class="source"><pre>0    [main] DEBUG - I know me 0 
    -0    [main] DEBUG - I know me 1 
    -0    [main] DEBUG - I know me 2 
    -0    [main] DEBUG - who calls thee? 
    -Caller+0   at chapters.layouts.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28)
    -0    [main] DEBUG - I know me 4</pre></div>
    -
    -
    -		<p>When a logging request is issued, the corresponding logging
    -		event is evaluated. Only the third logging event matches the
    -		evaluation criteria, causing its caller data to be displayed. For
    -		other logging events, the evaluation criteria do not match and no
    -		caller data is printed.
    -		</p>
    -
    -
    -		<p>One can change the expression to correspond a real world
    -		scenario. For instance, one could combine the logger name and
    -		request level. Thus, logging requests of level <em>WARN</em> and
    -		up, originating from a sensitive part of an application, e.g. a
    -		financial transaction module, would have their caller data
    -		displayed.
    -		</p>
    -
    -		<p><b>Important:</b> With the <em>caller</em> conversion word,
    -		caller data is output when <em>the expression evaluates to
    -		<b>true</b>.</em></p>
    -
    -		<p>Let us consider at a different situation. When exceptions are
    -		included in a logging request, their stack trace is also
    -		output. However, one might want to suppress the stack trace for
    -		some specific exceptions.
    -		</p>
    -
    -		<p>The Java code shown below creates three log requests, each with
    -		an exception. The second exception is different from the others:
    -		it contains the string "do not display this" and it is of type
    -		<code>chapters.layouts.TestException</code>. As its message
    -		commands, let us now prevent the second exception from being
    -		printed.</p>
    -
    -   <p><em>
    -			Example: Sample usage of EventEvaluators
    -			<a href="../xref/chapters/layouts/ExceptionEvaluatorExample.html">
    -			(logback-examples/src/main/java/chapters/layouts/ExceptionEvaluatorExample.java)</a>
    -		</em>
    -    </p>
    -<pre class="prettyprint source">package chapters.layouts;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class ExceptionEvaluatorExample {
    -
    -  public static void main(String[] args) {
    -    Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class);
    -    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -
    -    try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(lc);
    -      lc.reset();
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -       // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
    -
    -    for (int i = 0; i &lt; 3; i++) {
    -      if (i == 1) {
    -        logger.debug("logging statement " + i, new TestException(
    -            "do not display this"));
    -      } else {
    -        logger.debug("logging statement " + i, new Exception("display"));
    -      }
    -    }
    -  }
    -}</pre>
    -		
    -		<p>In the next configuration file, the evaluation expression
    -		matches events containing a throwable of type
    -		<code>chapters.layouts.TextException</code>, precisely the type of
    -		exceptions we wish to suppress.
    -    </p>
    -
    -		<em>
    -			Example: Sample usage of EventEvaluators
    -			(logback-examples/src/main/resources/chapters/layouts/exceptionEvaluatorConfig.xml)
    -		</em>
    -		<pre class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;evaluator name="DISPLAY_EX_EVAL">
    -    &lt;expression>throwable != null &amp;amp;&amp;amp; throwable instanceof  \
    -      chapters.layouts.TestException&lt;/expression>
    -  &lt;/evaluator></b>
    -	
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%msg%n<b>%ex{full, DISPLAY_EX_EVAL}</b>&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>With this configuration, each time an instance of the
    -		<em>chapters.layouts.TestException</em> is included within a logging
    -		request, the stack trace will be suppressed.
    -		</p>
    -
    -    <p>Launching the command</p>
    -
    -    <p class="source">java chapters.layouts.ExceptionEvaluatorExample src/main/java/chapters/layouts/exceptionEvaluatorConfig.xml</p>
    -
    -    <p>will yield</p>
    -
    -<p class="source">logging statement 0
    -java.lang.Exception: display
    -  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]
    -logging statement 1
    -logging statement 2
    -java.lang.Exception: display
    -  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]</p>
    -
    -
    -    <p>Notice how the second log statement has no stack trace. We
    -    effectively suppressed the stack trace for the
    -    <code>TextException</code>. The text between square brackets at
    -    the end of each stack trace line is <a
    -    href="#xThrowable">packaging information</a> discussed
    -    earlier.</p>
    -
    -		<p><span class="label notice">Note</span> With the <b><em>%ex</em></b> conversion
    -		specifier, the stack trace is displayed when <em>the expression
    -		evaluates to <b>false</b>.</em></p>
    -
    -
    -    
    -		<h2 class="doAnchor" name="customConversionSpecifier">Creating a
    -		custom conversion specifier</h2>
    -
    -		<p>Up to this point we have presented the built-in conversion
    -		words in <code>PatternLayout</code>. But it is also possible to
    -		add conversion words of your own making.</p>
    -		
    -		<p>Building a custom conversion specifier consists of two steps.
    -    </p>
    -		
    -    <h4>Step 1</h4>
    -
    -		<p>First, you must extend the <code>ClassicConverter</code>
    -		class. <a
    -		href="../xref/ch/qos/logback/classic/pattern/ClassicConverter.html">
    -		<code>ClassicConverter</code></a> objects are responsible for
    -		extracting information out of <code>ILoggingEvent</code> instances
    -		and producing a String. For example,
    -		<a href="../xref/ch/qos/logback/classic/pattern/LoggerConverter.html">
    -		<code>LoggerConverter</code></a>, the converter underlying the
    -		%logger conversion word, extracts the name of the logger from 
    -		<code>ILoggingEvent</code> and returns it as a String. It might
    -		abbreviate the logger name in the process.</p>
    -		
    -		<p>Here is a customer converter which returns the time elapsed
    -		since its creaton in nanoseconds:</p>
    -		
    -<em> Example: Sample Converter Example 
    -<a href="../xref/chapters/layouts/MySampleConverter.html">
    -(src/main/java/chapters/layouts/MySampleConverter.java)</a></em>
    -<pre class="prettyprint source">public class MySampleConverter extends ClassicConverter {
    -
    -  long start = System.nanoTime();
    -
    -  <b>@Override</b>
    -  <b>public String convert(ILoggingEvent event) {</b>
    -    <b>long nowInNanos = System.nanoTime();</b>
    -    <b>return Long.toString(nowInNanos-start);</b>
    -  <b>}</b>
    -}</pre>
    -
    -		<p>This implementation is pretty straightforward. The
    -		<code>MySampleConverter</code> class extends
    -		<code>ClassicConverter</code>, and implements the
    -		<code>convert</code> method which returns the number of
    -		nano-seconds elapsed since its creation.
    -		</p>
    -
    -    <h4>Step 2</h4>
    -
    -		<p>In the second step, we must let logback know about the new
    -		<code>Converter</code>. For this purpose, we need to declare the
    -		new conversion word in the configuration file, as shown below:</p>
    -		
    -<em> Example: Sample Converter Example (src/main/java/chapters/layouts/mySampleConverterConfig.xml)</em>
    - <span class="asGroovy" onclick="return asGroovy('mySampleConverterConfig');">View as .groovy</span>
    -<pre id="mySampleConverterConfig" class="prettyprint source">&lt;configuration>
    -
    -  <b>&lt;conversionRule conversionWord="nanos" 
    -                  converterClass="chapters.layouts.MySampleConverter" /></b>
    -	
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern><b>%-6nanos</b> [%thread] - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="STDOUT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -		<p>Once the new conversion word has been declared in the
    -		configuration file, we can refer to it within
    -		<code>PatternLayout</code> pattern, as with any other
    -		conversion word.</p>
    -		
    -    <p>The command:</p>
    -
    -    <div class="source">java chapters.layouts.SampleLogging src/main/java/chapters/layouts/mySampleConverterConfig.xml </div>
    -
    -    <p>should yield output akin to:</p>
    -    
    -    <pre class="source">4868695 [main] DEBUG - Everything's going well
    -5758748 [main] ERROR - maybe not quite...</pre>
    -
    -
    -		<p>The reader might want to take a look at other
    -		<code>Converter</code> implementations such as
    -		<a href="../xref/ch/qos/logback/classic/pattern/MDCConverter.html">
    -		<code>MDCConverter</code></a> to learn about more complex
    -		behaviours, such as option handling. For creating your own
    -		coloring schemes have a look at <a
    -		href="../xref/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.html"><code>HighlightingCompositeConverter</code></a>.
    -		</p>
    -
    -   	
    -
    -    <h2 class="doAnchor" name="ClassicHTMLLayout">HTMLLayout</h2>
    -	
    -	  <p><a
    -	  href="../xref/ch/qos/logback/classic/html/HTMLLayout.html"><code>HTMLLayout</code></a>
    -	  (as included in logback-classic) generates logs in HTML
    -	  format. <code>HTMLLayout</code> outputs logging events in an HTML
    -	  table where each row of the table corresponds to a logging
    -	  event.</p>
    -		
    -		<p>Here is a sample output produced by <code>HTMLLayout</code>
    -		using its default CSS stylesheet:</p>
    -		<img src="images/chapters/layouts/htmlLayout0.gif" alt="HTML Layout Sample Image"/>
    -		
    -		<p>The content of table columns are specified with the help of a
    -		conversion pattern. See <a
    -		href="#ClassicPatternLayout"><code>PatternLayout</code></a> for
    -		documentation on conversion patterns. As such, you have full
    -		control over the contents and format of the table. You can select
    -		and display any combination of converters
    -		<code>PatternLayout</code> knows about.
    -		</p>
    -
    -		<p>One notable exception about the use of
    -		<code>PatternLayout</code> with <code>HTMLLayout</code> is that
    -		conversion specifiers should not be separated by space characters
    -		or more generally by literal text. Each specifier found in the
    -		pattern will result in a separate column.  Likewise a separate
    -		column will be generated for each block of literal text found in
    -		the pattern, potentially wasting valuable real-estate on your
    -		screen.</p>
    -     
    -    <p>Here is simple but functional configuration file illustrating
    -    the use of <code>HTMLLayout</code>.
    -    </p>
    -
    -<em> Example: HTMLLayout Example (src/main/java/chapters/layouts/htmlLayoutConfig1.xml)</em>
    -<span class="asGroovy" onclick="return asGroovy('htmlLayoutConfig1');">View as .groovy</span>
    -<pre id="htmlLayoutConfig1" class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
    -      &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -        <b>&lt;pattern&gt;%relative%thread%mdc%level%logger%msg&lt;/pattern&gt;</b>
    -      &lt;/layout&gt;
    -    &lt;/encoder>
    -    &lt;file>test.html&lt;/file&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration>
    -</pre>
    -
    -   <p>The <a
    -   href="../xref/chapters/layouts/TrivialMain.html">TrivialMain</a>
    -   application logs a few messages finishing with an exception. The
    -   command:</p>
    -
    -   <p class="source">java chapters.layouts.TrivialMain src/main/java/chapters/layouts/htmlLayoutConfig1.xml</p>
    -
    -    <p>will create the file <em>test.html</em> in the current
    -    folder. The contents of <em>test.html</em> should be similar to:
    -		</p>
    -		<img src="images/chapters/layouts/htmlLayout1.png" alt="HTML Layout Sample Image"/>
    -
    -    <h3>Stack traces</h3>
    -
    -		<p> If you use the <em>%em</em> conversion word to display stack
    -		traces, a table column will be created to display stack traces. In
    -		most cases the column will be empty, wasting screen
    -		real-estate. Moreover, printing a stack trace on a separate column
    -		does not yield very readable results. Fortunately, the
    -		<em>%ex</em> conversion word is not the only way to display stack
    -		traces.
    -		</p>
    -
    -		<p>A better solution is available through implementations of
    -		<code>IThrowableRenderer</code> interface.  Such an implementation
    -		can be assigned to <code>HTMLLayout</code> to manage the display
    -		data related to exceptions. By default, a
    -		<a href="../xref/ch/qos/logback/classic/html/DefaultThrowableRenderer.html">
    -			<code>DefaultThrowableRenderer</code></a> is assigned to each
    -			<code>HTMLLayout</code> instance. It writes exceptions on a
    -			<em>new table row</em>, along with its stack trace, in an easily
    -			readable manner, as shown on the figure above.
    -		</p>
    -
    -		<p>If for some reason, you still wish to use the <em>%ex</em>
    -		pattern, then you can specify <a
    -		href="../xref/ch/qos/logback/core/html/NOPThrowableRenderer.html">
    -		<code>NOPThrowableRenderer</code></a> in the configuration file in
    -		order to disable displaying a separate row for the stack trace. We
    -		don't have the faintest idea why you would want to do that, but if
    -		you wished, you could.
    -		</p>
    -
    -    <h3>CSS</h3>
    -
    -    <p>The presentation of the HTML created by <code>HTMLLayout</code>
    -    is controlled through a Cascading Style Sheet (CSS). In the
    -    absence of specific instructions, <code>HTMLLayout</code> will
    -    default to its internal CSS. However, you can instruct
    -    <code>HTMLLayout</code> to use an external CSS file. For this
    -    purpose a <code>cssBuilder</code> element can be nested within a
    -    <code>&lt;layout&gt;</code> element, as shown below.
    -		</p>
    -
    -<pre class="prettyprint source">&lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -  &lt;pattern>%relative...%msg&lt;/pattern>
    -  &lt;cssBuilder class="ch.qos.logback.classic.html.UrlCssBuilder">
    -    &lt;!-- url where the css file is located --&gt;
    -    &lt;url>http://...&lt;/url>
    -  &lt;/cssBuilder>	
    -&lt;/layout></pre>
    -
    -	
    -		<p>The <code>HTMLLayout</code> is often used in conjunction with
    -		<code>SMTPAppender</code> so that outgoing email is pleasantly
    -		formatted in HTML.
    -		</p>
    -
    -
    -    <h2 class="doAnchor" name="log4jXMLLayout">Log4j XMLLayout</h2>
    -
    -    <p><a
    -    href="../xref/ch/qos/logback/classic/log4j/XMLLayout.html">XMLLayout</a>
    -    (part of logback-classic) generates output in a log4j.dtd
    -    compliant format to interoperate with tools such as <a
    -    href="http://logging.apache.org/chainsaw/index.html">Chainsaw</a>
    -    and <a href="http://vigilog.sourceforge.net/">Vigilog</a> capable
    -    of processing files generated by <a
    -    href="http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html">log4j's
    -    XMLLayout</a>.
    -    </p>
    -
    -
    -    <p>As the original XMLLayout in log4j version 1.2.15, XMLLayout in
    -    logback-classic takes two boolean properties, <span
    -    class="option">locationInfo</span> and <span
    -    class="option">properties</span>. Setting <span
    -    class="option">locationInfo</span> to true enables the inclusion
    -    of location info (caller data) in the each event. Setting <span
    -    class="option">properties</span> to true enables the inclusion of
    -    MDC information. Both options are set to false by default.
    -    </p>
    -
    -    <p>Here is a sample configuration</p>
    -
    -<em> Example: Log4jXMLLayout Example (src/main/java/chapters/layouts/log4jXMLLayout.xml)</em>
    -<span class="asGroovy" onclick="return asGroovy('log4jXMLLayout');">View as .groovy</span>
    -    <pre id="log4jXMLLayout" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender">
    -    &lt;file>test.xml&lt;/file>
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
    -      &lt;layout class="ch.qos.logback.classic.log4j.XMLLayout">
    -        &lt;locationInfo>true&lt;/locationInfo>
    -      &lt;/layout>
    -    &lt;/encoder> 
    -  &lt;/appender> 
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="FILE" />
    -  &lt;/root>
    -&lt;/configuration> </pre>
    -    
    -		<h1 class="doAnchor" name="logback-access">Logback access</h1>
    -
    -		<p>Most logback-access layouts are mere adaptations of
    -		logback-classic layouts. Logback-classic and logback-access
    -		modules address different needs, but in general offer comparable
    -		functionality.</p>
    -		
    -		<h2>Writing your own Layout</h2>
    -
    -		<p>Writing a custom <code>Layout</code> for logback access is
    -		nearly identical to its sibling <code>Layout</code> in
    -		logback-classic.</p>
    -		
    -
    -		<h3 class="doAnchor" name="AccessPatternLayout">PatternLayout</h3>
    -
    -		<p><a href="../xref/ch/qos/logback/access/PatternLayout.html">
    -		<code>PatternLayout</code></a> in logback-access can be configured
    -		in much the same way as its classic counterpart. However it
    -		features additional conversion specifiers suited for logging
    -		particular bits of information available only in HTTP servlet
    -		requests and HTTP servlet responses.
    -    </p>
    -
    -		<p>Below is a list of conversion specifiers for 
    -		<code>PatternLayout</code> in logback-access.</p>
    -		
    -		<table  class="bodyTable striped" border="0" cellpadding="8">
    -      <tr>
    -        <th align="center">Conversion Word</th>
    -        <th align="center">Effect</th>
    -      </tr>
    -      <tr>
    -        <td class="word" name="remoteIP">
    -          <b>a / remoteIP</b>
    -        </td>
    -        <td>
    -          <p>Remote IP address.</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="localIP"><b>A / localIP</b></td>
    -        <td>
    -          <p>Local IP address.</p>
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td class="word" name="bytesSent"><b>b / B / bytesSent</b></td>
    -        <td>
    -          <p>
    -            Response's content length.
    -          </p>
    -        </td>
    -      </tr>				
    -      <tr>
    -        <td class="word" name="clientHost"><b>h / clientHost</b></td>
    -        <td>
    -          <p>
    -            Remote host.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="protocol"><b>H / protocol</b></td>
    -        <td>
    -          <p>Request protocol.</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="remoteLogName"><b>l</b></td>
    -        <td>
    -          <p>
    -            Remote log name. In logback-access, this converter always
    -            returns the value "-".
    -          </p>
    -        </td>
    -      </tr>
    -      
    -      <tr>
    -        <td class="word" name="reqParameter"><b>reqParameter{paramName}</b></td>
    -        <td>
    -          <p>Parameter of the response.</p>
    -          <p>This conversion word takes the first option in braces and looks
    -          for the corresponding parameter in the request.</p>
    -          <p><b>%reqParameter{input_data}</b> 
    -          displays the corresponding parameter.</p>
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td class="word" name="header"><b>i{header} / header{header}</b></td>
    -        <td>
    -          <p>Request header.</p>
    -          <p>This conversion word takes the first option in braces and looks
    -          for the corresponding header in the request.</p>
    -          <p><b>%header{Referer}</b> displays the referer of the request.</p>
    -          <p>
    -            If no option is specified, it displays every available header.
    -          </p>
    -        </td>
    -      </tr>	
    -      <tr >
    -        <td class="word" name="requestMethod"><b>m / requestMethod</b></td>
    -        <td>
    -          <p>Request method.</p>
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td class="word" name="requestURL"><b>r / requestURL</b></td>
    -        <td>
    -          <p>
    -            URL requested.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="statusCode"><b>s / statusCode</b></td>
    -        <td>
    -          <p>
    -            Status code of the request.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -          <td class="word" name="elapsedTime"><b>D / elapsedTime</b></td>
    -          <td>
    -              <p>
    -                  The time taken to serve the request, in milliseconds.
    -              </p>
    -          </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="elapsedSeconds"><b>T / elapsedSeconds</b></td>
    -        <td>
    -          <p>
    -            The time taken to serve the request, in seconds.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="dateAccess"><b>t / date</b></td>
    -        <td>
    -          <p>Outputs the date of the logging event.  The date
    -          conversion specifier may be followed by a set of braces
    -          containing a date and time pattern strings used by
    -          <code>java.text.SimpleDateFormat</code>.  <em>ISO8601</em>
    -          is also a valid value.
    -					</p>
    -					<p>For example, <b>%t{HH:mm:ss,SSS}</b> or
    -					<b>%t{dd&nbsp;MMM&nbsp;yyyy&nbsp;;HH:mm:ss,SSS}</b>.
    -					If no date format specifier is given then the
    -					Common Log Format date format is assumed, that is: <b>%t{dd/MMM/yyyy:HH:mm:ss&nbsp;Z}</b>
    -					</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="httpUser"><b>u / user</b></td>
    -        <td>
    -          <p>
    -            Remote user.
    -          </p>
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td class="word" name="queryString"><b>q / queryString</b></td>
    -        <td>
    -          <p>
    -            Request query string, prepended with a '?'.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="requestURI"><b>U / requestURI</b></td>
    -        <td>
    -          <p>
    -            Requested URI.
    -          </p>
    -        </td>
    -      </tr>		
    -      <tr>
    -        <td class="word" name="sessionID"><b>S / sessionID</b></td>
    -        <td>
    -          <p>Session ID.</p>
    -        </td>
    -      </tr>
    -      <tr >
    -        <td class="word" name="server"><b>v / server</b></td>
    -        <td>
    -          <p>Server name.</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="threadName"><b>I / threadName</b></td>
    -        <td>
    -          <p>Name of the thread which processed the request.</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="localPort"><b>localPort</b></td>
    -        <td>
    -          <p>Local port.</p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="reqAttribute"><b>reqAttribute{attributeName}</b></td>
    -        <td>
    -          <p>Attribute of the request.</p>
    -          <p>This conversion word takes the first option in braces and looks
    -          for the corresponding attribute in the request.</p>
    -          <p><b>%reqAttribute{SOME_ATTRIBUTE}</b> 
    -          displays the corresponding attribute.</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="reqCookie"><b>reqCookie{cookie}</b></td>
    -        <td>
    -          <p>Request cookie.</p>
    -          <p>This conversion word takes the first option in braces and looks
    -          for the corresponding cookie in the request.</p>
    -          <p><b>%cookie{COOKIE_NAME}</b> displays corresponding cookie.</p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="responseHeader"><b>responseHeader{header}</b></td>
    -        <td>
    -          <p>
    -            Header of the response.
    -          </p>
    -          <p>This conversion word takes the first option in braces and looks
    -          for the corresponding header in the response.</p>
    -          <p><b>%header{Referer}</b> displays the referer of the response.</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="requestContent"><b>requestContent</b></td>
    -        <td>
    -          <p>This conversion word displays the content of the request,
    -          that is the request's <code>InputStream</code>. It is used
    -          in conjunction with a <a
    -          href="../xref/ch/qos/logback/access/servlet/TeeFilter.html">
    -          <code>TeeFilter</code></a>, a
    -          <code>javax.servlet.Filter</code> that replaces the original
    -          <code>HttpServletRequest</code> by a <a
    -          href="../xref/ch/qos/logback/access/servlet/TeeHttpServletRequest.html">
    -          <code>TeeHttpServletRequest</code></a>. The latter object
    -          allows access to the request's <code>InputStream</code>
    -          multiple times without any loss of data.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="fullRequest"><b>fullRequest</b></td>
    -        <td>
    -          <p>This converter outputs the data associated with the
    -          request, including all headers and request contents.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="responseContent"><b>responseContent</b></td>
    -        <td>
    -          <p>This conversion word displays the content of the
    -          response, that is the response's
    -          <code>InputStream</code>. It is used in conjunction with a
    -          <a href="../xref/ch/qos/logback/access/servlet/TeeFilter.html">
    -          <code>TeeFilter</code></a>, a
    -          <code>javax.servlet.Filter</code> that replaces the original
    -          <code>HttpServletResponse</code> by a <a
    -          href="../xref/ch/qos/logback/access/servlet/TeeHttpServletResponse.html">
    -          <code>TeeHttpServletResponse</code></a>. The latter object
    -          allows access to the request's <code>InputStream</code>
    -          multiple times without any loss of data.
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="fullResponse"><b>fullResponse</b></td>
    -        <td>
    -          <p>This conversion word takes all the available data
    -          associated with the response, including all headers of the
    -          response and response contents.
    -          </p>
    -        </td>
    -      </tr>
    -    </table>
    -		
    -		<p>Logback access' <code>PatternLayout</code> also recognizes three keywords, which
    -		act like shortcuts.</p>
    -
    -    <table  class="bodyTable">
    -      <tr>
    -        <th>keyword</th>
    -        <th>equivalent conversion pattern</th>
    -      </tr>
    -      <tr class="a">
    -        <td><em>common</em> or <em>CLF</em></td>
    -        <td><em>%h %l %u [%t] "%r" %s %b</em></td>
    -      </tr>
    -      <tr class="b">
    -        <td><em>combined</em></td>
    -        <td><em>%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"</em></td>
    -      </tr>
    -
    -    </table>
    -
    - 	
    -	 	<p>The <em>common</em> keyword corresponds to the pattern <em>'%h %l %u [%t] "%r" %s %b'</em>
    -	 	which displays client host, remote log name, user, date, requested URL, status code 
    -	 	and response's content length</p>
    -	 	
    -	 	<p>The <em>combined</em> keyword is a shortcut for <em>'%h %l %u [%t]
    -	 	"%r" %s %b "%i{Referer}" "%i{User-Agent}"'</em>. This pattern
    -	 	begins much like the <em>common</em> pattern but also displays two
    -	 	request headers, namely referer, and user-agent.</p>
    -
    -		<h3 class="doAnchor" name="AccessHTMLLayout">HTMLLayout</h3>
    -		
    -		<p>The <a
    -		href="../xref/ch/qos/logback/access/html/HTMLLayout.html"><code>HTMLLayout</code></a>
    -		class found in logback-access is similar to the <a
    -		href="#ClassicHTMLLayout"><code>HTMLLayout</code></a> class from
    -		logback-classic.
    -    </p>
    -		
    -		<p>By default, it will create a table containing the following data:</p>
    -		
    -		<ul>
    -			<li>Remote IP</li>
    -			<li>Date</li>
    -			<li>Request URL</li>
    -			<li>Status code</li>
    -			<li>Content Length</li>
    -		</ul>
    -		
    -		<p>Here is a sample output produced by <code>HTMLLayout</code> in
    -		logback-access:</p>
    -		<img src="images/chapters/layouts/htmlLayoutAccess.gif" alt="Access HTML Layout Sample Image"/>
    -
    -		<p>What can be better than a real world example? Our own log4j
    -		properties for logback <a
    -		href="http://logback.qos.ch/translator/">translator</a> makes use
    -		of logback-access to demonstrate live output from
    -		<code>RollingFileAppender</code> with <code>HTMLLayout</code>.</p>
    -
    -
    -    <p>On every new user request to our <a
    -    href="http://logback.qos.ch/translator/">translator</a>
    -    web-application, a new entry will be added to the access logs,
    -    which you can view by <a
    -    href="http://logback.qos.ch/translator/logs/access.html">following
    -    this link</a>.</p>
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/layouts_ja.html b/logback-site/src/site/pages/manual/layouts_ja.html
    deleted file mode 100644
    index e874695356..0000000000
    --- a/logback-site/src/site/pages/manual/layouts_ja.html
    +++ /dev/null
    @@ -1,1645 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1"></meta>
    -    <title>第6章 レイアウト</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -    <h1>第6章 レイアウト</h1>
    -
    -    <div class="quote">
    -      <p>TCPの実装は頑健性の一般原則に従う。つまり、自分自身の行動は保守的に、他者から行われる行動については寛容さを持って受け入れるということだ。
    -      </p>
    -      <p>-JON POSTEL、RFC 793</p>
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -    <h2 class="doAnchor">レイアウトとは何か</h2>
    -
    -    <p>レイアウトといってもフロリダ州の大規模集合住宅とは何の関係もありません。レイアウトはlogbackのコンポーネントであり、受け取ったロギングイベントを文字列に変換する役割を担っています。<a href="http://logback.qos.ch/xref/ch/qos/logback/core/Layout.html"><code>Layout</code></a>インターフェイスの<code>format()</code>メソッドは、引数としてロギングイベントとみなされる(任意の型の)オブジェクトを受け取り、文字列を返します。<code>Layout</code>インターフェイスの概要を次に示します。
    -    </p>
    -
    -    <pre class="prettyprint source">public interface Layout&lt;E&gt; extends ContextAware, LifeCycle {
    -
    -  String doLayout(E event);
    -  String getFileHeader();
    -  String getPresentationHeader();
    -  String getFileFooter();
    -  String getPresentationFooter();
    -  String getContentType();
    -}</pre>
    -
    -    <p>このインターフェイスは簡潔ですが、あらゆる書式化のニーズを十分に満たしています。<em>Catch-22</em>に登場するテキサス育ちの開拓者であるジョセフ・ヘラーならこう叫んでいるところです。「レイアウトを実装しようにもたったの5つしかメソッドがない!!?ナンデ?!」
    -    </p>
    -
    -    <h2>Logback-classic モジュール</h2>
    -
    -    <p>logback-classic モジュールは<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/spi/ILoggingEvent.html">ch.qos.logback.classic.spi.ILoggingEvent</a></code>だけを扱うようになっています。この節を読み進めていけば理由が明らかになります。</p>
    -
    -    <h2 class="doAnchor" name="writingYourOwnLayout">レイアウトを自作する</h2>
    -
    -    <p>logback-classic モジュールで使うために、簡潔で十分な機能性のあるレイアウトを自作してみましょう。出力したいのは次のようなものです。アプリケーションが起動してから経過した時間、ロギングイベントのログレベル、ブラケットで囲んだ呼び出しスレッド名、ロガー名、ダッシュ(-のこと)とそれに続くロギングイベントのメッセージ、最後に改行文字も加えておきましょう。
    -    </p>
    -
    -    <p>出力例は次のようになります。</p>
    -
    -    <div class="source">10489 DEBUG [main] com.marsupial.Pouch - Hello world.</div>
    -
    -    <p>テキサスの開拓者が実装したレイアウトのコードを見てましょう。</p>
    -    <p class="example">例:自作レイアウトのサンプル実装(<a href="http://logback.qos.ch/xref/chapters/layouts/MySampleLayout.html">logback-examples/src/main/java/chapters/layouts/MySampleLayout.java</a>)</p>
    -
    -    <pre class="prettyprint source">package chapters.layouts;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class MySampleLayout extends LayoutBase&lt;ILoggingEvent&gt; {
    -
    -  public String doLayout(ILoggingEvent event) {
    -    StringBuffer sbuf = new StringBuffer(128);
    -    sbuf.append(event.getTimeStamp() - event.getLoggingContextVO.getBirthTime());
    -    sbuf.append(" ");
    -    sbuf.append(event.getLevel());
    -    sbuf.append(" [");
    -    sbuf.append(event.getThreadName());
    -    sbuf.append("] ");
    -    sbuf.append(event.getLoggerName();
    -    sbuf.append(" - ");
    -    sbuf.append(event.getFormattedMessage());
    -    sbuf.append(CoreConstants.LINE_SEP);
    -    return sbuf.toString();
    -  }
    -}</pre>
    -
    -    <p><code>MySampleLayout</code>は<a href="http://logback.qos.ch/xref/ch/qos/logback/core/LayoutBase.html"><code>LayoutBase</code></a>を継承しているのに気付きましたか。このクラスは全てのレイアウトのインスタンスに共通する内部状態を管理するものです。例えば、レイアウトが開始しているか、停止しているか、ヘッダーはあるか、フッターはあるか、コンテンツタイプはあるか、といったものです。おかげで、開発者は書式化の方法にだけ集中することができるのです。<code>LayoutBase</code>はジェネリッククラスです。上記のコードでは、<code>MySampleLayout</code>は<code>LayoutBase&lt;ILoggingEvent&gt;</code>を継承しています。
    -    </p>
    -
    -    <p><code>doLayout(ILoggingEvent event)</code>メソッド(<code>MySampleLayout</code>の実装する唯一のメソッドです)では、最初に空の<code>StringBuffer</code>を生成して、ロギングイベントのパラメータを付け足していきます。開拓者らしく、慎重に書式化したようです。ロギング要求で1つ以上のパラメータが渡されることを考えればこれは大事なことです。</p>
    -
    -    <p><code>doLayout()</code>メソッドでは、StringBufferにいろいろな文字列を追加したあとで1つの文字列に変換して、その文字列を呼び出し元に返します。
    -    </p>
    -
    -    <p>上記の<code>doLayout</code>メソッドでは、ロギングイベントに含まれる可能性のあるあらゆる例外を無視します。実際のレイアウトの実装では、ほぼ確実に例外の内容を出力したいはずです。
    -    </p>
    -
    -    <h3 class="doAnchor" name="configuringYourOwnLayout">自作レイアウトの設定</h3>
    -
    -    <p>自作レイアウトは、他のコンポーネントと同じように設定されます。前述したように<code>FileAppender</code>とその派生クラスにはエンコーダーが必要です。<code>FileAppender</code>のニーズを満たすため、<code>LayoutWrappingEncoder</code>に自作した<code>MySampleLayout</code>をラップしてみましょう。次のような設定になります。</p>
    -
    -  <p class="example">例:MySampleLayoutの設定(<a href="http://logback.qos.ch/xref/chapters/layouts/sampleLayoutConfig.xml">logback-examples/src/main/java/chapters/layouts/sampleLayoutConfig.xml</a>)</p>
    -  <span class="asGroovy" onclick="return asGroovy(&#39;logback_Console&#39;);">Groovyで表示</span>
    -<pre id="sampleLayoutConfig" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    <b>&lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"&gt;</b>
    -      <b>&lt;layout class="chapters.layouts.MySampleLayout" /&gt;</b>
    -    <b>&lt;/encoder&gt;</b>
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>サンプルアプリケーションの<code><a href="http://logback.qos.ch/xref/chapters/layouts/SampleLogging.html">chapters.layouts.SampleLogging</a></code>は、一つ目の引数で指定された設定ファイルを使ってlogbackを設定します。そして、デバッグメッセージとエラーメッセージを1つづつロギングします。</p>
    -
    -    <p><em>logback-examples</em>ディレクトリに移動して、次のコマンドを実行してみましょう。
    -    </p>
    -
    -    <p class="command">java chapters.layouts.SampleLogging src/main/java/chapters/layouts/sampleLayoutConfig.xml</p>
    -
    -    <p>コンソールには次のように出力されます。</p>
    -
    -<div class="source"><pre>0 DEBUG [main] chapters.layouts.SampleLogging - Everything's going well
    -0 ERROR [main] chapters.layouts.SampleLogging - maybe not quite...</pre></div>
    -
    -    <p>簡単でしょう?エレア派の懐疑論者ピュロンは、それ自体が不確実であるということ以外に確実なことは無い、と主張しました。つまり、確かなことなど何もないというのです。彼はきっとこう質問するでしょう。「それでレイアウトのオプションについてはどうなっているの?」上記の自作レイアウトを少し変更したバージョンが<a href="http://logback.qos.ch/xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2.java</code></a>です。このマニュアル全体を通じて言えることですが、レイアウトでもlogbackの他のコンポーネントでも、単にセッターメソッドを設定するだけでプロパティを指定できるようになります。
    -    </p>
    -
    -    <p><a href="http://logback.qos.ch/xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2</code></a>クラスには、2つのプロパティがあります。一つ目は、出力されるメッセージの先頭に追加する接頭辞です。二つ目は、ロギング要求を送信したスレッドの名前を表示するかどうかを選択する真偽値です。
    -    </p>
    -
    -    <p><a href="http://logback.qos.ch/xref/chapters/layouts/MySampleLayout2.html"><code>MySampleLayout2</code></a>のコードを見てみましょう。</p>
    -
    -    <pre class="prettyprint source">package chapters.layouts;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class MySampleLayout2 extends LayoutBase&lt;ILoggingEvent&gt; {
    -
    -  String prefix = null;
    -  boolean printThreadName = true;
    -
    -  <b>public void setPrefix(String prefix) {
    -    this.prefix = prefix;
    -  }
    -
    -  public void setPrintThreadName(boolean printThreadName) {
    -    this.printThreadName = printThreadName;
    -  }</b>
    -
    -  public String doLayout(ILoggingEvent event) {
    -    StringBuffer sbuf = new StringBuffer(128);
    -    <b>if (prefix != null) {
    -      sbuf.append(prefix + ": ");
    -    }</b>
    -    sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
    -    sbuf.append(" ");
    -    sbuf.append(event.getLevel());
    -    <b>if (printThreadName) {
    -      sbuf.append(" [");
    -      sbuf.append(event.getThreadName());
    -      sbuf.append("] ");
    -    } else {
    -      sbuf.append(" ");
    -    }</b>
    -    sbuf.append(event.getLoggerName());
    -    sbuf.append(" - ");
    -    sbuf.append(event.getFormattedMessage());
    -    sbuf.append(LINE_SEP);
    -    return sbuf.toString();
    -  }
    -}</pre>
    -
    -
    -    <p>プロパティのセッターメソッドを追加すれば設定ファイルから指定できるようになります。<code>printThreadName</code>プロパティの型は真偽値(boolean)であって文字列(<code>String</code>)ではないので注意してください。logbackのコンポーネントの設定については、<a href="http://logback.qos.ch/manual/configuration.html">設定に関する章</a>で詳しく説明しています。<a href="http://logback.qos.ch/manual/onJoran.html">Joranの章</a>にはより詳細な説明があります。<code>MySampleLayout2</code>のために特別に誂えた設定ファイルは次のとおりです。
    -    </p>
    -
    -
    -    <span class="asGroovy" onclick="return asGroovy(&#39;MySampleLayout2&#39;);">Groovyとして表示</span>
    -    <pre id="MySampleLayout2" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"&gt;
    -      &lt;layout class="chapters.layouts.MySampleLayout2"&gt;
    -        <b>&lt;prefix&gt;MyPrefix&lt;/prefix&gt;</b>
    -        <b>&lt;printThreadName&gt;false&lt;/printThreadName&gt;</b>
    -      &lt;/layout&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -   <p></p>
    -
    -
    -    <h2 class="doAnchor" name="ClassicPatternLayout">PatternLayout</h2>
    -
    -    <p>logback-classic の配布物には、<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/PatternLayout.html">PatternLayout</a></code>と呼ばれる柔軟性のあるレイアウトが含まれています。他のレイアウトと同じく、<code>PatternLayout</code>はロギングイベントを受け取って<code>文字列</code>を返します。<code>文字列</code>は、変換パターン文字列を調整してカスタマイズすることができます。
    -    </p>
    -
    -    <p><code>PatternLayout</code>の変換パターン文字列はC言語の<code>printf()</code>関数と非常によく似たもので、文字列リテラルと<em>変換指定</em>と呼ばれる書式制御式で構成されています。変換パターン文字列には、どんな文字列リテラルでも入れることができます。変換指定はパーセント記号"%"で始まり、オプションの<em>書式修飾子</em>、<em>変換指定子</em>、括弧で囲まれたパラメータが続いたものです。変換指定子には変換したいデータフィールドを指定します。例えばロガー名、レベル、日付、スレッド名などです。書式修飾子には、フィールド幅、パディング、左揃えや右揃えを指定します。
    -    </p>
    -
    -    <p>既に何度か述べたとおり、<code>FileAppender</code>とその派生クラスにはエンコーダーが必要です。結局、<code>FileAppender</code>やその派生クラスと<code>PatternLayout</code>を一緒に使うには、エンコーダーでラップしなければなりません。<code>FileAppender</code>と<code>PatternLayout</code>を組み合わせることがあまりにも一般的になってしまったことを考慮して、logback の配布物には<code>PatternLayoutEncoder</code>を含めるようになりました。これは単に<code>PatternLayout</code>をラップするだけのエンコーダーなので、エンコーダーであるにも関わらずPatternLayoutのように扱えるようになっています。プログラム的に<code>ConsoleAppender</code>と<code>PatternLayoutEncoder</code>を設定する例を示します。</p>
    -
    -
    -  <p class="example">例:PatternLayoutの使用例(<a href="http://logback.qos.ch/xref/chapters/layouts/PatternSample.html">logback-examples/src/main/java/chapters/layouts/PatternSample.java</a>)</p>
    -
    -    <pre class="prettyprint source">package chapters.layouts;
    -
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.Logger;
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.ConsoleAppender;
    -
    -public class PatternSample {
    -
    -  static public void main(String[] args) throws Exception {
    -    Logger rootLogger = (Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
    -    LoggerContext loggerContext = rootLogger.getLoggerContext();
    -    // we are not interested in auto-configuration
    -    loggerContext.reset();
    -
    -    <b>PatternLayoutEncoder encoder = new PatternLayoutEncoder();</b>
    -    <b>encoder.setContext(loggerContext);</b>
    -    <b>encoder.setPattern("%-5level [%thread]: %message%n");</b>
    -    <b>encoder.start();</b>
    -
    -    ConsoleAppender&lt;ILoggingEvent&gt; appender = new ConsoleAppender&lt;ILoggingEvent&gt;();
    -    appender.setContext(loggerContext);
    -    appender.setEncoder(encoder);
    -    appender.start();
    -
    -    rootLogger.addAppender(appender);
    -
    -    rootLogger.debug("Message 1");
    -    rootLogger.warn("Message 2");
    -  }
    -}</pre>
    -
    -    <p>この例では、変換パターン文字列として<b>"%-5level [%thread] %msg%n"</b>が設定されています。logbackに組み込みの変換指定子については後で簡単に説明します。<code>PatternSample</code>アプリケーションを実行してみましょう。</p>
    -
    -    <p class="source">java java chapters.layouts.PatternSample</p>
    -
    -    <p>コンソールにに次のように出力されます。</p>
    -
    -    <p class="source">DEBUG [main]: Message 1
    -WARN  [main]: Message 2</p>
    -
    -    <p>変換パターン文字列の<b>"%-5level [%thread] %msg%n"</b>には、文字列リテラルと変換指定子を明示的に区切る文字が無いことに気付きましたか。変換パターン文字列を解析する際、<code>PatternLayout</code>は文字列リテラル(空白文字、括弧、コロンなど)と変換指定を分離することができます。上記の例では、変換指定"%-5level"は、ロギングイベントのレベルを5文字の幅に左揃えすることを意味します。書式指定子については後で説明します。
    -    </p>
    -
    -    <p><code>PatternLayout</code>は変換パターン文字列をグループ化するために括弧を使います。<b>つまり '('と ')'には特別な意味があるので、リテラル文字列として使うときはエスケープしなければならないということです。</b> 括弧の特殊な性質について詳しくは<a href="http://logback.qos.ch/manual/layouts.html#Parentheses">後で説明</a>します。
    -    </p>
    -
    -    <p>前述のとおり、変換指定によっては括弧で囲んでオプションの引数を指定することができます。例えばこういう書き方です。<code>%logger{10}</code>"logger" が変換指定子で、10が引数です。オプションの指定の仕方について詳しくは<a href="http://logback.qos.ch/manual/layouts.html#cwOptions">後で説明</a>します。
    -    </p>
    -
    -    <p>利用できる変換指定とオプションを表にまとめました。1つのセルに複数の変換指定子が登場する場合、それらは別名という意味です。
    -    </p>
    -
    -    <table class="bodyTable properties striped" border="0">
    -      <tr>
    -        <th><a name="conversionWord" href="http://logback.qos.ch/manual/layouts.html#conversionWord">変換指定子</a></th>
    -        <th>効果</th>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="logger">
    -          <a name="logger" href="http://logback.qos.ch/manual/layouts.html#logger"><span class="anchor"></span></a>
    -          <b>c</b>{<em>length</em>} <br>
    -          <b>lo</b>{<em>length</em>} <br>
    -          <b>logger</b>{<em>length</em>} <br>
    -        </td>
    -
    -        <td>ロギングイベントを生成する一番元になったロガーの名前を出力します。
    -
    -          <p>この変換指定子に指定できるオプションは整数値だけです。ロガー名は省略アルゴリズムに従って、意味が通る程度に短縮されます。0を指定すると特別な振る舞いをします。ロガー名文字列の中で一番右端のドット(.)から右側だけを残すようになります。省略アルゴリズムの例を表にまとめました。
    -          </p>
    -
    -          <table class="bodyTable dark" border="0" cellpadding="8">
    -            <tr>
    -              <th>変換指定</th>
    -              <th>ロガー名</th>
    -              <th>結果</th>
    -            </tr>
    -            <tr>
    -              <td>%logger</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{0}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{5}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>m.s.s.Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{10}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>m.s.s.Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{15}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>m.s.sample.Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{16}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>m.sub.sample.Bar</td>
    -            </tr>
    -
    -            <tr>
    -              <td>%logger{26}</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -              <td>mainPackage.sub.sample.Bar</td>
    -            </tr>
    -          </table>
    -
    -          <p>オプションで指定した長さを越えるとしても、一番右側のドット(.)から後ろは省略されないので気をつけてください。それ以外の部分は最短で1文字になりますが、削除することはありません。</p>
    -
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="class">
    -          <b>C</b>{<em>length</em>} <br>
    -          <b>class</b>{<em>length</em>} <br>
    -        </td>
    -
    -        <td>
    -          <p>ロギング要求を生成した呼び出し元のクラスの完全名を出力します。</p>
    -
    -          <p><em>%logger</em>と同じように、引数の整数値に合わせて名前を短縮します。0には特別な意味があり、パッケージ名を除いた単純クラス名を出力するようになります。デフォルトでは、完全クラス名が出力されます。
    -          </p>
    -
    -          <p>送信側のクラス情報を生成するのはとても高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="contextName">
    -          <b>contextName</b><br>
    -          <b>cn</b><br></td>
    -          <td>ロギングイベントを生成したロガーの割り当てられたロギングコンテキストの名前。</td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="date">
    -          <b>d</b>{<em>pattern</em>} <br>
    -          <b>date</b>{<em>pattern</em>} <br>
    -          <b>d</b>{<em>pattern</em>, <em>timezone</em>} <br>
    -          <b>date</b>{<em>pattern</em>, <em>timezone</em>} <br>
    -        </td>
    -        <td>
    -         <p>ロギングイベントの日時を出力するために使います。引数として日時パターン文字列を指定することができます。日時パターン文字列は<a href="https://docs.oracle.com/javase/8/docs/api/java/text/SimpleDateFormat.html"><code>java.text.SimpleDateFormat</code></a>と互換性があります。</p>
    -
    -         <p>引数に<em>"ISO8601"</em>と指定すると ISO8601 の書式になります。日時パターン文字列が指定されなかった場合、デフォルトは<a href="http://en.wikipedia.org/wiki/ISO_8601">ISO8601書式</a>になるので注意してください。</p>
    -
    -         <p>いくつかサンプルを見てください。ここでは日時が2006年10月20日(金曜日)であることを想定しています。これはこのマニュアルを書いた人が昼食から戻ってきた日時です。</p>
    -
    -         <table class="bodyTable dark" cellpadding="8">
    -           <tr>
    -             <th>変換パターン</th>
    -            <th>結果</th>
    -           </tr>
    -           <tr>
    -             <td>%d</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>
    -           <tr>
    -             <td>%date</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>
    -           <tr>
    -             <td>%date{ISO8601}</td>
    -             <td>2006-10-20 14:06:49,812</td>
    -           </tr>
    -           <tr>
    -             <td>%date{HH:mm:ss.SSS}</td>
    -             <td>14:06:49.812</td>
    -           </tr>
    -           <tr>
    -             <td>%date{dd MMM yyyy;HH:mm:ss.SSS}</td>
    -             <td>20 oct. 2006;14:06:49.812  </td>
    -           </tr>
    -         </table>
    -
    -          <p>二番目の引数にはタイムゾーンを指定します。たとえば、'%date{HH:mm:ss.SSS, Australia/Perth}' と指定すると、世界中で一番孤立しているオーストラリアのパースの時刻を出力するようになります。タイムゾーンが指定されなかった場合、JVMのタイムゾーンが使用されます。指定したタイムゾーンが未知のものであったりタイプミスだった場合、<a href="http://docs.oracle.com/javase/6/docs/api/java/util/TimeZone.html#getTimeZone(java.lang.String)">TimeZone.getTimeZone(String)</a>メソッドの仕様に基づいてGMTが指定されたものとして解釈します。
    -          </p>
    -
    -          <p><span class="label">よくある間違い</span>は、カンマ(,)が引数の区切り文字として解釈されてしまうことです。<code>HH:mm:ss,SSS</code>というパターン文字列は<code>HM:mm:ss</code>というパターン文字列<code>SSS</code>というタイムゾーンが指定されたものとして解釈されてしまいます。日時パターン文字列にカンマ(,)を入れたければ、パターン文字列をクォートで囲んでください。たとえば次のようにします。%date{<b>"</b>HH:mm:ss,SSS<b>"</b>}.
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="file">
    -          <b>F</b><br /><b>
    -file</b>
    -        </td>
    -
    -        <td>
    -          <p>ロギング要求を発行したクラスのソースコードファイル名を出力します。
    -          </p>
    -
    -          <p>ファイル情報を生成するのはとても高速であるとは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="caller">
    -          <b>caller{depth}</b><br />
    -<b>caller{depth, evaluator-1, ... evaluator-n}</b>
    -        </td>
    -
    -        <td>
    -          <p>ロギングイベントを生成した呼び出し元の位置情報(スタックの深さ、ソースコードファイルの行番号)。
    -          </p>
    -
    -          <p>位置情報の中身はJVM実装によって変わりますが、普通ならロギングイベントを生成したメソッドの定義されたクラスの完全名、ソースコードファイル名、行番号が含まれます。
    -          </p>
    -
    -          <p>表示されるメソッド呼び出しの深さを指定するため、オプションとして整数値を指定できます。
    -          </p>
    -
    -          <p>例えば、<b>%caller{2}</b>と書いたら次のように出力されます。</p>
    -
    -<pre class="source white_bg">0    [main] DEBUG - logging statement
    -Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
    -Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)</pre>
    -
    -          <p>そして<b>%caller{3}</b>と書いたら次のように出力されます。</p>
    -
    -<pre class="source white_bg">16   [main] DEBUG - logging statement
    -Caller+0   at mainPackage.sub.sample.Bar.sampleMethodName(Bar.java:22)
    -Caller+1   at mainPackage.sub.sample.Bar.createLoggingRequest(Bar.java:17)
    -Caller+2   at mainPackage.ConfigTester.main(ConfigTester.java:38)</pre>
    -
    -          <p>ロギングイベントの送信元情報を計算するかどうかを判定するため、オプションとして評価器を指定できるようになっています。たとえば、<b>%caller{3,CALLER_DISPLAY_EVAL}</b>と指定すると評価器の<em>CALLER_DISPLAY_EVAL</em>が<b>真</b>を返す場合にだけ、3行分のスタックトレースを出力することになります。
    -        </p>
    -
    -         <p>評価器の種類は後で紹介します。</p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="line">
    -          <b>L</b><br /><b>
    -line</b>
    -        </td>
    -
    -        <td><p>ロギング要求が生成されたソースコードファイル中の行番号を出力します。</p>
    -
    -          <p>行番号を算出するのは決して高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。
    -
    -          </p>
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="message">
    -          <b>m</b><br /><b>
    -msg</b><br /><b>
    -message</b>
    -        </td>
    -        <td>
    -          <p>アプリケーションがロギングイベントに関連付けたメッセージを出力します。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="method">
    -          <b>M</b><br /><b>
    -method</b>
    -        </td>
    -
    -        <td>
    -          <p>ロギング要求が生成されたメソッド名を出力します。</p>
    -          <p>メソッド名を生成するのは決して高速とは言えません。実行速度が問題にならない場合にだけ使うほうが良いでしょう。
    -</p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="newline">
    -          <b>n</b>
    -        </td>
    -
    -        <td>
    -          <p>プラットフォーム依存の行区切り文字を出力します。</p>
    -          <p>この変換指定子を使っても、行区切り文字として可搬性の無い"\n" や"\r\n" を指定した場合と性能は変わりません。したがって、どんなときでも行区切り文字そのものではなく、この変換指定子を使うべきです。
    -          </p>
    -        </td>
    -
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="level">
    -          <b>p</b><br /><b>
    -le</b><br /><b>
    -level</b>
    -        </td>
    -        <td>ロギングイベントのレベルを出力します。</td>
    -      </tr>
    -
    -      <tr>
    -
    -        <td class="word" name="relative">
    -          <b>r</b><br /><b>
    -relative</b>
    -        </td>
    -
    -        <td>アプリケーションが開始してから、ロギングイベントを生成するまでの経過時間をミリ秒単位で出力します。
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="relative">
    -          <b>t</b><br /><b>
    -thread</b>
    -        </td>
    -
    -        <td>ロギングイベントを生成したスレッドの名前を出力します。
    -        </td>
    -
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="mdc">
    -          <b>X</b>{<em>key:-defaultVal</em>} <br>
    -          <b>mdc</b>{<em>key:-defaultVal</em>} <br>
    -        </td>
    -
    -        <td>
    -
    -          <p>ロギングイベントを生成したスレッドに関連付けられていたMDC(診断コンテキスト)の値を出力します。
    -          </p>
    -
    -          <p><b>%mdc{uesrid}</b>のように<b>mdc</b>オプションにキーが指定されている場合、対応するMDCの値が出力されます。MDCの値がnullの場合、 <b>:-</b>演算子で指定したデフォルト値が出力されます。デフォルト値が無かったら空文字列が出力されます。
    -          </p>
    -
    -          <p>キーが未指定のときは、MDCの内容がすべて出力されます。そのときの書式は"key1=val1,key2=val2"のようになります。
    -          </p>
    -
    -          <p>詳しくは<a href="http://logback.qos.ch/manual/mdc.html">MDCの章</a>を参照してください。</p>
    -
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="ex">
    -          <b>ex</b>{<em>depth</em>} <br>
    -            <b>exception</b>{<em>depth</em>} <br>
    -          <b>throwable</b>{<em>depth</em>} <br>
    -          <br>
    -          <b>ex</b>{depth, evaluator-1, ..., evaluator-n} <br>
    -          <b>exception</b>{depth, evaluator-1, ..., evaluator-n} <br>
    -          <b>throwable</b>{depth, evaluator-1, ..., evaluator-n}
    -        </td>
    -
    -        <td>
    -          <p>ロギングイベントに例外オブジェクトが関連付けられていたら、その例外オブジェクトのスタックトレースを出力します。デフォルトでは完全なスタックトレースを出力します。
    -         </p>
    -
    -         <p><em></em>throwable変換指定子に指定できるオプションは次のとおりです。</p>
    -         <ul>
    -           <li><em>short</em> :スタックトレースの一行目だけを出力します</li>
    -           <li><em>full</em> :完全なスタックトレースを出力します</li>
    -           <li>任意の整数:スタックトレースの先頭から数えて指定した行数を出力します</li>
    -         </ul>
    -
    -         <p>いくつか例を見てみましょう。</p>
    -
    -         <table class="bodyTable">
    -            <tr class="a">
    -              <th>変換指定</th>
    -              <th>結果</th>
    -            </tr>
    -            <tr class="b">
    -              <td>%ex</td>
    -              <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
    -  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td>
    -            </tr>
    -            <tr class="a">
    -              <td>%ex{short}</td>
    -              <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)</pre></td>
    -            </tr>
    -            <tr class="b">
    -              <td>%ex{full}</td>
    -              <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)
    -  at mainPackage.ExceptionLauncher.main(ExceptionLauncher.java:38)</pre></td>
    -            </tr>
    -            <tr class="a">
    -              <td>%ex{2}</td>
    -              <td><pre>mainPackage.foo.bar.TestException: Houston we have a problem
    -  at mainPackage.foo.bar.TestThrower.fire(TestThrower.java:22)
    -  at mainPackage.foo.bar.TestThrower.readyToLaunch(TestThrower.java:17)</pre></td>
    -            </tr>
    -         </table>
    -
    -          <p>スタックトレースを出力するかどうかを判定するため、オプションとして評価器を指定できるようになっています。例えば、<b>%ex{full,EX_DISPLAY_EVAL}</b>と指定した場合、評価器<em>EX_DISPLAY_EVAL</em>が<b>偽</b>を返すときだけ例外オブジェクトの完全なスタックトレースが出力されます。評価器についてはこのドキュメントの後のほうで説明しています。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="xThrowable">
    -          <b>xEx</b>{<em>depth</em>} <br>
    -          <b>xException</b>{<em>depth</em>} <br>
    -          <b>xThrowable</b>{<em>depth</em>} <br>
    -          <br>
    -          <b>xEx</b>{depth, evaluator-1, ..., evaluator-n} <br>
    -          <b>xException</b>{depth, evaluator-1, ..., evaluator-n} <br>
    -          <b>xThrowable</b>{depth, evaluator-1, ..., evaluator-n}
    -        </td>
    -
    -        <td>
    -          <p>クラスのパッケージングに関する情報が追加されていること以外は%throwableと同様です。</p>
    -
    -          <p>変換パターン文字列に%xThrowableや他のthrowable関連の変換指定子を指定しなかった場合、<code>PatternLayout</code>は末尾に%xThrowableを自動的に追加します。スタックトレースの情報は非常に重要だからです。スタックトレースを出力したくなければ、%xThrowableの代わりに%nopexを指定すればよいです。%nopexの説明も参照してください。
    -         </p>
    -
    -          <p>例外のスタックトレースの各行の終わりに、そのクラスが含まれるjarファイル名とMANIFEST.MFに書かれた"Implementation-Version"が追加されます。この画期的なテクニックを考案したのは<a href="http://macstrac.blogspot.com/2008/09/better-stack-traces-in-java-with-log4j.html">James Strachan</a>です。パッケージングの情報が不確かな場合、先頭にチルダ(~)が付きます。
    -          </p>
    -
    -          <p>例を見てみましょう。</p>
    -
    -          <p class="source small">java.lang.NullPointerException
    -  at com.xyz.Wombat(Wombat.java:57) <b><span class="red">~</span>[wombat-1.3.jar:1.3]</b>
    -  at  com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
    -  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.5.0_06]
    -  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) ~[na:1.5.0_06]
    -  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) ~[na:1.5.0_06]
    -  at java.lang.reflect.Method.invoke(Method.java:585) ~[na:1.5.0_06]
    -  at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:59) [junit-4.4.jar:na]
    -  at org.junit.internal.runners.MethodRoadie.runTestMethod(MethodRoadie.java:98) [junit-4.4.jar:na]
    -  ...etc </p>
    -
    -          <p>logbackはパッケージング情報が正しく出力されるよう、適切な幅を確保します。どれだけクラスローダーの階層が複雑になっていても頑張ります。残念ながら正確性が担保できないときは先頭にチルダ(~)を付けます。従って、理屈の上では実際のパッケージング情報とは異なる内容を出力させることも可能です。ですから、前の例で Wombat クラスのパッケージング情報の先頭にはチルダ(~)があるので、本当は [wombat.jar:1.7] となるはずだったかもしれないのです。
    -          </p>
    -
    -          <p><a href="http://jira.qos.ch/browse/LBCLASSIC-212">利用者からのフィードバックによると</a>、NetBeansはパッケージング情報を縮めてしまうそうです。Netbeansのユーザーは、変換パターン文字列の最後に"%ex"を追加して、スタックトレースにパッケージング情報が出てこないようにしたほうが良いでしょう。たとえば、"%d %logger - %m%n" という変換パターン文字列を使っているなら、"%d %logger - %m%n<b>%ex</b>" と書き換えればよいのです。</p>
    -        </td>
    -
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="nopex">
    -          <b>nopex</b> <br>
    -          <b>nopexception</b>
    -        </td>
    -
    -        <td>
    -          <p>スタックトレースの情報を<em>扱うように見えます</em>が、実際は何も出力しません。つまり、うまく例外を無視できるのです。
    -          </p>
    -
    -          <p>%nopex変換指定子を使うと、<code>PatternLayout</code>が内部的に実装している安全弁を無かったことにします。安全弁とは、変換パターン文字列に例外を扱う変換指定子が含まれていなかったらこっそり最後に%xThrowableを追加することです。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="marker">
    -          <b>marker</b>
    -        </td>
    -
    -        <td>
    -          <p>ロギング要求に関連付けられたマーカーを出力します。</p>
    -
    -          <p>マーカーに子マーカーがある場合、次のような書式で両方のマーカーを出力します。
    -          </p>
    -          <p>
    -            <em>parentName [ child1, child2 ]</em>
    -          </p>
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="property">
    -          <b>property{key}</b>
    -        </td>
    -
    -        <td><p><em>キー</em>という名前のプロパティに設定された値を出力します。関連するドキュメントは<a href="http://logback.qos.ch/manual/configuration.html#variableSubstitution">変数の定義</a>と<a href="http://logback.qos.ch/manual/configuration.html#scopes">変数のスコープ</a>です。
    -
    -        <em>キー</em>がロガーコンテキストのプロパティではなかったら、システムプロパティを探します。</p>
    -
    -
    -         <p><em>キー</em>に対するデフォルト値はありません。プロパティが見つからなかったら、"Property_HAS_NO_KEY"という文字列が値になります。エラーであることがすぐにわかりますね。</p>
    -
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="replace">
    -          <b>replace(<em>p</em>){r, t}</b>
    -        </td>
    -
    -        <td>
    -          <p>変換パターン文字列"p"について、正規表現'r'にマッチした部分を文字列't'で置き換えます。たとえば、"%replace(%msg){'\s',''}" とすると、ロギングイベントに設定されたメッセージに含まれるすべての空白文字を削除します。
    -          </p>
    -
    -          <p>変換パターン文字列"p"はどれだけ複雑になってもいいですし、複数の変換指定を含めることもできます。たとえば、"%replace(%logger %msg)'\.','/'}" とすると、ロガー名とメッセージに含まれる全てのドット(.)をスラッシュ(/)で置き換えます。
    -          </p>
    -
    -        </td>
    -      </tr>
    -
    -
    -      <tr>
    -        <td class="word" name="rootException">
    -          <b>rEx</b>{<em>depth</em>} <br>
    -          <b>rootException</b>{<em>depth</em>} <br>
    -          <br>
    -          <b>rEx</b>{depth, evaluator-1, ..., evaluator-n} <br>
    -          <b>rootException</b>{depth, evaluator-1, ..., evaluator-n}
    -        </td>
    -
    -        <td>
    -          <p>ロギングイベントに例外オブジェクトが関連付けられていたら、その例外オブジェクトのスタックトレースを出力します。
    -標準とは逆に、例外の発生元から順番にスタックトレースを出力します。こんな出力になります(サンプルなのでだいぶ削っています)。</p>
    -
    -         <pre class="small">java.lang.NullPointerException
    -  at com.xyz.Wombat(Wombat.java:57) ~[wombat-1.3.jar:1.3]
    -  at com.xyz.Wombat(Wombat.java:76) ~[wombat-1.3.jar:1.3]
    -Wrapped by: org.springframework.BeanCreationException: Error creating bean with name 'wombat':
    -  at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:248) [spring-2.0.jar:2.0]
    -  at org.springframework.AbstractBeanFactory.getBean(AbstractBeanFactory.java:170) [spring-2.0.jar:2.0]
    -  at org.apache.catalina.StandardContext.listenerStart(StandardContext.java:3934) [tomcat-6.0.26.jar:6.0.26]
    -</pre>
    -
    -         <p>%rootExceptionには、%xExceptionと同じオプションを指定できます。それに、パッケージング情報も出力します。簡単に言うと、%rootException は %xException とほとんど変わりませんが、スタックトレースの出力順だけが逆になっているのです。
    -         </p>
    -
    -         <p>%のrootExceptionの作者であるTomasz Nurkiewiczは、自身のブログエントリ<a href="http://nurkiewicz.blogspot.com/2011/09/logging-exceptions-root-cause-first.html">"Logging eceptions root cause first"</a>で解説しています。</p>
    -        </td>
    -      </tr>
    -
    -    </table>
    -
    -
    -    <h4 class="doAnchor" name="percentIsSpecial">%文字の特別な意味</h4>
    -
    -    <p>変換パターン文字列というコンテキストにおいて、%文字には特別な意味があります。文字列リテラルとして使用するにはバックスラッシュ(\)を前につけてエスケープしなければなりません。たとえば "%d %p \%%m%n" といったようにします。
    -    </p>
    -
    -    <h4 class="doAnchor" name="restrictionsOnLiterals">変換指定子の直後の文字列リテラルの制限</h4>
    -
    -    <p>ほとんどの場合文字列リテラルにはスペースや他の区切り文字が含まれるので、変換指定子と混同されることはありません。例えば、変換パターン文字列"%level [%thread] - %mmessage%n" には文字列リテラル <code>" ["</code>と<code>"] - "</code>が含まれています。しかし、Javaの識別子に使える文字リテラルが変換指定子の直後に現れると、logbackの文字列パターン解析器はその文字リテラルも変換指定子の一部だと勘違いしてしまいます。例えば、変換パターン文字列 "%date<b>%nHello</b>" は、%date と %nHello という二つの変換指定子として解釈されてしまいます。もちろん、%nHello は変換指定子に存在しないため、logback は %PARSER_ERROR[nHello] というエラーを出力するでしょう。%nのすぐ後に文字列リテラル "Hello" を出したければ、%nに空の引数リストを指定すればよいでしょう。たとえば、"%date<b>%n{}</b>Hello" とすれば、%date %n 文字列リテラル "Hello" という順に解釈させることができるでしょう。
    -
    -    </p>
    -
    -    <h2 class="doAnchor" name="formatModifiers">書式修飾子</h2>
    -
    -    <p>デフォルトでは、関連する情報はそのまま出力されます。しかし、書式修飾子を使えばそれぞれのデータを出力する幅の最小値と最大値、それに文字揃えを変えることが出来ます。
    -    </p>
    -
    -    <p>書式修飾子のオプションは%記号と修飾文字の間に入れます。
    -    </p>
    -
    -    <p>最初に紹介する書式修飾子のオプションは<em>左揃え</em>です。これはただのマイナス(-)を指定します。次は<em>最小幅</em>です。整数値で出力する文字数を指定します。出力する文字列長が指定した文字数より小さい時は、最小幅を全てうめるまで右側か左側のどちらかをパディングします。デフォルトは右揃えなので左側にパディングしますが、左揃えにして右側にパディングさせることもできます。パディング文字は半角スペースです。出力する文字列長が指定した文字数より大きい時は、全て出力できるように幅を広げます。後ろを切り捨てることはありません。
    -    </p>
    -
    -    <p>この振る舞いは最大幅を指定すれば変えられます。<em>最大幅</em>はドット(.)の後に整数値で指定します。出力する文字列長が指定した文字数より大きい時は、溢れた分だけ出力する文字列の<em>先頭</em>から削除されます。たとえば、最大幅を8にしたとき、出力する文字列長が10だったら、先頭の2文字が削除されることになります。C言語のprintf関数なら文字列の後ろのほうが削除されるので、逆の振る舞いをすることになります。
    -    </p>
    -
    -    <p>ドット(.)に続けてマイナス文字(-)を指定すれば、後ろから削除することもできます。その場合、最大幅が8として、出力する文字列長が10だったら、末尾の2文字が削除されることになります。
    -    </p>
    -
    -    <p>書式修飾子の例をいくつか見ていきましょう。
    -    </p>
    -
    -    <table class="bodyTable" border="0" cellpadding="8">
    -      <tr>
    -        <th>書式修飾子</th>
    -        <th>左寄せ</th>
    -        <th>最小幅</th>
    -        <th>最大幅</th>
    -        <th>コメント</th>
    -      </tr>
    -      <tr class="a">
    -        <td align="center">%20logger</td>
    -        <td align="center">しない</td>
    -        <td align="center">20</td>
    -        <td align="center">なし</td>
    -        <td>ロガー名が20文字未満であれば半角スペースで左側にパディングします。
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td align="center">%-20logger</td>
    -        <td align="center">する</td>
    -        <td align="center">20</td>
    -        <td align="center">なし</td>
    -        <td>ロガー名が20文字未満であれば半角スペースで右側にパディングします。
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td align="center">%.30logger</td>
    -        <td align="center">指定できない</td>
    -        <td align="center">なし</td>
    -        <td align="center">30</td>
    -        <td>ロガー名が30文字を超える場合、先頭から切り捨てます。
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td align="center">%20.30logger</td>
    -        <td align="center">しない</td>
    -        <td align="center">20</td>
    -        <td align="center">30</td>
    -        <td>ロガー名が20文字未満であれば半角スペースで左側にパディングします。ロガー名が30文字を超える場合、先頭から切り捨てます。
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td align="center">%-20.30logger</td>
    -        <td align="center">する</td>
    -        <td align="center">20</td>
    -        <td align="center">30</td>
    -        <td>ロガー名が20文字未満であれば半角スペースで右側にパディングします。ロガー名が30文字を超える場合、<em>先頭</em>から切り捨てます。
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td align="center">%.-30logger</td>
    -        <td align="center">指定できない</td>
    -        <td align="center">なし</td>
    -        <td align="center">30</td>
    -        <td>ロガー名が30文字を超える場合、<em>末尾</em>から切り捨てます。
    -        </td>
    -      </tr>
    -    </table>
    -
    -    <p>書式修飾子による文字列の切り捨ての例を表にまとめました。角括弧"[]"は出力される文字列ではなく、出力幅を示しているだけなので注意してください。</p>
    -
    -
    -    <table class="bodyTable" border="0" cellpadding="8">
    -      <tr>
    -        <th>書式修飾子</th>
    -        <th>ロガー名</th>
    -        <th>結果</th>
    -      </tr>
    -      <tr class="b">
    -        <td align="center">[%20.20logger]</td>
    -        <td align="center">main.Name</td>
    -        <td align="center"><pre>[           main.Name]</pre></td>
    -      </tr>
    -      <tr class="a">
    -        <td align="center">[%-20.20logger]</td>
    -        <td align="center">main.Name</td>
    -        <td align="center"><pre>[main.Name           ]</pre></td>
    -      </tr>
    -      <tr class="a">
    -        <td align="center">[%10.10logger]</td>
    -        <td align="center">main.foo.foo.bar.Name</td>
    -        <td align="center"><pre>[o.bar.Name]</pre></td>
    -      </tr>
    -      <tr class="b">
    -        <td align="center">[%10.-10logger]</td>
    -        <td align="center">main.foo.foo.bar.Name</td>
    -        <td align="center"><pre>[main.foo.f]</pre></td>
    -      </tr>
    -    </table>
    -
    -    <h3 class="doAnchor" name="oneLetterLevel">レベルを1文字で出力する</h3>
    -
    -    <p>ロギングレベルをTRACE、DEBUG、WARN、INFO、ERRORのような文字列として出力するのではなく、T、D、W、I、Eのように一文字だけ出力したい場合もあるでしょう。<a href="http://logback.qos.ch/manual/layouts.html#customConversionSpecifier">カスタムコンバーター</a>を実装してもできるでしょうし、書式修飾子を使うこともできます。書式修飾子を使うなら<code>"%.-1level"</code>とすればよいでしょう。
    -    </p>
    -
    -    <h2 class="doAnchor" name="cwOptions">変換指定子のオプション</h2>
    -
    -    <p>変換指定子にはオプションを指定することができます。オプションは必ず中括弧で囲むようにします。オプションで何ができるのか、すでに目にしてきたものがあります。たとえば、<em>%mdc{someKey}</em>のようにMDC変換指定子と組み合わせることができます。
    -    </p>
    -
    -    <p>変換指定子のオプションは複数になるかもしれません。たとえば、評価器を使う変換指定子があります。すぐ後で説明しますが、こんな風に評価器の名前を複数並べることがあるかもしれません。</p>
    -
    -    <pre class="prettyprint source">&lt;pattern&gt;%-4relative [%thread] %-5level - %msg%n \
    -  <b>%caller{2, DISP_CALLER_EVAL, OTHER_EVAL_NAME, THIRD_EVAL_NAME}</b>&lt;/pattern&gt;</pre>
    -
    -    <p>指定するオプションに括弧、スペース、カンマなどの特別な文字が含まれるときは、シングルクォートやダブルクォートで囲みます。たとえば、次のような変換パターン文字列になります。</p>
    -
    -    <pre class="prettyprint source">&lt;pattern&gt;%-5level - %replace(%msg)<b>{'\d{14,16}', 'XXXX'}</b>%n&lt;/pattern&gt;</pre>
    -
    -
    -    <p>この例では、<code>replace</code>変換指定子にオプションとして<code>\d{14,16}</code>と<code>XXXX</code>を指定しています。これは、メッセージ中に14桁から16桁の数字文字列があったらそれをXXXXで置き換えるものです。クレジットカード番号をマスクするのに役立ちます。"\d"は一桁の数字を表す正規表現の省略形です。後ろに"{14,16}"をつけているので、一つ前の文字、つまり一桁の数字が14個から16個繰り返される場合にマッチすることになります。
    -    </p>
    -
    -    <h2 class="doAnchor" name="Parentheses">括弧は特別扱い</h2>
    -
    -    <p>logbackでは、パターン文字列の中の括弧は、トークンをグループ化するものとして扱います。それぞれのグループはそれ自体を1つのパターン文字列として扱うことができます。logback0.9.27から、<a href="http://logback.qos.ch/manual/layouts.html#replace">%replace</a>のような変換指定子を組み合わせて部分パターン文字列を作れるようになりました。
    -    </p>
    -
    -    <p>こんなパターン文字列があるとします。</p>
    -
    -    <p class="source"><b>%-30(</b>%d{HH:mm:ss.SSS} [%thread]<b>)</b> %-5level %logger{32} - %msg%n</p>
    -
    -    <p>これは、部分パターン文字列"%d{HH:mm:ss.SSS} [%thread]" によって生成された出力をグループ化します。つまり、この部分パターン文字列によって生成された出力が30文字未満の文字列だった場合、右側にパディングされるのです。
    -    </p>
    -
    -    <p>グループ化しなかった場合はこういう出力になります。</p>
    -
    -    <p class="source">13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
    -13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
    -13:09:30 [main] DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
    -13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
    -13:09:38 [btpool0-7] INFO c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
    -13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
    -13:09:40 [btpool0-7] DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
    -13:09:40 [btpool0-7] INFO c.q.l.d.prime.NumberCruncherImpl - Found factor 2
    -    </p>
    -
    -    <p>"%-30()" でグループ化するとこうなります。</p>
    -
    -    <p class="source">13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Classload hashcode is 13995234
    -13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Initializing for ServletContext
    -13:09:30 [main]            DEBUG c.q.logback.demo.ContextListener - Trying platform Mbean server
    -13:09:30 [pool-1-thread-1] INFO  ch.qos.logback.demo.LoggingTask - Howdydy-diddly-ho - 0
    -13:09:38 [btpool0-7]       INFO  c.q.l.demo.lottery.LotteryAction - Number: 50 was tried.
    -13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Beginning to factor.
    -13:09:40 [btpool0-7]       DEBUG c.q.l.d.prime.NumberCruncherImpl - Trying 2 as a factor.
    -13:09:40 [btpool0-7]       INFO  c.q.l.d.prime.NumberCruncherImpl - Found factor 2
    -    </p>
    -
    -
    -    <p>後者のほうが読みやすいんじゃないでしょうか。</p>
    -
    -    <p>括弧を文字列リテラルとして扱いたいときは、バックスラッシュ(\)でエスケープしなければなりません。こんな感じです。
    -"<b>\(</b>%d{HH:mm:ss.SSS} [%thread]<b>\)</b>"
    -    </p>
    -
    -    <h2 class="doAnchor" name="coloring">カラー化</h2>
    -
    -    <p><a href="http://logback.qos.ch/manual/layouts.html#Parentheses">括弧</a>でグループ化した部分パターン文字列には色を指定することができます。logback1.0.5から<code>PatternLayout</code>で指定できるようになった色付け用の変換指定子は次のとおりです。
    -    <ul>
    -      <li>"%black"</li>
    -      <li>"%red"</li>
    -      <li>"%green"</li>
    -      <li>"%yellow"</li>
    -      <li>"%blue"</li>
    -      <li>"%magenta"</li>
    -      <li>"%cyan"</li>
    -      <li>"%white"</li>
    -      <li>"%gray"</li>
    -      <li>"%boldRed"</li>
    -      <li>"%boldGreen"</li>
    -      <li>"%boldYellow"</li>
    -      <li>"%boldBlue"</li>
    -      <li>"%boldMagenta"</li>
    -      <li>"%boldCyan"</li>
    -      <li>"%boldWhite"</li>
    -      <li>"%highlight"</li>
    -    </ul>
    -    これらの変換指定子にはオプションとして部分パターン文字列を指定することが想定されています。そしてその部分パターン文字列から生成された出力には色が付きます。
    -    </p>
    -
    -    <p>色付けをわかりやすく説明する設定ファイルを次に示します。%cyan変換指定子には部分パターン文字列として"%logger{15}"を指定してあるのがわかりますか。こうすると、ロガー名は15文字に短縮されるだけでなく、文字色がシアンになります。%highlight変換指定子を使うと、部分パターン文字列の文字色が、ログレベルがERRORなら赤太字、ログレベルがWARNなら赤字、ログレベルがINFOなら青字、それ以外のログレベルならデフォルトの色になります。</p>
    -
    -  <p class="example">例:ログレベルに応じた強調表示(<a href="http://logback.qos.ch/xref/chapters/layouts/highlighed.xml">logback-examples/src/main/java/chapters/layouts/highlighed.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;highlighted&#39;);">Groovyとして表示</span>
    -
    -
    -<pre id="highlighted" class="prettyprint">&lt;configuration debug="true"&gt;
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;!-- On Windows machines setting withJansi to true enables ANSI
    -         color code interpretation by the Jansi library. This requires
    -         org.fusesource.jansi:jansi:1.8 on the class path.  Note that
    -         Unix-based operating systems such as Linux and Mac OS X
    -         support ANSI color codes by default. --&gt;
    -    <b>&lt;withJansi&gt;true&lt;/withJansi&gt;</b>
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;[%thread] <b>%highlight(%-5level)</b> <b>%cyan(%logger{15})</b> - %msg %n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -     <p>この設定ファイルを使うと次のように出力されます。</p>
    -
    -<pre class="source">[main] <span style="color:#611">WARN</span>  <span style="color:#2bd">c.l.TrivialMain</span> - a warning message 0
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number1
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number2
    -[main] <span style="color:#00f">INFO</span>  <span style="color:#2bd">c.l.TrivialMain</span> - hello world number3
    -[main] DEBUG <span style="color:#2bd">c.l.TrivialMain</span> - hello world number4
    -[main] <span style="color:#611">WARN</span>  <span style="color:#2bd">c.l.TrivialMain</span> - a warning message 5
    -[main] <span style="color:#f00">ERROR</span> <span style="color:#2bd">c.l.TrivialMain</span> - Finish off with fireworks</pre>
    -
    -    <p>色付け用変換指定子を使うとパターン文字列が複数行になってしまうこともあります。<a href="http://logback.qos.ch/manual/layouts.html#customConversionSpecifier">変換指定を自作する</a>では設定ファイルで使用できる変換指定子を登録する手順について議論しています。</p>
    -
    -    <h2 class="doAnchor" name="Evaluators">評価器</h2>
    -
    -    <p>前述したように、オプションをリストで指定できると、変換指定子に<a href="http://logback.qos.ch/xref/ch/qos/logback/core/boolex/EventEvaluator.html">EventEvaluator</a>に基づく動的な振る舞いを複数指定できる必要があるときは便利です。
    -    <code>EventEvaluator</code>の役割は、ロギングイベントが基準に合致するかどうかを判定することです。
    -    </p>
    -
    -    <p>早速<code>EventEvaluator</code>の例を見てみましょう。次の設定ファイルは、ロギングイベントの日付、スレッド、レベル、メッセージと送信者情報をコンソールに出力するものです。ロギングイベントの送信者情報を抽出するのはとてもコストが高いので、特定のロガーが生成したロギング要求の場合だけに限定しています。さらに、メッセージに特定の文字列が含まれている場合だけにしています。ですので、特定のロギングイベントについてだけ、送信者情報の生成と出力を行うことになります。そうすれば、送信者情報のいらないケースではアプリケーションの性能ペナルティが発生しません。
    -    </p>
    -
    -    <p>評価器と<em>具体的な評価式</em>については、<a href="http://logback.qos.ch/manual/filters.html#evalutatorFilter">フィルタの章の独立したセクション</a>で紹介しています。もし評価器を実戦投入しようとしているなら必ず読んでおいてください。この後で紹介する例では、明示的に書いていませんが<code>JaninoEventEvaluator</code>を使用しています。ですので実行するには<a href="http://docs.codehaus.org/display/JANINO/Home">Jainoライブラリ</a>が必要になります。設定方法をまとめたドキュメントの<a href="http://logback.qos.ch/setup.html#janino">該当するセクション</a>を読んでください。</p>
    -
    -  <p class="example">例:EventEvaluatorsの使用例(<a href="http://logback.qos.ch/xref/chapters/callerEvaluatorConfig.xml">logback-examples/src/main/java/chapters/callerEvaluatorConfig.xml</a>)</p>
    -<span class="asGroovy" onclick="return asGroovy(&#39;callerEvaluatorConfig&#39;);">Groovyとして表示</span>
    -
    -
    -    <pre id="callerEvaluatorConfig" class="prettyprint source">&lt;configuration&gt;
    -  <b>&lt;evaluator name="DISP_CALLER_EVAL"&gt;
    -    &lt;expression&gt;logger.contains("chapters.layouts") &amp;amp;&amp;amp; \
    -      message.contains("who calls thee")&lt;/expression&gt;
    -  &lt;/evaluator&gt;</b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;
    -        %-4relative [%thread] %-5level - %msg%n<b>%caller{2, DISP_CALLER_EVAL}</b>
    -      &lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>この設定ファイル中の評価式は、ロガー名に"chapters.layouts" が含まれており、かつ、メッセージに"who clals thee" が含まれているロギングイベントにマッチするものです。XMLのエンコーディングルールに従うため、&amp;文字は&amp;amp;のようにエスケープしなければなりません。</p>
    -
    -    <p>この設定ファイルを使用するクラスを見てみましょう。</p>
    -
    -  <p class="example">例:EventEvaluatorsの使用例(<a href="http://logback.qos.ch/xref/chapters/CallerEvaluatorExample.html">logback-examples/src/main/java/chapters/CallerEvaluatorExample.java</a>)</p>
    -    <pre class="prettyprint source">package <b>chapters.layouts</b>;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class CallerEvaluatorExample {
    -
    -  public static void main(String[] args)  {
    -    Logger logger = LoggerFactory.getLogger(CallerEvaluatorExample.class);
    -    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -
    -    try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(lc);
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -      // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
    -
    -    for (int i = 0; i &lt; 5; i++) {
    -      if (i == 3) {
    -        logger.debug(<b>"who calls thee</b>?");
    -      } else {
    -        logger.debug("I know me " + i);
    -      }
    -    }
    -  }
    -}</pre>
    -
    -    <p>このアプリケーションは特別何もしません。forループでロギング要求を5回生成します。三回目だけ "who calls thee?" というメッセージを出力します。
    -    </p>
    -
    -    <p>次のコマンドで実行します。</p>
    -
    -    <p class="source">java chapters.layouts.CallerEvaluatorExample src/main/java/chapters/layouts/callerEvaluatorConfig.xml</p>
    -
    -    <p>そうすると、コンソールに次のように出力されます。</p>
    -
    -    <div class="source"><pre>0    [main] DEBUG - I know me 0
    -0    [main] DEBUG - I know me 1
    -0    [main] DEBUG - I know me 2
    -0    [main] DEBUG - who calls thee?
    -Caller+0   at chapters.layouts.CallerEvaluatorExample.main(CallerEvaluatorExample.java:28)
    -0    [main] DEBUG - I know me 4</pre></div>
    -
    -
    -    <p>ロギング要求が生成されたら、そのロギングイベントが評価されます。三回目のロギングイベントだけが評価条件にマッチするので、送信者情報が出力されます。他のロギングイベントは評価基準とマッチしないので送信者情報が出力されていません。
    -    </p>
    -
    -
    -    <p>現実世界のシナリオに対応できるように評価式を変更することにしましょう。例えばロガー名とロギング要求のレベルを組み合わせることもできます。そうすれば、ロギング要求のレベルが<em>WARN</em>より高くて、かつ、アプリケーションの重要部品、例えばお金に絡むトランザクションを処理するモジュールで生成された場合にだけ送信者情報を出力する、といったことができるでしょう。
    -    </p>
    -
    -    <p><b>重要:</b> <em>caller変換指定</em>によって送信者情報が出力されるのは、評価式が<b>true</b>と<em>評価された</em>ときだけです。</p>
    -
    -    <p>別の状況を考えてみましょう。ロギング要求に例外オブジェクトが設定されていると、スタックトレースも出力されます。しかし、特定の例外オブジェクトのスタックトレースは出力したくないこともあります。
    -    </p>
    -
    -    <p>次のコードでは、例外オブジェクトを指定したロギング要求を3つ生成しています。二つ目の例外オブジェクトは他のものと違ってメッセージに "do not display this" という文字列が含まれていますし、例外クラスが <code>chapters.layouts.TestException</code>になっています。このメッセージを指示とみなして、二つ目の例外オブジェクトのスタックトレースが出力されないようにしてみましょう。</p>
    -
    -  <p class="example">例:ExceptionEventEvaluatorsの使用例(<a href="http://logback.qos.ch/xref/chapters/ExceptionEvaluatorExample.html">logback-examples/src/main/java/chapters/ExceptionEvaluatorExample.java</a>)</p>
    -<pre class="prettyprint source">package chapters.layouts;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -import ch.qos.logback.core.util.StatusPrinter;
    -
    -public class ExceptionEvaluatorExample {
    -
    -  public static void main(String[] args) {
    -    Logger logger = LoggerFactory.getLogger(ExceptionEvaluatorExample.class);
    -    LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -
    -    try {
    -      JoranConfigurator configurator = new JoranConfigurator();
    -      configurator.setContext(lc);
    -      lc.reset();
    -      configurator.doConfigure(args[0]);
    -    } catch (JoranException je) {
    -       // StatusPrinter will handle this
    -    }
    -    StatusPrinter.printInCaseOfErrorsOrWarnings(lc);
    -
    -    for (int i = 0; i &lt; 3; i++) {
    -      if (i == 1) {
    -        logger.debug("logging statement " + i, new TestException(
    -            "do not display this"));
    -      } else {
    -        logger.debug("logging statement " + i, new Exception("display"));
    -      }
    -    }
    -  }
    -}</pre>
    -
    -    <p>次の設定ファイルで指定した評価式は、<code>chapters.layouts.TextException</code>の例外オブジェクトが設定されたロギングイベントにマッチします。この例外型はスタックトレースの出力を抑止したい型そのものです。
    -    </p>
    -
    -  <p class="example">例:ExceptionEventEvaluatorsの使用例(<a href="http://logback.qos.ch/xref/chapters/exceptionEvaluatorConfig.xml">logback-examples/src/main/java/chapters/exceptionEvaluatorConfig.xml</a>)</p>
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;evaluator name="DISPLAY_EX_EVAL"&gt;
    -    &lt;expression&gt;throwable != null &amp;amp;&amp;amp; throwable instanceof  \
    -      chapters.layouts.TestException&lt;/expression&gt;
    -  &lt;/evaluator&gt;</b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%msg%n<b>%ex{full, DISPLAY_EX_EVAL}</b>&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>この設定は、<em>chapters.layouts.TestException</em>のインスタンスが設定されたロギング要求については、常にスタックトレースの出力を抑止します。
    -    </p>
    -
    -    <p>実行してみましょう。</p>
    -
    -    <p class="source">java chapters.layouts.ExceptionEvaluatorExample src/main/java/chapters/layouts/exceptionEvaluatorConfig.xml</p>
    -
    -    <p>そうすると、次のような出力になります。</p>
    -
    -<p class="source">logging statement 0
    -java.lang.Exception: display
    -  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]
    -logging statement 1
    -logging statement 2
    -java.lang.Exception: display
    -  at chapters.layouts.ExceptionEvaluatorExample.main(ExceptionEvaluatorExample.java:43) [logback-examples-0.9.19.jar:na]</p>
    -
    -
    -    <p>二つ目のロギング要求による出力にはスタックトレースが含まれていないことがわかりますか。うまく<code>TestException</code>のスタックトレースを出力しないようにできました。スタックトレースの各行の最後にある角括弧で囲まれた部分は<a href="http://logback.qos.ch/manual/layouts.html#xThrowable">パッケージング情報</a>です。</p>
    -
    -    <p><b><em>%ex</em></b>変換指定があるので、<em>評価式</em>の結果が<b>false</b>になったときだけスタックトレースが出力されるようになっています。</p>
    -
    -
    -
    -    <h2 class="doAnchor" name="customConversionSpecifier">変換指定子を自作する</h2>
    -
    -    <p>ここまでに、<code>PatternLayout</code>に組み込みの変換指定子を見てきました。ですが、変換指定子を自作することもできるのです。</p>
    -
    -    <p>カスタム変換指定子を作るには2つの手順を踏みます。
    -    </p>
    -
    -    <h4>ステップ1</h4>
    -
    -    <p>まず、<code>ClassicConverter</code>を継承したクラスを作ります。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/pattern/ClassicConverter.html">ClassicConverter</a></code>の役割は、<code>ILoggingEvent</code>から情報を抽出して出力する文字列を生成することです。たとえば、 %logger変換指定子を実装した<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/pattern/LoggerConverter.html">LoggerConverter</a></code>は、<code>ILoggingEvent</code>から抽出したロガーの名前を文字列として返します。ロガー名の省略はこの手順で行います。</p>
    -
    -    <p>アプリケーションが起動してからの経過時間をナノ秒単位で返すカスタム変換指定子の実装を見てみましょう。</p>
    -
    -  <p class="example">例:自作コンバーターの例(<a href="http://logback.qos.ch/xref/chapters/MySampleConverter.html">logback-examples/src/main/java/chapters/MySampleConverter.java</a>)</p>
    -<pre class="prettyprint source">public class MySampleConverter extends ClassicConverter {
    -
    -  long start = System.nanoTime();
    -
    -  <b>@Override</b>
    -  <b>public String convert(ILoggingEvent event) {</b>
    -    <b>long nowInNanos = System.nanoTime();</b>
    -    <b>return Long.toString(nowInNanos-start);</b>
    -  <b>}</b>
    -}</pre>
    -
    -    <p>この実装は非常に簡単です。<code>MySampleConverter</code>クラスは<code>ClassicConverter</code>を継承しており、<code>convert(ILoggingEvent)</code>メソッドはアプリケーションが起動してからの経過時間をナノ秒単位で計算して、それを文字列化しているだけです。
    -    </p>
    -
    -    <h4>ステップ2</h4>
    -
    -    <p>logback に自作した<code>Converter</code>のことを教えなければいけません。そのためには、設定ファイルで新しい変換指定子を宣言する必要があります。</p>
    -
    -  <p class="example">例:自作コンバーターの設定例(<a href="http://logback.qos.ch/xref/chapters/mySampleConverterConfig.xml">logback-examples/src/main/java/chapters/mySampleConverterConfig.xml</a>)</p>
    -<span class="asGroovy" onclick="return asGroovy(&#39;mySampleConverterConfig&#39;);">Groovyとして表示</span>
    -<pre id="mySampleConverterConfig" class="prettyprint source">&lt;configuration&gt;
    -
    -  <b>&lt;conversionRule conversionWord="nanos"
    -                  converterClass="chapters.layouts.MySampleConverter" /&gt;</b>
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;<b>%-6nanos</b> [%thread] - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="STDOUT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -    <p>設定ファイルで宣言した新しい変換指定子は、<code>PatternLayout</code>に指定する変換パターン文字列の中で他の変換指定と同じように使うことが出来ます。</p>
    -
    -    <p>実行してみましょう。</p>
    -
    -    <div class="source">java chapters.layouts.SampleLogging src/main/java/chapters/layouts/mySampleConverterConfig.xml </div>
    -
    -    <p>次のような出力になるはずです。</p>
    -
    -    <pre class="source">4868695 [main] DEBUG - Everything's going well
    -5758748 [main] ERROR - maybe not quite...</pre>
    -
    -
    -    <p>オプションの扱い方や、もっと複雑な振る舞いをさせるやり方を知りたければ、<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/pattern/MDCConverter.html"><code>MDCConverter</code></a>などの既存の<code>Converter</code>実装を見てみるとよいでしょう。独自の色付けをしたければ<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/pattern/color/HighlightingCompositeConverter.html"><code>HighlightingCompositeConverter</code></a>を参考にするとよいでしょう。
    -    </p>
    -
    -
    -
    -    <h2 class="doAnchor" name="ClassicHTMLLayout">HTMLLayout</h2>
    -
    -    <p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/html/HTMLLayout.html"><code>HTMLLayout</code></a>はHTML形式のログを出力します(logback-classicに含まれています)。<code>HTMLLayout</code>はHTMLのテーブルを出力します。それぞれの行がロギングイベントに対応しています。</p>
    -
    -    <p>デフォルトのCSSを使った<code>HTMLLayout</code>の出力例を見てください。</p>
    -    <img src="images/chapters/layouts/htmlLayout0.gif" alt="HTMLレイアウトサンプル画像">
    -
    -    <p>テーブルの列は変換パターン文字列で指定します。<a href="http://logback.qos.ch/manual/layouts.html#ClassicPatternLayout"><code>PatternLayout</code></a>のドキュメントに書かれた変換パターン文字列の説明を見てください。テーブルの形も内容も全て制御することができます。<code>PatternLayout</code>で使えるあらゆるコンバーターを使うことができます。
    -    </p>
    -
    -    <p>自由に使えるとはいえ一つだけ例外的な制限があります。<code>PatternLayout</code>と<code>HTMLLayout</code>を使うときは、変換パターン文字列中で変換指定を半角スペースで区切ってはいけません。もっというと文字列リテラルを使わないようにしてください。変換パターン文字列中の変換指定はそれぞれがテーブルの列になるからです。同様に、変換指定を区切るつもりの文字列リテラルはそれぞれが列になってしまいます。下手をすると、横に超長いテーブルが出力されてしまうかもしれません。</p>
    -
    -    <p><code>HTMLLayout</code>の使い方を説明する設定ファイルを見てください。
    -    </p>
    -
    -  <p class="example">例:HTMLLayoutの設定例(<a href="http://logback.qos.ch/xref/chapters/htmlLayoutConfig1.xml">logback-examples/src/main/java/chapters/htmlLayoutConfig1.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;htmlLayoutConfig1&#39;);">Groovyとして表示</span>
    -<pre id="htmlLayoutConfig1" class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"&gt;
    -      &lt;layout class="ch.qos.logback.classic.html.HTMLLayout"&gt;
    -        <b>&lt;pattern&gt;%relative%thread%mdc%level%logger%msg&lt;/pattern&gt;</b>
    -      &lt;/layout&gt;
    -    &lt;/encoder&gt;
    -    &lt;file&gt;test.html&lt;/file&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;
    -</pre>
    -
    -   <p><a href="http://logback.qos.ch/xref/chapters/layouts/TrivialMain.html">TrivialMain</a>アプリケーションは、いくつかログを出力してから最後に例外オブジェクトを指定したログを出力します。実行してみましょう。</p>
    -
    -   <p class="source">java chapters.layouts.TrivialMain src/main/java/chapters/layouts/htmlLayoutConfig1.xml</p>
    -
    -    <p>すると、カレントフォルダに<em>test.html</em>というファイルが作成されます。<em>test.html</em>をブラウザで見ると次のようになっているでしょう。</p>
    -    <img src="images/chapters/layouts/htmlLayout1.png" alt="HTMLレイアウトサンプル画像">
    -
    -    <h3>スタックトレース</h3>
    -
    -    <p>スタックトレースを出力するには<em>%ex変換指定</em>を指定します。そうすると専用の列が追加されます。ほとんどの場合スタックトレースの列は空なので、無駄に画面を専有することでしょう。さらに、スタックトレースを専用の列に出力したところで読みやすくはならないです。なお、スタックトレースを出力するには、<em>%ex変換指定</em>を指定する以外の方法があります。
    -    </p>
    -
    -    <p>よりよい解決策は、自分で<code>IThrowableRenderer</code>インターフェイスを実装することです。実装したクラスは、例外オブジェクトに関係する情報を出力するため、<code>HTMLLayout</code>に割り当てることができます。デフォルトでは、<code>HTMLLayout</code>インスタンスに<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/html/DefaultThrowableRenderer.html">DefaultThrowableRenderer</a></code>が割り当てられています。これは例外オブジェクトのスタックトレースを<em>新しい行</em>に出力します。前の図を見るとわかりますが多少読みやすいです。
    -    </p>
    -
    -    <p>それでも理由があって<em>%ex</em>を使いたいときは、設定ファイルに<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/html/NOPThrowableRenderer.html">NOPThrowableRenderer</a></code>を指定すればスタックトレースを別々の行に出力しないようにできます。あなたの望み通りに全てが上手くいくようなアイデアはありません。それでも、こうしたいという気持ちがあればきっと実現できます。
    -    </p>
    -
    -    <h3>CSS</h3>
    -
    -    <p><code>HTMLLayout</code>で生成したHTMLの見た目はCSSで調整することができます。特別な指定がなければ、<code>HTMLLayout</code>は組み込みのCSSを使います。しかし、外部のCSSを使うようにも指定できます。そのためには、<code>layout要素</code>に<code>cssBuilder要素</code>をネストさせればよいです。
    -    </p>
    -
    -<pre class="prettyprint source">&lt;layout class="ch.qos.logback.classic.html.HTMLLayout"&gt;
    -  &lt;pattern&gt;%relative...%msg&lt;/pattern&gt;
    -  &lt;cssBuilder class="ch.qos.logback.classic.html.UrlCssBuilder"&gt;
    -    &lt;!-- url where the css file is located --&gt;
    -    &lt;url&gt;http://...&lt;/url&gt;
    -  &lt;/cssBuilder&gt;
    -&lt;/layout&gt;</pre>
    -
    -
    -    <p><code>HTMLLayout</code>は<code>SMTPAppender</code>と組み合わせて使われることがあります。そうするとメールの内容がHTMLで綺麗に飾られたものになります。</p>
    -
    -
    -    <h2 class="doAnchor" name="log4jXMLLayout">Log4jのXMLLayout</h2>
    -
    -    <p><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/log4j/XMLLayout.html">XMLLayout</a> (logback-classicに含まれています)を使うとlog4j.dtdに準拠した書式で出力できるようになります。つまり、<a href="http://logging.apache.org/chainsaw/index.html">Chainsaw</a>や<a href="http://vigilog.sourceforge.net/">Vigilog</a>などの、<a href="http://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/xml/XMLLayout.html">log4jのXML書式</a>を入力とするツールと相互運用できるのです。
    -    </p>
    -
    -
    -    <p>オリジナルのXMLLayoutはlog4j1.2.15で導入されたものです。logback-classic の XMLLayoutには<span class="option">locationInfo</span>と<span class="option">properties</span>という二つのプロパティが指定できます。<span class="option">locationInfo</span>プロパティにtrueを指定すると、ロギングイベントの位置情報(送信者情報)を含められるようになります。<span class="option">property</span>プロパティにtrueを指定すると、MDCの情報を含められるようになります。どちらのプロパティもデフォルトはfalseです。
    -    </p>
    -
    -    <p>設定例を見てみましょう。</p>
    -
    -  <p class="example">例:Log4jXMLLayoutの例(<a href="http://logback.qos.ch/xref/chapters/log4jXMLLayout.xml">logback-examples/src/main/java/chapters/log4jXMLLayout.xml</a>)</p>
    -
    -<span class="asGroovy" onclick="return asGroovy(&#39;log4jXMLLayout&#39;);">Groovyとして表示</span>
    -    <pre id="log4jXMLLayout" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="FILE" class="ch.qos.logback.core.FileAppender"&gt;
    -    &lt;file&gt;test.xml&lt;/file&gt;
    -    &lt;encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder"&gt;
    -      &lt;layout class="ch.qos.logback.classic.log4j.XMLLayout"&gt;
    -        &lt;locationInfo&gt;true&lt;/locationInfo&gt;
    -      &lt;/layout&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="FILE" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt; </pre>
    -
    -    <h1 class="doAnchor" name="logback-access">logback access モジュール</h1>
    -
    -    <p>logback-access モジュールのほとんどのレイアウトは、logback-classic モジュールのレイアウトをそのまま移植しただけです。logback-classic モジュールと logback-access モジュールは用途が違うのですが、だいたい同じ機能を提供しています。</p>
    -
    -    <h2>レイアウトを自作する</h2>
    -
    -    <p>logback-access 用の<code>レイアウト</code>を自作するのは、logback-classic の<code>レイアウト</code>を自作するのとほとんど変わりありません。</p>
    -
    -
    -    <h3 class="doAnchor" name="AccessPatternLayout">PatternLayout</h3>
    -
    -    <p>logback-access の<code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/PatternLayout.html">PatternLayout</a></code>は、logback-classic とほとんど同じように設定することができます。ですが、HTTPリクエストやHTTPレスポンスにしか無い、ロギングに適した情報のための変換指定が追加されています。
    -    </p>
    -
    -    <p>logback-accessの<code>PatternLayout</code>で使える変換指定を表にまとめました。</p>
    -
    -    <table class="bodyTable striped" border="0" cellpadding="8">
    -      <tr>
    -        <th align="center">変換指定</th>
    -        <th align="center">効果</th>
    -      </tr>
    -      <tr>
    -        <td class="word" name="remoteIP">
    -          <b>a</b><br /><b>
    -remoteIP</b>
    -        </td>
    -        <td>
    -          <p>リモートIPアドレス。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="localIP"><b>A</b><br /><b>
    -localIP</b></td>
    -        <td>
    -          <p>ローカルIPアドレス。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="bytesSent"><b>b</b><br /><b>
    -B</b><br /><b>
    -bytesSent</b></td>
    -        <td>
    -          <p>レスポンスのコンテンツ長。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="clientHost"><b>h</b><br /><b>
    -clientHost</b></td>
    -        <td>
    -          <p>リモートホスト名。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="protocol"><b>H</b><br /><b>
    -protocol</b></td>
    -        <td>
    -          <p>リクエストプロトコル。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="remoteLogName"><b>l</b></td>
    -        <td>
    -          <p>リモートログ名。logback-accessではこのコンバーターの値は常に "-" です。
    -          </p>
    -        </td>
    -      </tr>
    -
    -      <tr>
    -        <td class="word" name="reqParameter"><b>reqParameter{paramName}</b></td>
    -        <td>
    -          <p>リクエストパラメータ。</p>
    -          <p>この変換指定子は、オプションで指定されたリクエストパラメータを探します。</p>
    -          <p><b>%reqParameter{input_data}</b>とすると、リクエストパラメータのinput_dataの値を出力します。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="header"><b>i{header}</b><br /><b>
    -header{header}</b></td>
    -        <td>
    -          <p>リクエストヘッダ。</p>
    -          <p>この変換指定子はオプションで指定されたリクエストヘッダを探します。</p>
    -          <p><b>%header{Referer}</b>とすると、リクエストヘッダのRefererの値を出力します。</p>
    -          <p>オプションを指定しなかったら、利用可能なヘッダを全て出力します。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="requestMethod"><b>m</b><br /><b>
    -requestMethod</b></td>
    -        <td>
    -          <p>リクエストメソッド。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="requestURL"><b>r</b><br /><b>
    -requestURL</b></td>
    -        <td>
    -          <p>要求されたURL。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="statusCode"><b>s</b><br /><b>
    -statusCode</b></td>
    -        <td>
    -          <p>レスポンスのステータスコード。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -          <td class="word" name="elapsedTime"><b>D</b><br /><b>
    -elapsedTime</b></td>
    -          <td>
    -              <p>リクエストを処理するのにかかった時間。ミリ秒単位です。
    -              </p>
    -          </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="dateAccess"><b>t</b><br /><b>
    -date</b></td>
    -        <td>
    -          <p>ロギングイベントの日時を出力します。オプションとして、<code>java.text.SimpleDateFormat</code>で使用できる日時パターン文字列を指定します。<em>ISO8601</em>も有効な値です。
    -          </p>
    -          <p>具体的には、<b>%t {HH:mm:ss,SSS} </b>や、<b>%t{dd MMM yyyy;HH:mm:ss,SSS}</b>などです。日時パターン文字列が指定されなかったら、共通ログ書式の日時パターン文字列である<b>T6{dd/MMM/yyyy:HH:mm:ss Z}</b>が指定されたものとして扱います。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="httpUser"><b>u</b><br /><b>
    -user</b></td>
    -        <td>
    -          <p>リモートユーザー。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="requestURI"><b>U</b><br /><b>
    -requestURI</b></td>
    -        <td>
    -          <p>要求されたURI。</p>
    -        </td>
    -      </tr>
    -      <tr>
    -        <td class="word" name="server"><b>v</b><br /><b>
    -server</b></td>
    -        <td>
    -          <p>サーバー名。</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="localPort"><b>localPort</b></td>
    -        <td>
    -          <p>ローカルポート番号。</p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="reqAttribute"><b>reqAttribute{attributeName}</b></td>
    -        <td>
    -          <p>リクエストの属性。</p>
    -          <p>この変換指定子はオプションで指定された属性を探します。
    -</p>
    -          <p><b>%reqAttribute{SOME_ATTRIBUTE}</b>とすると、リクエスト属性SOME_ATTRIBUTEの値を出力します。</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="reqCookie"><b>reqCookie{cookie}</b></td>
    -        <td>
    -          <p>リクエストクッキー。</p>
    -          <p>この変換指定子はオプションで指定されたリクエストクッキーを探します。
    -</p>
    -          <p><b>%cookie{COOKIE_NAME}</b>とすると、クッキーCOOKIE_NAMEの値を出力します。</p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="responseHeader"><b>responseHeader{header}</b></td>
    -        <td>
    -          <p>レスポンスヘッダー。
    -          </p>
    -          <p>この変換指定子はオプションで指定されたレスポンスヘッダを探します。
    -</p>
    -          <p><b>%responseHeader{Server}</b>とすると、レスポンスヘッダServerの値を出力します。</p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="requestContent"><b>requestContent</b></td>
    -        <td>
    -          <p>この変換指定はリクエストの中身、つまり、リクエストの<code>InputStream</code>を出力します。実際は、<code><a href="http://logback.qos.ch/xref/ch/qos/logback/access/servlet/TeeFilter.html">TeeFilter</a></code>によって元の<code>HttpServletRequest</code>を<code>TeeHttpServletRequest</code>に置き換えます。だから、データを失わずに何度でもリクエストの<code>InputStream</code>にアクセスできるのです。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="fullRequest"><b>fullRequest</b></td>
    -        <td>
    -          <p>リクエストに関連するヘッダ、コンテンツを全て出力します。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="b">
    -        <td class="word" name="responseContent"><b>responseContent</b></td>
    -        <td>
    -          <p>レスポンスの中身、つまり、レsポンスの<code>InputStream</code>を出力します。実際は、<code>TeeFilter</code>によって元の<code>HttpServletResponse</code>を<code>TeeHttpServletResponse</code>に置き換えます。だから、データを失わずに何度でもレスポンスの<code>InputStream</code>にアクセスできるのです。
    -          </p>
    -        </td>
    -      </tr>
    -      <tr class="a">
    -        <td class="word" name="fullResponse"><b>fullResponse</b></td>
    -        <td>
    -          <p>レスポンスに関連するヘッダ、コンテンツを全て出力します。
    -          </p>
    -        </td>
    -      </tr>
    -    </table>
    -
    -    <p>logback-accessの<code>PatternLayout</code>ではさらに3つのキーワードが利用できます。これはいわゆるショートカットです。</p>
    -
    -    <table class="bodyTable">
    -      <tr>
    -        <th>キーワード</th>
    -        <th>等価な変換パターン文字列</th>
    -      </tr>
    -      <tr class="a">
    -        <td><em>common</em>
    -<em>CLF</em></td>
    -        <td><em>%h %l %u [%t] "%r" %s %b</em></td>
    -      </tr>
    -      <tr class="b">
    -        <td><em>combined</em></td>
    -        <td><em>%h %l %u [%t] "%r" %s %b "%i{Referer}" "%i{User-Agent}"</em></td>
    -      </tr>
    -
    -    </table>
    -
    -
    -    <p>キーワード<em>common</em>は変換文字列パターン<em>'%h %l %u [%t] "%r" %s %b'</em>と同じ意味になります。先頭から順番に、クライアントホスト、リモートログ名、ユーザー名、日時、リクエストURL、ステータスコード、レスポンスのコンテンツ長が出力されます。</p>
    -
    -    <p>キーワード<em>combined</em>は変換文字列パターン<em>'%h %l %u [%t]
    -    "%r" %s %b "%i{Referer}" "%i{User-Agent}"'</em>のショートカットです。先頭は<em>common</em>キーワードの変換パターン文字列とほとんど代わりませんが、referer、user-agent というリクエストヘッダが追加されているのが違います。</p>
    -
    -    <h3 class="doAnchor" name="AccessHTMLLayout">HTMLLayout</h3>
    -
    -    <p>logback-accessモジュールの<a href="http://logback.qos.ch/xref/ch/qos/logback/access/html/HTMLLayout.html"><code>HTMLLayout</code></a>は、logback-classicモジュールの<a href="http://logback.qos.ch/manual/layouts.html#ClassicHTMLLayout"><code>HTMLLayout</code></a>とほとんど変わりません。
    -    </p>
    -
    -    <p>デフォルトでは次の項目を含む表を作ります。</p>
    -
    -    <ul>
    -      <li>リモートIP</li>
    -      <li>日付</li>
    -      <li>リクエストURL</li>
    -      <li>レスポンスのステータスコード</li>
    -      <li>コンテンツ長</li>
    -    </ul>
    -
    -    <p>logack-accessの<code>HTMLLayout</code>による出力例を見てください。</p>
    -    <img src="images/chapters/layouts/htmlLayoutAccess.gif" alt="アクセスHTMLレイアウトサンプル画像">
    -
    -    <p>実際に使うと上でもっと良くするにはどうしたらいいでしょうか?log4j.properties for logback <a href="http://logback.qos.ch/translator/">translator</a>は、logback-access の<code>RollingFileAppender</code>と<code>HTMLLayout</code>を使ってリアルタイム出力をするようになっています。</p>
    -
    -
    -    <p>Webアプリケーションの<a href="http://logback.qos.ch/translator/">translator</a>に対するユーザーからのリクエストに対して、都度新しいアクセスログが出力されます。<a href="http://logback.qos.ch/translator/logs/access.html">このリンク先</a>でそれを見ることが出来ます。</p>
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/loggingSeparation.html b/logback-site/src/site/pages/manual/loggingSeparation.html
    deleted file mode 100755
    index a1f021d9aa..0000000000
    --- a/logback-site/src/site/pages/manual/loggingSeparation.html
    +++ /dev/null
    @@ -1,508 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 9: Logging separation</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">	
    -	
    -    <h1>Chapter 9: Logging separation</h1>
    -
    -    <a href="loggingSeparation_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -      
    -    <div class="quote">
    -      <p><em>It is not knowledge, but the act of learning, not
    -      possession but the act of getting there, which grants the greatest
    -      enjoyment. When I have clarified and exhausted a subject, then I
    -      turn away from it, in order to go into darkness again; the
    -      never-satisfied man is so strange if he has completed a structure,
    -      then it is not in order to dwell in it peacefully, but in order to
    -      begin another. I imagine the world conqueror must feel thus, who,
    -      after one kingdom is scarcely conquered, stretches out his arms
    -      for others.</em></p>
    -
    -      <p>&mdash;KARL FRIEDRICH GAUSS, Letter to Bolyai, 1808.</p>
    -
    -      <p><em>Style, like sheer silk, too often hides eczema.</em></p>
    -      
    -      <p>&mdash;ALBERT CAMUS, <em>The Fall</em></p>
    -
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>		
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -    <h2>The problem: Logging Separation</h2>
    -
    -    <p>The chapter deals with a relatively difficult problem of
    -    providing a separate logging environment for multiple applications
    -    running on the same web or EJB container. In the remainder of this
    -    chapter the term "application" will be used to refer to either a
    -    web-application or a J2EE application interchangeably.  In a
    -    separated logging environment, each application sees a distinct
    -    logback environment, so that the logback configuration of one
    -    application does not interfere with the settings of another. In
    -    more technical terms, each web-application has a distinct copy of
    -    <code>LoggerContext</code> reserved for its own use. Recall that
    -    in logback, each logger object is manufactured by a
    -    <code>LoggerContext</code> to which it remains attached for as
    -    long as the logger object lives in memory. A variant of this
    -    problem is the separation of application logging and the logging
    -    of the container itself.
    -    </p>
    -
    -    <h2 class="doAnchor" name="easy">The simplest and easiest
    -    approach</h2>
    -
    -    <p>Assuming your container supports child-first class loading,
    -    separation of logging can be accomplished by embedding a copy of
    -    slf4j and logback jar files in each of your applications. For
    -    web-applications, placing slf4j and logback jar files under the
    -    <em>WEB-INF/lib</em> directory of the web-application is
    -    sufficient to endow each web-application with a separate logging
    -    environment. A copy of the <em>logback.xml</em> configuration file
    -    placed under <em>WEB-INF/classes</em> will be picked up when
    -    logback is loaded into memory. 
    -    </p>
    -
    -    <p>By virtue of class loader separation provided by the container,
    -    each web-application will load its own copy of
    -    <code>LoggerContext</code> which will pickup its own copy of
    -    <em>logback.xml</em>.</p>
    -    
    -    <p>Easy as pie.</p>
    -
    -    <p>Well, not exactly. Sometimes you will be forced to place SLF4J
    -    and logback artifacts in a place accessible from all applications,
    -    typically because a shared library uses SLF4J. In that case, all
    -    applications will share the same logging environment. There are
    -    various other scenarios where a copy of SLF4J and logback
    -    artifacts is necessarily placed at a spot where it can be seen by
    -    all applications making logging separation by class loader
    -    separation impossible. All hope is not lost. Please read on.
    -    </p>
    -
    -    <h2 class="doAnchor" name="contextSelectors">Context
    -    Selectors</h2>
    -
    -    <p>Logback provides a mechanism for a single instance of SLF4J and
    -    logback classes loaded into memory to provide multiple logger
    -    contexts. When you write:
    -    </p>
    -
    -    <pre class="prettyprint source">Logger logger = LoggerFactory.getLogger("foo");</pre>
    -
    -    <p>the <code>getLogger</code>() method in
    -    <code>LoggerFactory</code> class will ask the SLF4J binding for a
    -    <code>ILoggerFactory</code>. When SLF4J is bound to logback, the
    -    task of returning an <code>ILoggerFactory</code> is delegated to
    -    an instance of <a
    -    href="../apidocs/ch/qos/logback/classic/selector/ContextSelector.html">ContextSelector</a>. Note
    -    that <code>ContextSelector</code> implementations always return
    -    instances <code>LoggerContext</code>. This class implements the
    -    <code>ILoggerFactory</code> interface.  In other words, a context
    -    selector has the option to returning any
    -    <code>LoggerContext</code> instance it sees fit according to its
    -    own criteria. Hence the name context <em>selector</em>.
    -    </p>
    -
    -    <p>By default, the logback binding uses <a
    -    href="../xref/ch/qos/logback/classic/selector/DefaultContextSelector.html">DefaultContextSelector</a>
    -    which always returns the same <code>LoggerContext</code>, called
    -    the default logger context.</p>
    -
    -    <p>You can specify a different context selector by setting the
    -    <em>logback.ContextSelector</em> system property. Suppose you
    -    would like to specify that context selector to an instance of the
    -    <code>myPackage.myContextSelector</code> class, you would add the
    -    following system property: </p>
    -
    -    <p class="source">-Dlogback.ContextSelector=myPackage.myContextSelector</p>
    -
    -    <p>The context selector needs to implement the
    -    <code>ContextSelector</code> interface and have a constructor
    -    method admitting a <code>LoggerContext</code> instance as its only
    -    parameter.
    -    </p>
    -
    -
    -    <h3 class="doAnchor"
    -    name="ContextJNDISelector">ContextJNDISelector</h3>
    -
    -    <p>Logback-classic ships with a selector called
    -    <code>ContextJNDISelector</code> which selects the logger context
    -    based on data available via JNDI lookup. This approach leverages
    -    JNDI data separation mandated by the J2EE specification. Thus, the
    -    same environment variable can be set to carry a different value in
    -    different applications. In other words, calling
    -    <code>LoggerFactory.getLogger()</code> from different applications
    -    will return a logger attached to a different logger context, even
    -    if there is a single LoggerFactory class loaded into memory shared
    -    by all applications. That's logging separation for you.
    -    </p>
    -
    -    <p>To enable <code>ContextJNDISelector</code>, the
    -    <em>logback.ContextSelector</em> system property needs to be set
    -    to "JNDI", as follows:</p>
    -
    -    <p class="source">-Dlogback.ContextSelector=JNDI</p>
    -
    -    <p>Note that the value <code>JNDI</code> is a convenient shorthand
    -    for
    -    <code>ch.qos.logback.classic.selector.ContextJNDISelector</code>.</p>
    -
    -    <h3 class="doAnchor" name="settingJNDIVariables">Setting JNDI
    -    variables in applications</h3>
    -    
    -    <p>In each of your applications, you need to name the logging
    -    context for the application. For a web-application, JNDI
    -    environment entries are specified within the <em>web.xml</em>
    -    file. If "kenobi" was the name of your application, you would add
    -    the following XML element to kenobi's web.xml file:</p>
    -
    -    <pre class="prettyprint source">&lt;env-entry>
    -  &lt;env-entry-name>logback/context-name&lt;/env-entry-name>
    -  &lt;env-entry-type>java.lang.String&lt;/env-entry-type>
    -  &lt;env-entry-value>kenobi&lt;/env-entry-value>
    -&lt;/env-entry></pre>
    -
    -    <p>Assuming you have enabled <code>ContextJNDISelector</code>,
    -    logging for Kenobi will be done using a logger context named
    -    "kenobi". Moreover, the "kenobi" logger context will be
    -    initialized by <em>convention</em> by looking up the configuration
    -    file called <em>logback-kenobi.xml</em> as a <em>resource</em>
    -    using the thread context class loader. Thus, for example for the
    -    kenobi web-application, <em>logback-kenobi.xml</em> should be
    -    placed under the <em>WEB-INF/classes</em> folder.
    -    </p>
    -
    -    <p>If you wish to, you may specify a different configuration file
    -    other than the convention, by setting the
    -    "logback/configuration-resource" JNDI variable. For example, for
    -    the kenobi web-application, if you wish to specify
    -    <em>aFolder/my_config.xml</em> instead of the conventional
    -    <em>logback-kenobi.xml</em>, you would add the following XML
    -    element to web.xml
    -    </p>
    -
    -
    -   <pre class="prettyprint source">&lt;env-entry>
    -  &lt;env-entry-name>logback/configuration-resource&lt;/env-entry-name>
    -  &lt;env-entry-type>java.lang.String&lt;/env-entry-type>
    -  &lt;env-entry-value>aFolder/my_config.xml&lt;/env-entry-value>
    -&lt;/env-entry></pre> 
    -
    -    <p>The file <em>my_config.xml</em> should be placed under
    -    <em>WEB-INF/classes/aFolder/</em>. The important point to remember
    -    is that the configuration is looked up as a Java resource using
    -    the current thread's context class loader.
    -    </p>
    -    
    -
    -    <h3 class="doAnchor" name="jndiTomcat">Configuring Tomcat for
    -    ContextJNDISelector</h3>
    -
    -    <p>First, place the logback jars (that is
    -    logback-classic-${project.version}.jar,
    -    logback-core-${project.version}.jar and
    -    slf4j-api-${slf4j.version}.jar) in Tomcat's global (shared) class
    -    folder. In Tomcat 6.x, this directory is
    -    <em>$TOMCAT_HOME/lib/</em>.
    -    </p>
    -
    -    <p>The <em>logback.ContextSelector</em> system property can be set
    -    by adding the following line to the <em>catalina.sh</em> script,
    -    catalina.bat in Windows, found under <em>$TOMCAT_HOME/bin</em>
    -    folder.</p>
    -
    -    <p class="source">JAVA_OPTS="$JAVA_OPTS -Dlogback.ContextSelector=JNDI"</p>
    -
    -
    -    <h3 class="doAnchor" name="hotDeploy">Hot deploying
    -    applications</h3>
    -
    -    <p>When the web-application is recycled or shutdown, we strongly
    -    recommend that the incumbent <code>LoggerContext</code> be closed
    -    so that it can be properly garbage collected. Logback ships with a
    -    <code>ServletContextListener</code> called <a
    -    href="../xref/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.html"><code>ContextDetachingSCL</code></a>,
    -    designed specifically for detaching the
    -    <code>ContextSelector</code> instance associated with the older
    -    web-application instance. It can be installed by adding the
    -    following lines into your web-applications <em>web.xml</em>
    -    file.</p>
    -
    -    <pre class="prettyprint source">&lt;listener>
    -  &lt;listener-class>ch.qos.logback.classic.selector.servlet.ContextDetachingSCL&lt;/listener-class>
    -&lt;/listener></pre>
    -
    -    <p><span class="label notice">Note</span> Most containers invoke
    -    the <code>contextInitialized()</code> method of listeners in the
    -    order in which they are declared but invoke their
    -    <code>contextDestroyed()</code> method in reverse order. It
    -    follows that if you have multiple
    -    <code>ServletContextListener</code> declarations in your
    -    <em>web.xml</em>, then <code>ContextDetachingSCL</code> should be
    -    declared <em>first</em> so that its
    -    <code>contextDestroyed()</code> method is invoked <em>last</em>
    -    during application shutdown. </p>
    -
    -    <h3 class="doAnchor" name="betterPerf">Better performance</h3>
    -
    -    <p>When <code>ContextJNDISelector</code> is active, each time a
    -    logger is retrieved, a JNDI lookup must be performed. This can
    -    negatively impact performance, especially if you are using
    -    non-static (a.k.a. instance) logger references. Logback ships with a
    -    servlet filter named <a
    -    href="../xref/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.html">LoggerContextFilter</a>,
    -    specifically designed to avoid the JNDI lookup cost. It can
    -    be installed by adding the following lines to your application's
    -    web.xml file.</p>
    -
    - <pre class="prettyprint source">&lt;filter>
    -  &lt;filter-name>LoggerContextFilter&lt;/filter-name>
    -  &lt;filter-class>ch.qos.logback.classic.selector.servlet.LoggerContextFilter&lt;/filter-class>
    -&lt;/filter>
    -&lt;filter-mapping>
    -  &lt;filter-name>LoggerContextFilter&lt;/filter-name>
    -  &lt;url-pattern>/*&lt;/url-pattern>
    -&lt;/filter-mapping></pre>
    -
    -   <p>At the beginning of each http-request,
    -   <code>LoggerContextFilter</code> will obtain the logger context
    -   associated with the application and then place it in a
    -   <code>ThreadLocal</code> variable. <code>ContextJNDISelector</code>
    -   will first check if the <code>ThreadLocal</code> variable is
    -   set. If it is set, then JNDI lookup will skipped. Note that at the
    -   end of the http request, the <code>ThreadLocal</code> variable will
    -   be nulled.  Installing <code>LoggerContextFilter</code> improves
    -   logger retrieval performance by a wide margin.
    -   </p>
    -
    -   <p>Nulling the <code>ThreadLocal</code> variable allows garbage
    -   collection of the web-application when it is stopped or
    -   recycled.</p>
    -
    -
    -   <h2 class="doAnchor" name="tamingStaticRefs">Taming static
    -   references in shared libraries</h2>
    -
    -   <p><code>ContextJNDISelector</code> works nicely to create logging
    -   separation when SLF4J and logback artifacts are shared by all
    -   applications. When <code>ContextJNDISelector</code> is active, each
    -   call to <code>LoggerFactory.getLogger()</code> will return a logger
    -   belonging to a logger context associated with the calling/current
    -   application.</p>
    -
    -   <p>The common idiom for referencing a logger is via a static
    -   reference. For example,
    -   </p>
    -
    -   <pre class="prettyprint source">public class Foo {
    -  <b>static</b> Logger logger = LoggerFactory.getLogger(Foo.class);
    -  ...
    -}</pre>
    -
    -    <p>Static logger references are both memory and CPU
    -    efficient. Only one logger reference is used for all instances of
    -    the class. Moreover, the logger instance is retrieved only once,
    -    when the class is loaded into memory. If the host class belongs to
    -    some application, say kenobi, then the static logger will be
    -    attached to kenobi's logger context by virtue of
    -    <code>ContextJNDISelector</code>. Similarly, if the host class
    -    belongs to some other application, say yoda, then its static
    -    logger reference will be attached to yoda's logger context, again
    -    by virtue of <code>ContextJNDISelector</code>.
    -    </p>
    -    
    -    <p>If a class, say <code>Mustafar</code>, belongs to a library
    -    shared by both <em>kenobi</em> and <em>yoda</em>, as long as
    -    <code>Mustafar</code> has non static loggers, each invocation of
    -    <code>LoggerFactory.getLogger()</code> will return a logger
    -    belonging to a logger context associated with the calling/current
    -    application. But if <code>Mustafar</code> has a static logger
    -    reference, then its logger will be attached to the logger context of the
    -    application that calls it first. Thus,
    -    <code>ContextJNDISelector</code> does not provide logging
    -    separation in case of shared classes using static logger
    -    references. This corner case has eluded a solution for eons.</p>
    -
    -
    -    <p>The only way to solve this issue transparently and perfectly
    -    would be to introduce another level of indirection inside loggers
    -    so that each logger-shell somehow delegated work to an inner
    -    logger attached to the appropriate context. This approach would be
    -    quite difficult to implement and would incur a significant
    -    computational overhead. It is not an approach we plan to pursue.
    -    </p>
    -
    -    <p>It goes without saying that one could trivially solve the
    -    "shared class static logger" problem by moving the shared classes
    -    inside the web-apps (unshare them). If unsharing is not possible,
    -    then we can solicit the magical powers of <a
    -    href="appenders.html#SiftingAppender"><code>SiftingAppender</code></a>
    -    in order to separate logging using JNDI data as separation
    -    criteria.
    -    </p>
    -
    -    <p>Logback ships with a discriminator called <a
    -    href="../xref/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.html">JNDIBasedContextDiscriminator</a>
    -    which returns the name of the current logger context as computed
    -    by <code>ContextJNDISelector</code>. The
    -    <code>SiftingAppender</code> and
    -    <code>JNDIBasedContextDiscriminator</code> combination will create
    -    separate appenders for each web-application.
    -    </p>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
    -
    -  &lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    -    &lt;discriminator class="ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator">
    -      &lt;defaultValue>unknown&lt;/defaultValue>
    -    &lt;/discriminator>
    -    &lt;sift>
    -      &lt;appender name="FILE-${contextName}" class="ch.qos.logback.core.FileAppender">
    -        &lt;file><b>${contextName}.log</b>&lt;/file>
    -        &lt;encoder>
    -          &lt;pattern>%-50(%level %logger{35}) cn=%contextName - %msg%n&lt;/pattern>
    -         &lt;/encoder>
    -      &lt;/appender>
    -     &lt;/sift>
    -    &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SIFT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -   <p>If kenobi and yoda are web-applications, then the above
    -   configuration will output yoda's log output to <em>yoda.log</em>
    -   and kenobi's logs to <em>kenobi.log</em>; this even works for logs generated
    -   by static logger references located in shared classes.</p>
    - 
    -   <p>You can try out the technique just described with the help of the
    -   <a
    -   href="http://github.com/ceki/logback-starwars">logback-starwars</a>
    -   project.
    -   </p>
    - 
    - 
    -   <p>The above approach solves the logging separation problem but is
    -   rather complex. It requires the proper installation of
    -   <code>ContextJNDISelector</code> and mandates that appenders be
    -   wrapped by <code>SiftingAppender</code> which is a non-trivial beast
    -   in itself.
    -   </p>
    - 
    -   <p>Note that each logging context can be configured using the same
    -   file or alternatively different files. The choice is up to
    -   you. Instructing all contexts to use the same configuration file
    -   is simpler as only one file has to be maintained.  Maintaining a
    -   distinct configuration file for each application is harder to
    -   maintain but allows for more flexibility.</p>
    - 
    -   <p>So are we done yet? Can we declare victory and go home? Well, not
    -   quite.</p>
    - 
    -   <p>Let's assume the web-application <code>yoda</code> is initialized
    -   before <code>kenobi</code>. To initialize <code>yoda</code>, visit
    -   <code>http://localhost:port/yoda/servlet</code> which will invoke
    -   the <code>YodaServlet</code>. This servlet just says hello and logs
    -   message before calling the <code>foo</code> method in
    -   <code>Mustafar</code> which not-surprisingly logs a simple message
    -   and returns.
    -   </p>
    - 
    -   <p>After <code>YodaServlet</code> is called, the contents of
    -   <em>yoda.log</em> file should contain</p>
    - 
    -   <pre class="source">DEBUG ch.qos.starwars.yoda.YodaServlet             cn=yoda - in doGet()
    -DEBUG ch.qos.starwars.shared.Mustafar              cn=yoda - in foo()</pre>
    - 
    -   <p>Note how both log entries are associated with the "yoda" context
    -   name. At this stage and until the server stops, the
    -   <code>ch.qos.starwars.shared.Mustafar</code> logger is attached to
    -   the 'yoda' context and will remain so until the server is stopped.
    -   </p>
    -
    -   <p>Visiting <code>http://localhost:port/kenobi/servlet</code> will
    -   output the following in <em>kenobi.log</em>.</p>
    -
    -   <pre class="source">DEBUG ch.qos.starwars.kenobi.KenobiServlet          <b>cn=kenobi</b> - in doGet()
    -DEBUG ch.qos.starwars.shared.Mustafar               <b>cn=yoda</b> - in foo()</pre>
    -
    -   <p>Note that even if the
    -   <code>ch.qos.starwars.shared.Mustafar</code> logger outputs to
    -   <em>kenobi.log</em> it is still attached to 'yoda'. Thus, we have
    -   two distinct logging contexts logging to the same file, in this
    -   case <em>kenobi.log</em>. Each of these contexts reference
    -   <code>FileAppender</code> instances, nested within distinct
    -   <code>SiftingAppender</code> instances, that are logging to the same
    -   file. Although logging separation seems to function according to
    -   our wishes, FileAppender instances cannot safely write to the same
    -   file unless they enable <span class="option">prudent</span>
    -   mode. Otherwise, the target file will be corrupted.</p>
    -   
    -   <p>Here is the configuration file enabling prudent mode:</p>
    -
    -    <pre class="prettyprint source">&lt;configuration>
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />  
    -
    -  &lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender">
    -    &lt;discriminator class="ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator">
    -      &lt;defaultValue>unknown&lt;/defaultValue>
    -    &lt;/discriminator>
    -    &lt;sift>
    -      &lt;appender name="FILE-${contextName}" class="ch.qos.logback.core.FileAppender">
    -        &lt;file>${contextName}.log&lt;/file>
    -        <b>&lt;prudent>true&lt;/prudent></b>
    -        &lt;encoder>
    -          &lt;pattern>%-50(%level %logger{35}) cn=%contextName - %msg%n&lt;/pattern>
    -         &lt;/encoder>
    -      &lt;/appender>
    -     &lt;/sift>
    -    &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SIFT" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -
    -   <p>If you were able to keep up with the discussion thus far and
    -   have actually tried the logback-starwars examples, then you must be
    -   truly obsessed with logging. You should consider seeking <a
    -   href="http://www.qos.ch/shop/products/professionalSupport">professional
    -   help</a>.</p>
    -
    -
    -
    -   <script src="../templates/footer.js"
    -   type="text/javascript"></script> </div>
    -   
    -   </body> 
    -</html>
    diff --git a/logback-site/src/site/pages/manual/loggingSeparation_ja.html b/logback-site/src/site/pages/manual/loggingSeparation_ja.html
    deleted file mode 100644
    index b030b2815e..0000000000
    --- a/logback-site/src/site/pages/manual/loggingSeparation_ja.html
    +++ /dev/null
    @@ -1,270 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第9章 ログの分離</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">	
    -	
    -    <h1>第9章 ログの分離</h1>
    -      
    -    <div class="quote">
    -      <p><em>知識ではなくそれを学ぼうとする行為、所持することではなく得ようとする行為、それこそが最上の娯楽なのです。私は課題を研究し尽くしてしまうと、誰にも見つからないようにそこから離れてしまいます。私という決して満足できない人間は、何かを成し遂げた後平和裏に過ごすのではなく、すぐに違うことを始めてしまう奇人なのです。1つの国を征服したそばから、次の国に手を出そうとする、世界を征服するというのはこういうことなんだという確信があります。</em></p>
    -
    -      <p>-カール・フレデリッヒ・ガウス、1808年に記されたボヨイへの手紙</p>
    -
    -      <p><em>薄い絹のような洋服であってもほとんどの湿疹は隠れてしまう。</em></p>
    -      
    -      <p>-アルベール・カミュ、 <em>秋</em></p>
    -
    -    </div>
    -
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -    <h2>問題:ログの分離</h2>
    -
    -    <p>本章では、同じWebコンテナあるいはEJBコンテナ上で稼働する複数のアプリケーションで、ログを分離するにはどうすればいいのか検討します。文中で"application"という言葉を使う時は、WebアプリケーションかJ2EEアプリケーションのどちらかを表しています。分離されたログ環境では、それぞれのアプリケーションは独立したlogback環境になります。したがって、アプリケーションのlogbackの設定は、他の設定には影響しません。技術的な言葉で言うと、それぞれのWebアプリケーションは専用の独立した<code>LoggerContext</code>を使用します。logbackに話を戻します。<code>LoggerContext</code>に生成されたロガーオブジェクトは、メモリ上に残っている間はLoggerContextに割り当てられたままです。類似の問題として、アプリケーションのロギングとコンテナのロギングの分離、というものがあります。
    -    </p>
    -
    -    <h2 class="doAnchor" name="easy">一番簡単でわかりやすい方法</h2>
    -
    -    <p>コンテナが子孫優先クラスローディングをサポートしていることにしましょう。その場合 slf4j と logback の jar ファイルをアプリケーション自体に含めるようにすればログを分離することができます。Webアプリケーションなら<em>WEB-INF/lib</em>ディレクトリの下に置くだけで、アプリケーションごとのロギングを分離するには十分です。logbackがメモリにロードされるとき、<em>WEB-INF/classes</em>に置かれた<em>logback.xml</em>のコピーが選ばれます。
    -    </p>
    -
    -    <p>コンテナがクラスローダーを別々にしてくれるので、Webアプリケーションは自分の<code>LoggerContext</code>に自分の<em>logback.xml</em>を選ばせることができます。</p>
    -    
    -    <p>朝飯前です。</p>
    -
    -    <p>少し正確さにかけますね・・・SLF4Jとlogbackを全てのアプリケーションからアクセスできる場所に置かなければならないこともあるでしょう。共有ライブラリがSLF4Jを使っている場合などです。そうすると、全てのアプリケーションは同じlogback環境を共有することになってしまいます。他にも、全てのアプリケーションから参照できる場所にSLF4Jとlogbackを置かなければならないような状況はあるでしょう。そして、それはクラスローダー分離に基づくログの分離を不可能にしてしまいます。絶望しなくてもいいですよ。まだ希望は残されています。読み進めてください。
    -    </p>
    -
    -    <h2 class="doAnchor" name="contextSelectors">コンテキストセレクタ</h2>
    -
    -    <p>logbackにはメモリにロードされている単一のインスタンスによって複数のLoggerContextを扱う仕組みがあります。次のように書いたとしましょう。</p>
    -
    -    <pre class="prettyprint source">Logger logger = LoggerFactory.getLogger("foo");</pre>
    -
    -    <p><code>LoggerFactory</code>の<code>getLogger()</code>メソッドは、SLF4Jのバインディングに<code>ILoggerFactory</code>を問い合わせます。SLF4Jがlogbackにバインドされているときは、<code>ILoggerFactory</code>を返すというタスクが、<a href="http://logback.qos.ch/apidocs/ch/qos/logback/classic/selector/ContextSelector.html">ContextSelector</a>に委譲されるのです。<code>ContextSelector</code>は常に<code>LoggerContext</code>のインスタンスを返すので気をつけておきましょう。ContextSelectorは、<code>ILoggerFactory</code>インターフェイスを実装しています。言い換えると、コンテキストセレクタは独自の基準に従って<code>LoggerContext</code>のインスタンスを返すことができるのです。だから<em>コンテキストセレクタ</em>という名前なのです。
    -    </p>
    -
    -    <p>デフォルトでは、logbackバインディングは<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/selector/DefaultContextSelector.html">DefaultContextSelector</a>を使います。これは、デフォルトのロガーコンテキストと呼ばれる常に同じ<code>LoggerContext</code>を返します。</p>
    -
    -    <p>システムプロパティの<em>logback.ContextSelector</em>を使うと、別のコンテキストセレクタを指定することができます。コンテキストセレクタに<code>myPackage.myContextSelector</code>を指定したければ、次のように指定すればよいのです。</p>
    -
    -    <p class="source">-Dlogback.ContextSelector=myPackage.myContextSelector</p>
    -
    -    <p>コンテキストセレクタは、<code>ContextSelector</code>インターフェイスを実装しなければなりません。そして、<code>LoggerContext</code>を唯一の引数とするコンストラクタが必要です。
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="ContextJNDISelector">ContextJNDISelector</h3>
    -
    -    <p>logback-classic の配布物には<code>ContextJNDISelector</code>というコンテキストセレクタが含まれています。これは JNDI を介して参照できるロガーコンテキストを選択できるようにするものです。これは J2EE の仕様にある JNDI のデータ分離に基づいています。したがって、同じ環境変数を使って、アプリケーションごとに異なる値を渡すことが出来るようになります。言い換えると、単独のLoggerFactoryを全てのアプリケーションで共有している状態でも、それぞれのアプリケーションによる<code>LoggerFactory.getLogger()</code>の呼び出しに対して、独立したロガーコンテキストに割り当てられたロガーを返すようになるのです。これでログを分離することができるでしょう。
    -    </p>
    -
    -    <p><code>ContextJNDISelector</code>を有効化するには、システムプロパティの<em>logback.ContextSelector</em>に"JNDI"を指定します。</p>
    -
    -    <p class="source">-Dlogback.ContextSelector=JNDI</p>
    -
    -    <p><code>JNDI</code>という値は、<code>ch.qos.logback.classic.selector.ContextJNDISelector</code>の省略形です。</p>
    -
    -    <h3 class="doAnchor" name="settingJNDIVariables">JNDI変数の設定</h3>
    -    
    -    <p>それぞれのアプリケーションが、専用のコンテキスト名を設定しなければなりません。Webアプリケーションの場合、JNDI環境変数は<em>web.xml</em>で指定します。アプリケーション名が "knobi" なら、web.xml に次のような設定を追加すればよいでしょう。</p>
    -
    -    <pre class="prettyprint source">&lt;env-entry&gt;
    -  &lt;env-entry-name&gt;logback/context-name&lt;/env-entry-name&gt;
    -  &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
    -  &lt;env-entry-value&gt;kenobi&lt;/env-entry-value&gt;
    -&lt;/env-entry&gt;</pre>
    -
    -    <p><code>ContextJNDISelector</code>が有効になっているとしたら、kenobiアプリケーションのロガーは"kenobi"ロガーコンテキストに割り当てられたものになっているでしょう。また、"kenobi"ロガーコンテキストの設定ファイルは、<em>命名規約</em>にしたがって <em>logback-kenobi.xml</em>という名前になります。スレッドコンテキストクラスローダーの<em>リソース</em>から見つけて初期化に使われます。つまり、kenobiアプリケーションの<em>WEB-INF/classes</em>の下に<em>logback-kenobi.xml</em>を配置しておかなければなりません。
    -    </p>
    -
    -    <p>"logback/configuration-resource" という JNDI 環境変数を使えば、命名規約に関わらず他の名前の設定ファイルを使うこともできます。例えば、kenobiアプリケーションで命名規約に従った<em>logback-kenobi.xml</em>ではなく<em>aFolder/my_config.xml</em>を使うとしたら、次のような設定をweb.xmlに追加することになります。</p>
    -
    -
    -   <pre class="prettyprint source">&lt;env-entry&gt;
    -  &lt;env-entry-name&gt;logback/configuration-resource&lt;/env-entry-name&gt;
    -  &lt;env-entry-type&gt;java.lang.String&lt;/env-entry-type&gt;
    -  &lt;env-entry-value&gt;aFolder/my_config.xml&lt;/env-entry-value&gt;
    -&lt;/env-entry&gt;</pre> 
    -
    -    <p><em>my_config.xml</em>は、<em>WEB-INF/classes/aFolder/</em>の下に置かなければなりません。設定ファイルは、現在のスレッドのコンテキストクラスローダーによってJavaのリソースと同じように探索されるということを覚えておいてください。
    -    </p>
    -    
    -
    -    <h3 class="doAnchor" name="jndiTomcat">ContextJNDISelectorを使うためのTomcatの設定</h3>
    -
    -    <p>まず、logback.jar(logback-classic-1.1.2.jar logback-core-1.1.1.jar slf4j-api-1.7.6.jar)をTomcatのグローバルな共有クラスフォルダ(shared)に置きましょう。Tomcat 6.x の場合は、<em>$TOMCAT_HOME/lib</em> の下に置いてください。
    -    </p>
    -
    -    <p>そして、<em>$TOMCAT_HOME/bin/catalina.sh(Windows の場合は catalina.bat)</em>の適切な場所で、次のようにシステムプロパティの<em>logback.ContextSelector</em>を設定してください。</p>
    -
    -    <p class="source">JAVA_OPTS="$JAVA_OPTS -Dlogback.ContextSelector=JNDI"</p>
    -
    -
    -    <h3 class="doAnchor" name="hotDeploy">ホットデプロイ</h3>
    -
    -    <p>アプリケーションがリサイクルされるとき、あるいは、シャットダウンするとき、利用中の<code>LoggerContext</code>はクローズしましょう。確実にガベージコレクションされるためには欠かせません。声を大にしておすすめします。logbackの配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/selector/servlet/ContextDetachingSCL.html"><code>ContextDetachingSCL</code></a>という<code>ServletContextListener</code>が含まれています。これは古いアプリケーションのインスタンスに関連付けられている<code>ContextSelector</code>をデタッチするためだけに用意されたものです。<em>web.xml</em>に次のような設定を追加すれば有効になります。</p>
    -
    -    <pre class="prettyprint source">&lt;listener&gt;
    -  &lt;listener-class&gt;ch.qos.logback.classic.selector.servlet.ContextDetachingSCL&lt;/listener-class&gt;
    -&lt;/listener&gt;</pre>
    -
    -    <p>ほぼ全てのコンテナ実装は、web.xml に記載された順番でリスナーの<code>contextInitialized()</code>メソッドを呼び出します。ですが、<code>contextDestroyed()</code>メソッドの呼び出される順番は記載された順番の<span class="label notice">逆順</span>です。つまり、<em>web.xml</em>に複数の<code>ServletContextListener</code>を宣言している場合は、<code>ContextDetachingSCL</code>を<em>先頭</em>で宣言しなければならないということです。そうすれば、<code>contextDestroyed()</code>メソッドは<em>一番最後</em>に呼び出されるようになります。</p>
    -
    -    <h3 class="doAnchor" name="betterPerf">パフォーマンスの改善</h3>
    -
    -    <p><code>ContextJNDISelector</code>が有効になっていると、ロガーを取得するたびにJNDIの検索が行われるようになります。このことが、性能に悪影響を及ぼす可能性があります。特に静的変数ではない(インスタンス変数の)ロガーを使っている場合はその可能性は高まります。logbackの配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/selector/servlet/LoggerContextFilter.html">LoggerContextFilter</a>というサーブレットフィルターが含まれています。これはJNDIの検索にかかるコストを解消するために設計されたものです。web.xmlに次のような設定を追加すれば有効になります。
    -</p>
    -
    - <pre class="prettyprint source">&lt;filter&gt;
    -  &lt;filter-name&gt;LoggerContextFilter&lt;/filter-name&gt;
    -  &lt;filter-class&gt;ch.qos.logback.classic.selector.servlet.LoggerContextFilter&lt;/filter-class&gt;
    -&lt;/filter&gt;
    -&lt;filter-mapping&gt;
    -  &lt;filter-name&gt;LoggerContextFilter&lt;/filter-name&gt;
    -  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    -&lt;/filter-mapping&gt;</pre>
    -
    -   <p>HTTPリクエストを受け付けたら、まず初めに<code>LoggerContextFilter</code>はアプリケーションに関連付けられたロガーコンテキストを取得して、<code>ThreadLocal</code>に保存します。<code>ContextJNDISelector</code>は、最初に<code>ThreadLocal</code>変数が設定されているかどうかをチェックします。設定されているときは、JNDIの検索をスキップします。HTTPリクエストの処理の最後に、<code>ThreadLocal</code>変数はnullにされるので気をつけてください。<code>LoggerContextFilter</code>を使うようにすると、ロギングの性能は大幅に改善します。
    -   </p>
    -
    -   <p><code>ThreadLocal</code>の変数はnullになるので、アプリケーションがリサイクルされるときや停止するときのガベージコレクションでちゃんと回収されます。</p>
    -
    -
    -   <h2 class="doAnchor" name="tamingStaticRefs">共有ライブラリ内部の静的参照を飼いならす</h2>
    -
    -   <p>全てのアプリケーションでSLF4Jとlogbackを共有しているとき、<code>ContextJNDISelector</code>を使うとうまくログを分離できます。<code>ContextJNDISelector</code>が有効なら、それぞれのアプリケーションの<code>LoggerFactory.getLogger()</code>メソッドの呼び出しは、アプリケーションに関連付けられたロガーコンテキストに割り当てられたロガーを返してくれます。</p>
    -
    -   <p>静的変数としてロガーを参照するのが一般的なイディオムです。</p>
    -
    -   <pre class="prettyprint source">public class Foo {
    -  <b>static</b> Logger logger = LoggerFactory.getLogger(Foo.class);
    -  ...
    -}</pre>
    -
    -    <p>静的変数でロガーを参照するのは、メモリとCPUのどちらにとっても効率的です。そのクラスの全てのインスタンスは、ただ一つの参照を使いまわせるからです。また、ロガーのインスタンスを取得するのは、クラスがメモリにロードされたときだけです。ロガー静的変数を持っているクラスが kenobi アプリケーションに含まれているなら、そのロガーのインスタンスは<code>ContextJNDISelector</code>から取得したkenobi用のロガーコンテキストに割り当てられることになります。同様に、別のアプリケーション yoda に含まれているクラスのロガーインスタンスは、やはり<code>ContextJNDISelector</code>から取得した yoda用のロガーコンテキストに割り当てられることになります。
    -    </p>
    -    
    -    <p>もし<em>kenobi</em>アプリケーションと<em>yoda</em>アプリケーションの両方で共有しているライブラリに含まれる<code>Mustafar</code>クラスだったら、ただし<code>Mustafar</code>クラスではインスタンス変数やローカル変数でロガーを参照していたらどうなるでしょうか。<code>LoggerFactory.getLogger()</code>メソッドは、それが呼ばれたアプリケーションに関連付けられたロガーコンテキストに割り当てられたロガーを返してくれるでしょう。しかし、<code>Mustafar</code>クラスが静的変数でロガーを参照していたらどうなるでしょう。その場合、最初にそのクラスを参照したアプリケーションに関連付けられたロガーコンテキストに割り当てられたロガーになってしまいます。つまり、<code>ContextJNDISelector</code>は、共有ライブラリのクラスが保持する静的変数のロガーについては、ログの分離ができないのです。このような状況は特に限定的なもので、長い間解決策が望まれつつも今に至ります。</p>
    -
    -
    -    <p>この問題を文句のつけようもないくらいに完全に解決するには、別の観点でロガーの内部事情に切り込まなければなりません。たとえば、ロガーは実際の仕事をするロガーを内包するものとして、ロガーに対するメッセージは内包するロガーに委譲するようにします。そして、内包されたロガーは適切なロガーコンテキストを参照するようにするのです。このアプローチは実装が大変だし、オーバーヘッドが非常に高くなってしまうでしょう。取りかかる気にはなれません。
    -    </p>
    -
    -    <p>クラスの共有を止めて、アプリケーションの内部に移動してしまうのが、"共有クラスの静的ロガー"問題に対するお手軽な解決方法の1つであることは言うまでもありません。共有しないようにするのが不可能なら、<a href="http://logback.qos.ch/manual/appenders.html#SiftingAppender"><code>SiftingAppender</code></a>の魔法を使ってログの分離を実現するしかないでしょう。
    -    </p>
    -
    -    <p>logbackの配布物には<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/sift/JNDIBasedContextDiscriminator.html">JNDIBasedContextDiscriminator</a>という弁別器が含まれています。これは<code>ContextJNDISelector</code>の返すロガーコンテキストの名前を返すものです。<code>SiftingAppender</code>と<code>JNDIBasedContextDiscriminator</code>を組み合わせると、アプリケーションごとに別々のアペンダーインスタンスを生成することができます。
    -    </p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;  
    -
    -  &lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"&gt;
    -    &lt;discriminator class="ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator"&gt;
    -      &lt;defaultValue&gt;unknown&lt;/defaultValue&gt;
    -    &lt;/discriminator&gt;
    -    &lt;sift&gt;
    -      &lt;appender name="FILE-${contextName}" class="ch.qos.logback.core.FileAppender"&gt;
    -        &lt;file&gt;<b>${contextName}.log</b>&lt;/file&gt;
    -        &lt;encoder&gt;
    -          &lt;pattern&gt;%-50(%level %logger{35}) cn=%contextName - %msg%n&lt;/pattern&gt;
    -         &lt;/encoder&gt;
    -      &lt;/appender&gt;
    -     &lt;/sift&gt;
    -    &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SIFT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <p>kenobi と yoda というWebアプリケーションがあるとして、この設定ファイルを使うと yoda のログは <em>yoda.log</em>に、kenobi のログは<em>kenobi.log</em>に出力されるようになります。これは共有クラスの静的ロガーでも有効です。</p>
    - 
    -   <p><a href="http://github.com/ceki/logback-starwars">logback-starwars</a>プロジェクトを使えば、ここで説明したテクニックを実際に試してみることができます。
    -   </p>
    - 
    - 
    -   <p>上記のアプローチは、ログの分離問題を解決できますがかなり複雑です。適切な<code>ContextJNDISelector</code>の設定や、アペンダーを<code>SiftingAppender</code>でラップするこは、手傷を負った猛獣のように手が付けられません。
    -   </p>
    - 
    -   <p>それぞれのロギングコンテキストは、同じ設定ファイルでも、それぞれ別の設定ファイルでも設定できることを忘れないようにしましょう。選ぶのはあなたです。コンテキストごとに設定ファイルをメンテナンスするよりは、全て同じ設定ファイルを使うようにするほうが簡単です。アプリケーションごとに設定ファイルをメンテナンスするのは大変ですが、柔軟性はあります。</p>
    - 
    -   <p>これで終わりでしょうか?勝どきを上げて、気持ちよく我が家に帰れるのでしょうか?実はまだ続くんです。</p>
    - 
    -   <p><code>yoda</code>が<code>kenobi</code>より前に初期化されるとしましょう。<code>yoda</code>を初期化するため、<code>http://localhost:port/yoda/servlet</code>を参照して<code>YodaServlet</code>を呼び出します。このサーブレットは単に挨拶を返すだけです。ただし、ログを取得してから<code>Mustafar</code>の<code>foo()</code>メソッドを呼び出します。このメソッドは単純にログを取得するだけのものです。
    -   </p>
    - 
    -   <p><code>YodaServlet</code>が呼び出された後、<em>yoda.log</em>の内容は次のようになります。</p>
    - 
    -   <pre class="source">DEBUG ch.qos.starwars.yoda.YodaServlet             cn=yoda - in doGet()
    -DEBUG ch.qos.starwars.shared.Mustafar              cn=yoda - in foo()</pre>
    - 
    -   <p>どちらのログも、"yoda"アプリケーションに関連付けられたロガーコンテキストによるものであることが分かりますか。ここからサーバーが停止するまで、<code>ch.qos.starwars.shared.Mustafar</code>のロガーは、"yoda"コンテキストに割り当てられたものになります。
    -   </p>
    -
    -   <p><code>http://localhost:port/kenobi/servlet</code>にアクセスした後の<em>kenobi.log</em>の内容は次のようになります。</p>
    -
    -   <pre class="source">DEBUG ch.qos.starwars.kenobi.KenobiServlet          <b>cn=kenobi</b> - in doGet()
    -DEBUG ch.qos.starwars.shared.Mustafar               <b>cn=yoda</b> - in foo()</pre>
    -
    -   <p>なんと、<code>ch.qos.starwars.shared.Mustafar</code>のロガーは<em>kenobi.log</em>に出力しているのに、"yoda"コンテキストに割り当てられているロガーのままです。つまり、別のロガーコンテキストに割り当てられたロガーが同じファイル(この場合は<em>kenobi.log</em>)に出力してしまうのです。それぞれのコンテキストが参照している<code>FileAppender</code>のインスタンスは、別々の<code>SiftingAppender</code>のインスタンスに内包されたもので、同じファイルにログを出力しています。ログの分離は思った通りに機能しているように見えますが、<span class="option">prudent</span>モードを有効化しない限り、FileAppender は同じファイルへのログの書き込みを安全に行うことができません。そうしないとファイルの内容が壊れてしまいます。</p>
    -   
    -   <p>prudent モードを有効にした設定ファイルを見てください。</p>
    -
    -    <pre class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;  
    -
    -  &lt;appender name="SIFT" class="ch.qos.logback.classic.sift.SiftingAppender"&gt;
    -    &lt;discriminator class="ch.qos.logback.classic.sift.JNDIBasedContextDiscriminator"&gt;
    -      &lt;defaultValue&gt;unknown&lt;/defaultValue&gt;
    -    &lt;/discriminator&gt;
    -    &lt;sift&gt;
    -      &lt;appender name="FILE-${contextName}" class="ch.qos.logback.core.FileAppender"&gt;
    -        &lt;file&gt;${contextName}.log&lt;/file&gt;
    -        <b>&lt;prudent&gt;true&lt;/prudent&gt;</b>
    -        &lt;encoder&gt;
    -          &lt;pattern&gt;%-50(%level %logger{35}) cn=%contextName - %msg%n&lt;/pattern&gt;
    -         &lt;/encoder&gt;
    -      &lt;/appender&gt;
    -     &lt;/sift&gt;
    -    &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SIFT" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -
    -   <p>ここまでの議論を読み解いてきて、その上で logback-starwars プロジェクトを試してみたのなら、きっともうロギングのことが頭から離れなくなっていると思います。これ以上のことは<a href="http://www.qos.ch/shop/products/professionalSupport">専門家の助け</a>を借りるしかないでしょう。</p>
    -
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -   
    -   </body> 
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/mdc.html b/logback-site/src/site/pages/manual/mdc.html
    deleted file mode 100755
    index 9edee9802b..0000000000
    --- a/logback-site/src/site/pages/manual/mdc.html
    +++ /dev/null
    @@ -1,759 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 8: Mapped Diagnostic Context</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>Chapter 8: Mapped Diagnostic Context</h1>
    -
    -    <a href="mdc_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <div class="quote">     
    -      <p><em>Lock the doors.</em></p>
    -      <p>&mdash;LEROY CAIN, Flight Director, Columbia Mission Control</p>
    -    </div>
    -    
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    
    -		<p>One of the design goals of logback is to audit and debug
    -		complex distributed applications.  Most real-world distributed
    -		systems need to deal with multiple clients simultaneously.  In a
    -		typical multithreaded implementation of such a system, different
    -		threads will handle different clients. A possible but slightly
    -		discouraged approach to differentiate the logging output of one
    -		client from another consists of instantiating a new and separate
    -		logger for each client.  This technique promotes the proliferation
    -		of loggers and may increase their management overhead.
    -		</p>
    -		
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -    
    -    <p>A lighter technique consists of uniquely stamping each log
    -   	request servicing a given client. Neil Harrison described this
    -   	method in the book <em>Patterns for Logging Diagnostic
    -   	Messages</em> in Pattern Languages of Program Design 3, edited by
    -   	R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley,
    -   	1997). Logback leverages a variant of this technique included in
    -   	the SLF4J API: Mapped Diagnostic Contexts (MDC).
    -		</p>
    -		
    -		<p>To uniquely stamp each request, the user puts contextual
    -		information into the <code>MDC</code>, the abbreviation of Mapped
    -		Diagnostic Context.  The salient parts of the MDC class are shown
    -		below. Please refer to the <a
    -		href="http://www.slf4j.org/api/org/slf4j/MDC.html">MDC
    -		javadocs</a> for a complete list of methods.
    -		</p>
    -
    -<pre class="prettyprint source">package org.slf4j;
    -
    -public class MDC {
    -  //Put a context value as identified by <em>key</em>
    -  //into the current thread's context map.
    -  <b>public static void put(String key, String val);</b>
    -
    -  //Get the context identified by the <code>key</code> parameter.
    -  <b>public static String get(String key);</b>
    -
    -  //Remove the context identified by the <code>key</code> parameter.
    -  <b>public static void remove(String key);</b>
    -
    -  //Clear all entries in the MDC.
    -  <b>public static void clear();</b>
    -}</pre>
    -
    -		<p>The <code>MDC</code> class contains only static methods.  It
    -		lets the developer place information in a <em>diagnostic
    -		context</em> that can be subsequently retrieved by certain logback
    -		components. The <code>MDC</code> manages contextual information on
    -		a <em>per thread basis</em>.  Typically, while starting to service
    -		a new client request, the developer will insert pertinent
    -		contextual information, such as the client id, client's IP
    -		address, request parameters etc. into the
    -		<code>MDC</code>. Logback components, if appropriately configured,
    -		will automatically include this information in each log entry.
    -		</p>
    -
    -    <p>Please note that MDC as implemented by logback-classic assumes
    -    that values are placed into the MDC with moderate frequency.  Also
    -    note that a child thread does not automatically inherit a copy of
    -    the mapped diagnostic context of its parent.</p>
    -
    -		<p>
    -			The next application named 
    -			<code><a href="../xref/chapters/mdc/SimpleMDC.html">SimpleMDC</a></code> 
    -			demonstrates this basic principle.
    -		</p>
    -<em>Example 7.1: Basic MDC usage (<a href="../xref/chapters/mdc/SimpleMDC.html">
    -logback-examples/src/main/java/chapters/mdc/SimpleMDC.java)</a></em>
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -import org.slf4j.MDC;
    -
    -import ch.qos.logback.classic.PatternLayout;
    -import ch.qos.logback.core.ConsoleAppender;
    -
    -public class SimpleMDC {
    -  static public void main(String[] args) throws Exception {
    -
    -    // You can put values in the MDC at any time. Before anything else
    -    // we put the first name
    -    MDC.put("first", "Dorothy");
    -
    -    [ SNIP ]
    -    
    -    Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    -    // We now put the last name
    -    MDC.put("last", "Parker");
    -
    -    // The most beautiful two words in the English language according
    -    // to Dorothy Parker:
    -    logger.info("Check enclosed.");
    -    logger.debug("The most beautiful two words in English.");
    -
    -    MDC.put("first", "Richard");
    -    MDC.put("last", "Nixon");
    -    logger.info("I am not a crook.");
    -    logger.info("Attributed to the former US president. 17 Nov 1973.");
    -  }
    -
    -  [ SNIP ]
    -
    -}</pre>
    -
    -		<p>The main method starts by associating the value
    -		<em>Dorothy</em> with the key <em>first</em> in the
    -		<code>MDC</code>. You can place as many value/key associations in
    -		the <code>MDC</code> as you wish.  Multiple insertions with the
    -		same key will overwrite older values.  The code then proceeds to
    -		configure logback.</p>
    -
    -    <p>For the sake of conciseness, we have the omitted the code that
    -    configures logback with the configuration file <a
    -    href="http://github.com/qos-ch/logback/blob/master/logback-examples/src/main/java/chapters/mdc/simpleMDC.xml">simpleMDC.xml</a>. Here
    -    is the relevant section from that file.
    -    </p>
    -
    - <pre class="prettyprint source">&lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> 
    -  &lt;layout>
    -    &lt;Pattern><b>%X{first} %X{last}</b> - %m%n&lt;/Pattern>
    -  &lt;/layout> 
    -&lt;/appender></pre>
    -    
    -
    -
    -    <p>Note the usage of the <em>%X</em> specifier within the
    -    <code>PatternLayout</code> conversion pattern. The <em>%X</em>
    -    conversion specifier is employed twice, once for the key named
    -    <em>first</em> and once for the key named <em>last</em>. After
    -    obtaining a logger corresponding to <code>SimpleMDC.class</code>,
    -    the code associates the value <em>Parker</em> with the key named
    -    <em>last</em>.  It then invokes the logger twice with different
    -    messages.  The code finishes by setting the <code>MDC</code> to
    -    different values and issuing several logging requests. Running
    -    SimpleMDC yields:
    -		</p>
    -
    -<div class="source"><pre>Dorothy Parker - Check enclosed.
    -Dorothy Parker - The most beautiful two words in English.
    -Richard Nixon - I am not a crook.
    -Richard Nixon - Attributed to the former US president. 17 Nov 1973.</pre></div>
    -
    -
    -		<p>The <code>SimpleMDC</code> application illustrates how logback
    -		layouts, if configured appropriately, can automatically output
    -		<code>MDC</code> information.  Moreover, the information placed
    -		into the <code>MDC</code> can be used by multiple logger
    -		invocations.
    -		</p>
    -		
    -		<h3 class="doAnchor">Advanced Use</h3>
    -		
    -		<p>Mapped Diagnostic Contexts shine brightest within client server
    -		architectures.  Typically, multiple clients will be served by
    -		multiple threads on the server.  Although the methods in the
    -		<code>MDC</code> class are static, the diagnostic context is
    -		managed on a per thread basis, allowing each server thread to bear
    -		a distinct <code>MDC</code> stamp. <code>MDC</code> operations
    -		such as <code>put()</code> and <code>get()</code> affect only the
    -		<code>MDC</code> of the <em>current</em> thread, and the children
    -		of the current thread. The <code>MDC</code> in other threads
    -		remain unaffected. Given that <code>MDC</code> information is
    -		managed on a per thread basis, each thread will have its own copy
    -		of the <code>MDC</code>.  Thus, there is no need for the developer
    -		to worry about thread-safety or synchronization when programming
    -		with the <code>MDC</code> because it handles these issues safely
    -		and transparently.
    -		</p>
    -
    -		<p>The next example is somewhat more advanced.  It shows how the
    -		<code>MDC</code> can be used in a client-server setting.  The
    -		server-side implements the <code>NumberCruncher</code> interface
    -		shown in Example 7.2 below. <code>The NumberCruncher</code>
    -		interface contains a single method named
    -		<code>factor()</code>. Using RMI technology, the client invokes
    -		the <code>factor()</code> method of the server application to
    -		retrieve the distinct factors of an integer.
    -		</p>
    -
    -<em>Example 7.2: The service interface (<a href="../xref/chapters/mdc/NumberCruncher.html">
    -logback-examples/src/main/java/chapters/mdc/NumberCruncher.java)</a></em>
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.rmi.Remote;
    -import java.rmi.RemoteException;
    -
    -/**
    - * NumberCruncher factors positive integers.
    - */
    -public interface NumberCruncher extends Remote {
    -  /**
    -   * Factor a positive integer <code>number</code> and return its
    -   * <em>distinct</em> factor's as an integer array.
    -   * */
    -  int[] factor(int number) throws RemoteException;
    -}</pre>
    -
    -		<p>
    -			The <code>NumberCruncherServer</code> application, listed in Example 7.3 below, 
    -			implements the <code>NumberCruncher</code> interface. Its main method exports 
    -			an RMI Registry on the local host that accepts requests on a well-known port.  
    -		</p>
    -
    -<em>Example 7.3: The server side (<a href="../xref/chapters/mdc/NumberCruncherServer.html">
    -logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java)</a></em>
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.rmi.RemoteException;
    -import java.rmi.registry.LocateRegistry;
    -import java.rmi.registry.Registry;
    -import java.rmi.server.UnicastRemoteObject;
    -import java.util.Vector;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -import org.slf4j.MDC;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -
    -
    -/**
    - * A simple NumberCruncher implementation that logs its progress when
    - * factoring numbers. The purpose of the whole exercise is to show the
    - * use of mapped diagnostic contexts in order to distinguish the log
    - * output from different client requests.
    - * */
    -public class NumberCruncherServer extends UnicastRemoteObject
    -  implements NumberCruncher {
    -
    -  private static final long serialVersionUID = 1L;
    -
    -  static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
    -
    -  public NumberCruncherServer() throws RemoteException {
    -  }
    -
    -  public int[] factor(int number) throws RemoteException {
    -    // The client's host is an important source of information.
    -    try {
    -      <b>MDC.put("client", NumberCruncherServer.getClientHost());</b>
    -    } catch (java.rmi.server.ServerNotActiveException e) {
    -      logger.warn("Caught unexpected ServerNotActiveException.", e);
    -    }
    -
    -    // The information contained within the request is another source
    -    // of distinctive information. It might reveal the users name,
    -    // date of request, request ID etc. In servlet type environments,
    -    // useful information is contained in the HttpRequest or in the  
    -    // HttpSession.
    -    <b>MDC.put("number", String.valueOf(number));</b>
    -
    -    logger.info("Beginning to factor.");
    -
    -    if (number &lt;= 0) {
    -      throw new IllegalArgumentException(number +
    -        " is not a positive integer.");
    -    } else if (number == 1) {
    -      return new int[] { 1 };
    -    }
    -
    -    Vector&lt;Integer> factors = new Vector&lt;Integer>();
    -    int n = number;
    -
    -    for (int i = 2; (i &lt;= n) &amp;&amp; ((i * i) &lt;= number); i++) {
    -      // It is bad practice to place log requests within tight loops.
    -      // It is done here to show interleaved log output from
    -      // different requests. 
    -      logger.debug("Trying " + i + " as a factor.");
    -
    -      if ((n % i) == 0) {
    -        logger.info("Found factor " + i);
    -        factors.addElement(new Integer(i));
    -
    -        do {
    -          n /= i;
    -        } while ((n % i) == 0);
    -      }
    -
    -      // Placing artificial delays in tight loops will also lead to
    -      // sub-optimal results. :-)
    -      delay(100);
    -    }
    -
    -    if (n != 1) {
    -      logger.info("Found factor " + n);
    -      factors.addElement(new Integer(n));
    -    }
    -
    -    int len = factors.size();
    -
    -    int[] result = new int[len];
    -
    -    for (int i = 0; i &lt; len; i++) {
    -      result[i] = ((Integer) factors.elementAt(i)).intValue();
    -    }
    -
    -    <b>// clean up
    -    MDC.remove("client");
    -    MDC.remove("number");</b>
    -
    -    return result;
    -  }
    -
    -  static void usage(String msg) {
    -    System.err.println(msg);
    -    System.err.println("Usage: java chapters.mdc.NumberCruncherServer configFile\n" +
    -      "   where configFile is a logback configuration file.");
    -    System.exit(1);
    -  }
    -
    -  public static void delay(int millis) {
    -    try {
    -      Thread.sleep(millis);
    -    } catch (InterruptedException e) {
    -    }
    -  }
    -
    -  public static void main(String[] args) {
    -    if (args.length != 1) {
    -      usage("Wrong number of arguments.");
    -    }
    -
    -    String configFile = args[0];
    -
    -    if (configFile.endsWith(".xml")) {
    -      try {
    -        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -        JoranConfigurator configurator = new JoranConfigurator();
    -        configurator.setContext(lc);
    -        lc.reset();
    -        configurator.doConfigure(args[0]);
    -      } catch (JoranException je) {
    -        je.printStackTrace();
    -      }
    -    }
    -
    -    NumberCruncherServer ncs;
    -
    -    try {
    -      ncs = new NumberCruncherServer();
    -      logger.info("Creating registry.");
    -
    -      Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
    -      registry.rebind("Factor", ncs);
    -      logger.info("NumberCruncherServer bound and ready.");
    -    } catch (Exception e) {
    -      logger.error("Could not bind NumberCruncherServer.", e);
    -
    -      return;
    -    }
    -  }
    -}</pre>
    -
    -		<p>The implementation of the <code>factor(int number)</code>
    -		method is of particular relevance. It starts by putting the
    -		client's hostname into the <code>MDC</code> under the key
    -		<em>client</em>. The number to factor, as requested by the client,
    -		is put into the <code>MDC</code> under the key
    -		<em>number</em>. After computing the distinct factors of the
    -		integer parameter, the result is returned to the client. Before
    -		returning the result however, the values for the <em>client</em>
    -		and <em>number</em> are cleared by calling the
    -		<code>MDC.remove()</code> method. Normally, a <code>put()</code>
    -		operation should be balanced by the corresponding
    -		<code>remove()</code> operation. Otherwise, the <code>MDC</code>
    -		will contain stale values for certain keys. We would recommend
    -		that whenever possible, <code>remove()</code> operations be
    -		performed within finally blocks, ensuring their invocation
    -		regardless of the execution path of the code.
    -		</p>	
    -		
    -		<p>
    -			After these theoretical explanations, we are ready to run the number 
    -			cruncher example. Start the server with the following command:
    -		</p>
    -		
    -<div class="source"><pre>java chapters.mdc.NumberCruncherServer src/main/java/chapters/mdc/mdc1.xml</pre></div>
    -		
    -		<p>
    -			The <em>mdc1.xml</em> configuration file is listed below:
    -		</p>
    -<em>Example 7.4: Configuration file (logback-examples/src/main/java/chapters/mdc/mdc1.xml)</em>
    -<pre class="prettyprint source">&lt;configuration>
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;layout>
    -      &lt;Pattern>%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n&lt;/Pattern>
    -    &lt;/layout>	    
    -  &lt;/appender>
    -  
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="CONSOLE"/>
    -  &lt;/root>  
    -&lt;/configuration></pre>
    -
    -		<p>
    -			Note the use of the <em>%X</em> conversion specifier within the 
    -			<span class="option">Pattern</span> option.
    -		</p>
    -	
    -		<p>
    -			The following command starts an instance of <code>NumberCruncherClient</code> 
    -			application:  	
    -		</p>
    -		
    -<div class="source"><pre>java chapters.mdc.NumberCruncherClient <em>hostname</em></pre></div>
    -
    -		<p>
    -			where <em>hostname</em> is the host where the 
    -			<code>NumberCruncherServer</code> is running
    -		</p>
    -		
    -		<p>
    -			Executing multiple instances of the client and requesting the server to factor 
    -			the numbers 129 from the first client and shortly thereafter 
    -			the number 71 from the second client, the server outputs the following:
    -		</p>
    -		
    -<div class="source"><pre>
    -<b>70984 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Beginning to factor.</b>
    -70984 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 2 as a factor.
    -71093 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 3 as a factor.
    -71093 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Found factor 3
    -71187 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 4 as a factor.
    -71297 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 5 as a factor.
    -71390 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 6 as a factor.
    -<b>71453 [RMI TCP Connection(5)-192.168.1.6] INFO  C:orion N:71 - Beginning to factor.</b>
    -71453 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 2 as a factor.
    -71484 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 7 as a factor.
    -71547 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 3 as a factor.
    -71593 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 8 as a factor.
    -71656 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 4 as a factor.
    -71687 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 9 as a factor.
    -71750 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 5 as a factor.
    -71797 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 10 as a factor.
    -71859 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 6 as a factor.
    -71890 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 11 as a factor.
    -71953 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 7 as a factor.
    -72000 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Found factor 43
    -72062 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 8 as a factor.
    -72156 [RMI TCP Connection(5)-192.168.1.6] INFO  C:orion N:71 - Found factor 71</pre></div>
    -
    -		<p>
    -			The clients were run from a machine called <em>orion</em> as can be seen in 
    -			the above output. Even if the server processes the requests of clients 
    -			near-simultaneously in separate threads, the logging output pertaining 
    -			to each client request can be distinguished by studying the output of the 
    -			<code>MDC</code>. Note for example the stamp associated with <em>number</em>, 
    -			i.e. the number to factor. 
    -		</p>
    -		
    -		<p>
    -			The attentive reader might have observed that the thread name could 
    -			also have been used to distinguish each request. The thread name can cause 
    -			confusion if the server side technology recycles threads. In that case, 
    -			it may be hard to determine the boundaries of each request, that is, 
    -			when a given thread finishes servicing a request and when it begins servicing the next.
    -			Because the <code>MDC</code> is under the control of the application developer, 
    -			<code>MDC</code> stamps do not suffer from this problem. 
    -		</p>
    -		
    -		
    -		
    -		<h3 class="doAnchor" name="autoMDC">Automating access to the <code>MDC</code></h3>
    -		
    -		<p>As we've seen, the <code>MDC</code> is very useful when dealing
    -			with multiple clients. In the case of a web application that
    -			manages user authentication, one simple solution could be to set
    -			the user's name in the <code>MDC</code> and remove it once the
    -			user logs out. Unfortunately, it is not always possible to
    -			achieve reliable results using this technique. Since
    -			<code>MDC</code> manages data on a <em>per thread</em> basis, a
    -			server that recycles threads might lead to false information
    -			contained in the <code>MDC</code>.
    -		</p>
    -		
    -		<p>To allow the information contained in the <code>MDC</code> to
    -			be correct at all times when a request is processed, a possible
    -			approach would be to store the username at the beginning of the
    -			process, and remove it at the end of said process. A servlet <a
    -			href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html">
    -			<code>Filter</code></a> comes in handy in this case.
    -		</p>
    -		
    -		<p>Within the servlet filter's <code>doFilter</code> method, we
    -		can retrieve the relevant user data through the request (or a
    -		cookie therein), store it the <code>MDC</code>.  Subsequent
    -		processing by other filters and servlets will automatically
    -		benefit from the MDC data that was stored previously. Finally,
    -		when our servlet filter regains control, we have an opportunity to
    -		clean MDC data.
    -		</p>
    -		
    -		<p>Here is an implementation of such a filter:</p>
    -
    -<em>Example 7.5: User servlet filter (<a href="../xref/chapters/mdc/UserServletFilter.html">
    -logback-examples/src/main/java/chapters/mdc/UserServletFilter.java)</a></em>
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.io.IOException;
    -import java.security.Principal;
    -
    -import javax.servlet.Filter;
    -import javax.servlet.FilterChain;
    -import javax.servlet.FilterConfig;
    -import javax.servlet.ServletException;
    -import javax.servlet.ServletRequest;
    -import javax.servlet.ServletResponse;
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpSession;
    -
    -import org.slf4j.MDC;
    -
    -public class UserServletFilter implements Filter {
    -
    -  private final String USER_KEY = "username";
    -  
    -  public void destroy() {
    -  }
    -
    -  public void doFilter(ServletRequest request, ServletResponse response,
    -    FilterChain chain) throws IOException, ServletException {
    -
    -    boolean successfulRegistration = false;
    -
    -    HttpServletRequest req = (HttpServletRequest) request;    
    -    Principal principal = req.getUserPrincipal();
    -    // Please note that we could have also used a cookie to 
    -    // retrieve the user name
    -
    -    if (principal != null) {
    -      String username = principal.getName();
    -      successfulRegistration = registerUsername(username);
    -    } 
    -
    -    try {
    -      chain.doFilter(request, response);
    -    } finally {
    -      if (successfulRegistration) {
    -        MDC.remove(USER_KEY);
    -      }
    -    }
    -  }
    -
    -  public void init(FilterConfig arg0) throws ServletException {
    -  }
    -  
    -
    -  /**
    -   * Register the user in the MDC under USER_KEY.
    -   * 
    -   * @param username
    -   * @return true id the user can be successfully registered
    -   */
    -  private boolean registerUsername(String username) {
    -    if (username != null &amp;&amp; username.trim().length() > 0) {
    -      MDC.put(USER_KEY, username);
    -      return true;
    -    }
    -    return false;
    -  }
    -}</pre>
    -
    -	<p>When the filter's <code>doFilter()</code> method is called, it
    -	first looks for a <code>java.security.Principal</code> object in the
    -	request. This object contains the name of the currently
    -	authenticated user. If a user information is found, it is registered
    -	in the <code>MDC</code>.
    -	</p>
    -		
    -	<p>Once the filter chain has completed, the filter removes the user
    -		information from the <code>MDC</code>.
    -	</p>
    -
    -  <p>The approach we just outlined sets MDC data only for the duration
    -  of the request and only for the thread processing it. Other threads
    -  are unaffected. Furthermore, any given thread will contain correct
    -  MDC data at any point in time.</p>
    -		
    -
    -
    -  <h3 class="doAnchor" name="managedThreads">MDC And Managed
    -  Threads</h3>
    -
    -  <p>A copy of the mapped diagnostic context can not always be
    -  inherited by worker threads from the initiating thread. This is the
    -  case when <code>java.util.concurrent.Executors</code> is used for
    -  thread management. For instance, <code>newCachedThreadPool</code>
    -  method creates a <code>ThreadPoolExecutor</code> and like other
    -  thread pooling code, it has intricate thread creation logic.
    -  </p>
    -
    -  <p>In such cases, it is recommended that
    -  <code>MDC.getCopyOfContextMap()</code> is invoked on the original
    -  (master) thread before submitting a task to the executor.  When the
    -  task runs, as its first action, it should invoke
    -  <code>MDC.setContextMapValues()</code> to associate the stored copy
    -  of the original MDC values with the new <code>Executor</code>
    -  managed thread.
    -  </p>
    -
    -  <h3 class="doAnchor" name="mis">MDCInsertingServletFilter</h3>
    -
    -  <p>Within web applications, it often proves helpful to know the
    -  hostname, request uri and user-agent associated with a given HTTP
    -  request. <a
    -  href="../xref/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.html"><code>MDCInsertingServletFilter</code></a>
    -  inserts such data into the MDC under the following keys.
    -  </p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>MDC key</th>
    -      <th>MDC value</th>
    -    </tr>
    -
    -    <tr  class="alt">
    -      <td><code>req.remoteHost</code></td>
    -      <td>as returned by the <a
    -      href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletRequest.html#getRemoteHost%28%29">getRemoteHost()</a>
    -      method
    -      </td>
    -    </tr>
    -
    -    <tr >
    -      <td><code>req.xForwardedFor</code></td>
    -      <td>value of the <a
    -      href="http://en.wikipedia.org/wiki/X-Forwarded-For">"X-Forwarded-For"</a>
    -      header
    -      </td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.method</code></td>
    -      <td>
    -        as returned by <a
    -        href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getMethod%28%29">getMethod()</a>
    -        method
    -      </td>
    -    </tr>
    -
    -    <tr>
    -      <td><code>req.requestURI</code></td>
    -      <td>
    -        as returned by <a
    -        href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getRequestURI%28%29">getRequestURI()</a>
    -        method
    -      </td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.requestURL</code></td>
    -      <td>
    -        as returned by <a
    -        href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getRequestURL%28%29">getRequestURL()</a>
    -        method
    -      </td>
    -    </tr>
    -
    -    <tr>
    -      <td><code>req.queryString</code></td>
    -      <td>
    -        as returned by <a
    -        href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getQueryString%28%29">getQueryString()</a>        method
    -      </td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.userAgent</code></td>
    -      <td>value of the "User-Agent" header
    -      </td>
    -    </tr>
    -
    -  </table>
    -
    -  <p>To install <code>MDCInsertingServletFilter</code> add the
    -  following lines to your web-application's <em>web.xml</em> file</p>
    -
    -  <pre class="prettyprint source">&lt;filter>
    -  &lt;filter-name>MDCInsertingServletFilter&lt;/filter-name>
    -  &lt;filter-class>
    -    ch.qos.logback.classic.helpers.MDCInsertingServletFilter
    -  &lt;/filter-class>
    -&lt;/filter>
    -&lt;filter-mapping>
    -  &lt;filter-name>MDCInsertingServletFilter&lt;/filter-name>
    -  &lt;url-pattern>/*&lt;/url-pattern>
    -&lt;/filter-mapping> </pre>
    -
    -  <p><b>If your web-app has multiple filters, make sure that
    -  <code>MDCInsertingServletFilter</code> is declared before other
    -  filters.</b> For example, assuming the main processing in your
    -  web-app is done in filter 'F', the MDC values set by
    -  <code>MDCInsertingServletFilter</code> will not be seen by the code
    -  invoked by 'F' if <code>MDCInsertingServletFilter</code> comes after
    -  'F'.
    -  </p>
    -
    -  <p>Once the filter is installed, values corresponding to each MDC
    -  key will be output by the %X <a
    -  href="layouts.html#conversionWord">conversion word</a> according to
    -  the key passes as first option. For example, to print the remote
    -  host followed by the request URI on one line, the date followed by
    -  the message on the next, you would set <code>PatternLayout</code>'s
    -  pattern to:
    -  
    -  </p>
    -
    -  <p class="source">%X{req.remoteHost} %X{req.requestURI}%n%d - %m%n</p>
    -
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/mdc_ja.html b/logback-site/src/site/pages/manual/mdc_ja.html
    deleted file mode 100644
    index 41837394b6..0000000000
    --- a/logback-site/src/site/pages/manual/mdc_ja.html
    +++ /dev/null
    @@ -1,542 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第8章 診断コンテキスト</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>第8章 診断コンテキスト</h1>
    -
    -    <div class="quote">     
    -      <p><em>ドアを閉めなさい</em></p>
    -      <p>—LEROY CAIN, Flight Director, Columbia Mission Control</p>
    -    </div>
    -    
    -    <script src="../templates/creative.js" type="text/javascript"></script>
    -    
    -		<p>logbackの設計目標の1つとして、複雑な分散アプリケーションの監査とデバッグに使うことがあります。現実世界のほとんどの分散型システムは、同時に複数のクライアントの相手をしなければなりません。こういうシステムの典型的なマルチスレッドの実装は、スレッドが別々のクライアントを処理するものです。それぞれのクライアントに対するログ出力を分離するために実際に行われているアプローチは、クライアントごとに新しいロガーを用意するという少々残念なものです。このやり方ではやたらめったらロガーを生成することになりますし、管理のためのオーバーヘッドも馬鹿になりません。
    -		</p>
    -		
    -    <script src="../templates/setup.js" type="text/javascript"></script>
    -
    -    
    -    <p>もう少し軽めのやり方としては、リクエストを受け付けたクライアントの固有の情報をログに出力する方法があります。この方法は書籍「Patterns for Logging Diagnostic Messages in Pattern Languages of Program Design 3」(Addison-Wesley, 1997)でニール・ハリソンが紹介しています。logback が利用しているのはSLF4J API の診断コンテキスト(MDC)で、これはさっき紹介した技法を応用したものです。
    -		</p>
    -		
    -		<p>リクエストごとの固有の情報として、利用者はコンテキストの情報を<code>MDC</code>(Mapped Diagnostic Contextの略です)に設定します。MDCクラスの特筆すべき部分を紹介します。メソッドの完全な説明は<a href="http://www.slf4j.org/api/org/slf4j/MDC.html">MDCのjavadoc</a>を参照してください。
    -		</p>
    -
    -<pre class="prettyprint source">package org.slf4j;
    -
    -public class MDC {
    -  //Put a context value as identified by <em>key</em>
    -  //into the current thread's context map.
    -  <b>public static void put(String key, String val);</b>
    -
    -  //Get the context identified by the <code>key</code> parameter.
    -  <b>public static String get(String key);</b>
    -
    -  //Remove the context identified by the <code>key</code> parameter.
    -  <b>public static void remove(String key);</b>
    -
    -  //Clear all entries in the MDC.
    -  <b>public static void clear();</b>
    -}</pre>
    -
    -		<p><code>MDC</code>クラスには静的メソッドしかありません。おかげで、開発者が<em>診断コンテキスト</em>に設定した情報は、logback のありとあらゆるコンポーネントから取得できるようになるのです。<code>MDC</code>はコンテキストの情報を<em>スレッドごとに</em>管理します。子スレッドは、親の診断コンテキストの<em>コピー</em>を自動的に継承します。普通なら、開発者はクライアントから受け付けた新しい要求の処理を始めるところで、クライアント識別子、IPアドレス、リクエストパラメーターなどの適切なコンテキスト情報を<code>MDC</code>に設定します。logback のコンポーネントがちゃんと設定されていれば、こういった情報は自動的にログ項目に含まれるようになります。
    -		</p>
    -
    -    <p>logback-classic の MDC 実装は、値の書き込みが比較的穏やかに行われることを想定しているのでくれぐれも注意してください。</p>
    -
    -		<p><code><a href="http://logback.qos.ch/xref/chapters/mdc/SimpleMDC.html">SimpleMDC</a></code>アプリケーションを使ってこの基本原則を説明しましょう。
    -		</p>
    -    <p class="example">例7.1:MDCの基本的な使い方(<a href="http://logback.qos.ch/xref/chapters/mdc/SimpleMDC.html">logback-examples/src/main/java/chapters/mdc/SimpleMDC.java</a>)</p>
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -import org.slf4j.MDC;
    -
    -import ch.qos.logback.classic.PatternLayout;
    -import ch.qos.logback.core.ConsoleAppender;
    -
    -public class SimpleMDC {
    -  static public void main(String[] args) throws Exception {
    -
    -    // You can put values in the MDC at any time. Before anything else
    -    // we put the first name
    -    MDC.put("first", "Dorothy");
    -
    -    [ SNIP ]
    -    
    -    Logger logger = LoggerFactory.getLogger(SimpleMDC.class);
    -    // We now put the last name
    -    MDC.put("last", "Parker");
    -
    -    // The most beautiful two words in the English language according
    -    // to Dorothy Parker:
    -    logger.info("Check enclosed.");
    -    logger.debug("The most beautiful two words in English.");
    -
    -    MDC.put("first", "Richard");
    -    MDC.put("last", "Nixon");
    -    logger.info("I am not a crook.");
    -    logger.info("Attributed to the former US president. 17 Nov 1973.");
    -  }
    -
    -  [ SNIP ]
    -
    -}</pre>
    -
    -		<p>mainメソッドでは、まずキー<em>first</em>で値<em>Drothy</em>を<code>MDC</code>に設定します。<code>MDC</code>に指定するキーも値も利用者の自由です。同じキーで書き込むと前の値を上書きします。コード上ではlogbackの設定が行われているところです。</p>
    -
    -    <p>簡潔にするため、logbackを<a href="http://github.com/qos-ch/logback/blob/master/logback-examples/src/main/java/chapters/mdc/simpleMDC.xml">simpleMDC.xml</a>で設定しているところは省略しました。設定ファイル中の該当する箇所は次のようになっています。
    -    </p>
    -
    - <pre class="prettyprint source">&lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt; 
    -  &lt;layout&gt;
    -    &lt;Pattern&gt;<b>%X{first} %X{last}</b> - %m%n&lt;/Pattern&gt;
    -  &lt;/layout&gt; 
    -&lt;/appender&gt;</pre>
    -    
    -
    -
    -    <p><code>PatternLayout</code>の変換パターン文字列中の<em>%X変換指定子</em>の使い方を見てください。<em>%X変換指定子</em>二回登場しています。一つ目のキーには<em>first</em>、二つ目のキーには<em>last</em>を指定しています。<code>SimpleMDC.class</code>のロガーを取得した後で、MDCにキー<em>last</em>で値<em>Parker</em>を設定しています。それから、メッセージを変えて二回ロギング要求を発行しています。最後に、また別の値を<code>MDC</code>に設定してから、いくつかロギング要求を発行しています。SimpleMDCを実行すると次のような出力になります。</p>
    -
    -<div class="source"><pre>Dorothy Parker - Check enclosed.
    -Dorothy Parker - The most beautiful two words in English.
    -Richard Nixon - I am not a crook.
    -Richard Nixon - Attributed to the former US president. 17 Nov 1973.</pre></div>
    -
    -
    -		<p><code>SimpleMDC</code>アプリケーションを見れば、logbackを適切に設定すると、<code>MDC</code>の値がどのようにレイアウトされて出力されるのかがわかります。また、<code>MDC</code>に設定された情報はロガーの呼び出し一度だけでなく、何度も利用できることがわかります。
    -		</p>
    -		
    -		<h3 class="doAnchor">応用</h3>
    -		
    -		<p>診断コンテキストが一番脚光を浴びるのはクライアント・サーバーアーキテクチャだ。一般的に、複数のクライアントはサーバ上の複数のスレッドで処理されます。しかし<code>MDC</code>クラスには静的メソッドしかないので、診断コンテキストはスレッドごとに管理するしかありません。つまり、サーバー上のスレッドそれぞれが<code>MDC</code>の分のコストを負うことになるのです。<code>MDC</code>の<code>put()</code>や<code>get()</code>ような操作は、<em>現在の</em>スレッドと子スレッドの<code>MDC</code>にしか影響しません。他のスレッドの<code>MDC</code>は影響を受けないのです。<code>MDC</code>の情報はスレッドごとに管理されているので、それぞれのスレッドは自分用の<code>MDC</code>を持っていることになります。したがって、開発者はスレッド安全性やスレッド間同期のことを考える必要はありません。<code>MDC</code>はそういったことを安全に、透過的に扱えるのです。
    -		</p>
    -
    -		<p>次の例は少し凝っています。クライアント・サーバ環境でどのように<code>MDC</code>を使うのか説明したものです。サーバーは例7.2の<code>NumberCrucher</code>インターフェイスを実装します。<code>The NumberCruncher</code>インターフェイスには、<code>factor()</code>というメソッドが1つあるだけです。クライアントは、RMIによってサーバアプリケーションの<code>factor()</code>メソッドを呼び出して、指定した整数の素因数を取得します。
    -		</p>
    -
    -    <p class="example">例7.2:サービスインターフェイス(<a href="http://logback.qos.ch/xref/chapters/mdc/NumberCruncher.html">logback-examples/src/main/java/chapters/mdc/NumberCruncher.java</a>)</p>
    -
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.rmi.Remote;
    -import java.rmi.RemoteException;
    -
    -/**
    - * NumberCruncher factors positive integers.
    - */
    -public interface NumberCruncher extends Remote {
    -  /**
    -   * Factor a positive integer <code>number</code> and return its
    -   * <em>distinct</em> factor's as an integer array.
    -   * */
    -  int[] factor(int number) throws RemoteException;
    -}</pre>
    -
    -		<p>例7.3の<code>NumberCruncherServer</code>アプリケーションが<code>NumberCruncher</code>インターフェイスを実装しています。mainメソッドはlocalhost上でRMIレジストリを公開して、well-knownポートでリクエストを待ち受けます。
    -		</p>
    -
    -    <p class="example">例7.3:サーバー実装(<a href="http://logback.qos.ch/xref/chapters/mdc/NumberCruncherServer.html">logback-examples/src/main/java/chapters/mdc/NumberCruncherServer.java</a>)</p>
    -
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.rmi.RemoteException;
    -import java.rmi.registry.LocateRegistry;
    -import java.rmi.registry.Registry;
    -import java.rmi.server.UnicastRemoteObject;
    -import java.util.Vector;
    -
    -import org.slf4j.Logger;
    -import org.slf4j.LoggerFactory;
    -import org.slf4j.MDC;
    -
    -import ch.qos.logback.classic.LoggerContext;
    -import ch.qos.logback.classic.joran.JoranConfigurator;
    -import ch.qos.logback.core.joran.spi.JoranException;
    -
    -
    -/**
    - * A simple NumberCruncher implementation that logs its progress when
    - * factoring numbers. The purpose of the whole exercise is to show the
    - * use of mapped diagnostic contexts in order to distinguish the log
    - * output from different client requests.
    - * */
    -public class NumberCruncherServer extends UnicastRemoteObject
    -  implements NumberCruncher {
    -
    -  private static final long serialVersionUID = 1L;
    -
    -  static Logger logger = LoggerFactory.getLogger(NumberCruncherServer.class);
    -
    -  public NumberCruncherServer() throws RemoteException {
    -  }
    -
    -  public int[] factor(int number) throws RemoteException {
    -    // The client's host is an important source of information.
    -    try {
    -      <b>MDC.put("client", NumberCruncherServer.getClientHost());</b>
    -    } catch (java.rmi.server.ServerNotActiveException e) {
    -      logger.warn("Caught unexpected ServerNotActiveException.", e);
    -    }
    -
    -    // The information contained within the request is another source
    -    // of distinctive information. It might reveal the users name,
    -    // date of request, request ID etc. In servlet type environments,
    -    // useful information is contained in the HttpRequest or in the  
    -    // HttpSession.
    -    <b>MDC.put("number", String.valueOf(number));</b>
    -
    -    logger.info("Beginning to factor.");
    -
    -    if (number &lt;= 0) {
    -      throw new IllegalArgumentException(number +
    -        " is not a positive integer.");
    -    } else if (number == 1) {
    -      return new int[] { 1 };
    -    }
    -
    -    Vector&lt;Integer&gt; factors = new Vector&lt;Integer&gt;();
    -    int n = number;
    -
    -    for (int i = 2; (i &lt;= n) &amp;&amp; ((i * i) &lt;= number); i++) {
    -      // It is bad practice to place log requests within tight loops.
    -      // It is done here to show interleaved log output from
    -      // different requests. 
    -      logger.debug("Trying " + i + " as a factor.");
    -
    -      if ((n % i) == 0) {
    -        logger.info("Found factor " + i);
    -        factors.addElement(new Integer(i));
    -
    -        do {
    -          n /= i;
    -        } while ((n % i) == 0);
    -      }
    -
    -      // Placing artificial delays in tight loops will also lead to
    -      // sub-optimal results. :-)
    -      delay(100);
    -    }
    -
    -    if (n != 1) {
    -      logger.info("Found factor " + n);
    -      factors.addElement(new Integer(n));
    -    }
    -
    -    int len = factors.size();
    -
    -    int[] result = new int[len];
    -
    -    for (int i = 0; i &lt; len; i++) {
    -      result[i] = ((Integer) factors.elementAt(i)).intValue();
    -    }
    -
    -    <b>// clean up
    -    MDC.remove("client");
    -    MDC.remove("number");</b>
    -
    -    return result;
    -  }
    -
    -  static void usage(String msg) {
    -    System.err.println(msg);
    -    System.err.println("Usage: java chapters.mdc.NumberCruncherServer configFile\n" +
    -      "   where configFile is a logback configuration file.");
    -    System.exit(1);
    -  }
    -
    -  public static void delay(int millis) {
    -    try {
    -      Thread.sleep(millis);
    -    } catch (InterruptedException e) {
    -    }
    -  }
    -
    -  public static void main(String[] args) {
    -    if (args.length != 1) {
    -      usage("Wrong number of arguments.");
    -    }
    -
    -    String configFile = args[0];
    -
    -    if (configFile.endsWith(".xml")) {
    -      try {
    -        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
    -        JoranConfigurator configurator = new JoranConfigurator();
    -        configurator.setContext(lc);
    -        lc.reset();
    -        configurator.doConfigure(args[0]);
    -      } catch (JoranException je) {
    -        je.printStackTrace();
    -      }
    -    }
    -
    -    NumberCruncherServer ncs;
    -
    -    try {
    -      ncs = new NumberCruncherServer();
    -      logger.info("Creating registry.");
    -
    -      Registry registry = LocateRegistry.createRegistry(Registry.REGISTRY_PORT);
    -      registry.rebind("Factor", ncs);
    -      logger.info("NumberCruncherServer bound and ready.");
    -    } catch (Exception e) {
    -      logger.error("Could not bind NumberCruncherServer.", e);
    -
    -      return;
    -    }
    -  }
    -}</pre>
    -
    -		<p>特に大事なのが<code>factor(int number)</code>メソッドの実装です。最初に、<code>MDC</code>へキー<em>client</em>でクライアントのホスト名を設定しています。そして、クライアントから渡された素因数分解する数をキー<em>number</em>で<code>MDC</code>に設定します。計算が終わったらクライアントに結果を返します。ですが、結果を返す前に、キー<em>client</em>とキー<em>number</em>で設定された値をクリアするため、<code>MDC.remove()</code>メソッドを呼んでいます。普通なら<code>put()</code>操作と<code>remove()</code>操作が同じ数だけ登場するべきです。そうしないと、<code>MDC</code>に特定のキーの値が残ってしまうからです。出来る限り<code>remove()</code>操作をfinallyブロックで実行するように仕込んでおくことをおすすめします。確実に実行されることを保証するためです。
    -		</p>	
    -		
    -		<p>理屈っぽい説明が続きましたが、cruncer アプリケーションを実行する準備が整いました。次のコマンドを実行してサーバーを起動しましょう。</p>
    -		
    -<div class="source"><pre>java chapters.mdc.NumberCruncherServer src/main/java/chapters/mdc/mdc1.xml</pre></div>
    -		
    -		<p><em>mdc1.xml</em>の内容は次のとおりです。</p>
    -
    -    <p class="example">例7.4:設定ファイル(<a href="http://logback.qos.ch/xref/chapters/mdc/mdc1.xml">logback-examples/src/main/java/chapters/mdc/mdc1.xml</a>)</p>
    -
    -<pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;layout&gt;
    -      &lt;Pattern&gt;%-4r [%thread] %-5level <b>C:%X{client} N:%X{number}</b> - %msg%n&lt;/Pattern&gt;
    -    &lt;/layout&gt;	    
    -  &lt;/appender&gt;
    -  
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="CONSOLE"/&gt;
    -  &lt;/root&gt;  
    -&lt;/configuration&gt;</pre>
    -
    -		<p><span class="option">パターン·</span>オプションに<em>%X変換指定子</em>が使われているのを見てください。
    -		</p>
    -	
    -		<p>次のコマンドで<code>NumberCruncherClient</code>アプリケーションを実行しましょう。</p>
    -		
    -<div class="source"><pre>java chapters.mdc.NumberCruncherClient <em>hostname</em></pre></div>
    -
    -		<p><em>hostname</em>の部分には<code>NumberCruncherServer</code>を実行しているサーバのホスト名を指定します。</p>
    -		
    -		<p>複数のクライアントを実行して、最初に起動したクライアントがサーバに129を要求して、その後すぐに二つ目のクライアントから71を要求したときのサーバ側のコンソールには、次のように出力されています。</p>
    -		
    -<div class="source"><pre>
    -<b>70984 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Beginning to factor.</b>
    -70984 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 2 as a factor.
    -71093 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 3 as a factor.
    -71093 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Found factor 3
    -71187 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 4 as a factor.
    -71297 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 5 as a factor.
    -71390 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 6 as a factor.
    -<b>71453 [RMI TCP Connection(5)-192.168.1.6] INFO  C:orion N:71 - Beginning to factor.</b>
    -71453 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 2 as a factor.
    -71484 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 7 as a factor.
    -71547 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 3 as a factor.
    -71593 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 8 as a factor.
    -71656 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 4 as a factor.
    -71687 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 9 as a factor.
    -71750 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 5 as a factor.
    -71797 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 10 as a factor.
    -71859 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 6 as a factor.
    -71890 [RMI TCP Connection(4)-192.168.1.6] DEBUG C:orion N:129 - Trying 11 as a factor.
    -71953 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 7 as a factor.
    -72000 [RMI TCP Connection(4)-192.168.1.6] INFO  C:orion N:129 - Found factor 43
    -72062 [RMI TCP Connection(5)-192.168.1.6] DEBUG C:orion N:71 - Trying 8 as a factor.
    -72156 [RMI TCP Connection(5)-192.168.1.6] INFO  C:orion N:71 - Found factor 71</pre></div>
    -
    -		<p>クライアントは<em>orion</em>というホスト名のマシンで実行されていることがわかります。サーバーがほぼ同時に別のスレッドでクライアントの要求を処理する場合でも、それぞれのクライアントからのリクエストによるログ出力は、<code>MDC</code>の出力を見れば分かるようになっています。たとえば、<em>number</em>として印付けられた素因数分解する数です。
    -		</p>
    -		
    -		<p>注意深い人なら、スレッド名を見るだけでもリクエストを区別できることが分かるでしょう。サーバ側の実装がスレッドを再利用するようになっていると混乱させられるかもしれません。そうなってしまったら、いつリクエストを受け付けて、いつ応答を返したのかを特定するのは難しいかもしれません。<code>MDC</code>を管理するのはアプリケーション開発者なので、そういった問題が起きることはありません。
    -		</p>
    -		
    -		
    -		
    -		<h3 class="doAnchor" name="autoMDC"><code>MDC</code>への自動的なアクセス</h3>
    -		
    -		<p>これまでに見てきたように、<code>MDC</code>があると複数のクライアントの相手をするとき便利です。ユーザー認証のあるWebアプリケーションなら、<code>MDC</code>にユーザー名を設定して、ログアウトするときにそれを削除するようにしておくのが、1つの簡単なソリューションになるでしょう。残念ながら、そのテクニックを使っていても常に確実な結果を得られるわけではありません。<code>MDC</code>がスレッド毎にデータを管理している限り、サーバがスレッドをリサイクするようになっていると、間違った情報が設定されていることがあるかもしれません。</p>
    -		
    -		<p>リクエストを処理するとき、いつでも<code>MDC</code>に正しい情報が設定されていることを確実にするためにできるのは、処理の始めにユーザー名を設定して、処理の終わりに削除することです。こういう場合サーブレット<code><a href="http://java.sun.com/javaee/5/docs/api/javax/servlet/Filter.html">Filter</a></code>を使うとよいでしょう。
    -		</p>
    -		
    -		<p>サーブレットフィルターの<code>doFilter()</code>メソッドで、リクエストに関連する情報(cookieも)を集めて、それを<code>MDC</code>に設定するのです。他のフィルターで行う後続処理やサーブレットからは、自動的に今設定したばかりのMDCの情報を参照することができます。最後にまたサーブレットフィルターが仕事をするとき、MDCに設定した内容を削除することができます。
    -		</p>
    -		
    -		<p>フィルターの実装例を見てみましょう。</p>
    -
    -    <p class="example">例7.5:ユーザー名サーブレットフィルター(<a href="http://logback.qos.ch/xref/chapters/mdc/UserServletFilter.html">logback-examples/src/main/java/chapters/mdc/UserServletFilter.html</a>)</p>
    -
    -<pre class="prettyprint source">package chapters.mdc;
    -
    -import java.io.IOException;
    -import java.security.Principal;
    -
    -import javax.servlet.Filter;
    -import javax.servlet.FilterChain;
    -import javax.servlet.FilterConfig;
    -import javax.servlet.ServletException;
    -import javax.servlet.ServletRequest;
    -import javax.servlet.ServletResponse;
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpSession;
    -
    -import org.slf4j.MDC;
    -
    -public class UserServletFilter implements Filter {
    -
    -  private final String USER_KEY = "username";
    -  
    -  public void destroy() {
    -  }
    -
    -  public void doFilter(ServletRequest request, ServletResponse response,
    -    FilterChain chain) throws IOException, ServletException {
    -
    -    boolean successfulRegistration = false;
    -
    -    HttpServletRequest req = (HttpServletRequest) request;    
    -    Principal principal = req.getUserPrincipal();
    -    // Please note that we could have also used a cookie to 
    -    // retrieve the user name
    -
    -    if (principal != null) {
    -      String username = principal.getName();
    -      successfulRegistration = registerUsername(username);
    -    } 
    -
    -    try {
    -      chain.doFilter(request, response);
    -    } finally {
    -      if (successfulRegistration) {
    -        MDC.remove(USER_KEY);
    -      }
    -    }
    -  }
    -
    -  public void init(FilterConfig arg0) throws ServletException {
    -  }
    -  
    -
    -  /**
    -   * Register the user in the MDC under USER_KEY.
    -   * 
    -   * @param username
    -   * @return true id the user can be successfully registered
    -   */
    -  private boolean registerUsername(String username) {
    -    if (username != null &amp;&amp; username.trim().length() &gt; 0) {
    -      MDC.put(USER_KEY, username);
    -      return true;
    -    }
    -    return false;
    -  }
    -}</pre>
    -
    -	<p><code>doFilter()</code>メソッドが呼ばれたら、最初にリクエストオブジェクトから<code>java.security.Principal</code>オブジェクトを取得します。このオブジェクトからは、現在認証されているユーザーのユーザー名を取得することができます。ユーザー情報があったらそれを<code>MDC</code>に設定します。</p>
    -		
    -	<p>フィルターチェインが完了すると、フィルターでは<code>MDC</code>からユーザー情報を削除します。</p>
    -
    -  <p>ここで紹介したやり方は、スレッドがリクエストを処理している間だけMDCに値を設定するものです。他のスレッドは影響を受けません。さらに、あらゆるスレッドが、任意の時点で正確なMDCデータを持つようになります。</p>
    -		
    -
    -
    -  <h3 class="doAnchor" name="managedThreads">MDCおよび管理スレッド</h3>
    -
    -  <p>ワーカースレッドが自身を初期化するとき、どんなときでも診断コンテキストを継承するわけではありません。これは<code>java.util.concurrent.Executors</code>がスレッドを管理しているときに発生します。例えば、 <code>newCachedThreadPool</code>は<code>ThreadPoolExecutor</code>オブジェクトを作成しますが、他のスレッドプールを生成するコードでも、スレッドの生成は複雑なロジックになっています。
    -  </p>
    -
    -  <p>そういう場合、元のスレッド(master)でタスクをエグゼキューターに渡す前に、<code>MDC.getCopyOfContextMap()</code>メソッドを呼ぶようにするとよいでしょう。タスクが実行されるとき、まず最初に<code>MDC.setContextMapValues()</code>メソッドを呼ぶようにするべきです。そうすると新しい<code>Executor</code>のスレッドに、元のMDCの値を関連付けることができます。
    -  </p>
    -
    -  <h3 class="doAnchor" name="mis">MDCInsertingServletFilter</h3>
    -
    -  <p>Webアプリケーションでは、受け付けたHTTPリクエストに関連するホスト名、リクエストURI、ユーザーエージェント文字列などがわかるようになっていると便利なのはよくご存知だと思います。<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/helpers/MDCInsertingServletFilter.html"><code>MDCInsertingServletFilter</code></a>はMDCに次のようなキーと値を設定します。
    -  </p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>キー</th>
    -      <th>値</th>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.remoteHost</code></td>
    -      <td><a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/ServletRequest.html#getRemoteHost%28%29">getRemoteHost()</a>メソッドの返すホスト名</td>
    -    </tr>
    -
    -    <tr>
    -      <td><code>req.xForwardedFor</code></td>
    -      <td><a href="http://en.wikipedia.org/wiki/X-Forwarded-For">"X-Forwarded-For"</a>ヘッダーの値</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.requestURI</code></td>
    -      <td><a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getRequestURI%28%29">getRequestURI()</a>メソッドの返すリクエストURI</td>
    -    </tr>
    -
    -    <tr>
    -      <td><code>req.requestURL</code></td>
    -      <td><a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getRequestURL%28%29">getRequestURL()</a>メソッドの返すリクエストURL</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td><code>req.queryString</code></td>
    -      <td><a href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/javax/servlet/http/HttpServletRequest.html#getQueryString%28%29">getQueryString()</a>メソッドの返すクエリ文字列</td>
    -    </tr>
    -
    -    <tr>
    -      <td><code>req.userAgent</code></td>
    -      <td>"User-Agent" ヘッダーの値</td>
    -    </tr>
    -
    -  </table>
    -
    -  <p><code>MDCInsertingServletFilter</code>を使用するには、Webアプリケーションの<em>web.xml</em>に次の設定を追加します。</p>
    -
    -  <pre class="prettyprint source">&lt;filter&gt;
    -  &lt;filter-name&gt;MDCInsertingServletFilter&lt;/filter-name&gt;
    -  &lt;filter-class&gt;
    -    ch.qos.logback.classic.helpers.MDCInsertingServletFilter
    -  &lt;/filter-class&gt;
    -&lt;/filter&gt;
    -&lt;filter-mapping&gt;
    -  &lt;filter-name&gt;MDCInsertingServletFilter&lt;/filter-name&gt;
    -  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    -&lt;/filter-mapping&gt; </pre>
    -
    -  <p><b>複数のフィルターを使用している場合、<code>MDCInsertingServletFilter</code>を他のフィルターより先に宣言するようにしてください。</b> たとえば、アプリケーションの主な処理がフィルター'F'で行われるとしたら、その前に<code>MDCInsertingServletFilter</code>を置かないと、MDCに設定される値を'F'から参照することはできません。
    -  </p>
    -
    -  <p>フィルターを配置したら、上記のMDCのキーを%X変換指定子で使えるようになります。例えば、リモートホストとリクエストURIを一行に出したければ、次のような変換パターン文字列を指定すればよいでしょう。</p>
    -
    -  <p class="source">%X{req.remoteHost} %X{req.requestURI}%n%d - %m%n</p>
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/menu.js b/logback-site/src/site/pages/manual/menu.js
    deleted file mode 100755
    index c20fd41327..0000000000
    --- a/logback-site/src/site/pages/manual/menu.js
    +++ /dev/null
    @@ -1,31 +0,0 @@
    -
    -document.write('<p class="menu_header">Chapter Index</p>')
    -document.write('<p class="menu"><a href="introduction.html"><b>Ch1: Introduction to logback</b></a></p>');
    -document.write('<p class="menu"><a href="architecture.html"><b>Ch2: Architecture</b></a></p>');
    -document.write('<p class="menu"><a href="configuration.html"><b>Ch3: Configuration</b></a></p>');
    -document.write('<p class="menu"><a href="appenders.html"><b>Ch4: Appenders</b></a></p>');
    -document.write('<p class="menu"><a href="encoders.html"><b>Ch5: Encoders</b></a></p>');
    -document.write('<p class="menu"><a href="layouts.html"><b>Ch6: Layouts</b></a></p>');
    -document.write('<p class="menu"><a href="filters.html"><b>Ch7: Filters</b></a></p>');
    -document.write('<p class="menu"><a href="mdc.html"><b>Ch8: Mapped Diagnostic Contexts</b></a></p>');
    -document.write('<p class="menu"><a href="loggingSeparation.html"><b>Ch9: Logging Separation</b></a></p>');
    -document.write('<p class="menu"><a href="jmxConfig.html"><b>Ch10: JMX Configurator</b></a></p>');
    -document.write('<p class="menu"><a href="onJoran.html"><b>Ch11: Joran</b></a></p>');
    -document.write('<p class="menu"><a href="groovy.html"><b>Ch12: Groovy Configuration</b></a></p>');
    -document.write('<p class="menu"><a href="migrationFromLog4j.html"><b>Ch13: Migration from log4j</b></a></p>');
    -document.write('<p class="menu"><a href="receivers.html"><b>Ch14: Receivers</b></a></p>');
    -document.write('<p class="menu"><a href="usingSSL.html"><b>Ch15: Using SSL</b></a></p>');
    -
    -document.write('<p style="border: 1px solid #cccccc;"></p>');
    -document.write('<p style="border: 1px solid #cccccc;"></p>');
    -document.write('<p/>');
    -document.write('<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>');
    -//<!-- logback -->
    -document.write('<ins class="adsbygoogle"');
    -document.write('     style="display:block"');
    -document.write('     data-ad-client="ca-pub-7471410671306824"');
    -document.write('     data-ad-slot="6377851613"');
    -document.write('     data-ad-format="auto"></ins>');
    -document.write('<script>');
    -document.write('(adsbygoogle = window.adsbygoogle || []).push({});');
    -document.write('</script>');
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/menu_ja.js b/logback-site/src/site/pages/manual/menu_ja.js
    deleted file mode 100644
    index 8f67c0e497..0000000000
    --- a/logback-site/src/site/pages/manual/menu_ja.js
    +++ /dev/null
    @@ -1,16 +0,0 @@
    -document.write('<p class="menu_header">目次</p>');
    -document.write('<p class="menu"><a href="introduction_ja.html"><b>第1章 はじめに</b></a></p>');
    -document.write('<p class="menu"><a href="architecture_ja.html"><b>第2章 アーキテクチャ</b></a></p>');
    -document.write('<p class="menu"><a href="configuration_ja.html"><b>第3章 設定</b></a></p>');
    -document.write('<p class="menu"><a href="appenders_ja.html"><b>第4章 アペンダー</b></a></p>');
    -document.write('<p class="menu"><a href="encoders_ja.html"><b>第5章 エンコーダー</b></a></p></p>');
    -document.write('<p class="menu"><a href="layouts_ja.html"><b>第6章 レイアウト</b></a></p>');
    -document.write('<p class="menu"><a href="filters_ja.html"><b>第7章 フィルター</b></a></p>');
    -document.write('<p class="menu"><a href="mdc_ja.html"><b>第8章 診断コンテキスト(MDC)</b></a></p>');
    -document.write('<p class="menu"><a href="loggingSeparation_ja.html"><b>第9章 ログの分離</b></a></p>');
    -document.write('<p class="menu"><a href="jmxConfig_ja.html"><b>第10章 JMXコンフィギュレーター</b></a></p>');
    -document.write('<p class="menu"><a href="onJoran_ja.html"><b>第11章 Joran</b></a></p>');
    -document.write('<p class="menu"><a href="groovy_ja.html"><b>第12章 Groovyによる設定</b></a></p>');
    -document.write('<p class="menu"><a href="migrationFromLog4j_ja.html"><b>第13章 log4jから移行する</b></a></p>');
    -document.write('<p class="menu"><a href="receivers_ja.html"><b>第14章 レシーバー</b></a></p>');
    -document.write('<p class="menu"><a href="usingSSL_ja.html"><b>第15章 SSLを使用する</b></a></p>');
    diff --git a/logback-site/src/site/pages/manual/migrationFromLog4j.html b/logback-site/src/site/pages/manual/migrationFromLog4j.html
    deleted file mode 100755
    index e53e0165ed..0000000000
    --- a/logback-site/src/site/pages/manual/migrationFromLog4j.html
    +++ /dev/null
    @@ -1,221 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 13: Migration from log4j</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../'</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>Chapter 13: Migration from log4j</h1> 
    -
    -    <a href="migrationFromLog4j_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <div class="quote">
    -      <p><em>The more things change, the more they remain the
    -      same. </em></p>
    -      
    -      <p>&mdash;ALPHONSE KARR, <em>Les Gu&ecirc;pes</em></p>
    -    </div>
    -
    -    <p>This chapter deals with the topic of migrating custom log4j
    -    components such as appenders or layouts to logback-classic.
    -    </p>
    -
    -    <p>Software which merely invokes log4j client API, that is the
    -    <code>Logger</code> or <code>Category</code> classes in
    -    <code>org.apache.log4j</code> package, can be automatically
    -    migrated to use SLF4J via the <a
    -    href="http://www.slf4j.org/migrator.html">SLF4J migrator
    -    tool</a>. To migrate <em>log4j.property</em> files into its
    -    logback equivalent, you can use the <a
    -    href="http://logback.qos.ch/translator/">log4j.properties
    -    translator</a>.
    -    </p>
    -
    -    <p>From a broader perspective, log4j and logback-classic are
    -    closely related. The core components, such as loggers, appenders
    -    and layouts exist in both frameworks and serve identical
    -    purposes. Similarly, the most important internal data-structure,
    -    namely <code>LoggingEvent</code>, exists in both frameworks with
    -    rather similar but non-identical implementations. Most notably, in
    -    logback-classic <code>LoggingEvent</code> implements the
    -    <code>ILoggingEvent</code> interface. Most of the changes required
    -    in migrating log4j components to logback-classic are related to
    -    differences in implementation of the <code>LoggingEvent</code>
    -    class. Rest assured, these differences are rather limited. If in
    -    spite of your best efforts you are unable to migrate any given
    -    log4j component to logback-classic, do post a question on the <a
    -    href="../mailinglist.html">logback-dev mailing list</a>. A logback
    -    developer should be able to provide guidance.
    -    </p>
    -
    -    
    -    <h3 class="doAnchor" name="log4jLayout">Migrating a log4j layout</h3>
    -
    -    <p>Let us begin by migrating a hypothetical and trivially simple
    -    log4j layout named <a
    -    href="../xref/chapters/migrationFromLog4j/TrivialLog4jLayout.html">TrivialLog4jLayout</a>
    -    which returns the message contained in a logging events as the
    -    formatted message. Here is the code.
    -    </p>
    -    
    -
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import org.apache.log4j.Layout;
    -import org.apache.log4j.spi.LoggingEvent;
    -
    -public class TrivialLog4jLayout extends Layout {
    -
    -  public void activateOptions() {
    -    // there are no options to activate
    -  }
    -
    -  public String format(LoggingEvent loggingEvent) {
    -    return loggingEvent.getRenderedMessage();
    -  }
    -
    -  public boolean ignoresThrowable() {
    -    return true;
    -  }
    -}</pre>
    -
    -    <p>The logback-classic equivalent named <a
    -    href="../xref/chapters/migrationFromLog4j/TrivialLogbackLayout.html">TrivialLogbackLayout</a>
    -    would be </p>
    -    
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class TrivialLogbackLayout extends <b>LayoutBase&lt;ILoggingEvent></b> {
    -
    -  public String <b>doLayout</b>(ILoggingEvent loggingEvent) {
    -    return loggingEvent.getMessage();
    -  }
    -}    </pre>
    - 
    -   <p>As you can see, in a logback-classic layout, the formatting
    -   method is named <code>doLayout</code> instead of
    -   <code>format</code>() in log4j. The <code>ignoresThrowable</code>()
    -   method is not needed and has no equivalent in logback-classic. Note
    -   that a logback-classic layout must extend the
    -   <code>LayoutBase&lt;ILoggingEvent></code> class.
    -   </p>
    -
    -   <p>The <code>activateOptions</code>() method merits further
    -   discussion. In log4j, a layout will have its
    -   <code>activateOptions</code>() method invoked by log4j
    -   configurators, that is <code>PropertyConfigurator</code> or
    -   <code>DOMConfigurator</code> just after all the options of the
    -   layout have been set. Thus, the layout will have an opportunity to
    -   check that its options are coherent and if so, proceed to fully
    -   initialize itself.</p>
    -
    -
    -   <p>In logback-classic, layouts must implement the <a
    -   href="../xref/ch/qos/logback/core/spi/LifeCycle.html">LifeCycle</a>
    -   interface which includes a method called <code>start</code>(). The
    -   <code>start</code>() method is the equivalent of log4j's
    -   <code>activateOptions</code>() method.
    -   </p>
    -
    -   <h3 class="doAnchor" name="log4jAppender">Migrating a log4j
    -   appender</h3>
    -   
    -   <p>Migrating an appender is quite similar to migrating a
    -   layout. Here is a trivially simple appender called <a
    -   href="../xref/chapters/migrationFromLog4j/TrivialLog4jAppender.html">TrivialLog4jAppender</a>
    -   which writes on the console the string returned by its layout.</p>
    -   
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import org.apache.log4j.AppenderSkeleton;
    -import org.apache.log4j.spi.LoggingEvent;
    -
    -
    -public class TrivialLog4jAppender extends AppenderSkeleton {
    -
    -  protected void append(LoggingEvent loggingevent) {
    -    String s = this.layout.format(loggingevent);
    -    System.out.println(s);
    -  }
    -
    -  public void close() {
    -    // nothing to do
    -  }
    -
    -  public boolean requiresLayout() {
    -    return true;
    -  }
    -}</pre>
    -
    -   <p>The logback-classic equivalent named <a
    -   href="../xref/chapters/migrationFromLog4j/TrivialLogbackAppender.html">TrivialLogbackAppender</a>
    -   would be written as</p>
    -
    -
    -   <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.AppenderBase;
    -
    -public class TrivialLogbackAppender extends AppenderBase&lt;ILoggingEvent> {
    -
    -  @Override
    -  public void start() {
    -    if (this.layout == null) {
    -      addError("No layout set for the appender named [" + name + "].");
    -      return;
    -    }
    -    super.start();
    -  }
    -
    -  @Override
    -  protected void append(ILoggingEvent loggingevent) {
    -    // note that AppenderBase.doAppend will invoke this method only if
    -    // this appender was successfully started.
    -    
    -    String s = this.layout.doLayout(loggingevent);
    -    System.out.println(s);
    -  }
    -}</pre>
    -
    -
    -   <p>Comparing the two classes, you should notice that the contents
    -   of the <code>append</code>() method remains unchanged. The
    -   <code>requiresLayout</code> method is not used in logback and can
    -   be removed. In logback, the <code>stop</code>() method is the
    -   equivalent of log4j's <code>close</code>() method. However,
    -   <code>AppenderBase</code> in logback-classic, contains a nop
    -   implementation for <code>stop</code> which is sufficient for the
    -   purposes of this trivial appender.
    -   </p>
    -
    -
    -
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/migrationFromLog4j_ja.html b/logback-site/src/site/pages/manual/migrationFromLog4j_ja.html
    deleted file mode 100644
    index fc36cec843..0000000000
    --- a/logback-site/src/site/pages/manual/migrationFromLog4j_ja.html
    +++ /dev/null
    @@ -1,157 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第13章 log4jからの移行</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>第13章 log4jからの移行</h1> 
    -
    -    <div class="quote">
    -      <p><em>多くのものが変化し、多くのものがそのままである。</em></p>
    -      
    -      <p>—ALPHONSE KARR, <em>Les Guêpes</em></p>
    -    </div>
    -
    -    <p>本章ではカスタマイズされたlog4jのコンポーネントのアペンダーやレイアウトをlogback-classicに移行する方法について議論します。
    -    </p>
    -
    -    <p>log4j のクライアントAPIとして<code>org.apache.log4j</code>パッケージの<code>Logger</code>や<code>Category</code>を使っているだけなら、SLF4Jの<a href="http://www.slf4j.org/migrator.html">SLF4J移行ツール</a>で自動的に移行することができます。<em>log4j.properties</em>から同じ内容のlogback.xmlを作るには、<a href="http://logback.qos.ch/translator/">log4j.properties トランスレーター</a>が使えます。
    -    </p>
    -
    -    <p>広い観点から見ると、log4jとlogback-classicは密接に関連し合っています。ロガーやアペンダー、レイアウトといった基本的なコンポーネントはどちらのフレームワークにもありますし、目的も同じです。同様に、どちらのフレームワークにも一番重要な内部データ構造として<code>LoggingEvent</code>がありますが、実装まで完全に同じわけではありません。一番の違いは、logback-classic の<code>LoggingEvent</code>は<code>ILoggingEvent</code>インターフェイスを実装しているところです。log4jのコンポーネントをlogback-classicに移行するために必要な変更のほとんどは、<code>LoggingEvent</code>の実装の差異を埋めることに費やされます。差異があるといってもその範囲は限られているので安心してください。出来る限りの努力をしたにも関わらず移行がうまくいかなかったら、<a href="http://logback.qos.ch/mailinglist.html">logback-dev メーリングリスト</a>で質問してください。logbackの開発者たちがアドバイスしてくれるはずです。
    -    </p>
    -
    -    
    -    <h3 class="doAnchor" name="log4jLayout">log4jのレイアウトを移行する</h3>
    -
    -    <p>さあ移行してみましょう。対象はlog4j の単純なレイアウト<a href="http://logback.qos.ch/xref/chapters/migrationFromLog4j/TrivialLog4jLayout.html">TrivialLog4jLayout</a>ということにします。これはロギングイベントのメッセージを指定された書式で書式化した文字列を返すものです。コードを見てください。
    -    </p>
    -    
    -
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import org.apache.log4j.Layout;
    -import org.apache.log4j.spi.LoggingEvent;
    -
    -public class TrivialLog4jLayout extends Layout {
    -
    -  public void activateOptions() {
    -    // there are no options to activate
    -  }
    -
    -  public String format(LoggingEvent loggingEvent) {
    -    return loggingEvent.getRenderedMessage();
    -  }
    -
    -  public boolean ignoresThrowable() {
    -    return true;
    -  }
    -}</pre>
    -
    -    <p>logback-classicで等価なレイアウト<a href="http://logback.qos.ch/xref/chapters/migrationFromLog4j/TrivialLogbackLayout.html">TrivialLogbackLayout</a>は次のようになります。</p>
    -    
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.LayoutBase;
    -
    -public class TrivialLogbackLayout extends <b>LayoutBase&lt;ILoggingEvent&gt;</b> {
    -
    -  public String <b>doLayout</b>(ILoggingEvent loggingEvent) {
    -    return loggingEvent.getMessage();
    -  }
    -}    </pre>
    - 
    -   <p>ご覧のように、文字列を書式化するメソッドはlog4jでは<code>format()</code>でしたが、logback-classic では<code>doLayout()</code>なのです。また、logback-classic には<code>ignoresThrowable()</code>に相当するメソッドはありません。logback-classicのレイアウトは<code>LayoutBase&lt;ILoggingEvent&gt;</code>を継承しなければならないので気をつけてください。
    -   </p>
    -
    -   <p><code>activateOptions()</code>メソッドの効果については大いに議論の余地があります。log4jでは、log4j configurator(<code>PropertyConfigurator</code>あるいは<code>DOMConfiguration</code>) から、レイアウトの全てのオプションが設定された後に<code>activateOptions()</code>メソッドが呼ばれるようになっています。したがって、レイアウトには自分に指定されたオプションの整合性をチェックするタイミングが無いので、全て自分で賄わなければなりません。</p>
    -
    -
    -   <p>logback-classic では、レイアウトは<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/LifeCycle.html">LifeCycle</a>インターフェイスを実装しなければなりません。このインターフェイスには<code>start()</code>メソッドがあります。この<code>start()</code>メソッドは、log4jの<code>activateOptions()</code>メソッドとほぼ同様の役割を果たします。
    -   </p>
    -
    -   <h3 class="doAnchor" name="log4jAppender">log4jのアペンダーを移行する</h3>
    -   
    -   <p>アペンダーの移行は、レイアウトの移行とほとんど同じです。単純なアペンダー<a href="http://logback.qos.ch/xref/chapters/migrationFromLog4j/TrivialLog4jAppender.html">TrivialLog4jAppender</a>を移行することを考えてみましょう。このアペンダーはレイアウトの返す文字列をコンソールに出力するだけのものです。</p>
    -   
    -    <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import org.apache.log4j.AppenderSkeleton;
    -import org.apache.log4j.spi.LoggingEvent;
    -
    -
    -public class TrivialLog4jAppender extends AppenderSkeleton {
    -
    -  protected void append(LoggingEvent loggingevent) {
    -    String s = this.layout.format(loggingevent);
    -    System.out.println(s);
    -  }
    -
    -  public void close() {
    -    // nothing to do
    -  }
    -
    -  public boolean requiresLayout() {
    -    return true;
    -  }
    -}</pre>
    -
    -   <p>logback-classicで等価なアペンダー<a href="http://logback.qos.ch/xref/chapters/migrationFromLog4j/TrivialLogbackAppender.html">TrivialLogbackAppender</a>は次のようになります。</p>
    -
    -
    -   <pre class="prettyprint source">package chapters.migrationFromLog4j;
    -
    -import ch.qos.logback.classic.spi.ILoggingEvent;
    -import ch.qos.logback.core.AppenderBase;
    -
    -public class TrivialLogbackAppender extends AppenderBase&lt;ILoggingEvent&gt; {
    -
    -  @Override
    -  public void start() {
    -    if (this.layout == null) {
    -      addError("No layout set for the appender named [" + name + "].");
    -      return;
    -    }
    -    super.start();
    -  }
    -
    -  @Override
    -  protected void append(ILoggingEvent loggingevent) {
    -    // note that AppenderBase.doAppend will invoke this method only if
    -    // this appender was successfully started.
    -    
    -    String s = this.layout.doLayout(loggingevent);
    -    System.out.println(s);
    -  }
    -}</pre>
    -
    -
    -   <p>コードを比べてみると分かるのですが、<code>append()</code>メソッドはそのままです。logbackでは<code>requiresLayout()</code>メソッドを使用しないので、これは削除できます。logbackの<code>stop()</code>メソッドは、log4jの<code>close()</code>メソッドと同じ意味です。logback-classicの<code>AppenderBase</code>には何もしない<code>stop()</code>メソッドの実装があるので、今回のように単純なアペンダーならそれで十分でしょう。
    -   </p>
    -
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/onJoran.html b/logback-site/src/site/pages/manual/onJoran.html
    deleted file mode 100755
    index ddc386fa69..0000000000
    --- a/logback-site/src/site/pages/manual/onJoran.html
    +++ /dev/null
    @@ -1,787 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 11: Joran</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -  </head>
    -  <body  onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../'</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript"> src="../templates/header.js"</script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>Chapter 11: Joran</h1>
    -
    -    <a href="onJoran_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -    <div class="quote">
    -      <p><em>The answer, my friend, is blowin' in the wind, The answer
    -      is blowin' in the wind.</em></p>
    -      
    -      <p>&mdash;BOB DYLAN, <em>The Freewheelin' Bob Dylan</em></p>
    -    </div>
    -
    -    <p>Joran stands for a cold north-west wind which, every now and
    -    then, blows forcefully on Lake Geneva. Located right in the middle
    -    of Western-Europe, the surface of Lake Geneva is smaller than many
    -    other European lakes. However, with its average depth of 153
    -    meters, it is unusually deep, and happens to be, by volume, the
    -    largest sweet water reserve in Western-Europe.
    -    </p>
    -
    -
    -    <p>As apparent in previous chapters, logback relies on Joran, a
    -    mature, flexible and powerful configuration framework. Many of the
    -    capabilities offered by logback modules are only possible on
    -    account of Joran. This chapter focuses on Joran, its basic design
    -    and its salient features.
    -    </p>
    -
    -    <p>Joran is actually a generic configuration system which can be
    -    used independently of logging. To emphasize this point, we should
    -    mention that the logback-core module does not have a notion of
    -    loggers. In that spirit, most of the examples in this chapter have
    -    nothing to do with loggers, appenders or layouts.
    -    </p>
    -
    -    <p>The examples presented in this chapter can be found under
    -    <em>LOGBACK_HOME/logback-examples/src/main/java/chapters/onJoran/</em>
    -    folder.
    -    </p>
    -
    -    <p>To install Joran, simply <a
    -    href="../download.html">download</a> logback and add
    -    <em>logback-core-${project.version}.jar</em> to your
    -    classpath.</p>
    -    
    -    <h2 class="doAnchor">Historical perspective</h2>
    -
    -    <p>Reflection is a powerful feature of the Java language, making
    -    it possible to configure software systems declaratively. For
    -    example, many important properties of an EJB are configured with
    -    the <em>ejb.xml</em> file. While EJBs are written in Java, many of
    -    their properties are specified within the <em>ejb.xml</em>
    -    file. Similarly, logback settings can be specified in a
    -    configuration file, expressed in XML format. Annotations available
    -    in JDK 1.5 and heavily used in EJB 3.0 replace many directives
    -    previously found in XML files. Joran also makes use of annotations
    -    but at a much smaller extent. Due to the dynamic nature of logback
    -    configuration data (compared to EJBs) Joran's use of annotations
    -    is rather limited.
    -    </p>
    -
    -    <p>In log4j, logback's predecessor, the
    -    <code>DOMConfigurator</code> class, which is part of log4j version
    -    1.2.x and later, could also parse configuration files written in
    -    XML. <code>DOMConfigurator</code> was written in a way that forced
    -    us, the developers, to tweak the code each time the structure of
    -    the configuration file changed. The modified code had to be
    -    recompiled and redeployed. Just as importantly, the code of the
    -    <code>DOMConfigurator</code> consisted of loops dealing with
    -    child elements containing many interspersed if/else
    -    statements. One could not help but notice that this particular
    -    code reeked of redundancy and duplication.  The <a
    -    href="http://jakarta.apache.org/commons/digester/">commons-digester
    -    project</a> had shown us that it was possible to parse XML files
    -    using pattern matching rules. At parse time, digester would apply
    -    rules that matched designated patterns. Rule classes were usually
    -    quite small and specialized. Consequently, they were relatively
    -    easy to understand and maintain.
    -    </p>
    -
    -    <p>Armed with the <code>DOMConfigurator</code> experience, we
    -    began developing <code>Joran</code>, a powerful configuration
    -    framework to be used in logback. Joran was largely inspired by the
    -    commons-digester project. Nevertheless, it uses a slightly
    -    different terminology. In commons-digester, a rule can be seen as
    -    consisting of a pattern and a rule, as shown by the
    -    <code>Digester.addRule(String pattern, Rule rule)</code>
    -    method. We find it unnecessarily confusing to have a rule to
    -    consist of itself, not recursively but with a different
    -    meaning. In Joran, a rule consists of a pattern and an action. An
    -    action is invoked when a match occurs for the corresponding
    -    pattern. This relation between patterns and actions lies at the
    -    core of Joran.  Quite remarkably, one can deal with quite complex
    -    requirements by using simple patterns, or more precisely with
    -    exact matches and wildcard matches.
    -    </p>
    -
    -    <h3 class="doAnchor" name="saxOrDom">SAX or DOM?</h3>
    -
    -    <p>Due to the event-based architecture of the SAX API, a tool based
    -    on SAX cannot easily deal with forward references, that is,
    -    references to elements which are defined later than the current
    -    element being processed. Elements with cyclical references are
    -    equally problematic. More generally, the DOM API allows the user to
    -    perform searches on all the elements and make forward jumps.
    -    </p>
    -    
    -    <p>This extra flexibility initially led us to choose the DOM API
    -    as the underlying parsing API for Joran. After some
    -    experimentation, it quickly became clear that dealing with jumps
    -    to distant elements while parsing the DOM tree did not make sense
    -    when the interpretation rules were expressed in the form of
    -    patterns and actions. <em>Joran only needs to be given the
    -    elements in the XML document in a sequential, depth-first
    -    order.</em>
    -    </p>
    -
    -    <p>Moreover, the SAX API offers element location information which
    -    allows Joran to display the exact line and column number where an
    -    error occurred. Location information comes in very handy in the
    -    identification of parsing problems.
    -    </p>
    -    
    -    <h3>Non goals</h3>
    -
    -    <p>Given its highly dynamic nature, the Joran API is not intended
    -    to be used to parse very large XML documents with many thousands
    -    of elements.
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="pattern">Pattern</h3>
    -
    -    <p>A Joran pattern is essentially a string. There are two kind of
    -    patterns, <em>exact</em> and <em>wildcard</em>. The pattern "a/b"
    -    can be used to match a <code>&lt;b></code> element nested within a
    -    top-level <code>&lt;a></code> element. The "a/b" pattern will not match
    -    any other element, hence the <em>exact</em> match designation.</p>
    -
    -    <p>Wildcards can be used to match suffixes or prefixes. For
    -    example, the "*/a" pattern can be used to match any suffix ending
    -    with "a", that is any <code>&lt;a></code> element within an XML
    -    document but not any elements nested within <code>&lt;a></code>.
    -    The "a/*" pattern will match any element prefixed by
    -    <code>&lt;a></code>, that is any element nested within an
    -    <code>&lt;a></code> element.
    -    </p>
    -
    -    <h3 class="doAnchor" name="action">Actions</h3>
    -    
    -    <p>As mentioned above, Joran parsing rules consists of the
    -    association of patterns. Actions extend the <a
    -    href="../xref/ch/qos/logback/core/joran/action/Action.html"><code>Action</code></a>
    -    class, consisting of the following abstract methods. Other methods
    -    have been omitted for brevity.
    -    </p>
    -
    -
    -    <pre class="prettyprint source">package ch.qos.logback.core.joran.action;
    -
    -import org.xml.sax.Attributes;
    -import org.xml.sax.Locator;
    -import ch.qos.logback.core.joran.spi.InterpretationContext;
    -
    -public abstract class Action extends ContextAwareBase {
    -  /**
    -   * Called when the parser encounters an element matching a
    -   * {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
    -   */
    -  public abstract void begin(InterpretationContext ic, String name,
    -      Attributes attributes) throws ActionException;
    -
    -  /**
    -   * Called to pass the body (as text) contained within an element.
    -   */
    -  public void body(InterpretationContext ic, String body)
    -      throws ActionException {
    -    // NOP
    -  }
    -
    -  /*
    -   * Called when the parser encounters an endElement event matching a
    -   * {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
    -   */
    -  public abstract void end(InterpretationContext ic, String name)
    -      throws ActionException;
    -}</pre>
    -
    -   <p>Thus, every action must implement the <code>begin()</code> and
    -   <code>end()</code> methods. The implementation of the
    -   <code>body()</code> method is optional on account of the
    -   empty/nop implementation provided by <code>Action</code>.</p>
    -
    -
    -   <h3 class="doAnchor" name="ruleStore">RuleStore </h3>
    -
    -   <p>As mentioned previously, the invocation of actions according to
    -   matching patterns is a central concept in Joran. A rule is an
    -   association of a pattern and an action. Rules are stored in a <a
    -   href="../xref/ch/qos/logback/core/joran/spi/RuleStore.html">RuleStore</a>.   
    -   </p>
    -
    -   <p>As mentioned above, Joran is built on top of the SAX API. As an
    -   XML document is parsed, each element generates events corresponding
    -   to the start, body and end of each element. When a Joran
    -   configurator receives these events, it will attempt to find in its
    -   rule store an action corresponding to the <em>current
    -   pattern</em>. For example, the current pattern for the start, body
    -   or end event of element <em>B</em> nested within a top-level
    -   <em>A</em> element is "A/B".  The current pattern is a data
    -   structure maintained automatically by Joran as it receives and
    -   processes SAX events. </p>
    -
    -   <p>When several rules match the current pattern, then exact
    -   matches override suffix matches, and suffix matches override prefix
    -   matches. For exact details of the implementation, please see the <a
    -   href="../xref/ch/qos/logback/core/joran/spi/SimpleRuleStore.html">SimpleRuleStore</a>
    -   class.
    -   </p>
    -   
    -
    -   <h3 class="doAnchor" name="interpretationContext">Interpretation
    -   context</h3>
    -
    -   <p>To allow various actions to collaborate, the invocation of begin
    -   and end methods include an interpretation context as the first
    -   parameter. The interpretation context includes an object stack, an
    -   object map, an error list and a reference to the Joran interpreter
    -   invoking the action. Please see the <a
    -   href="../xref/ch/qos/logback/core/joran/spi/InterpretationContext.html"><code>InterpretationContext</code></a>
    -   class for the exact list of fields contained in the interpretation
    -   context.
    -   </p>
    -   
    -   <p>Actions can collaborate together by fetching, pushing or popping
    -   objects from the common object stack, or by putting and fetching
    -   keyed objects on the common object map. Actions can report any error
    -   conditions by adding error items on the interpretation context's
    -   <code>StatusManager</code>.
    -   </p>
    -   
    -   <h3 class="doAnchor" name="helloWorld">Hello world</h3>
    -   
    -   <p>The first example in this chapter illustrates the minimal
    -   plumbing required for using Joran. The example consists of a
    -   trivial action called <a
    -   href="../xref/chapters/onJoran/helloWorld/HelloWorldAction.html">
    -   <code>HelloWorldAction</code></a> which prints "Hello World" on the
    -   console when its <code>begin()</code> method is invoked. The
    -   parsing of XML files is done by a configurator. For the purposes of
    -   this chapter, we have developed a very simple configurator called
    -   <a
    -   href="../xref/chapters/onJoran/SimpleConfigurator.html"><code>SimpleConfigurator</code></a>.
    -   The <a
    -   href="../xref/chapters/onJoran/helloWorld/HelloWorld.html"><code>HelloWorld</code></a>
    -   application brings all these pieces together:
    -   </p>
    -
    -   <ul>
    -     <li>It creates a map of rules and a <code>Context</code></li>
    -     <li>It creates a parsing rule by associating the
    -     <em>hello-world</em> pattern with a corresponding
    -     <code>HelloWorldAction</code> instance</li>
    -     <li>It creates a <code>SimpleConfigutator</code>, passing it the
    -     aforementioned rules map</li>
    -     <li>It then invokes the <code>doConfigure</code> method of the
    -     configurator, passing the designated XML file as parameter
    -     </li>
    -     <li>As a last step, the accumulated Status message in the context,
    -     if any, are printed</li>
    -   </ul>
    -
    -    <p>The <em>hello.xml</em> file contains one &lt;hello-world&gt;
    -    element, without any other nested elements. See the
    -    <em>logback-examples/src/main/java/chapters/onJoran/helloWorld/</em>
    -    folder for exact contents.
    -    </p>
    - 
    -    <p>Running the HelloWorld application with <em>hello.xml</em> file
    -    will print "Hello World" on the console.</p>
    -   
    -    <p class="command">java chapters.onJoran.helloWorld.HelloWorld src/main/java/chapters/onJoran/helloWorld/hello.xml</p>
    -
    -    <p>You are highly encouraged to poke about in this example, by adding
    -    new rules on the rule store, modifying the XML document
    -    (hello.xml) and adding new actions.
    -    </p>
    -
    -    <!-- ====================================================== -->
    -
    -    <h3 class="doAnchor" name="calculator">Collaborating actions</h3>
    -   
    -    <p>The <em>logback-examples/src/main/java/joran/calculator/</em>
    -    directory includes several actions which collaborate together
    -    through the common object stack in order to accomplish simple
    -    computations.
    -    </p>
    -
    -    <p>The <em>calculator1.xml</em> file contains a
    -    <code>computation</code> element, with a nested
    -    <code>literal</code> element. Here are its contents.
    -    </p>
    -
    -    <em>Example 10.<span class="autoEx"/>: First calculator example
    -    (logback-examples/src/main/java/chapters/onJoran/calculator/calculator1.xml)</em>
    -
    -    <em> </em>
    -    <pre class="prettyprint source">&lt;computation name="total">
    -  &lt;literal value="3"/>
    -&lt;/computation></pre>
    -
    -    <p>In the <a href="../xref/chapters/onJoran/calculator/Calculator1.html">
    -    <code>Calculator1</code></a> application, we declare various
    -    parsing rules (patterns and actions) collaborating together to
    -    compute a result based on the contents of an XML document. 
    -    </p>
    -
    -    <p> Running <code>Calculator1</code> application with
    -    <em>calculator1.xml</em></p>
    -
    -    <p class="command">java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator1.xml</p>
    -
    -    <p>will print:</p>
    -
    -    <p class="console">The computation named [total] resulted in the value 3</p>
    -
    -
    -    <p>Parsing the <em>calculator1.xml</em> document (listed above)
    -    involves the following steps:
    -    </p>
    -
    -    <ul>
    -      <li>The start event corresponding to the &lt;computation&gt;
    -      element translates into the current pattern
    -      "/computation". Since in the <a
    -      href="../xref/chapters/onJoran/calculator/Calculator1.html">
    -      <code>Calculator1</code></a> application we associated the
    -      pattern "/computation" with a
    -      <a
    -      href="../xref/chapters/onJoran/calculator/ComputationAction1.html">
    -      <code>ComputationAction1</code></a> instance, the
    -      <code>begin()</code> method of that
    -      <code>ComputationAction1</code> instance is invoked.
    -      </li>
    -
    -      <li><p>The start event corresponding to the &lt;literal&gt;
    -      element translates into the current pattern
    -      "/computation/literal". Given the association of the
    -      "/computation/literal" pattern with a
    -      <a
    -      href="../xref/chapters/onJoran/calculator/LiteralAction.html">
    -      <code>LiteralAction</code></a> instance, the
    -      <code>begin()</code> method of that <code>LiteralAction</code>
    -      instance is called.</p>
    -      </li>
    -
    -      <li><p>By the same token, the end event corresponding to the
    -      &lt;literal&gt; element triggers the invocation of the
    -      <code>end</code>() method of the same <code>LiteralAction</code>
    -      instance.</p>
    -      </li>
    -
    -
    -      <li><p>Similarly, the event corresponding to the end of
    -      &lt;computation&gt; element triggers the invocation the
    -      <code>end()</code> method of the <code>ComputationAction1</code>
    -      same instance.
    -      </p>
    -      </li>
    -    </ul>
    -
    -    <p>What is interesting here is the way actions collaborate.  The
    -    <code>LiteralAction</code> reads a literal value and pushes it in
    -    the object stack maintained by the
    -    <code>InterpretationContext</code>. Once done, any other action
    -    can pop the value to read or modify it. Here, the
    -    <code>end()</code> method of the <code>ComputationAction1</code>
    -    class pops the value from the stack and prints it.
    -    </p>
    -
    -    <!-- TO BE CONTINUED -->
    -
    -    <p>The next example, <em>calculator2.xml</em> file is a bit more
    -    complex, but also more interesting.</p>
    -
    -    <em>Example 10.<span class="autoEx"/>: Calculator configuration
    -    file
    -    (logback-examples/src/main/java/chapters/onJoran/calculator/calculator2.xml)</em>
    -  <pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;literal value="7"/&gt;
    -  &lt;literal value="3"/&gt;
    -  &lt;add/&gt;
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -
    -  <p>As in the previous example, in response to the &lt;literal&gt;
    -  element,the appropriate <a
    -  href="../xref/chapters/onJoran/calculator/LiteralAction.html">
    -  <code>LiteralAction</code></a> instance will push an integer,
    -  corresponding to the value attribute, at the top of the
    -  interpretation context's object stack. In this example, that is
    -  <em>calculator2.xml</em>, the values are 7 and 3. In response to the
    -  &lt;add&gt; element, the appropriate <a
    -  href="../xref/chapters/onJoran/calculator/AddAction.html"><code>AddAction</code></a>
    -  will pop two previously pushed integers, compute their sum and push
    -  the result, i.e. 10 (=7+3), at the top of the interpretation
    -  context's stack. The next literal element will cause LiteralAction
    -  to push an integer with value 3 at the top of the stack. In response
    -  to the &lt;multiply&gt; element, the appropriate <a
    -  href="../xref/chapters/onJoran/calculator/MultiplyAction.html"><code>MultiplyAction</code></a>
    -  will pop two previously pushed integers, i.e. 10 and 3, and compute
    -  their product.  It will push the result, i.e. 30, at the top of the
    -  stack. At the very end, in reponse to the end event corresponding to
    -  the &lt;/computation&gt; tag, the ComputationAction1 will print the
    -  object at the top of the stack. Thus, running:
    -  </p>
    -
    -  <p class="command">java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator2.xml </p>
    -  
    -  <p>will yield</p>
    -
    -  <p class="console">The computation named [toto] resulted in the value 30 </p>
    -  
    -
    -  <!--
    -
    -
    -  <p>Finally, a <em>calculator3.xml</em> is also provided, to
    -  demonstrate the possibility elements that contain instances of the
    -  same element. Here's the content of <em>calculator3.xml</em>:</p>
    -
    -  <em>Example 10.<span class="autoEx"/>: Calculator configuration file
    -  (logback-examples/src/main/java/chapters/onJoran/calculator/calculator3.xml)</em>
    -
    -<pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;computation&gt;
    -    &lt;literal value="7"/&gt;
    -    &lt;literal value="3"/&gt;
    -    &lt;add/&gt;
    -  &lt;/computation&gt;   
    - 
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -  <p>Much like the use of parentheses in an algebrical equation, the
    -  presence of a <code>computation</code> element nested in another is
    -  managed by the <a
    -  href="../xref/chapters/onJoran/calculator/ComputationAction2.html">
    -  <code>ComputationAction2</code></a> class using an internal
    -  stack. The well-formedness of XML will guarantee that a value saved
    -  by one <code>begin()</code> will be consumed only by the matching
    -  <code>end()</code> method.</p>
    -  -->
    -
    -  <h3 class="doAnchor" name="implicit">Implicit actions</h3>
    -
    -  <p>The rules defined thus far are called explicit actions because an
    -  pattern/action association could be found in the rule store for the
    -  current element. However, in highly extensible systems, the number
    -  and type of components can be so large so as to make it very tedious
    -  to associate an explicit action for all patterns.
    -  </p>
    -
    -  <p>At the same time, even in highly extensible systems one can
    -  observe recurrent rules linking various parts together. Assuming we
    -  could identify such rules, we could process components composed of
    -  sub-components unknown at compilation time (of logback). For
    -  example, Apache Ant is capable of handling tasks which contain tags
    -  unknown at compile time, simply by inspecting the component for
    -  methods whose names start with <em>add</em>, as in
    -  <code>addFile</code>, or <code>addClassPath</code>.  When Ant
    -  encounters an embedded tag within a task, it simply instantiates an
    -  object that matches the signature of the task class' add method and
    -  attaches the resulting object to the parent.
    -  </p>
    -
    -  <p>Joran supports a similar capability in the form of implicit
    -  actions. Joran keeps a list of implicit actions which are applied if
    -  no explicit pattern could match the current pattern.  However,
    -  applying an implicit action may not be always appropriate. Before
    -  executing the implicit action, Joran asks a given implicit action
    -  whether it is appropriate in the current situation. Only if the
    -  action replies in the affirmative does the Joran configurator invoke
    -  the (implicit) action. Note that this extra step makes it possible
    -  to support multiple implicit actions or possibly none, if no
    -  implicit action is appropriate for a given situation.
    -  </p>
    -
    -  <p>You can create and register a custom implicit action as
    -  illustrated in the next example contained within the
    -  <em>logback-examples/src/main/java/chapters/onJoran/implicit</em> folder.
    -  </p>
    -
    -  <p>The <a
    -  href="../xref/chapters/onJoran/implicit/PrintMe.html"><code>PrintMe</code></a>
    -  application associates an <a
    -  href="../xref/chapters/onJoran/implicit/NOPAction.html">
    -  <code>NOPAction</code></a> instance with the pattern "*/foo", that
    -  is any element named as "foo". As its name indicates, the
    -  <code>begin</code>() and <code>end</code>() methods of
    -  <code>NOPAction</code> are empty. The <code>PrintMe</code>
    -  application also registers an instance of <a
    -  href="../xref/chapters/onJoran/implicit/PrintMeImplicitAction.html">PrintMeImplicitAction</a>
    -  in its list of implicit actions. The
    -  <code>PrintMeImplicitAction</code> is applicable for any element
    -  which has a <span class="attr">printme</span> attribute set to
    -  true. See the <code>isApplicable()</code> method in
    -  <code>PrintMeImplicitAction</code>.  The <code>begin()</code>() method
    -  of <code>PrintMeImplicitAction</code> prints the name of the current
    -  element on the console.
    -  </p>
    -
    -  <p>The XML document <em>implicit1.xml</em> is designed to illustrate
    -  how implicit actions come into play.</p>
    -
    -  <em>Example 10.<span class="autoEx"/>: Usage of implicit rules
    -  (logback-examples/src/main/java/chapters/onJoran/implicit/implicit1.xml)</em>
    -  <pre class="prettyprint source">&lt;foo&gt;
    -  &lt;xyz printme="true"&gt;
    -    &lt;abc printme="true"/&gt;
    -  &lt;/xyz&gt;
    -
    -  &lt;xyz/&gt;
    -
    -  &lt;foo printme="true"/&gt;
    -
    -&lt;/foo&gt;</pre>
    -
    -  <p>Running</p>
    -
    -  <p class="command">java chapters.onJoran.implicit.PrintMe src/main/java/chapters/onJoran/implicit/implicit1.xml</p>
    -  <p>yields:</p>
    -
    -  <p class="console">Element [xyz] asked to be printed.
    -Element [abc] asked to be printed.
    -20:33:43,750 |-ERROR in c.q.l.c.joran.spi.Interpreter@<b>10:9</b> - no applicable action for [xyz], current pattern is [[foo][xyz]]</p>
    -
    -  <p>Given that <code>NOPAction</code> instance is explicitly
    -  associated with the "*/foo" pattern, <code>NOPAction</code>'s
    -  <code>begin()</code> and <code>end()</code> methods are invoked on
    -  &lt;foo> elements. <code>PrintMeImplicitAction</code> is never
    -  triggered for any of the &lt;foo&gt; elements. For other elements,
    -  since there are no matching explicit actions, the
    -  <code>isApplicable()</code> method of
    -  <code>PrintMeImplicitAction</code> is invoked. It will return true
    -  only for elements having a <span class="attr">printme</span>
    -  attribute set to true, namely the first &lt;xyz> element (but not
    -  the second) and the &lt;abc> element. The second &lt;xyz> element on
    -  line 10, there are no applicable actions, an internal error message
    -  is generated. This message is printed by the
    -  <code>StatusPrinter.print</code> invocation, the last statement in
    -  the <code>PrintMe</code> application. This explains the output shown
    -  above (see previous paragraph).
    -  </p>
    -
    -  <h3 class="doAnchor" name="iaPractice">Implicit actions in
    -  practice</h3>
    -  
    -  <p>The respective Joran configurators of logback-classic and
    -  logback-access include just two implicit actions, namely <a
    -  href="../xref/ch/qos/logback/core/joran/action/NestedBasicPropertyIA.html">
    -  <code>NestedBasicPropertyIA</code></a> and <a
    -  href="../xref/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.html">
    -  <code>NestedComplexPropertyIA</code></a>.
    -  </p>
    -
    -  <p><code>NestedBasicPropertyIA</code> is applicable for any property
    -  whose type is a primitive type (or equivalent object type in the
    -  <code>java.lang</code> package), an enumeration type, or any type
    -  adhering to the "valueOf" convention.  Such properties are said to
    -  be <em>basic</em> or <em>simple</em>. A class is said to adhere to
    -  the "valueOf" convention if it contains a static method named
    -  <code>valueOf</code>() taking a <code>java.lang.String</code> as
    -  parameter and returning an instance of the type in question.  At
    -  present, the <a
    -  href="../xref/ch/qos/logback/classic/Level.html"><code>Level</code></a>,
    -  <a
    -  href="../xref/ch/qos/logback/core/util/Duration.html"><code>Duration</code></a>
    -  and <a
    -  href="../xref/ch/qos/logback/core/util/FileSize.html"><code>FileSize</code></a>
    -  classes follow this convention.
    -  </p>
    -  
    -  <p><code>NestedComplexPropertyIA</code> action is applicable, in the
    -  remaining cases where <code>NestedBasicPropertyIA</code> is not
    -  applicable <em>and</em> if the object at the top of the object stack
    -  has a setter or adder method for a property name equal to the
    -  current element name. Note that such properties can in turn contain
    -  other components. Thus, such properties are said to be
    -  <em>complex</em>.  In presence of a complex property, <a
    -  href="../xref/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.html">
    -  <code>NestedComplexPropertyIA</code></a> will instantiate the
    -  appropriate class for the nested component and attach it to the
    -  parent component (at the top of the object stack) by using the
    -  setter/adder method of the parent component and the nested element's
    -  name. The corresponding class is specified by the <span
    -  class="attr">class</span> attribute of the (nested) current
    -  element. However, if the <span class="attr">class</span> attribute
    -  is missing, the class name can be deduced implicitly, if any of the
    -  following is true:
    -  </p>
    -
    -  <ol>
    -    <li>there is an internal rule associating the parent object's
    -    property with a designated class
    -    </li>
    -    <li>the setter method contains a @DefaultClass attribute
    -    designating a given class</li>
    -
    -    <li>the parameter type of the setter method is a concrete class
    -    possessing a public constructor
    -    </li>
    -  </ol>
    -
    -  <h4 class="doAnchor" name="defaultClassMapping">Default class
    -  mapping</h4>
    -
    -  <p>In logback-classic, there are a handful of internal rules mapping
    -  parent class/property name pairs to a default class. These are
    -  listed in the table below.</p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>Parent class </th>
    -      <th>property name </th>
    -      <th>default nested class</th>
    -    </tr>
    -
    -    <tr >
    -      <td>ch.qos.logback.core.AppenderBase</td>
    -      <td>encoder</td>
    -      <td>ch.qos.logback.classic.encoder.PatternLayoutEncoder</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td>ch.qos.logback.core.UnsynchronizedAppenderBase</td>
    -      <td>encoder</td>
    -      <td>ch.qos.logback.classic.encoder.PatternLayoutEncoder</td>
    -    </tr>
    -
    -      <tr >
    -      <td>ch.qos.logback.core.AppenderBase</td>
    -      <td>layout</td>
    -      <td>ch.qos.logback.classic.PatternLayout</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td>ch.qos.logback.core.UnsynchronizedAppenderBase</td>
    -      <td>layout</td>
    -      <td>ch.qos.logback.classic.PatternLayout</td>
    -    </tr>
    -
    -    <tr>
    -      <td>ch.qos.logback.core.filter.EvaluatorFilter</td>
    -      <td>evaluator</td>
    -      <td>ch.qos.logback.classic.boolex.JaninoEventEvaluator</td>
    -    </tr>
    -  </table>
    -
    -  <p>This list may change in future releases. Please see
    -  logback-classic <a
    -  href="../xref/ch/qos/logback/classic/joran/JoranConfigurator.html">JoranConfigurator</a>'s
    -  <code>addDefaultNestedComponentRegistryRules</code> method for the
    -  latest rules.
    -  </p>
    -
    -  <p>In logback-access, the rules are very similar. In the default
    -  class for the nested component, the ch.qos.logback.classic package
    -  is replaced by ch.qos.logback.access. See logback-access <a
    -  href="../xref/ch/qos/logback/access/joran/JoranConfigurator.html">JoranConfigurator</a>'s
    -  <code>addDefaultNestedComponentRegistryRules</code> method for the
    -  latest rules.
    -  </p>
    -  
    -  <h4 class="doAnchor">Collection of properties</h4>
    -
    -  
    -  <p>Note that in addition to single simple properties or single
    -  complex properties, logback's implicit actions support collections of
    -  properties, be they simple or complex. Instead of a setter method,
    -  the property is specified by an "adder" method.</p>
    -
    -  <h3 class="doAnchor" name="newRule">New rules on the fly</h3>
    -
    -  <p>Joran includes an action which allows the Joran interpreter to
    -  learn new rules on the fly, that is while interpreting an XML
    -  document.  See the
    -  <em>logback-examples/src/main/java/chapters/onJoran/newRule/</em> directory
    -  for sample code. In this package, the <a
    -  href="../xref/chapters/onJoran/newRule/NewRuleCalculator.html">
    -  <code>NewRuleCalculator</code></a> application sets up just two
    -  rules, one rule to process the top-most element, and a second rule
    -  to learn new rules. Here is the relevant code from
    -  <code>NewRuleCalculator</code>.
    -  </p>
    -
    -  <pre class="prettyprint source">ruleMap.put(new Pattern("*/computation"), new ComputationAction1());
    -<b>ruleStore.addRule(new Pattern("/computation/newRule"), new NewRuleAction());</b></pre>
    -
    -  <p><a
    -  href="../xref/ch/qos/logback/core/joran/action/NewRuleAction.html"><code>NewRuleAction</code></a>,
    -  part of logback-core, works pretty much like the other actions.  It
    -  has a <code>begin()</code> and <code>end()</code> method, and is
    -  called each time the parser finds a <em>newRule</em> element. When
    -  invoked, the <code>begin()</code> method looks for <em>pattern</em>
    -  and <em>actionClass</em> attributes. It then instantiates the
    -  corresponding action class and adds the pattern/action association
    -  as a new rule in Joran's rule store.</p>
    -
    -
    -  <p>Here is how new rules can be declared in an xml file:</p>
    -
    -  <pre class="prettyprint source">&lt;newRule pattern="*/computation/literal"
    -          actionClass="chapters.onJoran.calculator.LiteralAction"/&gt;</pre>
    -
    -  <p>Using such newRule declarations, we can transform
    -  <code>NewRuleCalculator</code> to behave like the
    -  <code>Calculator1</code> application we saw earlier.  involving the
    -  calculation, could be expressed this way:</p>
    -
    -  <em>Example 10..<span class="autoEx"/>: Configuration file using new
    -  rules on the fly
    -  (logback-examples/src/main/java/chapters/onJoran/newrule/newRule.xml)</em>
    -
    -  <pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;newRule pattern="*/computation/literal" 
    -            actionClass="chapters.onJoran.calculator.LiteralAction"/&gt;
    -  &lt;newRule pattern="*/computation/add" 
    -            actionClass="chapters.onJoran.calculator.AddAction"/&gt;
    -  &lt;newRule pattern="*/computation/multiply" 
    -            actionClass="chapters.onJoran.calculator.MultiplyAction"/&gt;
    -
    -  &lt;computation&gt;
    -    &lt;literal value="7"/&gt;
    -    &lt;literal value="3"/&gt;
    -    &lt;add/&gt;
    -  &lt;/computation&gt;   
    - 
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -
    -  <p class="command">java java chapters.onJoran.newRule.NewRuleCalculator src/main/java/chapters/onJoran/newRule/newRule.xml</p>
    -
    -  <p>yields</p>
    -
    -  <p class="console">The computation named [toto] resulted in the value 30</p>
    -
    -  <p>which is identical to the output of the <a
    -  href="#calculator">original calculator example</a>.</p>
    -
    -
    -  <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/onJoran_ja.html b/logback-site/src/site/pages/manual/onJoran_ja.html
    deleted file mode 100644
    index be30e95687..0000000000
    --- a/logback-site/src/site/pages/manual/onJoran_ja.html
    +++ /dev/null
    @@ -1,433 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第11章 Joran</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h1>第11章 Joran</h1>
    -
    -    <div class="quote">
    -      <p><em>答えは風に吹かれている。答えは風に吹かれている。</em></p>
    -      
    -      <p>—BOB DYLAN, <em>The Freewheelin' Bob Dylan</em></p>
    -    </div>
    -
    -    <p>Joranとは、ジュネーブ湖で一年中吹きすさんでいる冷たい北西風のことです。ジュネーブ湖は西ヨーロッパの中央からやや右側にある湖で、ヨーロッパにいくつもある他の湖と比べるとずっと狭い湖です。しかし、平均水深は153メートルと非常に深く、西ヨーロッパ最大の淡水湖として知られています。
    -    </p>
    -
    -
    -    <p>これまでの章で説明したように、logbackはJoran設定フレームワークの成熟した、柔軟で、強力な機能を頼りにしています。logbackのモジュールが提供する機能の大部分は、Joran無しでは実現できません。この章では、Joranの設計の根幹部分と、顕著な特徴に焦点を当てていきます。
    -    </p>
    -
    -    <p>Joranはロギングとは完全に無関係な、汎用設定システムです。この点を明らかにするため、logback-coreモジュールにはロガーに関わる要素が一切存在しないことに触れておかなければなりません。また、本章に登場するほとんどの例に、ロガーもアペンダーもレイアウトも出てこないこともそれを後押ししています。
    -    </p>
    -
    -    <p>この章で使っている例は<em>LOGBACK_HOME/logback-examples/src/main/java/chapters/onJoran</em>に配置されています。
    -    </p>
    -
    -    <p>Joranのインストールは簡単です。<a href="http://logback.qos.ch/download.html">logbackをダウンロード</a>して、クラスパスに<em>logback-core-1.1.2.jar</em>を追加するだけです。</p>
    -    
    -    <h2 class="doAnchor">歴史的な観点</h2>
    -
    -    <p>リフレクションは、宣言的にソフトウェアシステムを設定できるようにしてくれる、Java言語の強力な機能です。たとえば、EJBの重要なプロパティの多くは<em>ejb.xml</em>で設定します。EJBがJavaで実装されているとしても、それらのプロパティのほとんどが<em>ejb.xml</em>で指定されるのです。同じように、logbackの設定もXML形式の設定ファイルで指定します。JDK1.5から導入されたアノテーションは、以前ならXMLで設定されていたものを置き換えるためにEJB3.0で多用されています。Joranもアノテーションは利用しますが、ほんの少しだけです。EJBに比べてlogbackの設定には動的な要素が多いので、Joranでアノテーションを活用できる範囲が限られてしまうのです。
    -    </p>
    -
    -    <p>logbackの前身のlog4jでは、<code>DOMConfigurator</code>(log4j1.2.x以降に含まれています)がXML形式の設定ファイルをパースするために使われていました。<code>DOMConfigurator</code>は、設定ファイルの構造を変えるたびに、コードを微調整しなければならない作りになっていました。修正したコードは、再コンパイルして再デプロイしなければなりません。同じくらい重要なのが、<code>DOMConfigurator</code>のコードは、たくさんのまばらなif/else文を含む子要素をループするような作りになっていたことです。たいして役に立ちませんでしたし、冗長であちこちに重複があるコードでした。このとき、<a href="http://jakarta.apache.org/commons/digester/">Apace commons-digester</a>ではパターンマッチ規則に基づいたXMLのパースができていました。digesterは、パース時に指定したパターンにマッチしたら、こちらも指定されたルールを適用します。たいていのルールクラスは小さくて、1つのことしかやっていませんでした。そのため、理解するのも保守するのも比較的簡単でした。
    -    </p>
    -
    -    <p>私たちは<code>DOMConfigurator</code>の経験を武器にしてlogbackで使うための設定フレームワーク<code>Joran</code>の開発を始めました。Joranはcommons-digesterに強く影響を受けています。にもかかわらず、使っている用語が若干異なります。たとえば、commons-digester だとルールとパターンは一緒に使うものでした。Digesterの<code>addRule(String pattern, Rule rule)</code>メソッドがいい例です。私たちは、ルールがルールによって構成されている(再帰的という意味ではなく別の意味で)と、不必要な混乱を招くことに気づきました。そこで、Joranではルールがパターンとアクションで構成されるものとしました。アクションとは、対応するパターンがマッチしたときに行われる操作です。パターンとアクションの関係が、Joranの中核を為していると言ってもおかしくありません。それが顕著にあらわれているのが、単純なパターンを使って非常に複雑な要件を満たすことができるというところです。具体的には正確なマッチングとワイルドカードのマッチングによって実現されています。
    -    </p>
    -
    -    <h3 class="doAnchor" name="saxOrDom">SAXかDOMか</h3>
    -
    -    <p>SAXのAPIはイベントベースのアーキテクチャなので、SAXをベースにしたツールで前方参照を扱うのは決して簡単なことではありません。前方参照とは、現在の要素よりも後で定義される要素を参照することです。同様に循環参照も手強い相手です。一般的な話ですが、DOM APIなら全要素を検索対象にできるし、前方の要素にジャンプすることもできるのです。
    -    </p>
    -    
    -    <p>こういった柔軟性についてのあれこれを鑑みて、Joranでは当初DOM APIを使ってパースしていました。試行錯誤の後、パターンとアクションの形式で表現されたルールを解釈する上で、DOMツリーをパースしてる途中で離れた要素にジャンプできても意味が無いことが明らかになりました。<em>Joranに必要だったのは、XMLドキュメントの要素を、深さ優先で逐次的に走査していくことだけだったのです。</em>
    -    </p>
    -
    -    <p>また、SAX APIには要素の位置を取得するものがあったので、Joranは問題のあった行番号と列番号を表示できるようにもなりました。位置情報があれば、パースエラーの起きている場所を簡単に特定することができます。
    -    </p>
    -    
    -    <h3>対象外事項</h3>
    -
    -    <p>高度な可変性を求められていることもあり、JoranのAPIは数千要素にもなる巨大なXMLドキュメントを扱うようには設計されていません。
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="pattern">パターン</h3>
    -
    -    <p>Joranのパターンとは基本的に文字列です。<em>正確なパターン</em>と<em>ワイルドカードパターン</em>の2種類があります。パターン"a/b" は、最上位要素<code>a</code>にネストされた要素<code>b</code>にマッチします。他の要素にはマッチしないことから、これは<em>正確なパターン</em>だと言えます。</p>
    -
    -    <p>ワイルドカードは、接尾辞または接頭辞をマッチさせるときに使われます。たとえばパターン"*/a" は接尾辞が"a"であるものすべてにマッチします。つまり、XMLドキュメント中で要素<code>a</code>をネストしているあらゆる要素がマッチするのです。パターン"a/*"は接頭辞が"a"なので、要素<code>a</code>がネストしているあらゆる要素がマッチすることになります。
    -    </p>
    -
    -    <h3 class="doAnchor" name="action">アクション</h3>
    -    
    -    <p>前に述べたように、Joranはパターンに関連付けられたルールをパースします。アクションは、<a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/action/Action.html"><code>Action</code></a>クラスを継承したもので、次のような抽象メソッドで肉付けします。他のメソッドは簡潔にするために省略されています。
    -    </p>
    -
    -
    -    <pre class="prettyprint source">package ch.qos.logback.core.joran.action;
    -
    -import org.xml.sax.Attributes;
    -import ch.qos.logback.core.joran.spi.ExecutionContext;
    -
    -public abstract class Action {
    -  /**
    -   * Called when the parser encounters an element matching a
    -   * {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
    -   */
    -  public abstract void begin(InterpretationContext ic, String name,
    -      Attributes attributes) throws ActionException;
    -
    -  /**
    -   * Called to pass the body (as text) contained within an element.
    -   */
    -  public void body(InterpretationContext ic, String body)
    -      throws ActionException {
    -    // NOP
    -  }
    -
    -  /*
    -   * Called when the parser encounters an endElement event matching a
    -   * {@link ch.qos.logback.core.joran.spi.Pattern Pattern}.
    -   */
    -  public abstract void end(InterpretationContext ic, String name)
    -      throws ActionException;
    -}</pre>
    -
    -   <p>ごらんのように、アクションは<code>begin()</code>メソッドと<code>end()</code>メソッドを実装しなければなりません。<code>body()</code>メソッドを実装するかどうかは選択可能ですが。<code>Action</code>クラスは空実装を提供しているからです。</p>
    -
    -
    -   <h3 class="doAnchor" name="ruleStore">RuleStore</h3>
    -
    -   <p>前述のように、パターンマッチと関連するアクションの実行がJoranの中心的な考え方です。ルールはパターンとアクションを関連付けるものです。そして<a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/spi/RuleStore.html">RuleStore</a>に保存されます。
    -   </p>
    -
    -   <p>前述したとり、JoranはSAX APIを使っています。SAX APIとは、XMLドキュメントのそれぞれの要素についてstart/body/endというイベントを生成しながらパースを進めていくものです。Joranコンフィギュレーターは、イベントを受け付けると<em>今のパターン</em>に対応したアクションをルールストアから探してきます。たとえば、最上位要素<em>A</em>にネストされた要素<em>B</em>のstart/body/endイベントなら、今のパターンとは"A/B"になります。今のパターンとは、JoranがSAXイベントを受け付けながら自動的に調整するデータ構造なのです。</p>
    -
    -   <p>今のパターンにいくつかのルールがマッチするときは、正確なマッチが接尾辞マッチより優先されます。そして接尾辞マッチは接頭辞マッチより優先されます。実装の詳細については<a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/spi/SimpleRuleStore.html">SimpleRuleStoreの</a>を参照してください。
    -   </p>
    -   
    -
    -   <h3 class="doAnchor" name="interpretationContext">解釈コンテキスト</h3>
    -
    -   <p>いろいろなアクションが協調して動作させるため、beginメソッドとendメソッドの一つ目の引数に解釈コンテキストが渡されます。解釈コンテキストには、オブジェクトスタック、オブジェクトマップ、エラーリスト、アクションを呼び出したJoranインタプリタへの参照が含まれています。解釈コンテキストの完全なフィールドが知りたければ<a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/spi/InterpretationContext.html"><code>InterpretationContext</code></a>を見てください。
    -   </p>
    -   
    -   <p>アクションは、共通のオブジェクトスタックに対するフェッチ、プッシュ、ポップといった操作や、共通のオブジェクトマップに対してキーと共にオブジェクトをプットしたりフェッチしたりすることで、他のアクションと共同作業をすることができます。また、解釈コンテキストの<code>StatusManager</code>にエラーを追加することで、問題が起きたことを報告することができます。
    -   </p>
    -   
    -   <h3 class="doAnchor" name="helloWorld">こんにちは</h3>
    -   
    -   <p>最初に、Joranを使うために必要最低限の構成を見てもらいます。<code><a href="http://logback.qos.ch/xref/chapters/onJoran/helloWorld/HelloWorldAction.html">HelloWorldAction</a></code>は、<code>begin()</code>でコンソールに"Hello World"と出力するだけの小さなアクションです。XMLファイルはコンフィギュレーターでパースします。この章の説明用に、非常に小さくて単純な<a href="http://logback.qos.ch/xref/chapters/onJoran/SimpleConfigurator.html"><code>SimpleConfigurator</code></a>というコンフィギュレーターを用意しました。<a href="http://logback.qos.ch/xref/chapters/onJoran/helloWorld/HelloWorld.html"><code>HelloWorld</code></a>アプリケーションはこれらの部品を全部使用します。</p>
    -
    -   <ul>
    -     <li>ルールマップと<code>Context</code>を用意します</li>
    -     <li><code>HelloWorldAction</code>と<em>hello-world</em>パターンを関連付けて、パースルールを用意します</li>
    -     <li><code>SimpleConfigutator</code>を用意して、ルールマップを渡します</li>
    -     <li>XMLファイルを引数として、コンフィギュレーターの<code>doConfigure()</code>メソッドを呼び出します</li>
    -     <li>最後に、もしあれば解釈コンテキストに蓄積されたステータスメッセージを出力します</li>
    -   </ul>
    -
    -    <p><em>hello.xml</em>には何もネストしていない1つのhello-world要素があります。<em>logback-examples/src/main/java/chapters/onJoran/helloWorld/</em>フォルダを参照してください。
    -    </p>
    - 
    -    <p><em>hello.xml</em>ファイルを指定してHelloWorldアプリケーションを実行すると、コンソールに"Hello World"と出力します。</p>
    -   
    -    <p class="command">java chapters.onJoran.helloWorld.HelloWorld src/main/java/chapters/onJoran/helloWorld/hello.xml</p>
    -
    -    <p>ルールストアに新しいルールを追加したり、XMLドキュメントを変更してみたり、新しいアクションを追加するなど、いろいろと試してみたくなったでしょう?
    -    </p>
    -
    -    <!-- ====================================================== -->
    -
    -    <h3 class="doAnchor" name="calculator">アクションの協調</h3>
    -   
    -    <p>共通のオブジェクトスタックを通じて協調して簡単な計算をするアクションが、<em>logback-examples/src/main/java/joran/calculator/</em>ディレクトリに入っています。
    -    </p>
    -
    -    <p><em>calculator1.xml</em>を見ると、<code>literal要素</code>をネストした<code>computation要素</code>があるので見てみましょう。
    -    </p>
    -
    -    <p class="example">例10:Calculatorの設定例(<a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/calculator1.xml">logback-examples/src/main/java/chapters/onJoran/calculator/calculator1.xml</a>)</p>
    -
    -    <pre class="prettyprint source">&lt;computation name="total"&gt;
    -  &lt;literal value="3"/&gt;
    -&lt;/computation&gt;</pre>
    -
    -    <p><code><a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/Calculator1.html">Calculator1</a></code>アプリケーションでは、さまざまな解析ルール(パターンとアクション)を宣言しています。これらはXMLドキュメントの内容に基づき、協調して結果を算出するものです。
    -    </p>
    -
    -    <p><em>calculator1.xml</em>を指定して<code>Calculator1</code>を実行してみましょう。</p>
    -
    -    <p class="command">java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator1.xml</p>
    -
    -    <p>次のように出力されます。</p>
    -
    -    <p class="console">The computation named [total] resulted in the value 3</p>
    -
    -
    -    <p>上記の<em>calculator1.xml</em>は次のように解釈されます。</p>
    -
    -    <ul>
    -      <li>computation要素のstartイベントが、"/computation" パターンとみなされます。<code><a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/Calculator1.html">Calculator1</a></code>アプリケーションは"/computation"パターンと<a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/ComputationAction1.html"><code>ComputationAction1</code></a>を関連付けています。ですので、<code>ComputationAction1</code>のインスタンスの<code>begin()</code>メソッドが実行されます。
    -      </li>
    -
    -      <li><p>literal要素のstartイベントが、"/computation/literal" パターンとみなされます。"/computation/literal" パターンは<code><a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/LiteralAction.html">LiteralAction</a></code>と関連付けられています。ですので、<code>LiteralAction</code>のインスタンスの<code>begin()</code>メソッドが実行されます。</p>
    -      </li>
    -
    -      <li><p>また、literal要素のendイベントより、<code>LiteralAction</code>のインスタンスの<code>end()</code>メソッドを実行されます。</p>
    -      </li>
    -
    -
    -      <li><p>同様に、computation要素のendイベントより、<code>ComputationAction1</code>のインスタンスの<code>end()</code>メソッドが実行されます。
    -      </p>
    -      </li>
    -    </ul>
    -
    -    <p>ここで注目して欲しいのは、アクションがどのように協調しているのかということです。<code>LiteralAction</code>は設定ファイルからリテラル値を読み取り、<code>InterpretationContext</code>の保持しているオブジェクトスタックに登録します。オブジェクトスタックに登録された値は、他のアクションから読み書きすることができるようになります。ここでは、 <code>ComputationAction1</code>の<code>end()</code>メソッドが、オブジェクトスタックから値をポップして、出力しています。
    -    </p>
    -
    -    <!-- TO BE CONTINUED -->
    -
    -    <p>次に<em>calculator2.xml</em>を見てみましょう。前の例よりも少し複雑で、面白いことをしています。</p>
    -
    -    <p class="example">例10:Calculatorの設定例(<a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/calculator2.xml">logback-examples/src/main/java/chapters/onJoran/calculator/calculator2.xml</a>)</p>
    -
    -  <pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;literal value="7"/&gt;
    -  &lt;literal value="3"/&gt;
    -  &lt;add/&gt;
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -
    -  <p>前の例と同じく、literal要素に対応する<code><a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/LiteralAction.html">LiteralAction</a></code>は、解釈コンテキストのオブジェクトスタックに整数値を登録します。ここ(<em>calculator2.xml</em>)ではまず7と3が登録されています。add要素には<a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/AddAction.html"><code>AddAction</code></a>が関連付けられています。これはオブジェクトスタックから二回整数値をポップして、それらを加算した結果をオブジェクトスタックにプッシュするものです。次のliteral要素に対応するLiteralActionは、オブジェクトスタックの一番上に整数値3をプッシュします。multiply要素には<a href="http://logback.qos.ch/xref/chapters/onJoran/calculator/MultiplyAction.html"><code>MultiplyAction</code></a>が関連付けられています。これはオブジェクトスタックから二回整数値をポップして、それらをかけあわせた結果をオブジェクトスタックにプッシュするものです。ここでは、計算結果の30がオブジェクトスタックの一番上にプッシュされることになります。一番最後に、computation要素に関連付けられたComputationAction1(のend()メソッド)によって、オブジェクトスタックの一番上の値が出力されます。実行してみましょう。</p>
    -
    -  <p class="command">java chapters.onJoran.calculator.Calculator1 src/main/java/chapters/onJoran/calculator/calculator2.xml </p>
    -  
    -  <p>そうすると、次のように出力されます。</p>
    -
    -  <p class="console">The computation named [toto] resulted in the value 30 </p>
    -  
    -
    -  <!--
    -
    -
    -  <p>Finally, a <em>calculator3.xml</em> is also provided, to
    -  demonstrate the possibility elements that contain instances of the
    -  same element. Here's the content of <em>calculator3.xml</em>:</p>
    -
    -  <em>Example 10.<span class="autoEx"/>: Calculator configuration file
    -  (logback-examples/src/main/java/chapters/onJoran/calculator/calculator3.xml)</em>
    -
    -<pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;computation&gt;
    -    &lt;literal value="7"/&gt;
    -    &lt;literal value="3"/&gt;
    -    &lt;add/&gt;
    -  &lt;/computation&gt;   
    - 
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -  <p>Much like the use of parentheses in an algebrical equation, the
    -  presence of a <code>computation</code> element nested in another is
    -  managed by the <a
    -  href="../xref/chapters/onJoran/calculator/ComputationAction2.html">
    -  <code>ComputationAction2</code></a> class using an internal
    -  stack. The well-formedness of XML will guarantee that a value saved
    -  by one <code>begin()</code> will be consumed only by the matching
    -  <code>end()</code> method.</p>
    -  -->
    -
    -  <h3 class="doAnchor" name="implicit">暗黙的なアクション</h3>
    -
    -  <p>ここまでに紹介してきたルールの定義は明示的なアクションと呼ばれます。現在のxml要素に対応するパターンとアクションのペアを、ルールストアから1つだけ取り出すことができるからです。しかし、高度な拡張性を備えたシステムにおいて、コンポーネントの種類は膨大な数になります。それゆえに、すべてのパターンに明示的なアクションを関連付けるのはとても面倒なことになるのです。
    -  </p>
    -
    -  <p>そうは言っても、高度な拡張性を備えたシステムなら、ルールに付随するコンポーネントそれぞれに結びついたルールを、循環して見つけることができます。そのようにルールを発見できるとすると、logbackの設定ファイルをパースする時点では未知のコンポーネントが含まれるコンポーネントを扱うことができるようになります。たとえば、Apacne Ant では、<code>addFile</code>や<code>addClassPath</code>といったコンポーネントを発見するメソッドを使うことで、設定ファイルをパースする時点では未知のタグを含んだタスクを扱えるようになっています。Antはタスクの処理中に未知のタグを発見すると、タグ名に基づいたクラスのオブジェクトを生成し、タスクの実装クラスに宣言されているaddXメソッドを呼び出して、親のオブジェクトに登録します。
    -  </p>
    -
    -  <p>Joranは暗黙的なアクションとして同じような機能を実現しています。現在のパターンが明示的なアクションにマッチしなかった時のために、暗黙的なアクションの一覧を保持するようになっています。しかし、暗黙的なアクションを適用することが必ずしも適切ではない場合があります。そこで、Joranは暗黙的なアクションを実行する前に、現在の状況が妥当であるかどうかを確認するようになっています。Joranからの確認に対して、これから実行されようとするアクションが肯定を返すときだけ、アクションを呼び出します。この例外対応によって、複数の暗黙的なアクションを備えつつ、適切なアクションが無ければ何もしないことの両方のケースに対応できるのです。
    -  </p>
    -
    -  <p>暗黙的なアクションの作成例が<em>logback-examples/src/main/java/chapters/onJoran/implicit</em>にあります。
    -  </p>
    -
    -  <p><a href="http://logback.qos.ch/xref/chapters/onJoran/implicit/PrintMe.html"><code>PrintMe</code></a>アプリケーションでは、"/*/foo" パターンと<a href="http://logback.qos.ch/xref/chapters/onJoran/implicit/NOPAction.html"><code>NOPAction</code></a>アクションを関連付けています。このパターンは任意のfoo要素にマッチします。<code>NOPAction</code>の<code>begin()</code>メソッドと<code>end()</code>メソッドは、名前のとおり空っぽです。<code>PrintMe</code>アプリケーションは、暗黙的なアクションの一覧に<a href="http://logback.qos.ch/xref/chapters/onJoran/implicit/PrintMeImplicitAction.html">PrintMeImplicitAction</a>も登録しています。<code>PrintMeImplicitAction</code>は、<span class="attr">printme属性</span>にtrueを指定されたあらゆる要素について適用可能なアクションです。<code>PrintMeImplicitAction</code>の<code>isApplicable()</code>メソッドを見ておいてください。<code>PrintMeImplicitAction</code>の<code>begin()</code>メソッドは、現在の要素の名前をコンソールに出力します。
    -  </p>
    -
    -  <p>暗黙的なアクションがどのように振る舞うのか、<em>implicit1.xml</em>で試してみましょう。</p>
    -
    -  <p class="example">例10: 暗黙的なルールの使い方(<a href="http://logback.qos.ch/xref/chapters/onJoran/implicit/implicit1.xml">logback-examples/src/main/java/chapters/onJoran/implicit/implicit1.xml</a>)</p>
    -
    -  <pre class="prettyprint source">&lt;foo&gt;
    -  &lt;xyz printme="true"&gt;
    -    &lt;abc printme="true"/&gt;
    -  &lt;/xyz&gt;
    -
    -  &lt;xyz/&gt;
    -
    -  &lt;foo printme="true"/&gt;
    -
    -&lt;/foo&gt;</pre>
    -
    -  <p>実行してみましょう。</p>
    -
    -  <p class="command">java chapters.onJoran.implicit.PrintMe src/main/java/chapters/onJoran/implicit/implicit1.xml</p>
    -  <p>次のように出力されます。</p>
    -
    -  <p class="console">Element [xyz] asked to be printed.
    -Element [abc] asked to be printed.
    -20:33:43,750 |-ERROR in c.q.l.c.joran.spi.Interpreter@<b>10:9</b> - no applicable action for [xyz], current pattern is [[foo][xyz]]</p>
    -
    -  <p><code>NOPAction</code>が"*/foo"パターンに関連付けられているので、foo要素について<code>NOPAction</code>の<code>begin()</code>メソッドと<code>end()</code>メソッドが実行されているはずです。そのため、foo要素については<code>PrintMeImplicitAction</code>は呼び出されないのです。他の明示的なアクションがマッチしない要素について、<code>PrintMeImplicitAction{\0}の<code>isApplicable()</code></code>メソッドが呼び出されます。isApplicable()メソッドは、<span class="attr">printme属性</span>にtrueが指定されている場合にだけtrueを返します。したがって、最初のxyz要素とabc要素についてはtrueを返します。10行目の二つ目のxyz要素には適用可能なアクションがないので、内部エラーメッセージが出力されます。エラーメッセージは<code>StatusPrinter</code>のprint()メソッドが出力しています。
    -  </p>
    -
    -  <h3 class="doAnchor" name="iaPractice">暗黙的なアクションの実装</h3>
    -  
    -  <p>logback-classic モジュールと logback-access モジュールのそれぞれの Joran コンフィギュレーターには、暗黙的なアクションが2つだけ含まれています。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/action/NestedBasicPropertyIA.html">NestedBasicPropertyIA</a></code>と<a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.html"><code>NestedComplexPropertyIA</code></a>です。</p>
    -
    -  <p><code>NestedBasicPropertyIA</code>は、プリミティブ型あるいはそのラッパークラス、列挙型、"valueOf" 規約に則った任意のクラスのプロパティに適用可能なアクションです。このアクションが適用可能なプロパティは<em>基本型</em>あるいは<em>単純型</em>と呼ばれています。"valueOf" 規約に則るというのは、<code>java.lang.String</code>を引数とする静的メソッド<code>valueOf()</code>によってインスタンス化できる、ということです。<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/Level.html"><code>Level</code></a>や<a href="http://logback.qos.ch/xref/ch/qos/logback/core/util/Duration.html"><code>Duration</code></a>、<a href="http://logback.qos.ch/xref/ch/qos/logback/core/util/FileSize.html"><code>FileSize</code></a>がこの規約に従っています。
    -  </p>
    -  
    -  <p><code>NestedComplexPropertyIA</code>は、<code>NestedBasicPropertyIA</code>が適用できない場合に、<em>かつ</em>、オブジェクトスタックの一番上のオブジェクトに、現在の要素名に対応するセッターあるいはアダーメソッドがある場合に適用可能なアクションです。このアクションが適用可能なプロパティは、更に他のコンポーネントを内包することがあるので注意しましょう。このアクションが適用可能なプロパティは<em>複雑型</em>と呼ばれています。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/action/NestedComplexPropertyIA.html">NestedComplexPropertyIA</a></code>が複雑型のプロパティを見つけると、ネストされたコンポーネントに対応する適切なクラスをインスタンス化して、親のコンポーネント(オブジェクトスタックの一番上のオブジェクト)に設定します。<span class="attr">class属性</span>でインスタンス化するクラスを指定することができます。<span class="attr">class属性</span>が指定されていない場合、次のいずれかを条件に従ってクラス名が決定されます。</p>
    -
    -  <ol>
    -    <li>親のオブジェクトのプロパティのクラスを決定する内部的なルールに基づいて決められたクラス名</li>
    -    <li>セッターメソッドの@DefaultClassアノテーションに指定あされ</li>
    -
    -    <li>セッターメソッドの引数が公開コンストラクタを持つ具象クラスならそのクラス名</li>
    -  </ol>
    -
    -  <h4 class="doAnchor" name="defaultClassMapping">デフォルトのクラスマッピング</h4>
    -
    -  <p>logback-classic では、親のクラスとプロパティ名に対応するデフォルトのクラスを規定した内部的なルールがあります。表にまとめました。</p>
    -
    -  <table class="bodyTable">
    -    <tr>
    -      <th>親クラス</th>
    -      <th>プロパティ名</th>
    -      <th>ネストするコンポーネントのデフォルトクラス</th>
    -    </tr>
    -
    -    <tr>
    -      <td>ch.qos.logback.core.AppenderBase</td>
    -      <td>encoder</td>
    -      <td>ch.qos.logback.classic.encoder.PatternLayoutEncoder</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td>ch.qos.logback.core.UnsynchronizedAppenderBase</td>
    -      <td>encoder</td>
    -      <td>ch.qos.logback.classic.encoder.PatternLayoutEncoder</td>
    -    </tr>
    -
    -      <tr>
    -      <td>ch.qos.logback.core.AppenderBase</td>
    -      <td>layout</td>
    -      <td>ch.qos.logback.classic.PatternLayout</td>
    -    </tr>
    -
    -    <tr class="alt">
    -      <td>ch.qos.logback.core.UnsynchronizedAppenderBase</td>
    -      <td>layout</td>
    -      <td>ch.qos.logback.classic.PatternLayout</td>
    -    </tr>
    -
    -    <tr>
    -      <td>ch.qos.logback.core.filter.EvaluatorFilter</td>
    -      <td>evaluator</td>
    -      <td>ch.qos.logback.classic.boolex.JaninoEventEvaluator</td>
    -    </tr>
    -  </table>
    -
    -  <p>これらは将来的に変更されるかもしれません。最新のルールについては、logback-classic モジュールの<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/joran/JoranConfigurator.html">JoranConfigurator</a>の<code>addDefaultNestedComponentRegistryRules()</code>メソッドを参照してください。
    -  </p>
    -
    -  <p>logback-accessモジュールのルールも似たようなものです。ネストするコンポーネントのデフォルトクラスについては、パッケージ名を ch.qos.logback.classic から ch.qos.logback.access に読み替えてください。最新のルールについては、logback-access モジュールの<a href="http://logback.qos.ch/xref/ch/qos/logback/access/joran/JoranConfigurator.html">JoranConfigurator</a>の<code>addDefaultNestedComponentRegistryRules()</code>メソッドを参照してください。
    -
    -  </p>
    -  
    -  <h4 class="doAnchor">コレクション型のプロパティ</h4>
    -
    -  
    -  <p>logbackの暗黙的なアクションは、単独の基本型プロパティ、複雑型プロパティに加えて、コレクション型のプロパティにも対応しています。ただし、セッターメソッドの代わりに、アダーメソッドを用意する必要があります。</p>
    -
    -  <h3 class="doAnchor" name="newRule">その場で新しいルールを定義する</h3>
    -
    -  <p>Joranには、XMLドキュメントを解釈している途中でも、Joranコンフィギュレーターに新しいルールを教えるためのアクションが含まれています。サンプルコードが<em>logback-examples/src/main/java/chapters/onJoran/newRule</em>ディレクトリにあります。<code><a href="http://logback.qos.ch/xref/chapters/onJoran/newRule/NewRuleCalculator.html">NewRuleCalculator</a></code>アプリケーションは、二つのルールを定義しています。1つは最上位要素を処理するためのもので、二つ目は動的に新しいルールを定義するためのものです。<code>NewRuleCalculator</code>の関連するコードをピックアップしました。
    -  </p>
    -
    -  <pre class="prettyprint source">ruleMap.put(new Pattern("*/computation"), new ComputationAction1());
    -<b>ruleStore.addRule(new Pattern("/computation/newRule"), new NewRuleAction());</b></pre>
    -
    -  <p><a href="http://logback.qos.ch/xref/ch/qos/logback/core/joran/action/NewRuleAction.html"><code>NewRuleAction</code></a>はlogback-coreの一部で、他のアクションと同じように動作します。パーサーが<em>newRule要素</em>を見つけるたびに、このアクションの<code>begin()</code>メソッドと<code>end()</code>メソッドが呼び出されます。<code>begin()</code>メソッドは、 <em>pattern属性</em>と<em>actionClass属性</em>を探します。その後、対応するアクションクラスをインスタンス化し、Joranのルールストアにパターンとアクションの関連付けを新しいルールとして追加します。</p>
    -
    -
    -  <p>XMLドキュメント中では次のように新しいルールを宣言します。</p>
    -
    -  <pre class="prettyprint source">&lt;newRule pattern="*/computation/literal"
    -          actionClass="chapters.onJoran.calculator.LiteralAction"/&gt;</pre>
    -
    -  <p>newRule宣言を使って、<code>NewRuleCalculator</code>に<code>Calculator1</code>のような振る舞いをさせることができます。</p>
    -
    -  <p class="example">例10: 動的にルールを定義する設定例(<a href="http://logback.qos.ch/xref/chapters/onJoran/newrule/newRule.xml">logback-examples/src/main/java/chapters/onJoran/newrule/newRule.xml</a>)</p>
    -
    -  <pre class="prettyprint source">&lt;computation name="toto"&gt;
    -  &lt;newRule pattern="*/computation/literal" 
    -            actionClass="chapters.onJoran.calculator.LiteralAction"/&gt;
    -  &lt;newRule pattern="*/computation/add" 
    -            actionClass="chapters.onJoran.calculator.AddAction"/&gt;
    -  &lt;newRule pattern="*/computation/multiply" 
    -            actionClass="chapters.onJoran.calculator.MultiplyAction"/&gt;
    -
    -  &lt;computation&gt;
    -    &lt;literal value="7"/&gt;
    -    &lt;literal value="3"/&gt;
    -    &lt;add/&gt;
    -  &lt;/computation&gt;   
    - 
    -  &lt;literal value="3"/&gt;
    -  &lt;multiply/&gt;
    -&lt;/computation&gt;</pre>
    -
    -
    -  <p class="command">実行してみましょう。
    -java chapters.onJoran.newRule.NewRuleCalculator src/main/java/chapters/onJoran/newRule/newRule.xml</p>
    -
    -  <p>次のように出力されます。</p>
    -
    -  <p class="console">The computation named [toto] resulted in the value 30</p>
    -
    -  <p>これは<a href="./10-onJoran.html#calculator">元のCalculator1</a>の出力とまったく同じです。</p>
    -
    -
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/receivers.html b/logback-site/src/site/pages/manual/receivers.html
    deleted file mode 100755
    index fa34eed90a..0000000000
    --- a/logback-site/src/site/pages/manual/receivers.html
    +++ /dev/null
    @@ -1,547 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 14: Receivers</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"/>    
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">      
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>    
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -
    -    <div id="content">
    -
    -      <h1>Chapter 14: Receivers</h1>
    -
    -    <a href="receivers_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -
    -      <div class="quote">
    -
    -        <p><em>You cannot swim for new horizons until you have courage 
    -           to lose sight of the shore.</em></p>
    -  
    -        <p>&mdash;WILLIAM FAULKNER</p>
    -      </div>
    -
    -
    -      <script src="../templates/creative.js" type="text/javascript"></script>
    -      <script src="../templates/setup.js" type="text/javascript"></script>
    -    
    -      <h2 class="doAnchor" name="whatIsAReceiver">What is a Receiver?</h2>
    -    
    -      <p>A <em>receiver</em> is a Logback component that receives logging
    -      events from a remote appender and logs each received event according
    -      to local policy.  Using a combination of socket-based appenders and
    -      receivers, it is possible to construct sophisticated topologies
    -      for distribution of application logging events over a network.</p>
    -  
    -      <p>A receiver extends the <a
    -      href="../xref/ch/qos/logback/classic/net/ReceiverBase.html">
    -      <code>ch.qos.logback.classic.net.ReceiverBase</code></a> class.
    -      By virtue of the fact that a receiver extends this class, a
    -      receiver participates in the Logback component <a
    -      href="../xref/ch/qos/logback/core/spi/LifeCycle.html">LifeCycle</a>
    -      and a receiver is <a
    -      href="../xref/ch/qos/logback/core/spi/ContextAware.html">
    -      ContextAware</a>.</p>
    -    
    -      <p>Historically, support for logging event delivery over a network
    -      connection in Logback has been provided by <code>SocketAppender</code>
    -      and the corresponding <code>SimpleSocketServer</code>.  The appender
    -      acts as a client, initiating a network connection to the server
    -      application, and delivering logging events via the network connection.  
    -      The receiver component and corresponding appender support offers much 
    -      greater flexibility.</p>
    -    
    -      <p>A receiver component is configured in <em>logback.xml</em>, just
    -      like any other logback component.  This allows the full capabilities
    -      of <a href="onJoran.html">Joran</a> to be utilized in configuring
    -      a receiver component.  Moreover, <em>any</em> application can 
    -      receive logging events from remote appenders by simply configuring
    -      one or more receiver components.</p>
    -    
    -      <p>Connection initiation between an appender and a receiver 
    -      can occur in either direction.  A receiver can act in the role of a
    -      server, passively listening for connections from remote appender 
    -      clients.  Alternatively, a receiver can act in the client role,
    -      initiating a connection to a remote appender which is acting in the 
    -      server role.  Regardless of the respective roles of the
    -      appender and receiver, <em>logging events always flow from 
    -      the appender towards the receiver</em>.</p>
    -    
    -      <p>The flexibility to allow a receiver to initiate the connection
    -      to an appender is particularly useful in certain situations:
    -      </p>
    -      <ul>
    -        <li>For security reasons, a central logging server may be
    -          located behind a network firewall that does not allow incoming
    -          connections.  Using receiver components acting in the client
    -          role, the central logging server (inside the firewall) 
    -          can initiate connections to the applications of interest 
    -          (outside the firewall).
    -        </li>
    -        <li>It is often desirable for developer tools (such as IDE plugins)
    -          and enterprise management applications to have access to the
    -          logging event stream of running applications.  Traditionally,
    -          Logback has supported this (for example in Logback Beagle) by
    -          requiring the recipient application (e.g. a developer tool running
    -          in an IDE) to act in the server role, passively listening for 
    -          connections from a remote appender.  This can prove difficult to 
    -          manage, especially for tools running on a developer's workstation, 
    -          which may indeed by mobile.  However, such tools can now be 
    -          implemented using a Logback receiver component acting in the 
    -          client role, initiating a connection to a remote appender in 
    -          order to receive logging events for local display, filtering, 
    -          and alerting.
    -        </li>
    -      </ul>
    -
    -      <p>A logback configuration can include any number of receiver components
    -      acting in any combination of the server or client roles.  The only 
    -      restrictions are that each receiver acting in the server role must
    -      listen on a distinct port, and each receiver acting in the client
    -      role will connect to exactly one remote appender.</p>
    -    
    -      <h2 class="doAnchor" name="receiverServerComponents">Receivers
    -      that Act in the Server Role</h2>
    -    
    -      <p>A receiver that is configured to act in the server role passively
    -      listens for incoming connections from remote appenders.  This is 
    -      functionally equivalent to using the standalone
    -      <code>SimpleSocketServer</code> application, except that by using
    -      the receiver component, <em>any</em> application that uses Logback
    -      Classic can receive logging events from remote appenders by simply
    -      configuring the receiver in <em>logback.xml</em>.</p>
    -        
    -      <p>
    -        <img border="1" src="images/chapters/receivers/serverSocketReceiver.png" "/>
    -      </p>
    -
    -      <p>Logback includes two receiver components that act in the
    -      server role; <a
    -      href="../xref/ch/qos/logback/classic/net/server/ServerSocketReceiver.html">
    -      <code>ServerSocketReceiver</code></a> and its SSL-enabled
    -      subtype
    -      <a href="../xref/ch/qos/logback/classic/net/server/SSLServerSocketReceiver.html">
    -      <code>SSLServerSocketReceiver</code></a>.  Both of these receiver
    -      components are designed to accept connections from incoming
    -      <code>SocketAppender</code> (or <code>SSLSocketAppender</code>)
    -      clients.</p>
    -        
    -      <p>The <code>ServerSocketReceiver</code> components provide the 
    -      following configurable properties:</p>
    -    
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">address</span></td>
    -          <td><code>String</code></td>
    -          <td>The local network interface address on which the receiver
    -          will listen.  If this property is not specified, the receiver
    -          will listen on all network interfaces.</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">port</span></td>
    -          <td><code>int</code></td>
    -          <td>The TCP port on which the receiver will listen.  If this
    -          property is not specified, a default value will be used.</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">ssl</span></td>
    -          <td><code>SSLConfiguration</code></td>
    -          <td>Supported only for <code>SSLServerSocketReceiver</code>, this
    -              property provides the SSL configuration that will be used by
    -              the receiver, as described in <a href="usingSSL.html">Using SSL</a>.
    -          </td>
    -        </tr>
    -      </table>
    -    
    -      <h3 class="doAnchor" name="usingServerSocketReceiver">Using
    -      ServerSocketReceiver</h3>
    -    
    -      <p>The following configuration uses the 
    -      <code>ServerSocketReceiver</code> component with a minimal local 
    -      appender and logger configuration.  Logging events received from
    -      a remote appender will be matched by the root logger and delivered
    -      to the local console appender.</p>
    -    
    -      <p class="example">Example: Basic ServerSocketReceiver Configuration
    -      (logback-examples/src/main/resources/chapters/receivers/socket/receiver1.xml)</p>
    -
    -      <span class="asGroovy" onclick="return asGroovy('receiver1');">View as .groovy</span>
    -  <pre id="receiver1" class="prettyprint source">&lt;configuration debug="true">
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.ServerSocketReceiver">
    -    &lt;port>${port}&lt;/port>
    -  &lt;/receiver>
    -
    -&lt;/configuration></pre>
    -  
    -      <p>Note that the receiver component's <em>class</em> 
    -      attribute identifies the receiver subtype that we wish to use.  In
    -      this example we are using <code>ServerSocketReceiver</code>.</p>
    -    
    -      <p>Our example server application is very similar in function and
    -      design to <code>SimpleSocketServer</code>.  It simply accepts a
    -      path for a logback configuration file as a command line argument,
    -      and runs the given configuration.  While our example is somewhat
    -      trivial, keep in mind that you can configure logback's 
    -      <code>ServerSocketReceiver</code> (or <code>SSLServerSocketReceiver</code>) 
    -      component in <em>any</em> application.
    -      </p>
    -    
    -      <p>From a shell in the <em>logback-examples</em> directory, 
    -      we can run our example server application as follows:</p>
    -    
    -      <p class="source">java -Dport=6000 <a href="../xref/chapters/receivers/socket/ReceiverExample.html">chapters.receivers.socket.ReceiverExample</a> \ 
    -      src/main/java/chapters/receivers/socket/receiver1.xml</p>
    -  
    -      <p>We can connect to the running receiver using a client application
    -      that is configured with a <code>SocketAppender</code>.  Our example
    -      client application simply loads a logback configuration that will
    -      connect a socket appender to our example receiver.  It then awaits
    -      input from the user in the form of a message that will be relayed to
    -      the receiver.  We can run the example client application as follows:
    -      </p>
    -    
    -      <p class="source">java -Dhost=localhost -Dport=6000 \
    -      <a href="../xref/chapters/receivers/socket/AppenderExample.html">chapters.receivers.socket.AppenderExample </a>\
    -      src/main/java/chapters/receivers/socket/appender1.xml</p>
    -    
    -      <h3 class="doAnchor" name="usingSSLServerSocketReceiver">Using
    -      SSLServerSocketReceiver</h3>
    -
    -      <p>The following configuration repeats the same minimal appender
    -      and logger configuration, but uses the SSL-enabled receiver component
    -      that acts in the server role.</p>
    -
    -      <p class="example">Example: Basic SSLServerSocketReceiver Configuration
    -      (logback-examples/src/main/resources/chapters/receivers/socket/receiver2.xml)</p>
    -      <span class="asGroovy" onclick="return asGroovy('receiver2');">View as .groovy</span>
    -  <pre id="receiver2" class="prettyprint source">&lt;configuration debug="true">
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver">
    -    &lt;port>${port}&lt;/port>
    -    &lt;ssl>
    -      &lt;keyStore>
    -        &lt;location>${keystore}&lt;/location>
    -        &lt;password>${password}&lt;/password>
    -      &lt;/keyStore>
    -    &lt;/ssl>
    -  &lt;/receiver>
    -
    -&lt;/configuration></pre>
    -
    -      <p>The essential differences between this configuration and the
    -      previous example using <code>ServerSocketReceiver</code> are the 
    -      specification of <code>SSLServerSocketReceiver</code> in the
    -      <em>class</em> attribute and the presence of the nested 
    -      <span class="prop">ssl</span> property, which is used here to 
    -      specify the location and password for the key store containing the 
    -      receiver's private key and certificate, using substitution variables. 
    -      See <a href="usingSSL.html">Using SSL</a> for details on 
    -      configuring SSL properties for Logback components.</p>    
    - 
    -      <p>We can run this configuration using the same example server
    -      configuration, with just a couple of additional configuration
    -      properties:</p>
    -
    -      <p class="source">java -Dport=6001 \
    -      -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver2.xml</p>
    -  
    -      <p>Note that the <em>keystore</em> property given on the command
    -      line specifies a file URL that identifies the location of the key 
    -      store.  You may also use a classpath URL as described in 
    -      <a href="usingSSL.html">Using SSL</a>.
    -      </p>
    -    
    -      <p>We can connect to the running receiver using a client application
    -      that is configured with a <code>SSLSocketAppender</code>.  We use 
    -      the sample example client application used in the previous example,
    -      with a configuration file that uses an SSL-enabled appender.  We
    -      run the example as follows:
    -      </p>
    -    
    -      <p class="source">java -Dhost=localhost -Dport=6001 \
    -      -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender2.xml</p>
    -    
    -      <p>Note that our example is using a self-signed X.509 credential that 
    -      is suitable for testing and experimentation, only.  <strong>In a 
    -      production setting, you should obtain an appropriate X.509 credential 
    -      to identify your SSL-enabled logback components</strong>.  See 
    -      <a href="usingSSL.html">Using SSL</a> for more information.</p>
    -  
    -      <h2 class="doAnchor" name="receiverClientComponents">Receivers
    -      that Act in the Client Role</h2>
    -    
    -      <p>A receiver that is configured to act in the client role initiates
    -      a connection to a remote appender.  The remote appender must be a
    -      server type, such as <code>ServerSocketAppender</code>.</p>  
    -    
    -      <p>
    -        <img border="1" src="images/chapters/receivers/socketReceiver.png"/>
    -      </p>
    -
    -      <p>Logback includes two receiver components that act in the client
    -      role; <a href="../xref/ch/qos/logback/classic/net/SocketReceiver.html">
    -      <code>SocketReceiver</code></a> and its SSL-enabled subtype
    -      <a href="../xref/ch/qos/logback/classic/net/SSLSocketReceiver.html">
    -      <code>SSLSocketReceiver</code></a>.  Both of these receiver
    -      components are designed to initiate a connection to a remote appender
    -      that is a <code>ServerSocketAppender</code> 
    -      (or <code>SSLServerSocketAppender</code>).</p>
    -        
    -      <p>The following configuration properties are supported by 
    -      <code>SocketReceiver</code> subtypes:</p>
    -    
    -      <table class="bodyTable striped">
    -        <tr>
    -        <th>Property Name</th>
    -        <th>Type</th>
    -        <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">remoteHost</span></td>
    -          <td><code>String</code></td>
    -          <td>The hostname or address of the remote server socket appender.</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">port</span></td>
    -          <td><code>int</code></td>
    -          <td>The port number of the remote server socket appender.</td>
    -        </tr> 
    -        <tr>
    -          <td><span class="prop" container="socket">reconnectionDelay</span></td>
    -          <td><code>int</code></td>
    -          <td>
    -            A positive integer representing the number of milliseconds to wait
    -            before attempting to reconnect after a connection failure.  The
    -            default value is 30000 (30 seconds).
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">ssl</span></td>
    -          <td><code>SSLConfiguration</code></td>
    -          <td>Supported only for <code>SSLSocketReceiver</code>, this
    -              property provides the SSL configuration that will be used for
    -              this receiver, as described in <a href="usingSSL.html">Using SSL</a>.
    -          </td>
    -        </tr>
    -      </table>
    -        
    -      <h3 class="doAnchor" name="usingSocketReceiver">Using
    -      SocketReceiver</h3>
    -
    -      <p>The configuration used for <code>SocketReceiver</code>
    -      is quite similar to the previous example that used
    -      <code>ServerSocketReceiver</code>.  The differences relate to the 
    -      fact that the roles of client and server are reversed; a receiver
    -      of type <code>SocketReceiver</code> is a client, and the remote
    -      appender acts as a server.</p>
    -        
    -      <p class="example">Example: Basic SocketReceiver Configuration
    -      (logback-examples/src/main/resources/chapters/receivers/socket/receiver3.xml)</p>
    -      <span class="asGroovy" onclick="return asGroovy('receiver3');">View as .groovy</span>
    -  <pre id="receiver3" class="prettyprint source">&lt;configuration debug="true">
    -    
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">    
    -    &lt;encoder>
    -      &lt;pattern>%date %-5level [%thread] %logger - %message%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>  
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.SocketReceiver">
    -    &lt;remoteHost>${host}&lt;/remoteHost>
    -    &lt;port>${port}&lt;/port>
    -    &lt;reconnectionDelay>10000&lt;/reconnectionDelay>
    -  &lt;/receiver>
    -
    -&lt;/configuration></pre>
    -        
    -      <p>This configuration will cause logback to connect to a 
    -      <code>ServerSocketAppender</code> running on the host and port specified
    -      by the <em>host</em> and <em>port</em> substitution variables.  Logging
    -      events received from the remote appender will be logged locally 
    -      (according to the configuration shown here) via a console appender.
    -      </p>
    -   
    -      <p>Assuming you are in the <em>logback-examples/</em> directory, 
    -      you can run this example configuration using the following command:</p>
    -
    -         
    -
    -      <p>The example loads the configuration and then simply waits for logging
    -      events from the remote appender.  If you run this example when the remote
    -      appender is not running, you'll see <em>connection refused</em> messages
    -      appearing in the log output, periodically.  The receiver will 
    -      periodically attempt to reconnect to the remote appender until it 
    -      succeeds or until the logger context is shut down.  The delay
    -      interval between attempts is configurable using the 
    -      <span class="prop">reconnectionDelay</span> property as shown in the
    -      example configuration.
    -      </p>
    -
    -      <p class="source">java -Dhost=localhost -Dport=6000 \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver3.xml</p>
    -
    -
    -      <p>We can provide a remote appender to which our example receiver
    -      can connect, using the same appender example used previously.  The 
    -      example loads a logback configuration containing a 
    -      <code>ServerSocketAppender</code>, and then waits input from the
    -      user consisting of a message that will be delivered to connected
    -      receivers.  We can run the example appender application as follows:
    -      </p>
    -
    -      <p class="source">java -Dport=6000 \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender3.xml</p>   
    -
    -      <p>If you enter a message to send when the receiver is not connected,
    -      note that the message is simply discarded.</p>
    -
    -      <h3 class="doAnchor" name="usingSSLSocketReceiver">Using
    -      SocketSSLReceiver</h3>
    -
    -
    -      <p>The configuration needed for <code>SSLSocketReceiver</code> is very 
    -      similar to that used with <code>SocketReceiver</code>.  The essential 
    -      differences are in the class specified for the receiver and the ability 
    -      to nest the <span class="prop">ssl</span> property to specify SSL 
    -      configuration properties.  The following example illustrates a basic 
    -      configuration:
    -      </p>
    -   
    -      <p class="example">Example: Basic SSLSocketReceiver Configuration
    -      (logback-examples/src/main/resources/chapters/receivers/socket/receiver4.xml)</p>
    -      <span class="asGroovy" onclick="return asGroovy('receiver4');">View as .groovy</span>
    -  <pre id="receiver4" class="prettyprint source">&lt;configuration debug="true">
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">    
    -    &lt;encoder>
    -      &lt;pattern>%date %-5level [%thread] %logger - %message%n&lt;/pattern>
    -    &lt;/encoder>         
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>  
    - 
    -  &lt;receiver class="ch.qos.logback.classic.net.SSLSocketReceiver">
    -    &lt;remoteHost>${host}&lt;/remoteHost>
    -    &lt;port>${port}&lt;/port>
    -    &lt;reconnectionDelay>10000&lt;/reconnectionDelay>
    -    &lt;ssl>
    -      &lt;trustStore>
    -        &lt;location>${truststore}&lt;/location>
    -        &lt;password>${password}&lt;/password>
    -      &lt;/trustStore>
    -    &lt;/ssl>
    -  &lt;/receiver>
    -
    -&lt;/configuration></pre>
    -
    -      <p>Note that the <em>class</em> attribute now specifies 
    -      <code>SSLSocketReceiver</code> and that in addition to the configuration
    -      properties shown in the previous example, this configuration contains
    -      an SSL configuration specifying the location and password for a 
    -      trust store that will be used in validating that the remote appender is
    -      trusted.  See <a href="usingSSL.html">Using SSL</a> for more information 
    -      on configuring SSL properties.
    -      </p>
    -  
    -      <p>You can run this example configuration using the following command:</p>
    -  
    -      <p class="source">java -Dhost=localhost -Dport=6001 \
    -      -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver4.xml</p>   
    -   
    -      <p>Once started, the receiver attempts to connect to the specified 
    -      remote appender.  Assuming that the appender is not yet running, you
    -      will see a "connection refused" message appearing in the log output
    -      periodically; the receiver will periodically retry the connection to
    -      the remote appender after delaying for the period of time specified by
    -      the <span class="prop">reconnectionDelay</span> property.
    -      </p>
    -  
    -      <p>We can provide a remote appender to which our example receiver
    -      can connect, using the same appender example used previously.  The 
    -      example loads a logback configuration containing a 
    -      <code>SSLServerSocketAppender</code>, and then awaits input from the  
    -      user consisting of a message that will be delivered to connected
    -      receivers.  We can run the example appender application as follows:
    -      </p>
    -
    -      <p class="source">java -Dport=6001 \
    -      -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender4.xml</p>   
    -
    -      <p>If you enter a message to send when the receiver is not connected,
    -      note that the message is simply discarded.</p>
    -
    -      <p>It is important to note once again that our example is using a 
    -      self-signed X.509 credential that is suitable for testing and 
    -      experimentation, only.  <strong>In a production setting, you should 
    -      obtain an appropriate X.509 credential to identify your SSL-enabled
    -      logback components</strong>.  See <a href="usingSSL.html">Using SSL</a> 
    -      for more information.</p>
    -  
    -        <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </div>
    -  </body>  
    -</html>
    diff --git a/logback-site/src/site/pages/manual/receivers_ja.html b/logback-site/src/site/pages/manual/receivers_ja.html
    deleted file mode 100644
    index d408c665be..0000000000
    --- a/logback-site/src/site/pages/manual/receivers_ja.html
    +++ /dev/null
    @@ -1,343 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第14章 レシーバー</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -
    -      <h1>第14章 レシーバー</h1>
    -
    -      <div class="quote">
    -
    -        <p><em>岸が見えなくなることを恐れない勇気が無ければ、地平線の向こうまで泳ぎ着くことはできない。</em></p>
    -  
    -        <p>—WILLIAM FAULKNER</p>
    -      </div>
    -
    -
    -    <script type="text/javascript" src="../templates/creative.js"></script>
    -    <script type="text/javascript" src="../templates/setup.js"></script>
    -    
    -      <h2 class="doAnchor" name="whatIsAReceiver">レシーバーとは何か?</h2>
    -    
    -      <p><em>レシーバー</em>とは、リモートアペンダーからロギングイベントを受信し、ローカルポリシーに基づいてログに記録するlogbackのコンポーネントです。ソケットベースのアペンダーとレシーバーを組み合わせることで、分散アプリケーションのロギングイベントをネットワーク越しに配信する高度なトポロジーを実現することができます。</p>
    -  
    -      <p>レシーバーは<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/ReceiverBase.html">ch.qos.logback.classic.net.ReceiverBase</a></code>クラスを継承しています。おかげで<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/LifeCycle.html">LifeCycle</a>に参加できるし、レシーバー自体が<a href="http://logback.qos.ch/xref/ch/qos/logback/core/spi/ContextAware.html">ContextAware</a>になります。</p>
    -    
    -      <p>歴史的な経緯により、logback によるネットワーク越しのロギングイベントの配信は<code>SocketAppender</code>と<code>SimpleSocketServer</code>によって実現されてきました。アペンダーはクライアントとして動作します。サーバーアプリケーションへのネットワーク接続を確立し、ロギングイベントを配信するのです。レシーバーコンポーネントと対応するアペンダーを使うことで、より優れた柔軟性を実現することができます。</p>
    -    
    -      <p>レシーバーコンポーネントは他のlogbackのコンポーネントと同様に<em>logback.xml</em>で設定します。つまり、<a href="./11-onJoran.html">Joran</a>の全ての機能を利用することができるのです。さらに、複数のレシーバーコンポーネントを設定するだけで、<em>あらゆる</em>アプリケーションのリモートアペンダーからロギングイベントを受け付けることができます。</p>
    -    
    -      <p>アペンダーとレシーバー間のネットワーク接続の確立はどちら側からでも始めることができます。レシーバーはサーバーとして振る舞うことができます。つまり、クライアントたるリモートアペンダーからの接続を待ち受けることができます。レシーバーはクライアントとして振る舞うこともできます。サーバーとして振る舞うリモートアペンダーに対してネットワーク接続を確立することができるのです。アペンダーとレシーバーのそれぞれの役割とは無関係に、<em>ロギングイベントは常にアペンダーからレシーバーに送信されます</em>。</p>
    -    
    -      <p>レシーバーからアペンダーに接続できることは、特定の状況においてとても有用です。</p>
    -      <ul>
    -        <li>セキュリティ上の観点から、中央ロギングサーバーはファイアーウォールの後ろ側に配置されることが多いです。つまり、外部からの接続が許されないのです。レシーバーコンポーネントがクライアントとして振る舞うことで、中央ロギングサーバー(ファイアーウォールの内側)から対象のアプリケーション(ファイアーウォールの外側)に接続することができます。
    -        </li>
    -        <li>IDEのプラグインなどの開発者ツールに最適です。また、実行中のアプリケーションが生成するロギングイベントのストリームへのアクセスを、企業統制の仕組みによって管理するのにも適しています。伝統的に、logbackでこのような状況に対応する(たとえばlogback Beagleなど)には、宛先のアプリケーション(たとえばIDEで実行される開発者ツール)がサーバーの役割を担う必要がありました。管理が大変になることは明らかです。特に、開発者のワークステーションで実行しているツールならなおさらです。モバイルPCが一般的になってきているのも悩みの種になります。今では、それらのツールはクライアントとしてのレシーバーコンポーネントを実装すればよくなりました。手元でロギングイベントをフィルタしたり、警告を上げたり、内容を確認するには、リモートアペンダーに接続すればよいのです。
    -        </li>
    -      </ul>
    -
    -      <p>logbackの設定には、サーバーあるいはクライアントのいずれかの役割を担うレシーバーコンポーネントを混在させることができます。数少ない制限として、サーバーとして振る舞うレシーバーコンポーネントはそれぞれ固有のポート番号で接続を待ち受けなければならないということと、クライアントとして振る舞うレシーバーコンポーネントはそれぞれただ1つのリモートアペンダーにしか接続してはならないということがあります。</p>
    -    
    -      <h2 class="doAnchor" name="receiverServerComponents">サーバーとして振る舞うレシーバー</h2>
    -    
    -      <p>サーバーとして振る舞うレシーバーはリモートアペンダーからの接続を待ち受けます。これはスタンドアローンの<code>SimpleSocketServer</code>アプリケーションと同じ機能です。ただし、レシーバーコンポーネントなら、<em>logback.xml</em>に設定するだけで、logback-classicモジュールを使っている<em>あらゆる{</em>\1}アプリケーションからロギングイベントを受信することができます。</p>
    -        
    -      <p>
    -        <img border="1" src="images/chapters/receivers/serverSocketReceiver.png">
    -      </p>
    -
    -      <p>logbackの配布物には二つのレシーバーコンポーネントが含まれています。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/server/ServerSocketReceiver.html">ServerSocketReceiver</a></code>とそのSSL対応の<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/server/SSLServerSocketReceiver.html"><code>SSLServerSocketReceiver</code></a>です。どちらも<code>SocketAppender</code>(あるいは<code>SSLSocketAppender</code>)からの接続を待ち受けるように設計されています。</p>
    -        
    -      <p><code>ServerSocketReceiver</code>の設定可能なプロパティは次のとおりです。</p>
    -    
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">address</span></td>
    -          <td><code>String</code></td>
    -          <td>レシーバーが待ち受けるローカルネットワークインターフェイスのネットワークアドレス。このプロパティが指定されない場合、レシーバーは全てのネットワークインターフェイスで待ち受けるようになります。</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">port</span></td>
    -          <td><code>int</code></td>
    -          <td>レシーバーが待ち受けるTCPのポート番号。このプロパティが指定されない場合、デフォルト値が使用されます。</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="serverSocketReceiver">ssl</span></td>
    -          <td><code>SSLConfiguration</code></td>
    -          <td><code>SSLServerSocketReceiver</code>でのみサポートされているプロパティです。このプロパティには<a href="./15-usingSSL.html">SSLを使用する</a>で説明されているSSLの設定を指定します。</td>
    -        </tr>
    -      </table>
    -    
    -      <h3 class="doAnchor" name="usingServerSocketReceiver">ServerSocketReceiverの使い方</h3>
    -    
    -      <p>次の設定は、最小限のアペンダーとロガーの設定とともに<code>ServerSocketReceiver</code>を使用するものです。リモートアペンダーから受信したロギングイベントは、ルートロガーに渡されて、ローカルのコンソールアペンダーに配信されます。</p>
    -    
    -      <p class="example">例:基本的なServerSocketReceiverの設定(<a href="http://logback.qos.ch/xref/chapters/receivers/socket/receiver1.xml">logback-examples/src/main/java/chapters/receivers/socket/receiver1.xml</a>)</p>
    -
    -      <span class="asGroovy" onclick="return asGroovy(&#39;receiver1&#39;);">Groovyとして表示</span>
    -  <pre id="receiver1" class="prettyprint source">&lt;configuration debug="true"&gt;
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.ServerSocketReceiver"&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -  &lt;/receiver&gt;
    -
    -&lt;/configuration&gt;</pre>
    -  
    -      <p>レシーバーコンポーネントの<em>class属性</em>に指定した値によって、レシーバーのサブタイプを指定します。ここでは<code>ServerSocketReceiver</code>を指定しています。</p>
    -    
    -      <p>例として、<code>SimpleSocketServer</code>と非常によく似た機能を持ったサーバーアプリケーションを用意しました。logbackの設定ファイルへのパスをコマンドライン引数から受け取って、指定された設定ファイルを読み込みます。この例のアプリケーションは大した仕事をするわけではありませんが、<code>ServerSocketReceiver</code> (あるいは<code>SSLServerSocketReceiver</code>)は<em>どんな</em>アプリケーションからでも利用できることを覚えておきましょう。
    -      </p>
    -    
    -      <p>コマンドをプロンプトで<em>logback-examplesディレクトリ</em>に移動して、次のコマンドを実行しましょう。</p>
    -    
    -      <p class="source">java -Dport=6000 <a href="http://logback.qos.ch/xref/chapters/receivers/socket/ReceiverExample.html">chapters.receivers.socket.ReceiverExample</a> \ 
    -      src/main/java/chapters/receivers/socket/receiver1.xml</p>
    -  
    -      <p>クライアントアプリケーションで<code>SocketAppender</code>を設定して、実行中のレシーバーに接続することができます。サンプルのクライアントアプリケーションは、上記のレシーバーに接続する設定ファイルを読み込むだけのものです。クライアントアプリケーションはユーザー入力を待ちます。ユーザー入力はそのままレシーバーに中継されます。クライアントアプリケーションは次のように実行します。</p>
    -    
    -      <p class="source">java -Dhost=localhost -Dport=6000 \
    -      <a href="http://logback.qos.ch/xref/chapters/receivers/socket/AppenderExample.html">chapters.receivers.socket.AppenderExample </a>\
    -      src/main/java/chapters/receivers/socket/appender1.xml</p>
    -    
    -      <h3 class="doAnchor" name="usingSSLServerSocketReceiver">SSLServerSocketReceiverの使い方</h3>
    -
    -      <p>次の設定は、前の設定とは違ってSSLに対応したレシーバーを使うものです。</p>
    -
    -      <p class="example">例:基本的なSSLServerSocketReceiverの設定(<a href="http://logback.qos.ch/xref/chapters/receivers/socket/receiver2.xml">logback-examples/src/main/java/chapters/receivers/socket/receiver2.xml</a>)</p>
    -
    -      <span class="asGroovy" onclick="return asGroovy(&#39;receiver2&#39;);">Groovyとして表示</span>
    -  <pre id="receiver2" class="prettyprint source">&lt;configuration debug="true"&gt;
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver"&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;ssl&gt;
    -      &lt;keyStore&gt;
    -        &lt;location&gt;${keystore}&lt;/location&gt;
    -        &lt;password&gt;${password}&lt;/password&gt;
    -      &lt;/keyStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/receiver&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -      <p>前の<code>ServerSocketReceiver</code>を使った設定とこちらの設定で根本的に違うのは、レシーバーの<em>class属性</em>に<code>SSLServerSocketReceiver</code>が指定されていることと、<span class="prop">ssl</span>プロパティがネストされていることです。sslプロパティにはレシーバーの秘密鍵と証明書が配置されたキーストアの場所とパスワードを、変数で指定しています。sslプロパティの設定内容について詳しくは<a href="./15-usingSSL.html">SSLを使用する</a>を参照してください。</p>    
    - 
    -      <p>いくつか引数を追加して次のようにサーバーアプリケーションを実行します。</p>
    -
    -      <p class="source">java -Dport=6001 \
    -      -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver2.xml</p>
    -  
    -      <p>コマンドラインで指定したプロパティ<em>keystore</em>には、キーストアのfileから始まるURLを指定します。<a href="./15-usingSSL.html">SSLを使用する</a>でも説明していますが、クラスパス上のリソースを指定することもできます。</p>
    -    
    -      <p>このレシーバーには、クライアントアプリケーションから<code>SSLSocketAppender</code>で接続することができます。SSL対応のアペンダーを使う設定ファイルを、前の例で使用したクライアントアプリケーションから使うことができます。次のように実行します。</p>
    -    
    -      <p class="source">java -Dhost=localhost -Dport=6001 \
    -      -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender2.xml</p>
    -    
    -      <p>この例では自己署名したX.509証明書を使用していますが、これを使っていいのはテストや実験的な用途だけです。<strong>本番環境では、SSL対応のアペンダーのために適切なX.509証明書を使うようにしてください</strong> 。詳細は<a href="./15-usingSSL.html">SSLを使用する</a>を参照してください。</p>
    -  
    -      <h2 class="doAnchor" name="receiverClientComponents">クライアントとして振る舞うレシーバー
    -</h2>
    -    
    -      <p>クライアントとして振る舞うように設定したレシーバーは、リモートアペンダーに接続します。リモートアペンダーは<code>ServerSocketAppender</code>などのサーバ型でなければなりません。</p>  
    -    
    -      <p>
    -        <img border="1" src="images/chapters/receivers/socketReceiver.png">
    -      </p>
    -
    -      <p>logackの配布物にはクライアントとして振る舞うレシーバーコンポーネントが2つ含まれています。<code><a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SocketReceiver.html">SocketReceiver</a></code>とそのSSL対応版の<a href="http://logback.qos.ch/xref/ch/qos/logback/classic/net/SSLSocketReceiver.html"><code>SSLSocketReceiver</code></a>です。どちらのレシーバーもリモートアペンダー(<code>ServerSocketAppender</code>あるいは<code>SSLServerSocketAppender</code>)に対して接続を確立するようになっています。</p>
    -        
    -      <p><code>SocketReceiver</code>の派生クラスで設定可能なプロパティは次のとおりです。</p>
    -    
    -      <table class="bodyTable striped">
    -        <tr>
    -        <th>プロパティ名</th>
    -        <th>型</th>
    -        <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">remoteHost</span></td>
    -          <td><code>String</code></td>
    -          <td>リモートアペンダーのホスト名またはIPアドレス。</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">port</span></td>
    -          <td><code>int</code></td>
    -          <td>リモートアペンダーの待ち受けポート番号。</td>
    -        </tr> 
    -        <tr>
    -          <td><span class="prop" container="socket">reconnectionDelay</span></td>
    -          <td><code>int</code></td>
    -          <td>接続異常が発生した後で、再接続をする前に待機する時間を表す正の整数値。単位はミリ秒。デフォルト値は30000(30秒)です。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="SocketReceiver">ssl</span></td>
    -          <td><code>SSLConfiguration</code></td>
    -          <td><code>SSLSocketReceiver</code>でのみ設定可能なプロパティ。<a href="./15-usingSSL.html">SSLを使用する</a>で説明しているとおり、SSLの設定を指定します。</td>
    -        </tr>
    -      </table>
    -        
    -      <h3 class="doAnchor" name="usingSocketReceiver">SocketReceiverの使い方</h3>
    -
    -      <p><code>SocketReceiver</code>の設定は<code>ServerSocketReceiver</code>の設定と非常によく似ています。これらの差は、サーバーとクライアントという真逆の役割に起因するものです。<code>SocketReceiver</code>はクライアントで、リモートアペンダはサーバーとして動作します。</p>
    -        
    -      <p class="example">例:基本的なSocketReceiverの設定(<a href="http://logback.qos.ch/xref/chapters/receivers/socket/receiver3.xml">logback-examples/src/main/java/chapters/receivers/socket/receiver3.xml</a>)</p>
    -      <span class="asGroovy" onclick="return asGroovy(&#39;receiver3&#39;);">Groovyとして表示</span>
    -  <pre id="receiver3" class="prettyprint source">&lt;configuration debug="true"&gt;
    -    
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;    
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date %-5level [%thread] %logger - %message%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;  
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.SocketReceiver"&gt;
    -    &lt;remoteHost&gt;${host}&lt;/remoteHost&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;reconnectionDelay&gt;10000&lt;/reconnectionDelay&gt;
    -  &lt;/receiver&gt;
    -
    -&lt;/configuration&gt;</pre>
    -        
    -      <p>この設定だと、logbackは<em>host</em>変数で指定されたホストの、<em>port</em>変数で指定されたポート番号で実行中の<code>ServerSocketAppender</code>へ接続します。リモートアペンダーから受け取ったロギングイベントは、手元でコンソールアペンダーに渡されます。
    -      </p>
    -   
    -      <p>コマンドプロンプトで<em>logback-examples</em>ディレクトリに移動して、次のコマンドを実行してみましょう。</p>
    -
    -         
    -
    -      <p>サンプルアプリケーションは上記の設定ファイルを読み込んだあと、リモートアペンダーからのロギングイベントを待ち受けます。リモートアペンダーが落ちているときは、定期的に<em>接続を拒否された</em>メッセージが出力されます。再接続が成功するか、アプリケーションが停止するまで、レシーバーは定期的にリモートアペンダーへの再接続を繰り返します。設定例にあるとおり、再接続の間隔は<span class="prop">reconnectionDelay</span>プロパティで指定することができます。<p class="source">java -Dhost=localhost -Dport=6000 \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver3.xml</p>
    -
    -      <p>この例のレシーバーは、前のアプリケーションのアペンダーにそのまま接続できます。アペンダー用のサンプルアプリケーションは、<code>ServerSocketAppender</code>を使った設定ファイルを読み込んでから、ユーザー入力を待ちます。ユーザー入力はアペンダーに接続してきたレシーバーに配信されます。アペンダー用のサンプルアプリケーションを実行してみましょう。</p>
    -
    -      <p class="source">java -Dport=6000 \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender3.xml</p>   
    -
    -      <p>レシーバーが接続する前に入力したメッセージは破棄されます。わかりやすいですね。</p>
    -
    -      <h3 class="doAnchor" name="usingSSLSocketReceiver">SocketSSLReceiverの使い方</h3>
    -
    -
    -      <p><code>SSLSocketReceiver</code>の設定は<code>SocketReceiver</code>の設定とほとんど変わりません。根本的に違うのは、レシーバーのclass属性の指定と、<span class="prop">ssl</span>プロパティがネストされていることです。基本的な設定を見てみましょう。</p>
    -   
    -      <p class="example">例:基本的なSSLSocketReceiverの設定(<a href="http://logback.qos.ch/xref/chapters/receivers/socket/receiver4.xml">logback-examples/src/main/java/chapters/receivers/socket/receiver4.xml</a>)</p>
    -
    -      <span class="asGroovy" onclick="return asGroovy(&#39;receiver4&#39;);">Groovyとして表示</span>
    -  <pre id="receiver4" class="prettyprint source">&lt;configuration debug="true"&gt;
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;    
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%date %-5level [%thread] %logger - %message%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;         
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;  
    - 
    -  &lt;receiver class="ch.qos.logback.classic.net.SSLSocketReceiver"&gt;
    -    &lt;remoteHost&gt;${host}&lt;/remoteHost&gt;
    -    &lt;port&gt;${port}&lt;/port&gt;
    -    &lt;reconnectionDelay&gt;10000&lt;/reconnectionDelay&gt;
    -    &lt;ssl&gt;
    -      &lt;trustStore&gt;
    -        &lt;location&gt;${truststore}&lt;/location&gt;
    -        &lt;password&gt;${password}&lt;/password&gt;
    -      &lt;/trustStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/receiver&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -      <p><em>class</em>属性に<code>SSLSocketReceiver</code>を指定していること、そして、リモートアペンダーが信頼できるかどうかを検証するために使用するトラストストアの場所とパスワードを指定しているところが重要なところです。sslプロパティの設定内容について詳しくは<a href="./15-usingSSL.html">SSLを使用する</a>を参照してください。
    -
    -      </p>
    -  
    -      <p>この設定をつかってレシーバーのサンプルアプリケーションを実行しましょう。</p>
    -  
    -      <p class="source">java -Dhost=localhost -Dport=6001 \
    -      -Dtruststore=file:src/main/java/chapters/appenders/socket/ssl/truststore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.ReceiverExample \
    -      src/main/java/chapters/receivers/socket/receiver4.xml</p>   
    -   
    -      <p>アプリケーションが開始すると、レシーバーは設定ファイルで指定されたリモートアペンダーに接続しようとします。まだアペンダーが実行されていないときは、定期的に"接続を拒否されました"というメッセージがログに出力されrます。レシーバーは<span class="prop">reconnectionDelay</span>プロパティで指定した時間間隔で、アペンダーに接続できるまで、再接続を繰り返します。
    -      </p>
    -  
    -      <p>レシーバーが接続するアペンダーのためのサンプルアプリケーションを実行しましょう。アプリケーションが開始すると、アプリケーションはユーザー入力を待ち受けます。<code>SSLServerSocketAppender</code>はレシーバーからの接続を待ち受けつつ、接続されているレシーバーにユーザー入力をメッセージとして発生したロギングイベントを配信します。アペンダーのサンプルアプリケーションを実行しましょう。</p>
    -
    -      <p class="source">java -Dport=6001 \
    -      -Dkeystore=file:src/main/java/chapters/appenders/socket/ssl/keystore.jks \
    -      -Dpassword=changeit \
    -      chapters.receivers.socket.AppenderExample \
    -      src/main/java/chapters/receivers/socket/appender4.xml</p>   
    -
    -      <p>レシーバーが接続していない状態で何か入力しても、そのメッセージは単純に破棄されるだけです。</p>
    -
    -      <p>繰り返しになりますが、この例では自己署名したX.509証明書を使用していますが、これはあくまでもテストだからです。<strong>本番環境においては、SSL対応のlogbackコンポーネントが自身の身元を証明するため、適切なX.509証明書を取得しなければなりません</strong>。詳しくは<a href="./15-usingSSL.html">SSLを使用する</a>を参照してください。
    -</p>
    -  
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </p></div>
    -  </body>  
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/underTheHood.html b/logback-site/src/site/pages/manual/underTheHood.html
    deleted file mode 100644
    index d2746dde9b..0000000000
    --- a/logback-site/src/site/pages/manual/underTheHood.html
    +++ /dev/null
    @@ -1,15 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">  
    -  <head> 
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>under the hood</title>
    -  </head>
    -  <body>
    -    <h2>Under The Hood Sequence Diagram</h2>
    -  
    -    <img src="images/chapters/architecture/underTheHoodSequence2.gif" alt="underTheHoodSequence2.gif"/>
    -
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/manual/usingSSL.html b/logback-site/src/site/pages/manual/usingSSL.html
    deleted file mode 100755
    index 379d2e84b8..0000000000
    --- a/logback-site/src/site/pages/manual/usingSSL.html
    +++ /dev/null
    @@ -1,1298 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Chapter 15: Using SSL</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />    
    -
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script type="text/javascript" src="../templates/header.js"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">      
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>    
    -    <div id="right">
    -      <script src="menu.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">    
    -
    -      <h1>Chapter 15: Using SSL</h1>
    -
    -
    -    <a href="usingSSL_ja.html">&#x548C;&#x8A33; (Japanese translation)</a>
    -      
    -      <div class="quote">
    -
    -        <p><em>The whole difference between construction and creation is 
    -        exactly this: that a thing constructed can only be loved after it 
    -        is constructed; but a thing created is loved before it exists.</em></p>
    -        <p>&mdash;CHARLES DICKENS</p>
    -      </div>
    -
    -
    -      <script src="../templates/creative.js" type="text/javascript"></script>
    -      <!-- script src="../templates/setup.js" type="text/javascript"></script -->
    -
    -      <p>Logback supports the use of the Secure Sockets Layer 
    -         (SSL) when delivering log events from a socket-based appender
    -         to a remote receiver.  When using an SSL-enabled appender and  
    -         corresponding receiver, serialized logging events are delivered 
    -         over a secure channel.
    -      </p>
    -      
    -      <h2 class="doAnchor">SSL and Component Roles</h2>
    -      
    -      <p>Logback components such as appenders and receivers may act in
    -      either the server role or the client role, with respect to network
    -      connection initiation. When acting in the server role, a logback
    -      component passively listens for connections from remote client
    -      components.  Conversely, a component acting in the client role 
    -      initiates a connection to remote server component.  For example, 
    -      an appender acting in the <em>client</em> role connects to a 
    -      receiver acting in the <em>server</em> role.  Or a receiver
    -      acting in the <em>client</em> role connects to an appender
    -      acting in the <em>server</em> role.</p>
    -      
    -      <p>The roles of the components are generally determined by the
    -      component type.  For example, an <code>SSLServerSocketAppender</code>
    -      is an appender component that acts in the server role, while an
    -      <code>SSLSocketAppender</code> is an appender component that acts
    -      in the client role.  Thus the developer or application administrator 
    -      can configure Logback components to support the desired direction
    -      of network connection initiation.</p>
    -      
    -      <p>The direction of connection initiation is significant in the 
    -      context of SSL, because in SSL a server component must possess an
    -      X.509 credential to identify itself to connecting clients.  A
    -      client component, when connecting to the server, uses the server's
    -      certificate to validate that the server is trusted.  The
    -      developer or application administrator must be aware of the 
    -      roles of Logback components, so as to properly configure the
    -      server's key store (containing the server's X.509 credential) 
    -      and the client's trust store (containing self-signed 
    -      root certificates used when validating server trust).</p>
    -      
    -      <p>When SSL is configured for <em>mutual authentication</em>, then
    -      both the server component and the client component must possess
    -      valid X.509 credentials whose trust can be asserted by their 
    -      respective peer.  Mutual authentication is configured in the
    -      server component, therefore the developer or application 
    -      administrator must be aware of which components are acting in 
    -      the server role.</p>
    -      
    -      <p>In this chapter, we use the term <em>server component</em>
    -      or simply <em>server</em> to refer to a Logback component such
    -      as an appender or receiver that is acting in the server role.  We
    -      use the term <em>client component</em> or simply <em>client</em>
    -      to refer to a component that is acting in the client role.
    -            
    -      <h2 class="doAnchor">SSL and X.509 Certificates</h2>
    -      
    -      <p>In order to use SSL-enabled Logback components, you will need an
    -         X.509 credential (a private key, corresponding certificate,
    -         and CA certification chain) to identify your components
    -         that act as SSL servers.  If you wish to use mutual authentication, 
    -         you will also need credentials for your components that 
    -         act as SSL clients.
    -      </p>      
    -      <p>While you can use a credential issued by a commercial
    -         certification authority (CA), you can also use a certificate issued
    -         from your own internal CA or even a self-signed certificate.  The
    -         following is all that is required:
    -      </p> 
    -      <ol>
    -        <li>The server component must be configured 
    -            with a key store containing the server's private key, 
    -            corresponding certificate, and CA certification chain
    -             (if not using a self-signed certificate).
    -        </li>
    -        <li>The client component must be configured 
    -            with a trust store containing trusted root CA 
    -            certificate(s) or the server's self-signed root certificate.
    -        </li>
    -      </ol>
    -          
    -      <h2 class="doAnchor">Configuring Logback Components for SSL</h2>
    -      <p>The Java Secure Sockets Extension (JSSE) and Java Cryptography 
    -         Architecture (JCA) which is used to implement Logback's SSL
    -         support has many configurable options, and a pluggable provider
    -         framework that allows the built-in SSL and cryptographic
    -         capabilities of the platform to be replaced or augmented.
    -         SSL-enabled Logback components provide the ability to fully specify 
    -         all of the configurable aspects of the SSL engine and cryptographic 
    -         providers, to meet your unique security needs.
    -      </p>
    -      
    -      <h3>Basic SSL Configuration using JSSE System Properties</h3>
    -      <p>Fortunately, nearly all of the configurable SSL properties for
    -         SSL-enabled Logback components have reasonable defaults.  In 
    -         most cases all that is needed is the configuration of some JSSE 
    -         system properties.
    -      </p>
    -      
    -      <p>The remainder of this section describes the specific JSSE
    -         properties that are needed in most environments. See 
    -         <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">
    -         Customizing JSSE</a> in the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -         JSSE Reference Guide</a> for more information on setting JSSE
    -         system properties to customize JSSE.      
    -      </p>
    -
    -      <p>If you're using any of Logback's SSL-enabled appender or receiver
    -         components that act in the server role (e.g. 
    -         <code>SSLServerSocketReceiver</code>, 
    -         <code>SSLServerSocketAppender</code>,
    -         or <code>SimpleSSLSocketServer</code>) you'll need to configure 
    -         JSSE system properties that provide the location, type, and 
    -         password of the key store containing a private key and 
    -         certificate.
    -      </p>
    -      
    -      <h4><a name="basicConfig.keyStore"></a>
    -          System Properties for Server Key Store Configuration</h4>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStore</code></td>
    -          <td>Specifies a filesystem path to the file containing your
    -              server components' private key and certificate.</td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStoreType</code></td>
    -          <td>Specifies the key store type.  If this property is not
    -              specified, the platform's default type (JKS) is assumed.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStorePassword</code></td>
    -          <td>Specifies the password needed to access the key store.
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <p>See <a href="#Examples">Examples</a> below for examples of
    -         setting these system properties when starting an application
    -         that uses Logback's SSL-enabled server components.
    -      </p>
    -         
    -      <p>If your server component is using a certificate 
    -         that was signed by a commercial certification authority (CA), 
    -         <strong>you probably don't need to provide <em>any</em> SSL 
    -         configuration in your applications that use SSL-enabled client 
    -         components</strong>.  When using a commercially-signed 
    -         certificate for your server  component, simply setting the 
    -         system key store properties for JVM that runs the server 
    -         component is usually all that is needed.
    -      </p>
    -               
    -      <p>If you are using either a self-signed server certificate
    -         or your server certificate was signed by a
    -         certification authority (CA) that is not among those whose root
    -         certificates are in the Java platform's default trust store
    -         (e.g. when your organization has its own internal certification
    -         authority), you will need to configure the JSSE system
    -         properties that provide the location, type, and password of the
    -         trust store containing your server's certificate or trusted
    -         root certificates for the certification authority (CA) that
    -         signed your server's certificate.  <strong>These properties will 
    -         need to be set in each application that utilizes an SSL-enabled
    -         client component</strong>.
    -      </p>
    -        
    -      <h4><a name="basicConfig.trustStore"></a>
    -          System Properties for Client Trust Store Configuration</h4>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStore</code></td>
    -          <td>Specifies a filesystem path to the file containing your
    -              server component's certificate or trusted root
    -              certificate(s) for the certification authority (CA) that
    -              signed the server certificate.</td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStoreType</code></td>
    -          <td>Specifies the trust store type.  If this property is not
    -              specified, the platform's default type (JKS) is assumed.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStorePassword</code></td>
    -          <td>Specifies the password needed to access the trust store.
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <p>See <a href="#Examples">Examples</a> below for examples of
    -         setting these system properties when starting an application 
    -         that utilizes Logback's SSL-enabled client components.
    -      </p>         
    -            
    -      <h3 class="doAnchor"><a name="SSLConfiguration"></a>
    -         Advanced SSL Configuration</h3>
    -      <p>In certain situations, the basic SSL configuration using 
    -         JSSE system properties is not adequate.  For example, if you
    -         are using the <code>SSLServerSocketReceiver</code> component in a web
    -         application, you may wish to use a different credential to
    -         identify your logging server for your remote logging clients
    -         than the credential that your web server uses to identify
    -         itself to web clients.  You might wish to use SSL client
    -         authentication on your logging server to ensure that only
    -         authentic and authorized remote loggers can connect. Or perhaps
    -         your organization has strict policies regarding the SSL
    -         protocols and cipher suites that may be utilized on the
    -         organization's network.  For any of these needs, you will need
    -         to make use of Logback's advanced configuration options for SSL.
    -      </p>
    -      <p>When configuring a Logback component that supports SSL, you 
    -         specify the SSL configuration using the <code>ssl</code> 
    -         property in the configuration of the component.          
    -      </p>      
    -      <p>For example, if you wish to use <code>SSLServerSocketReceiver</code>
    -         and configure the key store properties for your logging 
    -         server's credential, you could use a configuration such as the 
    -         following.
    -      </p>
    -
    -      <span class="asGroovy" onclick="return asGroovy('logback-ssl-serverKeyStore');">View as .groovy</span>
    -      <pre id="logback-ssl-serverKeyStore" class="prettyprint source">&lt;configuration>
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -  
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver">
    -    &lt;ssl>
    -      &lt;keyStore>
    -        &lt;location>classpath:/logging-server-keystore.jks&lt;/location>
    -        &lt;password>changeit&lt;/password>
    -      &lt;/keyStore>
    -    &lt;/ssl>
    -  &lt;/receiver> 
    -
    -&lt;/configuration></pre>
    -
    -      <p>This configuration specifies the location of the key store
    -         as <em>logging-server-keystore.jks</em> at the root of
    -         the application's classpath.  You could alternatively 
    -         specify a <code>file:</code> URL to identify the location of
    -         the key store.
    -      </p>
    -      <p>If you wanted to use <code>SSLSocketAppender</code> in your
    -         application's Logback configuration, but did not want to change
    -         the application's default trust store using the JSSE
    -         <code>javax.net.ssl.trustStore</code> property, you could 
    -         configure the appender as follows.
    -      </p>          
    -
    -      <span class="asGroovy" onclick="return asGroovy('logback-ssl-clientTrustStore');">View as .groovy</span>
    -      <pre id="logback-ssl-clientTrustStore" class="prettyprint source">&lt;configuration>
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
    -    &lt;ssl>
    -      &lt;trustStore>
    -        &lt;location>classpath:/logging-server-truststore.jks&lt;/location>
    -        &lt;password>changeit&lt;/password>
    -      &lt;/trustStore>
    -    &lt;/ssl>
    -  &lt;/appender>
    -  
    -  &lt;root level="debug">
    -    &lt;appender-ref ref="SOCKET" />
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -
    -      <p>This configuration specifies the location of the trust store
    -         as <em>logging-server-truststore.jks</em> at the root of
    -         the application's classpath.  You could alternatively 
    -         specify a <code>file:</code> URL to identify the location of
    -         the trust store.
    -      </p>
    -
    -      <h4>SSL Configuration Properties</h4>
    -      
    -      <p>JSSE exposes a large number of configurable options, and 
    -         Logback's SSL support makes nearly all of them available for
    -         you to specify in your SSL-enabled component configuration.  
    -         When using XML configuration, SSL properties are introduced to 
    -         these components by nesting an &lt;ssl> element in the 
    -         component configuration.  This configuration element corresponds 
    -         to the 
    -         <a href="../xref/ch/qos/logback/core/net/ssl/SSLConfiguration.html">
    -         <code>SSLConfiguration</code></a> class.
    -      </p>
    -      
    -      <p>When configuring SSL for your components
    -         you need only configure those SSL properties for which the
    -         defaults are not adequate.  Overspecifying the SSL configuration
    -         is often the cause of difficult-to-diagnose problems.
    -      </p>
    - 
    -      <p>The following table describes the top-level SSL configuration
    -         properties.  Many of these properties introduce additional
    -         subproperties, which are described in tables that follow 
    -         after the top-level properties are described.
    -      </p>
    -            
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">keyManagerFactory</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.html">
    -              <code>KeyManagerFactoryFactoryBean</code></a>
    -          </td>
    -          <td>Specifies the configuration used to create a
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/KeyManagerFactory.html">
    -              <code>KeyManagerFactory</code></a>.  The Java platform's default 
    -              factory will be used if this property is not configured.  See
    -              <a href="#KeyManagerFactoryFactoryBean">Key Manager Factory
    -              Configuration</a>
    -              below. 
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="ssl.keyStore"></a><span class="prop" container="ssl">keyStore</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">
    -              <code>KeyStoreFactoryBean</code></a>
    -          </td>
    -          <td>
    -            <p>Specifies the configuration used to create a
    -               <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html">
    -               <code>KeyStore</code></a>.  The KeyStore created by this property
    -               should contain a single X.509 credential (consisting of a 
    -               private key, corresponding certificate, and CA certificate chain).
    -               This credential is presented by the local SSL peer to the remote
    -               SSL peer.
    -            </p>
    -            <p>When configuring an SSL client (e.g. <code>SSLSocketAppender</code>),
    -               the <span class="prop" container="ssl">keyStore</span> property
    -               is needed only if the remote peer is configured to require
    -               client authentication. 
    -            </p>
    -            <p>When configuring an SSL server (e.g. <code>SimpleSSLSocketServer</code>)
    -               the <span class="prop" container="ssl">keyStore</span> property
    -               specifies the key store containing the server's credential.  If
    -               this property is not configured, the JSSE's
    -               <code>javax.net.ssl.keyStore</code> system property must be
    -               configured to provide the location of the server's key store.
    -               See <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">
    -               Customizing JSSE</a> in the 
    -               <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -               JSSE Reference Guide</a> for more information on setting JSSE
    -               system properties.
    -            </p>
    -            <p>See <a href="#KeyStoreFactoryBean">Key Store Configuration</a>
    -               below. 
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">parameters</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.html">
    -              <code>SSLParametersConfiguration</code></a></td>
    -          <td>Specifies various parameters used in SSL session negotiation.
    -              See <a href="#SSLParametersConfiguration">SSL Parameters Configuration</a>
    -              below.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">protocol</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the SSL protocol that will be used to create an 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/SSLContext.html">
    -              <code>SSLContext</code></a>.
    -              See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a>.  The Java platform's default protocol 
    -              will be used if this property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">provider</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the name of the JSSE provider that will be used to
    -              create an 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/SSLContext.html">
    -              <code>SSLContext</code></a>.  The Java platform's default JSSE 
    -              provider will be used if this property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">secureRandom</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.html">
    -              <code>SecureRandomFactoryBean</code></a>
    -          </td>
    -          <td>Specifies the configuration used to create a
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/SecureRandom.html">
    -              <code>SecureRandom</code></a> &mdash; a secure random number
    -              generator.  The Java platform's default generator will be used  
    -              if this property is not configured.  See
    -              <a href="#SecureRandomFactoryBean">Secure Random Generator
    -              Configuration</a> below. 
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">trustManagerFactory</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.html">
    -              <code>TrustManagerFactoryFactoryBean</code></a>
    -          </td>
    -          <td>Specifies the configuration used to create a
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/TrustManagerFactory.html">
    -              <code>TrustManagerFactory</code></a>.  The Java platform's default 
    -              factory will be used if this property is not configured.  See
    -              <a href="#TrustManagerFactoryFactoryBean">Trust Manager Factory</a>
    -              below. 
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="ssl.trustStore"></a><span class="prop" container="ssl">trustStore</span></td>
    -          <td><a href="../xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">
    -              <code>KeyStoreFactoryBean</code></a>
    -          </td>
    -          <td>
    -            <p>Specifies the configuration used to create a
    -               <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html">
    -               <code>KeyStore</code></a> used for validating identity of the
    -               remote SSL peer.  The KeyStore created by this property
    -               should contain one or more <em>trust anchors</em> &mdash; self-signed
    -               certificates marked as "trusted" in the keystore.  Typically,
    -               the trust store contains self-signed CA certificates.  
    -            </p>
    -            <p>The trust store specified by this property overrides any trust 
    -               store specified by the JSSE's <code>javax.net.ssl.trustStore</code> 
    -               system property and the platform's default trust store.See <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">
    -               Customizing JSSE</a> in the 
    -               <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -               JSSE Reference Guide</a> for more information on setting JSSE
    -               system properties.
    -            </p>
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4 class="doAnchor"><a name="KeyStoreFactoryBean"></a>
    -          Key Store Configuration</h4>
    -          
    -      <p>The <a href="../xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">
    -         <code>KeyStoreFactoryBean</code></a> specifies the 
    -         configuration needed to create a 
    -         <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html">
    -         <code>KeyStore</code></a> containing X.509 credentials.  The properties
    -         of this factory bean can be used in the
    -         <a href="#ssl.keyStore"><span class="prop" container="ssl">keyStore</span></a> 
    -         and 
    -         <a href="#ssl.trustStore"><span class="prop" container="ssl">trustStore</span></a>
    -         properties of the <a href="#SSLConfiguration">SSL Configuration</a>.
    -      </p>
    - 
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">location</span></td>
    -          <td><code>String</code></td>
    -          <td>A URL that specifies the location of the key store.  Use
    -              a <code>file:</code> URL to specify the location of the
    -              keystore on a filesystem.  Use a <code>classpath:</code>
    -              URL to specify a keystore than can be found on the classpath.
    -              If the URL doesn't specify a scheme, <code>classpath:</code>
    -              is assumed.</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">password</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the password needed to access the key store.</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">provider</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the name of the JCA provider that will be used to
    -              create a <code>KeyStore</code>.  The Java 
    -              platform's default key store provider will be used if this 
    -              property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">type</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the <code>KeyStore</code> type.
    -              See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html">
    -              Java Cryptography Architecture</a> specification.  The Java 
    -              platform's default key store type will be used  if this property 
    -              is not configured.
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="KeyManagerFactoryFactoryBean"></a>
    -          Key Manager Factory Configuration</h4>
    -          
    -      <p>The <a href="../xref/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.html">
    -         <code>KeyManagerFactoryFactoryBean</code></a> specifies the 
    -         configuration needed to create a 
    -         <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/KeyManagerFactory.html">
    -         <code>KeyManagerFactory</code></a>.  Generally, it isn't necessary
    -         to explicitly configure the key manager factory, as the platform's
    -         default factory is adequate for most needs.
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyManagerFactory">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the <code>KeyManagerFactory</code> algorithm name.
    -              See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a>.  The Java platform's default key 
    -              manager algorithm will be used if this property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyManagerFactory">provider</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the name of the JCA provider that will be used to
    -              create a <code>SecureRandom</code> generator.  The Java 
    -              platform's default JSSE provider will be used if this property 
    -              is not configured.
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <h4 class="doAnchor"><a name="SecureRandomFactoryBean"></a>
    -          Secure Random Generator Configuration</h4>
    -          
    -      <p>The <a href="../xref/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.html">
    -         <code>SecureRandomFactoryBean</code></a> specifies the 
    -         configuration needed to create a 
    -         <a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/SecureRandom.html">
    -         <code>SecureRandom</code></a> generator.  Generally, it isn't necessary
    -         to explicitly configure the secure random generator, as the platform's
    -         default generator is adequate for most needs.
    -      </p>
    - 
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="secureRandom">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the <code>SecureRandom</code> algorithm name.
    -              See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html">
    -              Java Cryptography Architecture</a> specification.  The Java 
    -              platform's default random number generation algorithm will be used 
    -              if this property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="secureRandom">provider</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the name of the JCA provider that will be used to
    -              create a <code>SecureRandom</code> generator.  The Java 
    -              platform's default JSSE provider will be used if this property 
    -              is not configured.
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="SSLParametersConfiguration"></a>
    -          SSL Parameters Configuration</h4>
    -          
    -      <p>The <a href="../xref/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.html">
    -         <code>SSLParametersConfiguration</code></a> allows the customization
    -         of allowed SSL protocols, cipher suites, and client authentication
    -         options. 
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.excludedCipherSuites"></a>
    -              <span class="prop" container="parameters">excludedCipherSuites</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>Specifies a comma-separated list of SSL cipher suite names or
    -               patterns to disable during session negotiation.  This property is
    -               used to filter the cipher suites supported by the SSL engine, 
    -               such that any cipher suite matched by this property is disabled.
    -            </p>
    -            <p>Each field in the comma-separated list specified for this 
    -               property may be a simple string or a regular expression.
    -            </p>
    -            <p>See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a> for a list of cipher suite names.
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.includedCipherSuites"></a>
    -              <span class="prop" container="parameters">includedCipherSuites</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>Specifies a comma-separated list of SSL cipher suite names or
    -               patterns to enable during session negotiation.  This property is
    -               used to filter the cipher suites supported by the SSL engine, 
    -               such that only those cipher suites matched by this property are 
    -               enabled.
    -            </p>
    -            <p>Each field in the comma-separated list specified for this 
    -               property may be a simple string or a regular expression.
    -            </p>
    -            <p>See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a> for a list of cipher suite names.
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.excludedProtocols"></a>
    -              <span class="prop" container="parameters">excludedProtocols</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>Specifies a comma-separated list of SSL protocol names or
    -               patterns to disable during session negotiation.  This property is
    -               used to filter the protocols supported by the SSL engine, 
    -               such that any protocol matched by this property is disabled.
    -            </p>
    -            <p>Each field in the comma-separated list specified for this 
    -               property may be a simple string or a regular expression.
    -            </p>
    -            <p>See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a> for a list of protocol names.
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.includedProtocols"></a>
    -              <span class="prop" container="parameters">includedProtocols</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>Specifies a comma-separated list of SSL protocol names or
    -               patterns to enable during session negotiation.  This property is
    -               used to filter the protocols supported by the SSL engine, 
    -               such that only those protocols matched by this property are 
    -               enabled.
    -            </p>
    -            <p>Each field in the comma-separated list specified for this 
    -               property may be a simple string or a regular expression.
    -            </p>
    -            <p>See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a> for a list of protocol names.
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.needClientAuth"></a>
    -              <span class="prop" container="parameters">needClientAuth</span></td>
    -          <td><code>boolean</code></td>
    -          <td>Set this property to the value <code>true</code> to 
    -              configure a server to <em>require</em> a valid client
    -              certificate.  This property is ignored when configured
    -              for a client component such as <code>SSLSocketAppender</code>.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.wantClientAuth"></a>
    -              <span class="prop" container="parameters">wantClientAuth</span></td>
    -          <td><code>boolean</code></td>
    -          <td>Set this property to the value <code>true</code> to 
    -              configure the server to <em>request</em> a client
    -              certificate.  This property is ignored when configured
    -              for a client component such as <code>SSLSocketAppender</code>.
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="TrustManagerFactoryFactoryBean"></a>
    -          Trust Manager Factory Configuration</h4>
    -          
    -      <p>The <a href="../xref/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.html">
    -         <code>TrustManagerFactoryFactoryBean</code></a> specifies the 
    -         configuration needed to create a 
    -         <a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/TrustManagerFactory.html">
    -         <code>TrustManagerFactory</code></a>.  Generally, it isn't necessary
    -         to explicitly configure the trust manager factory, as the platform's
    -         default factory is adequate for most needs.
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Property Name</th>
    -          <th>Type</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="trustManagerFactory">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the <code>TrustManagerFactory</code> algorithm name.
    -              See the <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">
    -              Standard Names</a> specification in the 
    -              <a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">
    -              JSSE Reference Guide</a>.  The Java platform's default key 
    -              manager algorithm will be used if this property is not configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="trustManagerFactory">provider</span></td>
    -          <td><code>String</code></td>
    -          <td>Specifies the name of the JCA provider that will be used to
    -              create a <code>SecureRandom</code> generator.  The Java 
    -              platform's default JSSE provider will be used if this property 
    -              is not configured.
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h2 class="doAnchor"><a name="Examples"></a>Examples</h2>
    -
    -      <h3>Using JSSE System Properties</h3>
    -      <p>JSSE system properties can be used to specify the location and 
    -      password for a key store containing your server's X.509 credential,
    -      or to specify the location and password for a trust store 
    -      containing self-signed root CA certificates used by your client
    -      components to validate server trust.</p>
    -
    -      <h4>Specifying the Server's Key Store</h4>
    -      <p>When running a server component, you need to specify the location
    -      and password for the key store containing the server's credential.
    -      One way to do this is using JSSE system properties.  The following
    -      example shows a command line that could be used to start the
    -      <code>SimpleSSLSocketServer</code> that is shipped with Logback.</p>
    -    
    -      <p class="source">java -DkeyStore=/etc/logback-server-keystore.jks \
    -     -DkeyStorePassword=changeit -DkeyStoreType=JKS \
    -     ch.qos.logback.net.SimpleSSLSocketServer 6000 /etc/logback-server-config.xml</p>
    -              
    -      <p>Note that when using the JSSE <em>keyStore</em> system property,
    -      a path to the key store is specified.  When specifying the location
    -      in <em>logback.xml</em>, a URL for the key store is specified.</p>
    -      
    -      <p>While this example starts the standalone server application
    -      provided with Logback, the same system properties could be specified
    -      to start any application that uses an SSL-enabled Logback server
    -      component.
    -      
    -      <h4>Specifying the Client's Trust Store</h4>
    -      
    -      <p>When using a client component, you need to specify the location
    -      and password for a trust store containing root CA certificates used
    -      for validating server trust.  One way to do this is using JSSE
    -      system properties.  The following example shows a command line
    -      that could be used to start an application named 
    -      <code>com.example.MyLoggingApplication</code> that uses one or
    -      more of Logback's SSL-enabled client components.</p>
    -
    -      <p class="source">java -DtrustStore=/etc/logback-client-truststore.jks \
    -     -DtrustStorePassword=changeit -DtrustStoreType=JKS \
    -     com.example.MyLoggingApplication</p>
    -      
    -      <p>Note that when using the JSSE <em>trustStore</em> system property,
    -      a path to the key store is specified.  When specifying the location
    -      in <em>logback.xml</em>, a URL for the trust store is specified.</p>
    -      
    -      <h3>Creating and Using a Self-Signed Server Component Credential</h3>
    -      <p>To generate a self-signed certificate, you can use the <em>keytool</em>
    -      utility that is shipped with the Java Runtime Environment (JRE).
    -      The instructions below walk through the process of creating a
    -      self-signed X.509 credential in a key store for your server 
    -      component and creating a trust store for use with your client
    -      components.
    -      </p>
    -      
    -      <h4>Creating the server component credential:</h4>
    -      <p>The following command will generate the self-signed client
    -      credential in a file named <em>server.keystore</em>.</p>
    -      <pre class="source">keytool -genkey -alias server -dname &quot;CN=my-logging-server&quot; \
    -    -keyalg RSA -validity 365 -keystore server.keystore
    -Enter keystore password: &lt;Enter password of your choosing>
    -Re-enter new password: &lt;Re-enter same password>
    -Enter key password for &lt;my-logging-server>
    -	(RETURN if same as keystore password):  &lt;Press RETURN>
    -</pre>
    -
    -      <p>The name <em>my-logging-server</em> used in the <em>dname</em>
    -      may be any valid name of your choosing.  You may wish to use the
    -      fully-qualified domain name of the server host. The
    -      <em>validity</em> argument specifies the number of calendar days
    -      from the present date until the credential expires.</p>
    -      
    -      <p>In production settings, it is especially important to choose a
    -      strong password for the key store containing your server credential.
    -      This password protects the server's private key, preventing it
    -      from being used by an authorized party.  Make note of the
    -      password, because you will need it in subsequent steps and when
    -      configuring your server.
    -      </p>
    -
    -      <h4>Creating a trust store for client components:</h4>
    -      <p>For use in the configuration of your client components, the 
    -      server's certificate needs to be exported from the key store
    -      created in the previous step, and imported into a trust store.  The
    -      following commands will export the certificate and import it into
    -      a trust store named <em>server.truststore</em>.</p>
    -      
    -      <pre class="source">keytool -export -rfc -alias server -keystore server.keystore \
    -    -file server.crt
    -Enter keystore password: &lt;Enter password you chose for in previous step>
    -
    -keytool -import -alias server -file server.crt -keystore server.truststore
    -Enter keystore password: &lt;Enter password of your choosing>
    -Re-enter new password: &lt;Re-enter same password>
    -Owner: CN=my-logging-server
    -Issuer: CN=my-logging-server
    -Serial number: 6e7eea40
    -Valid from: Sun Mar 31 07:57:29 EDT 2013 until: Mon Mar 31 07:57:29 EDT 2014
    -
    -   ...
    -
    -Trust this certificate? [no]:  &lt;Enter "yes">
    -</pre>
    -
    -      <p>The first command exports the server's certificate (but not the
    -      server's private key) from the key store and into a file named
    -      <em>server.crt</em>.  The second step creates a new trust store
    -      named <em>server.truststore</em> containing the server certificate.
    -      </p>
    -      
    -      <p>In production settings, it is especially important to choose a
    -      strong password for the trust store that is different from the 
    -      password you chose of the server key store.  Make note of this
    -      password, because you will need it when configuring your appender
    -      clients.
    -      </p>
    -          
    -      <h4>Configuring the server component:</h4>
    -      <p>You will need to copy the <em>server.keystore</em> file into your 
    -      server application's configuration.  The key store can be placed
    -      with your application's classpath resources, or it may simply be
    -      placed somewhere on the server host's filesystem.  When specifying
    -      the location of the key store in the configuration, you will use
    -      either a <code>classpath:</code> URL or <code>file:</code> URL, as
    -      appropriate.  A example server configuration follows:</p>
    -
    -      <p class="example">Example: Server Component Configuration</p>
    -      <pre class="prettyprint source">&lt;configuration debug="true">
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;encoder>
    -      &lt;pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -  
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="CONSOLE" />
    -  &lt;/root>
    -
    -  &lt;server class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver">
    -    &lt;ssl>
    -      &lt;keyStore>
    -        &lt;location>classpath:server.keystore&lt;/location>
    -        &lt;password>${server.keystore.password}&lt;/password>
    -      &lt;/keyStore>
    -    &lt;/ssl>
    -  &lt;/server>
    -&lt;/configuration></pre>
    -      
    -      <p>This example assumes that the key store is located at the root
    -      of the application's classpath.</p>
    -      
    -      <p>Note that this configuration specifies the key store password
    -      using the <em>server.keystore.password</em> substitution variable.
    -      This approach would allow you to avoid storing the password in
    -      any configuration file.  For example, your application could
    -      prompt for this password on the console at startup, and then 
    -      set the <em>server.keystore.password</em> as a system property 
    -      using the entered password before configuring the logging system.
    -      </p>
    -      
    -      <h4>Configuring client components:</h4>
    -      <p>You will need to copy the <em>server.truststore</em> file into
    -      the application configuration of each application that uses an
    -      SSL-enabled component acting in the client mode.  The trust store 
    -      can be placed with your application's classpath resources, or it
    -      may simply be placed somewhere on the filesystem.  When specifying 
    -      the location of the trust store in the configuration, you will use 
    -      either a <code>classpath:</code> URL or <code>file:</code> URL, as
    -      appropriate.  A example appender client configuration follows:</p>
    -
    -      <p class="example">Example: Appender Client Configuration</p>
    -
    -      <pre class="prettyprint source">&lt;configuration debug="true">
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender">
    -    &lt;remoteHost>${host}&lt;/remoteHost>
    -    &lt;ssl>
    -      &lt;trustStore>
    -        &lt;location>classpath:server.truststore&lt;/location>
    -        &lt;password>${server.truststore.password}&lt;/password>
    -      &lt;/trustStore>
    -    &lt;/ssl>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SOCKET" />
    -  &lt;/root>
    -&lt;/configuration></pre>
    -
    -      <p>This example assumes that the trust store is located at the root
    -      of the application's classpath.</p>
    -      
    -      <p>Note that this configuration specifies the trust store password
    -      using the <em>server.truststore.password</em> substitution variable.
    -      This approach would allow you to avoid storing the password in
    -      any configuration file.  For example, your application could
    -      prompt for this password on the console at startup, and then 
    -      set the <em>server.truststore.password</em> as a system property 
    -      using the entered password before configuring the logging system.
    -      </p>
    -
    -      <h2>Auditing the SSL Configuration</h2>
    -      <p>In settings where secure communications are required, it is often
    -      necessary to audit the configuration of components that use SSL to
    -      validate conformance with local security policies.  The SSL
    -      support in Logback addresses this need by providing detailed
    -      logging of SSL configuration when Logback is initialized.  You can 
    -      enable audit logging using the <code>debug</code> property in the
    -      configuration:</p>
    -      
    -      <pre class="prettyprint source">&lt;configuration debug="true">
    -  
    -  ...
    -  
    -&lt;/configuration></pre>
    -
    -      <p>With the debug property enabled, all of the relevant aspects of
    -      the resulting SSL configuration will be logged when the logging
    -      system is initialized.  A representative example of the information
    -      logged for SSL follows.</p>
    -      
    -      <p class="example">Example: SSL Configuration Audit Logging</p>
    -      
    -      <pre>06:46:31,941 |-INFO in SSLServerSocketReceiver@4ef18d37 - SSL protocol 'SSL' provider 'SunJSSE version 1.6'
    -06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key store of type 'JKS' provider 'SUN version 1.6': file:src/main/java/chapters/appenders/socket/ssl/keystore.jks
    -06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key manager algorithm 'SunX509' provider 'SunJSSE version 1.6'
    -06:46:31,973 |-INFO in SSLServerSocketReceiver@4ef18d37 - secure random algorithm 'SHA1PRNG' provider 'SUN version 1.6'
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv2Hello
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv3
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: TLSv1
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_MD5
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_SHA
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
    -</pre>
    -
    -      <p>The output shown here has been truncated for brevity's sake, 
    -      but would typically include the complete list of protocols, 
    -      providers, algorithms, and cipher suites, as well as the location
    -      of key store and trust store resources utilized in the configuration.
    -      </p>
    -      
    -      <p>While none of this audit logging is particularly sensitive,
    -      best practices for security would suggest that this logging should
    -      not remain enabled in production settings after the configuration
    -      has been validated.  Audit logging is disabled when the 
    -      <code>debug</code> property is removed or set to <code>false</code>.
    -      </p>
    -      
    -      <h2>Resolving SSL Exceptions</h2>
    -      <p>When SSL is misconfigured, it generally results in the client
    -         and server components being unable to negotiate an agreeable 
    -         session.  This problem usually manifests itself as exceptions 
    -         being thrown by both parties when the client attempts to connect 
    -         to the server.
    -      </p>
    -      <p>The content of the exception messages varies depending on whether 
    -         you are looking at the client's log or the server's log.  This
    -         is mostly due to inherent protocol limitations in error reporting
    -         during session negotiation.  As a consequence of this fact,
    -         in order to troubleshoot session negotiation problems, you will
    -         usually want to look at the logs of both the client and the
    -         server.
    -      </p>
    -      
    -      <h3>Server's Certificate is Not Available</h3>
    -      <p>When starting the server component, you
    -         see the following exception in the log:</p>
    -         
    -      <p><em>javax.net.ssl.SSLException: No available certificate or 
    -         key corresponds to the SSL cipher suites which are enabled</em>
    -      </p>
    -      
    -      <p>In most cases this means that you have not configured 
    -         the location of the key store containing the server's private 
    -         key and corresponding certificate.
    -      </p>
    -      
    -      <h4>Solution</h4>
    -      <p>Using either the 
    -         <a href="#basicConfig.keyStore">Key Store system
    -         properties</a> or the <a href="#ssl.keyStore">
    -         <span class="prop">keyStore</span></a> property of the 
    -         server component's <span class="prop">ssl</span> property, 
    -         you must specify the location and password for the key store 
    -         containing the server's private key and certificate.
    -      </p>
    -      
    -      <h3>Client Does Not Trust the Server</h3>
    -      <p>When the client attempts to connect to the server, you see the 
    -      following exception in the log:</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: 
    -              sun.security.validator.ValidatorException: 
    -              PKIX path building failed</em>
    -      </p>
    -      <p>This problem is the result of the server presenting a certificate
    -         the client does not trust.  The most common cause is that you 
    -         are using a self-signed server certificate (or a server 
    -         certificate that was signed by your organization's internal
    -         certification authority) and you have not configured the client
    -         so that it references a trust store containing the server's 
    -         self-signed certificate (or the trusted root certificate(s) for 
    -         the CA that signed your server certificate).         
    -      </p> 
    -      <p>This problem can also occur if your server certificate has
    -         expired or has been revoked.  If you have access to the server
    -         log you will likely see the following exception logged
    -         each time the client attempts to connect:
    -      </p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: Received fatal alert: ...</em>
    -      </p>
    -      
    -      <p>The remainder of the exception message will usually provide a
    -         code that indicates why the client rejected the server's
    -         certificate.
    -      </p>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Code</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_unknown</code></td>
    -          <td>Usually indicates that the client's trust store has not
    -              been properly configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_expired</code></td>
    -          <td>Indicates that the server's certificate has expired and
    -              needs to be replaced.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_revoked</code></td>
    -          <td>Indicates that the issuing certification authority (CA)
    -              has revoked the server's certificate and the certificate
    -              needs to be replaced.
    -          </td>
    -        </tr>
    -      </table>
    -        
    -      <h4>Solutions</h4>
    -      <p>If the server's log message is reporting <code>certificate_unknown</code>
    -         then using either the <a href="#basicConfig.trustStore">Trust Store system
    -         properties</a> or the <a href="#ssl.trustStore">
    -         <span class="prop">trustStore</span></a> property of the 
    -         appender component's <span class="prop">ssl</span> property, 
    -         you must specify the location and password for the trust 
    -         store containing the server's self-signed certificate or
    -         the issuing certificate authority's root certificate.
    -      </p>
    -
    -      <p>If the server's log message is reporting 
    -         <code>certificate_expired</code> or <code>certificate_revoked</code> 
    -         the server needs a new certificate.  The new certificate 
    -         and associated private key needs to be placed in the key store 
    -         specified in the server's configuration.  And, if using 
    -         a self-signed server certificate, the server's certificate also
    -         needs to be placed in the trust store specified in the appender
    -         client's configuration.
    -      </p>
    -      
    -      <h3>Server Does Not Trust the Client</h3>
    -      <p>NOTE: <strong>This problem can occur only if you have explicitly 
    -         configured the server to request a client certificate (using 
    -         either the <a href="#parameters.needClientAuth"><span class="prop">needClientAuth</span></a> or 
    -         <a href="#parameters.wantClientAuth"><span class="prop">wantClientAuth</span></a>
    -         property)</strong>.
    -      </p>
    - 
    -      <p>When the client attempts to connect to the logging
    -         server, you see the following exception in the client's log:
    -      </p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: ...</em>
    -      </p>
    -
    -      <p>The remainder of the exception message will usually provide a
    -         code that indicates why the server rejected the client's
    -         certificate.
    -      </p>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>Code</th>
    -          <th>Description</th>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_unknown</code></td>
    -          <td>Usually indicates that the server's trust store has not
    -              been properly configured.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_expired</code></td>
    -          <td>Indicates that the client's certificate has expired and
    -              needs to be replaced.
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_revoked</code></td>
    -          <td>Indicates that the issuing certification authority (CA)
    -              has revoked the client's certificate and the certificate
    -              needs to be replaced.
    -          </td>
    -        </tr>
    -      </table>
    -        
    -      <h4>Solutions</h4>
    -      <p>If the client's log message is reporting <code>bad_certificate</code>
    -         then using either the <a href="#basicConfig.trustStore">Trust Store system
    -         properties</a> or the <a href="#ssl.trustStore">
    -         <span class="prop">trustStore</span></a> property of the 
    -         server component's <span class="prop">ssl</span> property, 
    -         you must specify the location and password for the trust 
    -         store containing the client's self-signed certificate or
    -         the issuing certificate authority's root certificate.
    -      </p>
    -
    -      <p>If the server's log message is reporting 
    -         <code>certificate_expired</code> or <code>certificate_revoked</code> 
    -         the client needs a new certificate.  The new certificate 
    -         and associated private key needs to be placed in the key store 
    -         specified in the client's configuration.  And, if using 
    -         a self-signed client certificate, the client's certificate also
    -         needs to be placed in the trust store specified in the 
    -         servers's configuration.
    -      </p>
    -      
    -      <h3>Client and Server Cannot Agree on a Protocol</h3>
    -      <p>NOTE: <strong>This problem usually occurs only when you are
    -         explicitly 
    -         <a href="#parameters.excludedProtocols">excluding</a> or 
    -         <a href="#parameters.includedProtocols">including</a> SSL 
    -         protocols in your configuration</strong>.
    -      </p>
    -
    -      <p>When the client attempts to connect to the server, you see 
    -         the following exception in the log:</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: handshake_failure</em>
    -      </p>
    -      
    -      <p>The server's log message is usually more descriptive.  For
    -         example:</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: SSLv2Hello is disabled</em>
    -      </p>
    -      
    -      <p>Generally, this means that you have excluded a protocol from
    -         one of the peers and not the other.</p>
    -         
    -      <h4>Solution</h4>
    -      <p>Check the values specified for the 
    -         <a href="#parameters.excludedProtocols"><span class="prop">excludedProtocols</span></a> and
    -         <a href="#parameters.includedProtocols"><span class="prop">includedProtocols</span></a>
    -         properties on both the server and client.
    -      </p>
    -
    -      <h3>Client and Server Cannot Agree on a Cipher Suite</h3>
    -      <p>NOTE: <strong>This problem usually occurs only when you are
    -         explicitly 
    -         <a href="#parameters.excludedCipherSuites">excluding</a> or
    -         <a href="#parameters.includedCipherSuites">including</a> SSL
    -         cipher suites in your configuration</strong>.
    -      </p>
    -      
    -      <p>When the client attempts to connect to the
    -         server, you see the following exception in the log:
    -      </p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: handshake_failure</em>
    -      </p>
    -      
    -      <p>The server's log message is usually more descriptive:
    -      </p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: no cipher suites in common</em>
    -      </p>
    -      
    -      <p>This means that you have configured the cipher suites on the
    -         server and client such that the intersection
    -         of their respective sets of enabled cipher suites is empty.</p>
    -      
    -      <h4>Solution</h4>
    -      <p>Check the values specified for the 
    -         <a href="#parameters.excludedCipherSuites"><span class="prop">excludedCipherSuites</span></a> and
    -         <a href="#parameters.includedCipherSuites"><span class="prop">includedCipherSuites</span></a>
    -         properties on both the server and client.
    -      </p>
    -         
    -      <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/manual/usingSSL_ja.html b/logback-site/src/site/pages/manual/usingSSL_ja.html
    deleted file mode 100644
    index 88ec9bdf24..0000000000
    --- a/logback-site/src/site/pages/manual/usingSSL_ja.html
    +++ /dev/null
    @@ -1,811 +0,0 @@
    -<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=UTF-8"></meta>
    -    <title>第15章 SSLを使用する</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print"></link>
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen"></link>
    -  </head>
    -  <body dir="ltr" onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="../js/dsl.js"></script>
    -    <script type="text/javascript" src="../js/jquery-min.js"></script>
    -    <script type="text/javascript" src="../js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="menu_ja.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">    
    -      <h1>第15章 SSLを使用する</h1>
    -      
    -      <div class="quote">
    -
    -        <p><em>建築物と創造物の間には越えられない壁があります。それは、建築物は作られた後でしか愛されることができないのに、創造物は存在する前から愛されていることです。</em></p>
    -        <p>—CHARLES DICKENS</p>
    -      </div>
    -
    -    <script type="text/javascript" src="../templates/creative.js"></script>
    -
    -      <p>logbackはソケットベースのアペンダーから遠隔のレシーバーにログを配信するために、Secure Socket Layer(SSL)を使用することが出来ます。SSLに対応したアペンダーとレシーバーを使うと、シリアライズされたロギングイベントはセキュアなチャネルで配信されます。
    -      </p>
    -      
    -      <h2 class="doAnchor">SSLとそれを使うコンポーネントの役割</h2>
    -      
    -      <p>アペンダーやレシーバーはサーバーとしても振る舞うし、クライアントとしても振る舞うことができます。これはネットワーク接続を始める方向によるものです。サーバーとして振る舞うとき、logbackのコンポーネントは外部のクライアントコンポーネントからの接続を待ち受けます。逆に、クライアントとして振る舞うコンポーネントは、外部のサーバーコンポーネントに接続し始めます。たとえば、<em>クライアント</em>として振る舞うアペンダーは、<em>サーバー</em>として振る舞うレシーバーに接続するのです。あるいは、<em>クライアント</em>として振る舞うレシーバーが、 <em>サーバー</em>として振る舞うアペンダーに接続します。</p>
    -      
    -      <p>コンポーネントの役割は、基本的にコンポーネントタイプによって決まります。たとえば、 <code>SSLServerSocketAppender</code>はサーバーとして振る舞うアペンダーコンポーネントですし、<code>SSLSocketAppender</code>はクライアントとして振る舞うアペンダーコンポーネントです。このように、開発者もアプリケーション管理者も、思った通りの方向でネットワーク接続を始められるようにlogbackのコンポーネントを設定することができます。</p>
    -      
    -      <p>SSLのコンテキストにおいて接続を開始する方向は非常に重要です。なぜなら、サーバーコンポーネントは接続してくるクライアントに対してX.509証明書を使って自分のことを証明しなければならないからです。クライアントコンポーネントはサーバーに接続するとき、信頼できるかどうかをサーバーの証明書で検証します。開発者やアプリケーション管理者はlogbackのコンポーネントの役割をきちんと理解しておかなければなりません。つまり、サーバーならキーストア(サーバーのX.509証明書を配置します)を適切に構成し、クライアントならトラストストア(信頼できるサーバーかどうかを検証するときに使用する自己署名ルート証明書を配置します)を適切に構成しなければなりません。</p>
    -      
    -      <p>SSLが<em>相互認証</em>するように設定されている場合、サーバーコンポーネントとクライアントコンポーネントの両方が、それぞれのピアから信頼性を検証された正当なX.509証明書を持っていなければなりません。相互認証はサーバーコンポーネントで設定するものなので、開発者もアプリケーション管理者も、コンポーネントがちゃんとサーバーとして振る舞っていることをきちんと把握しておかなければなりません。</p>
    -      
    -      <p>本章では、サーバーとして振る舞うアペンダーやレシーバーのことを、単に<em>サーバーコンポーネント</em>あるいは<em>サーバー</em>と呼ぶことにします。そして、クライアントとして振る舞うコンポーネントは<em>クライアントコンポーネント</em>あるいは<em>クライアント</em>と呼ぶことにします。
    -            
    -      <h2 class="doAnchor">SSLとX.509証明書</h2>
    -      
    -      <p>SSLに対応したlogbackコンポーネントを使うには、SSLサーバーとして動作するコンポーネントが自分のことを証明するのに、X.509証明書(秘密鍵とそれに対応する証明書、および、CAの証明書チェーン)が必要になります。相互認証を使いたいときは、SSLクライアントとして動作するコンポーネントの証明書も必要です。
    -      </p>      
    -      <p>民間の証明機関(CA)だけでなく、独自のCAで発行した証明書を使うことができるのですが、自己署名した証明書を使うこともできます。必要事項は次のとおりです。</p> 
    -      <ol>
    -        <li>(自己署名証明書を使わない場合)サーバーコンポーネントに、サーバーの秘密鍵を含むキーストア、対応する証明書、およびCAの証明書チェーンを指定しなければなりません。
    -        </li>
    -        <li>クライアントコンポーネントに、信頼されたルートCA証明書(複数可)または、サーバの自己署名ルート証明書を含むトラストストアを指定しなければなりません。
    -        </li>
    -      </ol>
    -          
    -      <h2 class="doAnchor">SSL用のlogbackコンポーネントの設定</h2>
    -      <p>logbackがSSL対応するのに使っているJava Secure Sockets Extension(JSSE)とJava 暗号化アーキテクチャ(JCA)には設定可能な項目がたくさんあります。また、プラグイン可能なフレームワークなので、組み込みのSSLとプラットフォーム固有の暗号化機能は差し替え可能になっています。SSLに対応したlogbackのコンポーネントでは、SSLエンジンと暗号化プロバイダーに設定できることは全て設定できるようになっています。ですので、利用者によって固有のセキュリティ要件を満たすことができます。
    -      </p>
    -      
    -      <h3>JSSEのシステムプロパティを使った基本的なSSLの設定</h3>
    -      <p>SSLに対応したlogbackのコンポーネントでは、SSLの設定可能なプロパティのほとんどについて妥当な初期値が用意されています。したがって、ほとんどの場合いくつかJSEEのシステムプロパティを指定するだけで済むでしょう。
    -      </p>
    -      
    -      <p>このセクションの残りの部分では、ほとんどの環境で必要になる特定のJSSEプロパティについて説明します。システムプロパティを使ってJSSEをカスタマイズする方法の詳細については、
    -<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">JSSEのカスタマイズ</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>を参照してください。
    -      </p>
    -
    -      <p>サーバーとして振る舞うSSL対応のlogbackアペンダーやレシーバー(<code>SSLServerSocketReceiver</code>や<code>SSLServerSocketAppender</code>や<code>SimpleSSLSocketServer</code>)を使うときは、JSSEのシステムプロパティで秘密鍵と証明書を含むキーストアの場所と種類とパスワードを指定しなければなりません。
    -      </p>
    -      
    -      <h4><a name="basicConfig.keyStore"></a>サーバー側でキーストアを指定するためのシステムプロパティ</h4>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStore</code></td>
    -          <td>サーバーコンポーネントの秘密鍵と証明書を含むファイルの、ファイルシステム上のパスを指定します。</td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStoreType</code></td>
    -          <td>キーストアの種類を指定します。このプロパティが指定されていない場合、プラットフォームのデフォルト(JKS)になります。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.keyStorePassword</code></td>
    -          <td>キーストアにアクセスするためのパスワードを指定します。
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <p><a href="./15-usingSSL.html#Examples">こちらの例</a>で、SSL対応のサーバーコンポーネントを実行する際にシステムプロパティを指定する方法を確認してください。
    -      </p>
    -         
    -      <p>サーバーコンポーネントが民間の証明機関(CA)で署名された証明書を使うときは、<strong>おそらくクライアントコンポーネントでSSLの設定をする必要はありません。</strong>サーバー側では、JVMのシステムプロパティとしてキーストアを指定するだけでよいです。
    -      </p>
    -               
    -      <p>自己署名したサーバー証明書か、Javaのデフォルトのトラストストアに含まれないルート証明書(社内用の証明局など)で署名したサーバー証明書を使うときは、JSEEシステムプロパティでトラストストアの場所、種類、パスワードを指定するか、サーバー証明書を署名した信頼できるルート証明書を指定しなければなりません。<strong>SSL対応のクライアントコンポーネントを利用するアプリケーションごとに、これらのシステムプロパティを設定しなければなりません</strong> 。
    -      </p>
    -        
    -      <h4><a name="basicConfig.trustStore"></a>クライアント側でトラストストアを指定するためのシステムプロパティ</h4>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStore</code></td>
    -          <td>サーバーコンポーネントの証明書か、サーバー証明書に署名した認証局(CA)の信頼できるルート証明書のファイルシステム上のパスを指定します。</td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStoreType</code></td>
    -          <td>トラストストアの種類を指定します。このプロパティが指定されていない場合、プラットフォームのデフォルト(JKS)になります。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>javax.net.ssl.trustStorePassword</code></td>
    -          <td>トラストストアにアクセスするためのパスワードを指定します。
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <p><a href="./15-usingSSL.html#Examples">こちらの例</a>で、SSL対応のクライアントコンポーネントを実行する際にシステムプロパティを指定する方法を確認してください。
    -      </p>         
    -            
    -      <h3 class="doAnchor"><a name="SSLConfiguration"></a>高度なSSLの設定</h3>
    -      <p>JSSEシステムプロパティによる基本的なSSLの設定だけでは足りないことがあります。Webアプリケーションで<code>SSLServerSocketReceiver</code>を使用しているとき、WebクライアントがWebサーバーを識別するための証明書と、ロギングクライアントがロギングサーバーを識別するための証明書では別のものを使いたいはずです。ロギングサーバーには、認証、および、認可されたリモートロガーだけが接続できるよう、SSLのクライアントを認証したいこともあるでしょう。あるいは、内部ネットワークではSSLプロトコルと暗号スイートを使わなければならない、というポリシーを強制する組織があるかもしれません。これらのいずれかのニーズを満たすためには、logbackの高度なSSL設定オプションを確かめておいたほういいでしょう。</p>
    -      <p>logbackのコンポーネントでSSLを設定するときは、<code>ssl</code>プロパティに指定します。
    -      </p>      
    -      <p><code>SSLServerSocketReceiver</code>を使うときは、keystoreプロパティでサーバーの証明書を含んだキーストアを指定します。
    -      </p>
    -
    -      <span class="asGroovy" onclick="return asGroovy(&#39;logback-ssl-serverKeyStore&#39;);">Groovyとして表示</span>
    -      <pre id="logback-ssl-serverKeyStore" class="prettyprint source">&lt;configuration&gt;
    -
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -  
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -
    -  &lt;receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver"&gt;
    -    &lt;ssl&gt;
    -      &lt;keyStore&gt;
    -        &lt;location&gt;classpath:/logging-server-keystore.jks&lt;/location&gt;
    -        &lt;password&gt;changeit&lt;/password&gt;
    -      &lt;/keyStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/receiver&gt; 
    -
    -&lt;/configuration&gt;</pre>
    -
    -      <p>ここでは、キーストアの場所にアプリケーションのクラスパスのルートに配置された<em>logging-server-keystore.jks</em>が指定されています。もちろん、fileから始まるURLを指定することもできます。
    -      </p>
    -      <p>アプリケーションで<code>SSLSocketAppender</code>を使いたいけど、アプリケーション自体の(JSEEの)デフォルトのトラストストアを変更したくないときは、<code>javax.net.ssl.trustStore</code>プロパティを使うことができます。
    -      </p>          
    -
    -      <span class="asGroovy" onclick="return asGroovy(&#39;logback-ssl-clientTrustStore&#39;);">Groovyとして表示</span>
    -      <pre id="logback-ssl-clientTrustStore" class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"&gt;
    -    &lt;ssl&gt;
    -      &lt;trustStore&gt;
    -        &lt;location&gt;classpath:/logging-server-truststore.jks&lt;/location&gt;
    -        &lt;password&gt;changeit&lt;/password&gt;
    -      &lt;/trustStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/appender&gt;
    -  
    -  &lt;root level="debug"&gt;
    -    &lt;appender-ref ref="SOCKET" /&gt;
    -  &lt;/root&gt;
    -
    -&lt;/configuration&gt;</pre>
    -
    -      <p>ここでは、トラストストアの場所にアプリケーションのクラスパスのルートに配置された<em>logging-server-truststore.jks</em>が指定されています。
    -もちろん、fileから始まるURLを指定することもできます。
    -
    -      </p>
    -
    -      <h4>SSLプロパティ</h4>
    -      
    -      <p>JSSEは設定可能なオプションを大量に公開しています。logbackのSSL対応では公開された設定のほとんどを設定ファイル中で指定できるようになっています。XML形式の設定ファイルでは、SSLの各種設定をssl要素で設定します。ssl要素に対応しているのは<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/SSLConfiguration.html">SSLConfiguration</a></code>クラスです。
    -      </p>
    -      
    -      <p>SSLに対応したコンポーネントを設定するには、デフォルト値では困る時にこれらのプロパティを設定するだけでよいです。なんでもかんでも設定してしまうと、こんがらがってしまって問題を解析するのがとてもむずかしくなってしまいます。
    -      </p>
    - 
    -      <p>SSLの設定におけるトップレベルのプロパティは次のとおりです。これらのプロパティの多くについて、さらに下位のプロパティが存在します。トップレベルなプロパティの一覧表の後に説明します。
    -      </p>
    -            
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>Type</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">keyManagerFactory</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.html">
    -              <code>KeyManagerFactoryFactoryBean</code></a>
    -          </td>
    -          <td><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/KeyManagerFactory.html">KeyManagerFactory</a></code>の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのファクトリが使われます。<a href="./15-usingSSL.html#KeyManagerFactoryFactoryBean">キーマネージャファクトリの設定</a>を参照してください。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="ssl.keyStore"></a><span class="prop" container="ssl">keyStore</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">
    -              <code>KeyStoreFactoryBean</code></a>
    -          </td>
    -          <td>
    -            <p><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html">KeyStore</a></code>の設定を指定します。キーストアには、少なくとも1つのX.509証明書(秘密鍵と対応する証明書、あるいはCA証明書チェーン)が含まれていなければなりません。この証明書がSSLのリモートピアに提示されます。
    -            </p>
    -            <p>SSLクライアント(<code>SSLSocketAppender</code>など)の設定に<span class="prop" container="ssl">keyStore</span>プロパティが必要になるのは、リモートピアへの接続にクライアント認証が必要な場合だけです。
    -            </p>
    -            <p>SSLサーバー(<code>SimpleSSLSocketServer</code>など)の<span class="prop" container="ssl">keyStore</span>プロパティには、サーバー証明書を格納したキーストアを指定します。このプロパティが設定されていない場合は、JSSEのシステムプロパティ<code>javax.net.ssl.keyStore</code>に、サーバーのキーストアの場所を指定しておかなければなりません。詳細は<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">JSSEのカスタマイズ</a>を参照してください。
    -            </p>
    -            <p><a href="./15-usingSSL.html#KeyStoreFactoryBean">キーストアの設定</a>については後述します。
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">parameters</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.html">
    -              <code>SSLParametersConfiguration</code></a></td>
    -          <td>SSLセッションのネゴシエーションで使用するいろいろなパラメータを指定します。<a href="./15-usingSSL.html#SSLParametersConfiguration">SSLパラメータの設定</a>については後述します。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">protocol</span></td>
    -          <td><code>String</code></td>
    -          <td><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/SSLContext.html">SSLContext</a></code>を作成するために使用するSSLプロトコルを指定します。<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>に記載された<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>を指定してください。このプロパティが設定されていない場合は、JVMのデフォルトのプロトコル名が使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">provider</span></td>
    -          <td><code>String</code></td>
    -          <td><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/SSLContext.html">SSLContext</a></code>を作成するために使用するJSSEプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダ名が使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">secureRandom</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.html">
    -              <code>SecureRandomFactoryBean</code></a>
    -          </td>
    -          <td><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/SecureRandom.html">SecureRandom</a></code>(安全な乱数生成器)の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトの乱数生成器が使用されます。<a href="./15-usingSSL.html#SecureRandomFactoryBean">安全な乱数生成器の設定</a>については後述します。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="ssl">trustManagerFactory</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.html">
    -              <code>TrustManagerFactoryFactoryBean</code></a>
    -          </td>
    -          <td><code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/TrustManagerFactory.html">TrustManagerFactory</a></code>の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのファクトリーを使用します。<a href="./15-usingSSL.html#TrustManagerFactoryFactoryBean">トラストマネージャファクトリー</a>については後述します。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="ssl.trustStore"></a><span class="prop" container="ssl">trustStore</span></td>
    -          <td><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">
    -              <code>KeyStoreFactoryBean</code></a>
    -          </td>
    -          <td>
    -            <p>SSLのリモートピアの同一性を検証するために使う<code><a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html">KeyStore</a></code>の設定を指定します。キーストアには少なくとも1つ以上の<em>トラストアンカー</em>が含まれていなければなりません。"信頼できる"と印の付けられた自己署名証明書のことです。一般的に、トラストストアには自己署名CA証明書が含まれています。
    -            </p>
    -            <p>このプロパティで指定したトラストストアは、JSSEの<code>java.net.ssl.trustStore</code>システムプロパティで指定された全てのトラストストアを上書きします。詳細は<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#InstallationAndCustomization">JSSEのカスタマイズ</a>を参照してください。
    -
    -            </p>
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4 class="doAnchor"><a name="KeyStoreFactoryBean"></a>キーストアの設定</h4>
    -          
    -      <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/KeyStoreFactoryBean.html">KeyStoreFactoryBean</a></code>は、X.509証明書を格納した<a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/KeyStore.html"><code>KeyStore</code></a>を作成するために必要な設定を提供します。このファクトリーBeanのプロパティは、<a href="./15-usingSSL.html#SSLConfiguration">SSLの設定</a>で紹介した<a href="./15-usingSSL.html#ssl.keyStore"><span class="prop" container="ssl">keyStore</span></a>および<a href="./15-usingSSL.html#ssl.trustStore"><span class="prop" container="ssl">trustStore</span></a>として使うことができます。
    -      </p>
    - 
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">location</span></td>
    -          <td><code>String</code></td>
    -          <td>キーストアの場所をURLで指定します。ファイルシステム上のキーストアを指定するときは、<code>file:</code>形式のURLを指定します。<code>classpath:</code>形式のURLを指定すれば、クラスパス上のリソースを指定することもできます。URLスキームがないときは、<code>classpath:</code>が指定されたものとして扱います。</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">password</span></td>
    -          <td><code>String</code></td>
    -          <td>キーストアにアクセスするためのパスワードを指定します。</td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">provider</span></td>
    -          <td><code>String</code></td>
    -          <td><code>KeyStore</code>を作成するために使用するJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのキーストアプロバイダーが使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyStore">type</span></td>
    -          <td><code>String</code></td>
    -          <td><code>KeyStore</code>の種類を指定します。指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html">Java暗号化アーキテクチャ</a>に記載された仕様である<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">標準名</a>です。このプロパティが設定されていない場合は、JVMのデフォルトが使用されます。
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="KeyManagerFactoryFactoryBean"></a>キーマネージャファクトリーの設定</h4>
    -          
    -      <p><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/KeyManagerFactory.html"><code>KeyManagerFactory</code></a>を作成するために必要な設定は、<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/KeyManagerFactoryFactoryBean.html">KeyManagerFactoryFactoryBean</a></code>によって提供されます。普通はキーマネージャーファクトリーの明示的な設定は不要です。ほぼ全ての場合にJVMのデフォルトのファクトリーで十分だからです。
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyManagerFactory">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td><code>KeyManagerFactory</code>が使うアルゴリズム名を指定します。指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>に記載された<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>だけです。このプロパティが設定されていない場合は、JVMのデフォルトのアルゴリズムが使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="keyManagerFactory">provider</span></td>
    -          <td><code>String</code></td>
    -          <td><code>SecureRandom</code>生成器を生成するのに使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダーが使用されます。
    -          </td>
    -        </tr>
    -      </table>
    -
    -      <h4 class="doAnchor"><a name="SecureRandomFactoryBean"></a>安全な乱数生成器の設定</h4>
    -          
    -      <p><a href="http://docs.oracle.com/javase/1.5.0/docs/api/java/security/SecureRandom.html"><code>SecureRandom</code></a>生成器を作成するために必要な設定は、<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/SecureRandomFactoryBean.html">SecureRandomFactoryBean</a></code>によって提供されます。JVMのデフォルトの乱数生成器で十分なことがほとんどなので、普通は明示的に設定する必要がありません。
    -      </p>
    - 
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="secureRandom">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td><code>安全な乱数生成器</code>のアルゴリズム名を指定します。指定できるのは<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html">Java暗号化アーキテクチャの</a>に記載された<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/CryptoSpec.html#AppA">標準名</a>だけです。このプロパティが設定されていない場合は、JVMのデフォルトの乱数生成アルゴリズムが使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="secureRandom">provider</span></td>
    -          <td><code>String</code></td>
    -          <td><code>安全な乱数生成器</code>を生成するために使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダ名が使用されます。
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="SSLParametersConfiguration"></a> SSLパラメータの設定</h4>
    -          
    -      <p><code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/SSLParametersConfiguration.html">SSLParametersConfiguration</a></code>を使って、SSLプロトコル、暗号スイート、およびクライアント認証オプションをカスタマイズすることができます。
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.excludedCipherSpecs"></a>
    -              <span class="prop" container="parameters">excludedCipherSpecs</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>セッションネゴシエーションをする際に除外するSSL暗号方式名をカンマ区切りリストで指定します。このプロパティに指定する値は、SSLエンジンがサポートしている暗号方式を限定するために使用されます。マッチした暗号方式はすべて無効になります。
    -            </p>
    -            <p>カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。
    -            </p>
    -            <p>指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>にあるものだけです。
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.includedCipherSpecs"></a>
    -              <span class="prop" container="parameters">includedCipherSpecs</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>セッションネゴシエーションをする際に使用するSSL暗号方式名をカンマ区切りリストで指定します。
    -このプロパティに指定する値は、SSLエンジンがサポートしている暗号方式を特定するために使用されます。マッチした暗号方式はすべて有効になります。
    -            </p>
    -            <p>カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。
    -            </p>
    -            <p>指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>にあるものだけです。
    -
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.excludedProtocols"></a>
    -              <span class="prop" container="parameters">excludedProtocols</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>セッションネゴシエーションをする際に除外するSSLプロトコル名をカンマ区切りリストで指定します。このプロパティに指定する値は、SSLエンジンがサポートしているSSLプロトコルを限定するために使用されます。マッチしたSSLプロトコルはすべて無効になります。
    -            </p>
    -            <p>カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。
    -            </p>
    -            <p>指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>にあるものだけです。
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.includedProtocols"></a>
    -              <span class="prop" container="parameters">includedProtocols</span></td>
    -          <td><code>String</code></td>
    -          <td>
    -            <p>セッションネゴシエーションをする際に使用するSSLプロトコル名をカンマ区切りリストで指定します。
    -このプロパティに指定する値は、SSLエンジンがサポートしているSSLプロトコルを特定するために使用されます。マッチしたSSLプロトコルはすべて有効になります。
    -
    -            </p>
    -            <p>カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。
    -
    -            </p>
    -            <p>指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>にあるものだけです。
    -            </p>
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.needClientAuth"></a>
    -              <span class="prop" container="parameters">needClientAuth</span></td>
    -          <td><code>boolean</code></td>
    -          <td>サーバー側でクライアント認証が<em>必要</em>なときは、このプロパティに<code>true</code>を指定します。クライアントコンポーネント(<code>SSLSocketAppender</code>など)の場合はこのプロパティは無視されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><a name="parameters.wantClientAuth"></a>
    -              <span class="prop" container="parameters">wantClientAuth</span></td>
    -          <td><code>boolean</code></td>
    -          <td>サーバー側でクライアント認証が<em>必要</em>なときは、このプロパティに<code>true</code>を指定します。
    -クライアントコンポーネント(<code>SSLSocketAppender</code>など)の場合はこのプロパティは無視されます。
    -
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h4><a name="TrustManagerFactoryFactoryBean"></a>トラストマネージャファクトリーの設定</h4>
    -          
    -      <p><a href="http://docs.oracle.com/javase/1.5.0/docs/api/javax/net/ssl/TrustManagerFactory.html"><code>TrustManagerFactory</code></a>を作成するために必要な設定は、<code><a href="http://logback.qos.ch/xref/ch/qos/logback/core/net/ssl/TrustManagerFactoryFactoryBean.html">TrustManagerFactoryFactoryBean</a></code>が提供します。JVMのデフォルトのファクトリーで十分なことがほとんどなので、普通は明示的に設定する必要がありません。
    -
    -      </p>
    -
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>プロパティ名</th>
    -          <th>型</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="trustManagerFactory">algorithm</span></td>
    -          <td><code>String</code></td>
    -          <td><code>TrustManagerFactory</code>のアルゴリズム名を指定します。指定できるのは、<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html">JSSEリファレンスガイド</a>の<a href="http://docs.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#AppA">標準名</a>にあるものだけです。このプロパティが設定されていない場合は、JVMのデフォルトのキーマネージャのアルゴリズムが使用されます。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><span class="prop" container="trustManagerFactory">provider</span></td>
    -          <td><code>String</code></td>
    -          <td><code>安全な乱数生成器</code>を生成するために使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダが使用されます。
    -          </td>
    -        </tr>
    -      </table>
    -      
    -      <h2 class="doAnchor"><a name="Examples"></a>例</h2>
    -
    -      <h3>JSSEシステムプロパティを使用する</h3>
    -      <p>JSSEシステムプロパティは、サーバーのX.509証明書を含むキーストアの場所とパスワードを指定するために使われます。また、クライアントコンポーネントが信頼できるサーバーかどうかを検証するために使用する、自己署名ルートCA証明書を含むトラストストアの場所とパスワードを指定します。</p>
    -
    -      <h4>サーバーのキーストアを指定する</h4>
    -      <p>サーバーコンポーネントを実行する場合、サーバーの証明書を含むキーストアの場所とパスワードを指定しなければなりません。そのために、JSSEシステムプロパティを使用することができます。logbackの配布物に含まれる<code>SimpleSSLSocketServer</code>を実行してみましょう。</p>
    -    
    -      <p class="source">java -DkeyStore=/etc/logback-server-keystore.jks \
    -     -DkeyStorePassword=changeit -DkeyStoreType=JKS \
    -     ch.qos.logback.net.SimpleSSLSocketServer 6000 /etc/logback-server-config.xml</p>
    -              
    -      <p>JSSEシステムプロパティの<em>keyStore</em>に、サーバーのキーストアへのパスを指定するのを忘れないようにしてください。そのパスを<em>logback.xml</em>で指定するときはURLで指定します。</p>
    -      
    -      <p>この例ではlogbackの配布物に含まれているスタンドアローンのサーバーアプリケーションを実行しました。どんなアプリケーションでも、logbackのサーバーコンポーネントを使う場合は同じようにシステムプロパティを指定できます。
    -      
    -      <h4>クライアントのトラストストアを指定する</h4>
    -      
    -      <p>クライアントコンポーネントを使用する場合、信頼できるサーバーかどうかを検証するために使用する、ルートCA証明書を含むトラストストアの場所とパスワードを指定しなければなりません。そのためにJSSEシステムプロパティを使用することができます。logbackのSSL対応のクライアントコンポーネントを複数使用する<code>com.example.MyLoggingApplication</code>を実行してみましょう。</p>
    -
    -      <p class="source">java -DtrustStore=/etc/logback-client-truststore.jks \
    -     -DtrustStorePassword=changeit -DtrustStoreType=JKS \
    -     com.example.MyLoggingApplication</p>
    -      
    -      <p>JSSEシステムプロパティの<em>trustStore</em>に、トラストストアへのパスを指定するのを忘れないようにしてください。そのパスを<em>logback.xml</em>で指定するときはURLで指定します。</p>
    -      
    -      <h3>サーバーコンポーネントの自己署名証明書を生成して利用する</h3>
    -      <p>自己署名証明書を生成するには、Javaランタイム環境(JRE)に同梱されている<em>keytoolユーティリティ</em>を使用します。以下の手順では、サーバーコンポーネントの自己署名X.509証明書をキーストアに配置してから、クライアントコンポーネントの使用するトラストストアを作成します。
    -      </p>
    -      
    -      <h4>サーバコンポーネントの証明書の生成</h4>
    -      <p>次のコマンドを実行すると、<em>server.keystore</em>というファイル名の自己署名クライアント証明書を生成します。</p>
    -      <pre class="source">keytool -genkey -alias server -dname "CN=my-logging-server" \
    -    -keyalg RSA -validity 365 -keystore server.keystore
    -Enter keystore password: &lt;Enter password of your choosing&gt;
    -Re-enter new password: &lt;Re-enter same password&gt;
    -Enter key password for &lt;my-logging-server&gt;
    -	(RETURN if same as keystore password):  &lt;Press RETURN&gt;
    -</pre>
    -
    -      <p><em>dname</em>に指定した<em>my-logging-server</em>は、正当なものであればどんなものでも構いません。サーバーを実行するホストの完全修飾ドメイン名にするとよいでしょう。<em>validity</em>には証明書の有効期限が切れるまでの日数を指定します。</p>
    -      
    -      <p>本番環境の設定で重要になるのが、サーバー証明書を配置するキーストアにとても強力なパスワードを指定することです。このパスワードによって、サーバーの秘密鍵にアクセスできるのが限られた関係者だけであることを保証します。後の手順でこのパスワードが必要になるので、サーバーの設定をするときに心の中にメモしておいてください。
    -      </p>
    -
    -      <h4>クライアントコンポーネント用のトラストストアの作成</h4>
    -      <p>クライアントコンポーネントの設定に使用するため、前の手順で作成したサーバーの証明書をキーストアからエクスポートして、トラストストアにインポートしましょう。次のコマンドを実行すると、証明書をエクスポートして<em>server.truststore</em>というファイル名のトラストストアにインポートします。</p>
    -      
    -      <pre class="source">keytool -export -rfc -alias server -keystore server.keystore \
    -    -file server.crt
    -Enter keystore password: &lt;Enter password you chose for in previous step&gt;
    -
    -keytool -import -alias server -file server.crt -keystore server.truststore
    -Enter keystore password: &lt;Enter password of your choosing&gt;
    -Re-enter new password: &lt;Re-enter same password&gt;
    -Owner: CN=my-logging-server
    -Issuer: CN=my-logging-server
    -Serial number: 6e7eea40
    -Valid from: Sun Mar 31 07:57:29 EDT 2013 until: Mon Mar 31 07:57:29 EDT 2014
    -
    -   ...
    -
    -Trust this certificate? [no]:  &lt;Enter "yes"&gt;
    -</pre>
    -
    -      <p>最初のコマンドは、キーストアからエクスポートしたサーバー証明書(サーバーの秘密鍵ではありません)を<em>server.crt</em>というファイル名で保存します。二つ目のコマンドは、サーバー証明書を含んだ<em>server.truststore</em>という新しいトラストストアを作成します。
    -      </p>
    -      
    -      <p>本番環境の設定で重要になるのが、トラストストアにとても強力な、そして、サーバーのキーストアに使用したパスワードとは異なるパスワードを設定することです。ここで指定したパスワードは、クライアントのアペンダーの設定に必要になるので心の中にメモしておいてください。
    -      </p>
    -          
    -      <h4>サーバコンポーネントの設定</h4>
    -      <p><em>server.keystore</em>をアプリケーションアーカイブの内部に取り込まなければならないかもしれません。キーストアはアプリケーションのクラスパスリソースとして扱うこともできますし、単にサーバーを実行するホストのファイルシステム上に配置することもできます。設定ファイルからキーストアの場所を指定するときは、<code>classpath:</code>から始まるURLか、<code>file:</code>から始まるURLのいずれかを指定します。設定ファイルを見てみましょう。</p>
    -
    -      <p class="example">例:サーバーコンポーネントの設定</p>
    -      <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;
    -      &lt;pattern&gt;%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -  
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="CONSOLE" /&gt;
    -  &lt;/root&gt;
    -
    -  &lt;server class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver"&gt;
    -    &lt;ssl&gt;
    -      &lt;keyStore&gt;
    -        &lt;location&gt;classpath:server.keystore&lt;/location&gt;
    -        &lt;password&gt;${server.keystore.password}&lt;/password&gt;
    -      &lt;/keyStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/server&gt;
    -&lt;/configuration&gt;</pre>
    -      
    -      <p>この例では、キーストアがアプリケーションのクラスパスのルートに配置されていることを前提としています。</p>
    -      
    -      <p>また、キーストアのパスワードには<em>server.keystore.password</em>という変数を指定しています。こうしておけば、どの設定ファイルにもパスワードを書いておかなくてもよくなります。たとえば、アプリケーションが起動した後、ロギングシステムを設定する前にコンソールでパスワード入力を促すプロンプトを出すようにして、システムプロパティ<em>server.keystore.password</em>をプログラム的に設定すればよいのです。
    -      </p>
    -      
    -      <h4>クライアントコンポーネントの設定</h4>
    -      <p>クライアントとして振る舞うSSL対応のコンポーネントを使用するアプリケーションそれぞれについて、<em>server.truststore</em>をアプリケーションアーカイブの内部に取り込まなければならないかもしれません。トラストストアはアプリケーションのクラスパスリソースとして扱うこともできますし、単にクライアントを実行するホストのファイルシステム上に配置することもできます。
    -設定ファイルからトラストストアの場所を指定するときは、<code>classpath:</code>から始まるURLか、<code>file:</code>から始まるURLのいずれかを指定します。
    -アペンダーの設定を見てみましょう。</p>
    -
    -      <p class="example">例:アペンダーの設定</p>
    -
    -      <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -  &lt;appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"&gt;
    -    &lt;remoteHost&gt;${host}&lt;/remoteHost&gt;
    -    &lt;ssl&gt;
    -      &lt;trustStore&gt;
    -        &lt;location&gt;classpath:server.truststore&lt;/location&gt;
    -        &lt;password&gt;${server.truststore.password}&lt;/password&gt;
    -      &lt;/trustStore&gt;
    -    &lt;/ssl&gt;
    -  &lt;/appender&gt;
    -
    -  &lt;root level="DEBUG"&gt;
    -    &lt;appender-ref ref="SOCKET" /&gt;
    -  &lt;/root&gt;
    -&lt;/configuration&gt;</pre>
    -
    -      <p>この例では、トラストストアがアプリケーションのクラスパスのルートに配置されていることを前提としています。</p>
    -      
    -      <p>また、トラストストアのパスワードには<em>server.truststore.password</em>という変数を指定しています。
    -こうしておけば、どの設定ファイルにもパスワードを書いておかなくてもよくなります。
    -たとえば、アプリケーションが起動した後、ロギングシステムを設定する前にコンソールでパスワード入力を促すプロンプトを出すようにして、システムプロパティ<em>server.truststore.password</em>をプログラム的に設定すればよいのです。
    -
    -      </p>
    -
    -      <h2>SSLの設定を監査する</h2>
    -      <p>安全な通信が必要とされる環境では、SSLを使うコンポーネントがローカルセキュリティポリシーを満たしていることを検証するため、設定内容を監査しなければならない。logbackでは、自身を初期化している間に行われるSSLの設定について詳細なロギング情報を提供することでこれに対応しています。設定中で<code>debug</code>プロパティを使うことで、監査ログを有効化することができます。</p>
    -      
    -      <pre class="prettyprint source">&lt;configuration debug="true"&gt;
    -  
    -  ...
    -  
    -&lt;/configuration&gt;</pre>
    -
    -      <p>debugプロパティを有効にすると、ロギングシステムの初期化中に行われるSSLの設定に関するあらゆる情報が得られるようになります。SSL設定の監査ログとして出力されるのは次のようなものです。</p>
    -      
    -      <p class="example">例:SSL設定の監査ログ</p>
    -      
    -      <pre>06:46:31,941 |-INFO in SSLServerSocketReceiver@4ef18d37 - SSL protocol 'SSL' provider 'SunJSSE version 1.6'
    -06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key store of type 'JKS' provider 'SUN version 1.6': file:src/main/java/chapters/appenders/socket/ssl/keystore.jks
    -06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key manager algorithm 'SunX509' provider 'SunJSSE version 1.6'
    -06:46:31,973 |-INFO in SSLServerSocketReceiver@4ef18d37 - secure random algorithm 'SHA1PRNG' provider 'SUN version 1.6'
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv2Hello
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv3
    -06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: TLSv1
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_MD5
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_SHA
    -06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
    -</pre>
    -
    -      <p>ここで示した出力は大幅にカットしてありますが、一般的には次のようなものが含まれています。
    -          <ul>
    -            <li>完全なプロトコルのリスト</li>
    -            <li>プロバイダー</li>
    -            <li>アルゴリズム</li>
    -            <li>暗号スイート</li>
    -            <li>キーストアやトラストストアの場所</li>
    -          </ul>
    -      </p>
    -      
    -      <p>この監査ログに取り扱いに注意の必要な情報が含まれることはありませんが、セキュリティのベストプラクティスとしては、本番環境の設定の妥当性が確認されたなら、監査ログは無効化するべきです。<code>debug</code>プロパティを消すか、<code>false</code>を指定すれば監査ログは無効化されます。
    -      </p>
    -      
    -      <h2>SSLの設定誤りを解決する</h2>
    -      <p>SSLの設定が間違っていると、普通ならクライアントとサーバーのコンポーネントの間でセッションを確立できなくなります。これは、クライアントがサーバーに接続しようとしてそれができなかったときにそれぞれで例外をスローすることによって明らかになります。
    -      </p>
    -      <p>例外メッセージの内容は、見ているログによって異なります。それは、セッションネゴシエーション中に報告されるエラーの原因は、主にプロトコルの制限によるものだからです。したがって、セッションネゴシエーションの問題を解決するには、クライアントとサーバーの両方のログを調査しなければなりません。
    -      </p>
    -      
    -      <h3>サーバの証明書が使用できません</h3>
    -      <p>サーバーの証明書が使用できない時、サーバコンポーネントを起動すると次のような例外メッセージがログに出力されます。</p>
    -         
    -      <p><em>javax.net.ssl.SSLException: No available certificate or 
    -         key corresponds to the SSL cipher suites which are enabled</em>
    -      </p>
    -      
    -      <p>ほとんどの場合、サーバーの秘密鍵に対応する証明書を含むキーストアの場所を設定し忘れていることが原因です。
    -      </p>
    -      
    -      <h4>ソリューション</h4>
    -      <p>システムプロパティの<a href="./15-usingSSL.html#basicConfig.keyStore">キーストア</a>か、サーバーコンポーネントの<span class="prop">ssl</span>の設定で<span class="prop"><a href="./15-usingSSL.html#ssl.keyStore">keyStore</a></span>プロパティに、サーバ証明書を含むキーストアの場所を指定してください。あと、キーストアのパスワードも指定してください。
    -      </p>
    -      
    -      <h3>クライアントはサーバーを信頼できません</h3>
    -      <p>クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: 
    -              sun.security.validator.ValidatorException: 
    -              PKIX path building failed</em>
    -      </p>
    -      <p>これは、クライアントがサーバーの提示した証明書を信頼できないと判断した場合に発生します。よくある原因としては、サーバーが自己署名サーバー証明書(あるいは内部の認証局で署名したサーバー証明書)を使っている上で、クライアントの使用するトラストストアにサーバーの自己署名証明書が含まれていない(あるいはサーバー証明書に署名したCAの信頼できるルート証明書が含まれていない)ことです。
    -      </p> 
    -      <p>サーバー証明書が失効している場合も同じような現象が発生します。サーバー側のログを見ることが出来るなら、クライアントが接続しようとするたびに次のような例外メッセージが現れているのがわかると思います。</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: Received fatal alert: ...</em>
    -      </p>
    -      
    -      <p>例外メッセージの後半部分には、クライアントがサーバー証明書を拒否した理由を表すコードが現れます。
    -      </p>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>コー​​ド</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_unknown</code></td>
    -          <td>クライアントのトラストストアが正しく設定されていないことを示しています。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_expired</code></td>
    -          <td>サーバー証明書が失効しているので更新が必要なことを示しています。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_revoked</code></td>
    -          <td>サーバー証明書に署名した認証局(CA)が、そのサーバー証明書を無効にしたことを示しています。サーバー証明書を更新しなければなりません。
    -          </td>
    -        </tr>
    -      </table>
    -        
    -      <h4>ソリューション</h4>
    -      <p>サーバー側のログに<code>certificate_unknown</code>が現れているときは、システムプロパティ<a href="./15-usingSSL.html#basicConfig.trustStore">トラストストア</a>か、アペンダーの<span class="prop">ssl</span>設定に<a href="./15-usingSSL.html#ssl.trustStore">trustStore</a>プロパティを指定してください。サーバーの自己署名証明書か、証明書に署名したCAのルート証明書を格納した<span class="prop">トラストストア</span>の場所と、アクセスするためのパスワードを指定しなければなりません。
    -      </p>
    -
    -      <p>サーバー側のログに<code>certificate_expired</code>か<code>certificate_revoked</code>が現れているときは、新しいサーバー証明書が必要です。サーバーの設定で指定したキーストアの場所に、新しいサーバー証明書と対応する秘密鍵を配置してください。自己署名サーバー証明書を使っているときは、クライアントのアペンダーの設定に指定されているトラストストアにも新しい証明書を配置しなければなりません。
    -      </p>
    -      
    -      <h3>サーバーはクライアントを信頼できません
    -</h3>
    -      <p>注: <strong>この問題が生じるのは、サーバーがクライアント証明書を要求するように明示的に設定している場合だけです(<a href="./15-usingSSL.html#parameters.needClientAuth"><span class="prop">needClientAuth</span></a>か<a href="./15-usingSSL.html#parameters.wantClientAuth"><span class="prop">wantClientAuth</span></a>プロパティを指定したときです)。</strong>
    -      </p>
    - 
    -      <p>クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
    -</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: ...</em>
    -      </p>
    -
    -      <p>例外メッセージの後半部分には、サーバーがクライアントの証明書を拒否した理由を表すコードが現れます。
    -
    -      </p>
    -      <table class="bodyTable striped">
    -        <tr>
    -          <th>コー​​ド</th>
    -          <th>説明</th>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_unknown</code></td>
    -          <td>サーバのトラストストアが正しく設定されていないことを示しています。
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_expired</code></td>
    -          <td>クライアントの証明書が失効しているので更新が必要なことを示しています。
    -
    -          </td>
    -        </tr>
    -        <tr>
    -          <td><code>certificate_revoked</code></td>
    -          <td>クライアントの証明書に署名した認証局(CA)が、その証明書を無効にしたことを示しています。証明書を更新しなければなりません。
    -
    -          </td>
    -        </tr>
    -      </table>
    -        
    -      <h4>ソリューション</h4>
    -      <p>クライアント側のログに<code>bad_certificate</code>が現れているときは、システムプロパティ<a href="./15-usingSSL.html#basicConfig.trustStore">トラストストア</a>かサーバーコンポーネントの<span class="prop">ssl</span>設定の<span class="prop"><a href="./15-usingSSL.html#ssl.trustStore">trustStore</a></span>プロパティに、トラストストアの場所とパスワードを指定しなければなりません。トラストストアにはクライアントの自己署名証明書か、証明書に署名したCAのルート証明書が含まれていなければなりません。
    -      </p>
    -
    -      <p>サーバー側のログに<code>certifacte_expired</code>あるいは<code>certificate_revoked</code>が現れているときは、新しい証明書が必要です。クライアントの設定で指定したキーストアの場所に、新しい証明書と対応する秘密鍵を配置してください。自己署名証明書を使っているときは、サーバーの設定に指定されているトラストストアにも新しい証明書を配置しなければなりません。
    -
    -      </p>
    -      
    -      <h3>クライアントとサーバー間でSSLプロトコルを合意できません</h3>
    -      <p>注: <strong>この問題が生じるのは、明示的にSSLプロトコルを<a href="./15-usingSSL.html#parameters.excludedProtocols">除外</a>したり<a href="./15-usingSSL.html#parameters.includedProtocols">指定</a>している場合だけです</strong> 。
    -      </p>
    -
    -      <p>クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: handshake_failure</em>
    -      </p>
    -      
    -      <p>サーバーのログに現れるメッセージのほうがわかりやすいです。</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: SSLv2Hello is disabled</em>
    -      </p>
    -      
    -      <p>一方のピアで除外したプロトコルが、もう一方のピアでは除外されていない場合に発生します。</p>
    -         
    -      <h4>ソリューション</h4>
    -      <p>サーバーとクライアントの両方で、<a href="./15-usingSSL.html#parameters.excludedProtocols"><span class="prop">excludedProtocols</span></a>と<a href="./15-usingSSL.html#parameters.includedProtocols"><span class="prop">includedProtocols</span></a>に指定した値を確認してください。
    -      </p>
    -
    -      <h3>クライアントとサーバー間で暗号スイートを合意できません
    -</h3>
    -      <p>注: <strong>この問題が生じるのは、明示的に暗号スイートを<a href="./15-usingSSL.html#parameters.excludedCipherSuites">除外</a>したり<a href="./15-usingSSL.html#parameters.includedCipherSuites">指定</a>している場合だけです</strong> 。
    -      </p>
    -      
    -      <p>クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
    -</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException:  Received fatal 
    -         alert: handshake_failure</em>
    -      </p>
    -      
    -      <p>サーバーのログに現れるメッセージのほうがわかりやすいです。
    -</p>
    -      
    -      <p><em>javax.net.ssl.SSLHandshakeException: no cipher suites in common</em>
    -      </p>
    -      
    -      <p>クライアントとサーバーそれぞれの暗号スイート一覧について、1つも一致するものが無い場合に発生します。</p>
    -      
    -      <h4>ソリューション</h4>
    -      <p>サーバーとクライアントの両方で、<a href="./15-usingSSL.html#parameters.excludedCipherSuites"><span class="prop">excludedCipherSuites</span></a>と<a href="./15-usingSSL.html#parameters.includedCipherSuites"><span class="prop">includedCipherSuites</span></a>に指定した値を確認してください。
    -
    -      </p>
    -         
    -    <script src="../templates/footer.js" type="text/javascript"></script>
    -
    -    </p></p></div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/news.html b/logback-site/src/site/pages/news.html
    deleted file mode 100755
    index 23b904b96e..0000000000
    --- a/logback-site/src/site/pages/news.html
    +++ /dev/null
    @@ -1,3381 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>News</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="css/prettify.css" media="screen" />    
    -  </head>
    -
    -  <body onload="prettyPrint()">
    -    <script type="text/javascript">prefix='';</script>
    -    <script type="text/javascript" src="js/prettify.js"></script>
    -    <script src="templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="templates/right.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -    <h2>Logback News</h2>
    -  
    -    <p>You can receive logback-related announcements by subscribing to
    -    the <a href="http://www.qos.ch/mailman/listinfo/announce">QOS.ch
    -    announce</a> mailing list.</p>
    -
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>February 11th, 2018, Release of version 1.3.0-alpha4</h3>
    -
    -    <p class="highlight">The 1.3.x series is JPMS/Jigsaw/Java&nbsp;9
    -    modularized and requires slf4j-api version 1.8.x. Moreover, the
    -    1.3.x series requires Java&nbsp;8 at
    -    <b>runtime</b>.</p>
    -
    -    <p>The 1.3.x series is Jigsaw/Java&nbsp;9 modularized and requires
    -    slf4j-api version 1.8.x. Moreover, the 1.3.x series requires
    -    Java&nbsp;8 at <b>runtime</b> whereas building logback from source
    -    requires Java&nbsp;9.  <br/></p>
    -
    -    <br/>
    -    
    -    <div class="breaking">
    -      <p>Given that all currently available versions of Groovy are
    -      incompatible with Java 9, at least when built with Maven, we have
    -      momentarily <b>dropped support for Groovy configuration</b>, a.k.a
    -      Gaffer. For details refer to <a
    -      href="http://markmail.org/thread/nekeppbvwrfl7hbb#query:+page:1+mid:obdyvuv24kqpxm6v+state:results">this
    -      thread</a> and relevant <a href="https://issues.apache.org/jira/browse/GROOVY-8471">jira issue.</a></p>
    -    </div>
    -    
    -
    -    <br/>  
    -    
    -    <div class="breaking">
    -      <p>Due to lack of user interest, logback-classic no longer
    -      supports <a href="manual/loggingSeparation.html">logging
    -      separation</a> by way of <code>ContextSelector</code>.</p>
    -    </div>
    -    
    -    <p>Under JPMS (Java 9), module <code>ch.qos.logback.core</code> is
    -    now able to read module <code>java.xml</code>. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1381">LOGBACK-1381</a>
    -    reported by Mark Raynsford.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>February 10th, 2018, Release of version 1.3.0-alpha3</h3>
    -
    -    <p>Under JPMS (Java 9), allow user code to implement
    -    <code>ch.qos.logback.classic.spi.Configurator</code> as a
    -    service. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1380">LOGBACK-1380</a>
    -    reported by Mark Raynsford.
    -    </p>
    -
    -    
    -    <p>Fix issue with properties containing JSON strings as reported
    -    in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1101">LOGBACK-1101</a>.
    -    </p>
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>January 30th, 2018, Release of version 1.3.0-alpha2</h3>
    -
    -    <p>Depend on SLF4J version 1.8.0-beta1 instead of
    -    1.8.0-beta1-SNAPSHOT.</p>
    -
    -    <p>Fix build under Travis.</p>
    -    
    -    <h3>January 30th, 2018, Release of version 1.3.0-alpha1</h3>
    -    
    -
    -
    -    
    -    <p>In the absence of a <span class="attr">class</span> attribute,
    -    the <code>shutdownHook</code> configuration directive now
    -    correctly assumes
    -    <code>ch.qos.logback.core.hook.DefaultShutdownHook</code>
    -    class. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1162">LOGBACK-1162</a>
    -    reported by Alex Selesse.</p>
    -    
    -    <p>Logback's internal <code>ThreadPoolExecutor</code> now has an
    -    initial pool of 1 thread instead of 8 previously.</p>
    -
    -    <p>TimeBasedArchiveRemover is now able to deal with indexes over
    -    999. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1175">LOGBACK-1175</a>
    -    reported by Diego Furtado who also provided the relevant PR.
    -    </p>
    -
    -    <p><code>SizeAndTimeBasedRollingPolicy</code> will correctly
    -    remove archived log files created within a given time period even
    -    if the last modified date for said files has been modifed since
    -    archiving or is otherwise incorrect. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1361">LOGBACK-1361</a> as
    -    reported by Peter Risko.</p>
    -
    -    <p>String to Level conversion now handles strings with tailing
    -    spaces. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1288">LOGBACK-1288</a>
    -    reported by Gaurav Khanna.
    -    </p>
    -      
    -    <p><code>MDCFilter</code> will enforce the presence of
    -    <code>MDCKey</code> and <code>value</code> properties, refusing to
    -    start when either property absent. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1165">LOGBACK-1165</a>
    -    reported by Martin Steiger.
    -    </p>
    -
    -    <p>Added the feature to update mime message before sending the
    -    email. This enhancement was requested in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1284">LOGBACK-1284</a> by
    -    Miguel Vale who also provided the relevant PR.</p>
    -    
    -    <p>If omitted, the <span class="option">scanPeriod</span>
    -    attribute in configuration file defaults to 1 minute. The
    -    documentation <a
    -    href="manual/configuration.html#autoScan">stated
    -    as much</a> but the code had not followed through. This issue was
    -    reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1194">LOGBACK-1194</a>
    -    with F. Buechler providing the appropriate patch.</p>
    -
    -    <p>Updated JavaMail version to 1.6. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1094">LOGBACK-1094</a>
    -    reported by Romain Moreau.
    -    
    -    <p>Removed "final" modifier for the
    -    <code>LoggerContext.getLogger(String)</code> method. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1308">LOGBACK-1308</a>
    -    reported by Richard Sand.</p>
    -    
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>January 18th, 2018, Release of version 1.3.0-alpha0</h3>
    -    
    -   
    -    <p>Added support for minimum length in %i filename pattern. This
    -    fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1248">LOGBACK-1248</a>
    -    with John Gardiner Myers providing the relevant patch.</p>
    -
    -    <p>Correct parsing of <code>java.version</code> system property
    -    for Java 9 and later. This fixes <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1260">LOGBACK-1260</a>
    -    with Patrick Reinhart providing the relevant patch.
    -    </p>
    -
    -    <p>ConsoleAppender now works with consoles confused by output
    -    strings of zero length, e.g.  Java Web Start. This fixes issue <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1282">LOGBACK-1282</a>
    -    reported by Clas Forsberg.</p>
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>March 30th, 2017, Release of version 1.2.3</h3>
    -
    -    <p>Fix unintentional dependency on
    -    <code>OutputStreamAppender</code> in
    -    <code>LayoutWrappingEncoder</code> as reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1287">LOGBACK-1287</a>.
    -    </p>
    -    
    -    <p>Fix spurious <code>System.out.println</code> in AsyncAppender's
    -    worker thread. This issue was reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1292">LOGBACK-1292</a> by
    -    Nikolas Loutas with Thibault Meyer the relevant patch.</p>
    -
    -    <p>ConsoleAppender exception when run under Java WebStart. This
    -    problem was reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1282">LOGBACK-1282</a> by
    -    Clas Forsberg.</p>
    -    
    -    <h3>March 16th, 2017, Release of version 1.2.2</h3>
    -
    -    <p><code>AsyncAppender</code> no longer drops events when the
    -    current thread has its interrupt flag set. This issue was reported
    -    in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1247">LOGBACK-1247</a> by
    -    Jakob Bergendahl who also provided the relevant pull request.
    -    </p>
    -
    -    <p>Removed <code>JMSQueueAppender</code> and
    -    <code>JMSTopicAppender</code>. These appenders were undocumented
    -    and had no apparent users.</p>
    -    
    -    <p>Remove comment in logback-classic's
    -    <code>META-INF/services/javax.servlet.ServletContainerInitializer</code>
    -    file in order to keep Wildfly 8 happy. This issue was reported in
    -    <a href="https://jira.qos.ch/browse/LOGBACK-1265">LOGBACK-1265</a>
    -    by Andy Wilkinson.
    -    </p>
    -
    -    <p><code>FileSize.toString()</code> now reports files sizes in GB
    -    in addition to sizes in MB and KB and Bytes. This fixes issue <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1278">LOGBACK-1278</a>.</p>
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>March 1st, 2017, Release of version 1.1.11</h3>
    -    
    -    <p>Fix thread-safety issue with <code>PatternLayoutBase</code>
    -    reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1270">LOGBACK-1270</a> by
    -    Artem Bilan. Note that the fix was already present in the 1.2.x
    -    series and was back-ported to the 1.1.x series.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>February 9th, 2017, Release of version 1.2.1</h3>
    -
    -    <p>To ensure backward compatibility of configuration files, the
    -    <span class="prop">immediateFlush</span> property set for a
    -    <code>LayoutWrappingEncoder</code> is propagated to the enclosing
    -    <code>OutputStreamAppender</code>.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>February 8th, 2017, Release of version 1.2.0</h3>
    -
    -    <div class="breaking">
    -      <h4><code>Encoder</code> interface has changed and is no longer
    -      expected to handle an <code>OutputStream</code>.</h4>
    -    </div>
    -
    -
    -    <p><code>Encoder</code> interface has changed and is no longer
    -    expected to handle an <code>OutputStream</code>. This
    -    simplification allows finer-grain locking resulting in <a class="big"
    -    href="https://docs.google.com/spreadsheets/d/1cpb5D7qnyye4W0RTlHUnXedYK98catNZytYIu5D91m0/edit?usp=sharing">significantly
    -    improved performance</a>.
    -    </p>
    -
    -    <p class="highlight">Release 1.2.0 fixes a rather severe
    -    serialization vulnerability in <code>SocketServer</code> and
    -    <code>ServerSocketReceiver</code>. Users running these components
    -    should upgrade immediately.</p>
    -
    -    
    -    <p>This release fixes a rather severe serialization vulnerability
    -    in <code>SocketServer</code> and
    -    <code>ServerSocketReceiver</code>. Users running these components
    -    should upgrade immediately.
    -    </p>
    -
    -    <p>In <code>TimeBasedRollingPolicy</code>, fixed issue with <span
    -    class="option">totalSizeCap</span> of more than 2^31. This problem
    -    was reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1231">1231</a> by Simon
    -    Teng.
    -    </p>
    -
    -    <p>Logback-classic now searches for the file
    -    <em>logback-test.xml</em> first, <em>logback.groovy</em> second
    -    and <em>logback.xml</em> third. In previous versions
    -    <em>logback.groovy</em> was looked up first which was non-sensical
    -    in presense of <em>logback-test.xml</em>. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1245">LOGBACK-1245</a>
    -    reported by Joern Huxhorn.
    -    </p>
    - 
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>February 5th, 2017, Release of version 1.1.10</h3>
    -
    -    <p>Several changes to improve throughput (see <a
    -    href="https://docs.google.com/spreadsheets/d/1cpb5D7qnyye4W0RTlHUnXedYK98catNZytYIu5D91m0/edit?usp=sharing"><b>spreadsheet</b></a>)
    -    </p>
    -
    -    <ol>
    -      <li>The <code>ReentrantLock</code> in
    -      <code>OutputStreamAppender</code> is now "unfair". In previous
    -      versions of logback, a fair lock was used. Fair locks are much
    -      slower. Just as importanly, logback has no mandate to influence
    -      thread scheduling.
    -      </li>
    -
    -      <li><code>FileAppender</code> now offers the <span
    -      class="option">bufferSize</span> option. Previously, a
    -      fixed-size 8K buffer was used. Increasing the <span
    -      class="option">bufferSize</span>, for example to 256K,
    -      significantly reduces thread-contention.
    -      </li>
    -
    -      <li>Critical parts of the code now use
    -      <code>COWArrayList</code>, a custom developed allocation-free
    -      lock-free thread-safe implementation of the {@link List}
    -      interface. It is optimized for cases where iterations over the
    -      list vastly outnumber modifications on the list. It is based on
    -      <code>CopyOnWriteArrayList</code> but allows allocation-free
    -      iterations over the list.
    -      </li>
    -
    -      <li>In <code>PatternLayoutBase</code> the same
    -      <code>StringBuilder</code> is used over and over to reduce
    -      memory allocation. This is safe as long as the owning appender
    -      guarantees serial access to its layout.  In the next version of
    -      logback, i.e. 1.2.x, the read-write lock will no longer protect
    -      access to the layout and there will be no guarantee of
    -      serial access.
    -      </li>
    -    </ol>
    -
    -    <p>In web-applications, logback-classic will <a
    -    href="manual/configuration.html#webShutdownHook">automatically
    -    install a listener</a> which will stop the logging context and
    -    release resources when your web-app is reloaded. This enhancement
    -    was requested <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1170">LOGBACK-1170</a> by
    -    Martin Wegner.
    -    </p>
    -
    -    <p>The <code>AccessEvent.prepareForDeferredProcessing()</code>
    -    method was not idempotent. This caused subtle bugs under
    -    Jetty. See <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1189">LOGBACK-1189</a>
    -    for details. Many thanks to Per Olesen, Evan Meagher, Nick Babcock
    -    and Mark Elliot for hunting down this bug.
    -    </p>
    -    
    -    <p>As it may be time consuming in certain environments, the
    -    HOSTNAME property is now computed lazily. This optimization
    -    requested in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1221">LOGBACK-1221</a> by
    -    Eugene Petrenko.
    -    </p>
    -
    -
    -    <p>Joran now supports external XML entities. This feature was
    -    requested in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1091">1091</a> and the
    -    relevant PR graciously provided by Jonas Neukomm.
    -    </p>
    -
    -   <hr width="80%" align="center" />
    -
    -    <h3>January 20th, 2017, Release of version 1.1.9</h3>
    -
    -    <p>Logback's internal executor service had a thread pool size of 2
    -    which could be used up rather quickly, e.g. configuration scanning
    -    in addition to an active <code>ServerSocketAppender</code>. When
    -    both threads where permanently in use, compression could not
    -    proceed. To alleviate this problem, the thread pool size has been
    -    increased to 8. See issue <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1238">LOGBACK-1238</a>
    -    for more details.
    -    </p>
    -
    -    <p>Fixed issue with <code>FileAppender</code> instances embedded
    -    within <code>SiftingAppender</code> reporting filename collisions
    -    after reaching timeout and subsequently restarted. This problem
    -    was reported in <a
    -    href="https://jira.qos.ch/browse/LOGBACK-1167">LOGBACK-1167</a> by
    -    Michael Edgar.
    -    </p>
    -
    -    <p>Fixed <code>SizeAndTimeBasedFNATP</code> deprecation warning
    -    emitted even the replacement,
    -    i.e. <code>SizeAndTimeBasedRollingPolicy</code>, is in use. See <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1236">LOGBACK-1236</a>. This
    -    issue was reported by Claudius Nicolae.      
    -    </p>
    - 
    -    <p>Added proper implementation for
    -    <code>LobackValve.getScheduledExecutorService()</code> method. The
    -    missing implementation manifested itself in the form of an
    -    <code>UnsupportedOperationException</code> thrown by
    -    LogbackValve. This problem is further described in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1181">LOGBACK-1181</a>
    -    reported by Andreas von Roepenack.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>December 9th, 2016, Release of version 1.1.8</h3>
    -
    -    <p>Removed the two period safeguard, aka untouchable periods, for
    -    archive removal beyond the size specified by <span
    -    class="option">totalSizeCap</span> in
    -    <code>TimeBasedRollingPolicy</code>. It turns out the safegaurd is
    -    not required and is unexpected as attested by <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1166">LOGBACK-1166</a>.
    -    </p>
    -    
    -    <p>Fixed issue with Joran incorrectly reporting "Unexpected
    -    aggregationType AS_BASIC_PROPERTY_COLLECTION". This issue was
    -    raised in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1158">LOGBACK-1158</a> by
    -    Christian H&uuml;bner.
    -    </p>
    -
    -    <p>Gaffer (logback's groovy configurator) now supports the
    -    <code>valueOf</code> convention. This issue was raised in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1232">LOGBACK-1232</a> by
    -    Frans Orsel.
    -    </p>
    -
    -    <p>The <code>org.slf4j.impl.StaticLoggerBinder</code> class
    -    shipping in logback-classic no longer catches
    -    <code>Throwable</code> but <code>Exception</code>. This change was
    -    requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1159">LOGBACK-1159</a> by
    -    David J. M. Karlsen.
    -    </p>
    -
    -    <p><code>BeanDescriptionFactory</code> no longer outputs a
    -    superflous warning message in case the class contains bridge
    -    methods. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1164">LOGBACK-1164</a>
    -    reported by Phil Clay.
    -    </p>
    -
    -    <!--
    -    <p>Added logback-bom module for use as a Maven "Bill of Materials"
    -    <em>pom</em> file. This was requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1157">LOGBACK-1157</a>
    -    Joerg Sesterhenn.</p>
    -    -->    
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>March 29th, 2016, Release of version 1.1.7</h3>
    -    
    -    <p>Logback is now <a href="
    -    http://docs.oracle.com/javase/8/docs/technotes/guides/compactprofiles/compactprofiles.html">compact3
    -    profile</a> compatible. This improvement was requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1071">LOGBACK-1071</a> by
    -    Axel Fontaine with Max Urech providing the relevant pull-request.
    -    </p>
    -
    -    <p>Fixed <code>ConcurrentModificationException</code> being thrown
    -    when the <code>reset()</code> method is invoked on the
    -    <code>LoggerContext</code> instance. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-397">LOGBACK-397</a> by
    -    Szczepan Faber with Ross Sargant providing the relevant test case.
    -    </p>
    -
    -    <p><code>TimeBasedRollingPolicy</code> now supports the <a
    -    href="manual/appenders.html#tbrpTotalSizeCap"><span
    -    class="option">totalSizeCap</span></a> property which allows the
    -    user to limit the total size of archived logs.
    -    </p>
    -    
    -    <p><a
    -    href="manual/appenders.html#SizeAndTimeBasedRollingPolicy"><code>SizeAndTimeBasedRollingPolicy</code></a>
    -    offers the same functionality as
    -    <code>SizeAndTimeBasedFNATP</code> did previously but with a
    -    simpler configuration structure.
    -    </p>
    -
    -    <p>Archive removal by <code>RollingFileAppender</code> is now
    -    performed asynchronously.</p>
    -
    -    <p>Unnecessary and incompatible %i token in <span
    -    class="option">fileNamePattern</span> option with
    -    <code>RollingFileAppender/TimeBasedRollingPolicy</code> is now
    -    detected and the user alerted to the misconfiguration
    -    problem. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1143">LOGBACK-1143</a>.
    -    </p>
    -
    -
    -    <p>Joran can now handle logger names ending with a $, i.e. the
    -    first character in variable substitution. This issue was raised in
    -    <a href="http://jira.qos.ch/browse/LOGBACK-1149">LOGBACK-1149</a>
    -    by by Stevo Slavic.
    -    </p>
    -
    -
    -
    -    <hr width="80%" align="center" />
    -    
    -    <h3>February 29th, 2016, Release of version 1.1.6</h3>
    -
    -     <p><code>LogbackValve</code> (in logback-access) now attempts to
    -    load the configuration file as a resource if it cannot be found on
    -    the filesystem (<a
    -    href="http://jira.qos.ch/browse/LOGBACK-1069">LOGBACK-1069</a>). This
    -    is helpful in scenarios where applications are using an embedded
    -    Tomcat server.</p>
    -
    -    <p><code>JMXConfigurator.reloadDefaultConfiguration()</code>
    -    method now tolerates programmatic configuration without a
    -    URL. This change was provided by Vedran Pavic in <a
    -    href="https://github.com/qos-ch/logback/pull/302">PR 302</a>.
    -    </p>
    -
    -    <p><code>RollingFileAppender</code> will output an error message
    -    if the date time pattern in the %d token within the <span
    -    class="option">fileNamePattern</span> is not collision free. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1137">LOGBACK-1137</a>. In
    -    a similar vein, every instance of <code>FileAppender</code> will
    -    now detect if it shares the same <span class="option">File</span>
    -    option value as given for an appender defined earlier. In
    -    addition, <code>RollingFileAppender</code> instances now check for
    -    colliding <span class="option">FileNamePattern</span> values.</p>
    -
    -    <p>Fixed <code>NullPointerException</code> thrown by
    -    <code>RollingFileAppender</code> if the name of the rollover
    -    target path did not contain a parent. This issue was reported in
    -    <a href="http://jira.qos.ch/browse/LOGBACK-1054">LOGBACK-1054</a>
    -    by Paulius Matulionis. Analysis of the problem and the relevant PR
    -    was provided by Ferenc Palkovics.</p>
    -
    -    <p><code>BasicConfigurator.configure</code> method call executes
    -    significatly faster. Improved documentation for configuration
    -    using JDK 1.6 service-provider facility. These changes are in
    -    response to <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1141">LOGBACK-1141</a>
    -    requesting faster logback start-up time.
    -    </p>
    -
    -
    -    <p>Fixed issue with variable substitution with the value ending in
    -    a colon. This problem was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-1140">LOGBACK-1140</a> by
    -    Eric Cook.</p>
    -
    -    <hr width="80%" align="center" />
    -    
    -    <h3>February 13th, 2016, Release of version 1.1.5</h3>
    -
    -    <div class="breaking">
    -      <h4>MDC values are no longer inherited by child threads.</h4>
    -    </div>
    -
    -    <p>Child threads no longer inherit MDC values. In previous
    -    versions of logback as well as log4j 1.x, MDC values were
    -    inherited by child threads. Several users have argued convincingly
    -    that MDC inheritance by child threads was unhelpful and even
    -    dangerous. This change fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-422">LOGBACK-422</a> and <a
    -    href="http://jira.qos.ch/browse/LOGBACK-624">LOGBACK-624</a>
    -    </p>
    -
    -    <p>When the <code>FileNamePattern</code> string for
    -    <code>RollingFileAppender/SizeAndTimeBasedFNATP</code> lacks a %i
    -    token, then compression for the second archive in the same period
    -    cannot occur as the target file already exists. Under those
    -    circumstances, logback leaves behind .tmp files as reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-992">LOGBACK-992</a>, <a
    -    href="http://jira.qos.ch/browse/LOGBACK-173">LOGBACK-173</a> and
    -    <a
    -    href="http://jira.qos.ch/browse/LOGBACK-920">LOGBACK-920</a>. In
    -    this release, this particular condition is detected by
    -    <code>RollingFileAppender</code> which will not start but alert
    -    the user instead.
    -    </p>
    -
    -    <p>AsyncAppender is now configurable to never block. This feature
    -    was requested by Jeff Wartes in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-898">LOGBACK-898</a> with
    -    Jeff Wartes and Gareth Davis providing the relevant patch.
    -    </p>
    -  
    -
    -    <hr width="80%" align="center" />
    -    
    -    <h3>February 11th, 2016, Release of version 1.1.4</h3>
    -
    -    <div class="breaking">
    -      <h4>Logback 1.1.4 requires SLF4J version 1.7.16 or later.</h4>
    -    </div>
    -
    -    <p>Added event replay support as introduced in SLF4J version
    -    1.7.15. In most circumstances, logack-classic should run fine with
    -    earlier versions of slf4j-api, including all versions in the 1.6.x
    -    and 1.7.x series. However, with a version of slf4j-api.jar earlier
    -    than 1.7.15 in the classpath, attempting introspection of a
    -    <code>Logger</code> instance will result in a
    -    <code>NoClassDefFoundError</code> similar to that shown below.
    -    </p>
    -
    -    <pre class="prettyprint source">Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/event/LoggingEvent
    -        at java.lang.Class.getDeclaredMethods0(Native Method)
    -        at java.lang.Class.privateGetDeclaredMethods(Class.java:2451)
    -        at java.lang.Class.privateGetPublicMethods(Class.java:2571)
    -        at java.lang.Class.getMethods(Class.java:1429)
    -        at java.beans.Introspector.getPublicDeclaredMethods(Introspector.java:1261)
    -        at java.beans.Introspector.getTargetMethodInfo(Introspector.java:1122)
    -        at java.beans.Introspector.getBeanInfo(Introspector.java:414)
    -        at java.beans.Introspector.getBeanInfo(Introspector.java:161)
    -    </pre>
    -
    -    <p>
    -    </p>
    -
    -    <div class="breaking">
    -      <h4>Packaging data (as output in stack traces) is now disabled
    -      by default.
    -      </h4>
    -    </div>
    -
    -    <p>In case an application throws exceptions frequently, then
    -    computing packaging data can be very costly and will cause the
    -    application to run slower. Making bad worse. To alleviate this
    -    problem, packaging data is no longer computed by default. It has
    -    to be <a href="manual/configuration.html#packagingData">enabled
    -    explicitly</a>. In the absence of explicit instructions, i.e the
    -    user has not specified a converter handling exceptions,
    -    <code>PatternLayout</code> in logback-classic will follow the
    -    settings defining for the logging environment. If packaging data
    -    is disabled, then it add %ex as a suffix in the pattern, and if
    -    packaging data is enabled then %xEx will be added. These changes
    -    fix <a
    -    href="http://jira.qos.ch/browse/LOGBACK-730">LOGBACK-730</a> and
    -    <a href="http://jira.qos.ch/browse/LOGBACK-966">LOGBACK-966</a>.
    -    </p>
    -
    -   
    -    <p>Fixed a bug in
    -    <code>TimeBasedFileNamingAndTriggeringPolicyBase</code> causing
    -    time-based rolling policies to always rollover according to the
    -    local system time and ignore the time zone passed in the file name
    -    pattern. The issue was reported by Lukasz Sanek who also provided
    -    the the relevant fix. </p>
    -
    -    <p><code>AsyncAppenderBase</code> now restores the current
    -    thread's interrupt flag when catching an
    -    <code>InterruptedException</code>. The issue
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-910">LOGBACK-910</a>) was
    -    raised by Henrik Nordvik who also provided the relevant fix.</p>
    -
    -    <hr width="80%" align="center" />
    -    
    -    <h3>24th of March 2015, Release of version 1.1.3</h3>
    -
    -
    -    <div class="breaking">
    -      <h4>As of version 1.1.3, all logback modules require JDK 1.6
    -      instead of previously JDK 1.5. This change was put to
    -      consultation on the logback mailing lists with no objections
    -      raised.</h4>
    -    </div>
    -
    -
    -    <p>Fixed <code>FileAppender</code>'s prudent mode so that it properly recovers
    -    from IO Errors
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-1046">LOGBACK-1046</a>) </p>
    -
    -    <p>Irrelevant or internal stack trace lines can now be omitted. Pull request
    -    provided by Tomasz Nurkiewicz
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-540">LOGBACK-540</a>).</p>
    -
    -    <p><code>AccessEvent</code> now creates a copy of request attributes when
    -    its <code>prepareForDeferredProcessing()</code> method is called. This makes
    -    attributes visible even if an appender uses a background thread to process
    -    events.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-1033">LOGBACK-1033</a>)</p>
    -
    -    <p>All threads opened by <code>ch.qos.logback.core.util.ExecutorServiceUtil#THREAD_FACTORY</code>
    -    are now daemons, which fixes an application hang on shutdown when <code>LoggerContext#stop()</code>
    -    is not called (<a href="http://jira.qos.ch/browse/LOGBACK-929">LOGBACK-929</a>).
    -    Note that daemon threads are abruptly terminated by the JVM, which may cause
    -    undesirable results, such as corrupted files written by the <code>FileAppender</code>.
    -    It is still highly recommended for the application to call <code>LoggerContext#stop()</code>
    -    (e.g., in a shutdown hook) to gracefully shutdown appenders.</p>
    -
    -    <p>Fixed an issue with <code>RollingFileAppender</code> where the first
    -    rollover file could be unintentionally deleted, depending on the specified
    -    filename pattern
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-894">LOGBACK-894</a>).</p>
    -
    -    <p>Fixed an issue with <code>TeeHttpServletResponse</code>
    -    where the system default encoding would be used instead of the
    -    response encoding when constructing a new writer
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-1023">LOGBACK-1023</a>).</p>
    -
    -    <p>Fixed <code>ConfigurationDelegate.groovy</code> to allow any appender
    -    that implements <code>AppenderAttachable</code>, which supports third-party
    -    appenders, such as <b>reactor-logback's</b> <code>AsyncAppender</code>
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-1008">LOGBACK-1008</a>)</p>
    -
    -    <p>Added support for specifying the callstack depth range in the
    -    <code>PatternLayout</code>. For example, <code>%caller{1..2}</code>
    -    displays the first two calls in the callstack.</p>
    -
    -    <p>Added HTTP request method to <code>MDCInsertingServletFilter</code>.</p>
    -
    -    <p>Fixed time-zone dependent code in <code>RollingCalendarTest</code>.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-116">LOGBACK-116</a>)
    -    </p>
    -
    -    <p>Simplified connection logic of SocketApender to reduce multi-threading
    -    overhead and unnecessary instantiation of <code>SocketConnector</code>
    -    objects.</p>
    -
    -    <p>Fixed race condition in <code>SMTPAppenderBase</code> causing missing or
    -    duplicate emails.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-909">LOGBACK-909</a>)
    -    </p>
    -
    -    <p>Fixed an issue with <code>FileAppender</code> in prudent mode, where
    -    an interrupt could prevent further access to the file
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-875">LOGBACK-875</a>).</p>
    -
    -    <p>Fixed <code>IllegalStateException</code> when multiple threads
    -    write files to same directory
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-128">LOGBACK-128</a>).</p>
    -
    -    <p>Changed queue consumption strategy in <code>AbstractSocketAppender</code>
    -    from "take" to "peek/remove" in order to avoid losing an event on each socket
    -    connection break. Zero is now a deprecated queue size. A queue size of one
    -    should be taken instead to indicate synchronous processing.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-977">LOGBACK-977</a>)
    -    </p>
    -    
    -    <p><code>RequestLogImpl</code> now has an overridable <code>configure</code>
    -    method to allow extending implementations to configure it via methods
    -    other than the <code>logback-access.xml</code> file.</p>
    -
    -    <p>Fixed a bug in <code>AccessEvent</code> which could cause any
    -    <code>AsyncAppender</code> based appender to corrupt the request
    -    headers, request parameters, or response headers, before logging.</p>
    -
    -    <p>SQL scripts to setup your logback database are now provided as
    -    JAR resources
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-948">LOGBACK-948</a>).
    -    </p>
    -
    -    <p>Threads created internally by logback are now named
    -    <i>logback-n</i>, where <i>n</i> is an integer. For the
    -    first thread <i>n</i> is set to 1, for each successive thread
    -    <i>n</i> is incremented by 1.</p>
    -
    -    <p>Added max runtime parameter to <code>AsyncAppender</code> to allow
    -    the appender to flush events, up to a maximum delay, during a stop
    -    of the LoggerContext. This can be used to ensure that all queued
    -    events are flushed.</p>
    -    
    -    <p>Added new configuration element <code>shutdownHook</code> to allow the
    -    user to specify a ShutdownHook implementation that will stop the Logback
    -    context upon JVM exit.
    -    </p>
    -
    -    <p><code>BasicStatusManager</code> now prevents adding more than
    -    one instance of <code>OnConsoleStatusListener</code> as a status
    -    listener. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-976">LOGBACK-976</a>.
    -    </p>
    -
    -    <p><code>TimeBasedRollingPolicy</code> now accepts a time zone in
    -    its <code>%d</code> conversion pattern. (<a
    -    href="http://jira.qos.ch/browse/LOGBACK-611">LOGBACK-611</a>)
    -    </p>
    -
    -    <p>It is now possible to configure the character encoding used by
    -    <code>SyslogAppender</code> to encode messages using the
    -    <code>setCharset()</code> method. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-732">LOGBACK-732</a>
    -    </p>
    -
    -    <h3>2nd of April, 2014 - Release of version 1.1.2</h3>
    -
    -    <p>Create an abstract method, createOutputStream, as an extension point
    -    for subclasses of SyslogBaseAppender to create their own OutputStream.
    -    <a href="http://jira.qos.ch/browse/LOGBACK-890">LOGBACK-890</a>
    -    </p>
    -
    -    <p>Removed deprecated constructors in <code>SocketAppender</code>
    -    and related classes.</p>
    -
    -    <p>Fixed incorrect date format in <code>SyslogAppender</code>.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-936">LOGBACK-936</a>)
    -    </p>
    -
    -    <p>Fixed <code>NullPointerException</code> when substituting blank variables
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-959">LOGBACK-959</a>)</p>
    -
    -    <p>Fixed <code>NullPointerException</code> that occurs when stopping a
    -    <code>SyslogAppender</code> that did not properly initialize, e.g.,
    -    due to a hostname resolution failure.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-960">LOGBACK-960</a>)
    -    </p>
    -
    -    <p>Use fair locking in <code>OutputStreamAppender</code>. Patch provided by
    -    Sergey Bykov.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-268">LOGBACK-268</a>)
    -    </p>
    -
    -    <p>In case of missing included files, IncludeAction no longer
    -    prints a stack trace but prints a warning instead. (<a
    -    href="http://jira.qos.ch/browse/LOGBACK-954">LOGBACK-954</a>)</p>
    -
    -    <p>Fixed character escaping in XMLLayout
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-728">LOGBACK-728</a>)
    -    and HTMLLayout
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-440">LOGBACK-440</a>).</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>5th of February, 2014 - Release of version 1.1.1</h3>
    -
    -    <p>Logback now supports an unlimited level of <a
    -    href="manual/configuration.html#variableSubstitution">variable</a>
    -    resolution graphs rather than being limited to one level deep
    -    resolution.  This enhancement was requested by Anton Wiedermann in
    -    <a href="http://jira.qos.ch/browse/LOGBACK-943">LOGBACK-943</a>
    -    with Eric Dahl providing the relevant pull request.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LOGBACK-942">LOGBACK-942</a> which
    -    was causing <code>SocketAppender</code> to drop events. Eric Dahl
    -    provided the relevant pull request.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>January 28th, 2014 - Release of version 1.1.0</h3>
    -
    -    <p>Previously, logback silently ignored configuration files that did
    -    not have the ".xml" or ".groovy" file extension. Now, a <code>LogbackException</code>
    -    is thrown to flag this error.
    -    </p>
    -
    -    <p>Groovy configuration now supports <code>appenderRef</code>, which
    -    allows the use of <code>AsyncAppender</code> in Groovy.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-269">LOGBACK-269</a>)
    -    </p>
    -
    -    <p><code>SizeAndTimeBasedFNATP</code> now supports a protected function
    -    <code>#createArchiveRemover</code> that allows subclasses to specify
    -    a custom archive remover used in <code>RollingFileAppender</code>. The
    -    default archive remover is <code>SizeAndTimeBasedArchiveRemover</code>.
    -    </p>
    -
    -    <p><code>PackagingDataCalculator</code> now catches and ignores
    -    <code>UnsupportedOperationException</code> so that it can determine
    -    whether the JVM supports <code>Reflection#getCallerClass()</code>
    -    without printing that exception's stack trace. This only affects
    -    Java 7u40+, which removed support for <code>Reflection#getCallerClass()</code>.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-885">LOGBACK-885</a>)
    -    </p>
    -
    -    <p>To facilitate debugging, <code>SimpleSocketServer</code> now names
    -    its threads: "<b>Logback SimpleSocketServer (port <i>PORTNUM</i>)</b>".
    -    <code>SocketNode</code> client threads are named:
    -    "<b>Logback SocketNode (client: <i>IPADDR</i>)</b>".
    -    </p>
    -
    -    <p>Fixed silently lost messages from <code>SyslogAppender</code>
    -    when they exceeded the system datagram size limit. The default max
    -    message size was hard coded to 65KB but now matches the system limit
    -    (as read from
    -    <a href="http://docs.oracle.com/javase/6/docs/api/java/net/DatagramSocket.html#getSendBufferSize()">
    -    <code>DatagramSocket#getSendBufferSize()</code></a>) unless a
    -    nonzero limit is specified via <code>SyslogAppender#setMaxMessageSize()</code>.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-926">LOGBACK-926</a>)
    -    </p>
    -
    -    <p>Fixed various documentation typos, including
    -    <a href="http://jira.qos.ch/browse/LOGBACK-466">LOGBACK-466</a>,
    -    <a href="http://jira.qos.ch/browse/LOGBACK-768">LOGBACK-768</a>,
    -    <a href="http://jira.qos.ch/browse/LOGBACK-904">LOGBACK-904</a>,
    -    <a href="http://jira.qos.ch/browse/LOGBACK-921">LOGBACK-921</a>,
    -    and
    -    <a href="http://jira.qos.ch/browse/LOGBACK-927">LOGBACK-927</a>.
    -    </p>
    -
    -    <p><code>ContextBase.setStatusManager</code> now correctly validates
    -    the given status manager.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-912">LOGBACK-912</a>)
    -    </p>
    -
    -    <p>The <code>SocketNode</code> creates an <code>ObjectInputStream</code>
    -    using the socket's input stream, which blocks waiting for the
    -    stream header. This wait has been moved from the constructor to
    -    <code>SocketNode.run()</code> so that the node can be created without
    -    blocking.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-350">LOGBACK-350</a>)
    -    </p>
    -
    -    <p>Fixed <code>SecurityException</code> during initialization
    -    in Google AppEngine.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-760">LOGBACK-760</a>)
    -    </p>
    -
    -    <p>The <code>&lt;include&gt;</code> element now allows optional
    -    resources (previously, only optional files).
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-644">LOGBACK-928</a>)
    -    </p>
    -
    -    <p>Added new layout conversion word (<b>%D</b> or <b>%elapsedTime</b>),
    -    which gets the time taken to serve the request. This applies
    -    only to Jetty and Tomcat logs via logback-access.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-320">LOGBACK-320</a>)
    -    </p>
    -
    -    <p>Fixed SMTP error when the subject line contained multiple
    -    new-lines.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-865">LOGBACK-865</a>)
    -    </p>
    -
    -    <p>Fixed unnecessary re-initialization of the servlet's
    -    <code>LoggerContext</code> while attempting to destroy it.
    -    </p>
    -
    -    <p>Added the following syslog facilities to <code>SyslogStartConverter</code>:
    -    NTP, AUDIT, ALERT, CLOCK
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-754">LOGBACK-754</a>)
    -    </p>
    -
    -    <p>Fixed <code>NullPointerException</code> when setting a JUL
    -    logger's level to null, which should have reset the logger level
    -    to the parent's effective level.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-906">LOGBACK-906</a>)
    -    </p>
    -
    -    <p>Added support to get suppressed exceptions. This is currently
    -    only implemented for <code>RootCauseFirstThrowableProxyConverter</code>
    -    and <code>ThrowableProxyConverter</code>.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-516">LOGBACK-516</a>)
    -    </p>
    -
    -    <p>Fixed problem of Janino classes not found in some environments
    -    (axis2, GWT, etc.).
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-832">LOGBACK-832</a>)
    -    </p>
    -
    -    <p>Fixed various unit tests, including modifications for consistent
    -    results in Jenkins.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-842">LOGBACK-842</a>)
    -    </p>
    -
    -    <p>Allow Gaffer to read the <code>logback.debug</code> system
    -    property to enable verbose output during Groovy-based configuration.
    -    Also added debug statements to Gaffer.
    -    </p>
    -
    -    <p>Fixed <code>IllegalArgumentException</code> when missing periods
    -    and dollar signs in the logger name.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-384">LOGBACK-384</a>)
    -    </p>
    -
    -    <p>Fixed <code>NullPointerException</code> during JNDI lookup.</p>
    -
    -    <p>Fixed dangling data source connections from <code>DBAppender</code>
    -    that caused <i>"maximum open cursors exceeded"</i>.</p>
    -
    -    <p>Fixed incorrect stack trace depth when specifying <code>%throwable</code>
    -    with argument.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-160">LOGBACK-160</a>)
    -    </p>
    -
    -    <p>Fixed MySQL setup script to use lower-case table name to
    -    match the references in the file. This had caused an error
    -    on systems where case sensitivity matters.</p>
    -
    -    <p>Fixed <code>NullPointerException</code> when getting code source in
    -    <code>PackagingDataCalculator</code>.</p>
    -
    -    <p>The search path for <code>logback-access.xml</code> is now
    -    <code>${catalina.base}</code> and then <code>${catalina.home}</code>
    -    if not found.
    -    (<a href="http://jira.qos.ch/browse/LOGBACK-844">LOGBACK-844</a>)
    -    </p>
    -
    -    <p>The <code>LoggingEvent</code> constructor delays message
    -    formatting so that it can be computed lazily by the
    -    <code>getFormattedMessage</code> method. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-873">LOGBACK-873</a> and
    -    re-fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-495">LOGBACK-495</a>.
    -    </p>
    -
    -    <p><code>PackagingDataCalculator</code> now checks for the case
    -    where the caller has no protection domain. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-617">LOGBACK-617</a>
    -    reported by Yuri de Wit. The relevant fix was provided by Mikhail
    -    Mazursky.
    -    </p>
    -    
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>May 10th, 2013 - Release of version 1.0.13</h3>
    -  
    -    <p>In logback-access MANIFEST file, imports of Jetty and Tomcat
    -    are now optional. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-300">LOGBACK-300</a>
    -    reported by Christian Brensing who also provided the appropriate
    -    pull request.
    -    </p>
    -
    -    <p>Logback will now correctly parses variables with a default
    -    separator string nested within accolades, e.g. "{a:-b}". Such
    -    strings resemble variable references but lack the $ prefix, e.g
    -    "${a:-b}". This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-859">LOGBACK-859</a>
    -    reported by Yoni Moses.</p>
    -
    -    <p>In <code>InterpretationContext</code> class replaced code using
    -    JDK 1.6 API with code using JDK 1.5. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-860">LOGBACK-860</a>
    -    reported by Bas Stoker.
    -    </p>
    -
    -    <p>Updated the "org.fusesource.jansi:jansi" dependency to version
    -    1.9.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>April 26th, 2013 - Release of version 1.0.12</h3>
    -    
    -    <p>A new <code>SSLSocketAppender</code> extends the basic 
    -    <code>SocketAppender</code> providing the ability to deliver 
    -    logging events over the Secure Socket Layer (SSL).  The 
    -    corresponding <code>SimpleSSLSocketServer</code> extends the classic 
    -    <code>SimpleSocketServer</code> as a basic logging server 
    -    application that receives logging events from a 
    -    <code>SSLSocketAppender</code>.</p>
    -      
    -    <p class="highlight">Receiver components are configured in
    -    logback.xml just like any other logback component.</p>
    -
    -    <p>While <code>SimpleSocketServer</code> (and its new SSL-enabled
    -    counterpart, <code>SimpleSSLSocketServer</code>) provide an 
    -    easy-to-use standalone logging server application, a new component
    -    type known as a <em>receiver</em> allows <em>any</em> application 
    -    to receive logging events from remote appenders over a TCP/IP network 
    -    connection, using Logback Classic.  Receiver components are 
    -    configured in <em>logback.xml</em> just like any other logback 
    -    component.</p>
    -      
    -    <p>A receiver can either listen passively for connections from
    -    remote <code>SocketAppender</code> components acting as clients, 
    -    or it can assume the client role, initiating a connection to a 
    -    remote appender acting as a server.  The receiver components 
    -    shipped with Logback include full support for logging event 
    -    delivery over the Secure Sockets Layer (SSL).</p>
    -
    -    <p>All of the new socket-based receiver and appender components were 
    -    contributed by Carl Harris.  See 
    -    <a href="manual/receivers.html">Receivers</a> in the Logback Manual 
    -    for more information on configuring receiver components.  See 
    -    <a href="manual/appenders.html#SocketAppender">SocketAppender</a> and
    -    <a href="manual/appenders.html#serverSocketAppender">ServerSocketAppender</a>
    -    for information on configuring appenders as event sources for
    -    receiver components.</p>
    -
    -    <p><code>RollingFileAppender</code> will now detect when <span
    -    class="option">file</span> property collides with <span
    -    class="option">fileNamePattern</span>, emit <a
    -    href="codes.html#rfa_collision">an error message</a> and refuse to
    -    initialize. This was requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-796">LOGBACK-796</a> by
    -    Karl Pietrzak who also provided a patch.
    -    </p>
    -
    -    <p>In configuration files, the <code>&lt;include></code> element
    -    now admits the <span class="attr">optional</span> attribute. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-230">LOGBACK-230</a>
    -    reported by Attila Kiraly. Many thanks to Tommy Becker who
    -    contributed a patch.</p>
    -
    -    <p>In response to <a
    -    href="http://jira.qos.ch/browse/LOGBACK-829">LOGBACK-829</a>,
    -    serialization of <code>Logger</code> instances has been
    -    significantly simplified and much unnecessary bloat removed. As an
    -    added bonus, the new serialization of <code>Logger</code> objects
    -    is also compatible with serialization streams using older Logger
    -    instances. <code>Logger</code> instances serialized by logback
    -    1.0.11 and earlier can be read by logback version 1.0.12 and later
    -    and <em>vice-versa</em>.
    -    </p>
    -
    -    <p>The code detecting whether Groovy is available on the class
    -    path deals with the case where logback binaries are installed as
    -    endorsed libraries. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-831">LOGBACK-831</a>.
    -    </p>
    -
    -    <p class="highlight">Groovy configurator no longer supports
    -    <code>SiftingAppender</code>.</p>
    -
    -    <p>In response to <a
    -    href="http://jira.qos.ch/browse/LOGBACK-244">LOGBACK-244</a>, <a
    -    href="http://jira.qos.ch/browse/LOGBACK-724">LOGBACK-724</a> and
    -    in particular patches provided by Tommy Becker and David Roussel
    -    component tracking code has been simplified and completely
    -    re-written. <code>SiftingAppender</code> now supports the <a
    -    href="manual/appenders.html#siftTimeout">timeout</a> and <a
    -    href="manual/appenders.html#siftMaxAppenderCount">maxAppenderCount</a>
    -    parameters. As a direct consequence of modifications to component
    -    tracking code, the groovy configurator no longer supports
    -    <code>SiftingAppender</code>.</p>
    -
    -    <p>SiftingAppender now propagates properties defined elsewhere in
    -    the configuration file into the configuration process of nested
    -    appenders. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-833">LOGBACK-833</a> with
    -    David Roussel providing the appropriate patch.
    -    </p>
    -
    -    <p>As all other actions affecting properties,
    -    <code>TimestampAction</code> now inserts the user-specified
    -    property into the local scope by default. The property was
    -    inserted into the context scope in earlier versions of logback.
    -    This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-835">LOGBACK-835</a> with
    -    David Roussel providing the appropriate patch.
    -    </p>
    -
    -    <p>Logback is now able to retrieve the name of localhost when
    -    running under OS X and Java 7. This issue was reported by <a
    -    href="http://jira.qos.ch/browse/LOGBACK-749">LOGBACK-749</a> by
    -    Oliver Schrenk with patches provided by Ralph Goers and Pavel
    -    Valodzka.
    -    </p>
    -
    -    <p>The <a href="manual/layouts.html#mdc">mdc</a> converter can now
    -    handle default values. This feature was requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-246">LOGBACK-246</a> by
    -    Michael Osipov with Denis Bazhenov providing a patch.
    -    </p>
    -
    -    <p><code>DBAppender</code> in logback-classic module no longer
    -    assumes that caller information is always available. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-805">LOGBACK-805</a>
    -    reported by Daris Cooper who also provided a corrective patch.</p>
    -
    -    <p>In order to simplify our build, several unit tests have been
    -    ported from Scala to Java. It follows that logback no longer
    -    depends on Scala, not even during the test phase of the build.</p>
    -
    -    <h3>25th of March, 2013 - Release of version 1.0.11</h3>
    -
    -
    -    <p>Under Unix*, the basic/quick file rename method supplied by
    -    Java does not work if the source and target files are on different
    -    file systems. This problem was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-108">LOGBACK-108</a> by
    -    Daniel Potter. In order to deal with this issue, logback now will
    -    perform rename by copying if the source and target files are on
    -    different file systems and the host JDK is version 1.7 or
    -    later. See also the related <a
    -    href="codes.html#renamingError">error code</a>.</p>
    -
    -
    -    <p>The "cn" conversion word now correctly maps to
    -    <code>ContextNameConverter</code> class. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-463">LOGBACK-463</a>
    -    reported by Michael Osipov with Mark A. Ziesemer providing the
    -    appropriate patch.
    -    </p>
    -
    -    <p>Added gray to the list of <a
    -    href="manual/layouts.html#coloring">ANSI colors supported by
    -    logback</a>. The relevant pull request was kindly provided by
    -    Craig P. Motlin.</p>
    -
    -    <p>The <code>discoverConnectionProperties()</code> method in class
    -    <code>ConnectionSourceBase</code> no longer leaks
    -    connections. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-798">LOGBACK-798</a>
    -    reported by Sayevskiy Viacheslav. Many thanks to Ivan
    -    (Wee-Willie-Winkie) who contributed the appropriate fix.
    -    </p>
    -
    -    <p>In logback-access, more correct determination of whether
    -    contents of an <code>HttpServletRequest</code> are URL encoded or
    -    not. The <a href="https://github.com/qos-ch/logback/pull/71">bug
    -    fix</a> was submitted by David Schneider. The same issue was later
    -    independently reported by Grzegorz Kuligowski in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-814">LOGBACK-814</a>.</p>
    -
    -    <p>Both <code>SocketAppenderBase</code> and
    -    <code>SimpleSocketServer</code> now use a socket factory to create
    -    sockets. These changes were asked in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-815">LOGBACK-815</a> and
    -    <a href="http://jira.qos.ch/browse/LOGBACK-816">LOGBACK-816</a> by
    -    Carl Harris who also contributed the relevant patch.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>15th of March, 2013 - Release of version 1.0.10</h3>
    -
    -    <p>Upgraded the (optional) groovy dependency to version 2.0.7
    -    (from 2.0.0).</p>
    -
    -    <p>The logger cache field in <code>LoggerContext</code> now uses a
    -    <code>ConcurrentHashMap</code> instead of a regular
    -    <code>HashMap</code>. This dramatically improves the speed of
    -    logger retrieval and incidentally fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-142">LOGBACK-142</a>.
    -    </p>
    -
    -    <p>In <code>SyslogAppender</code> allow <a
    -    href="manual/appenders.html#syslogSuffixPattern">suffixPattern</a>
    -    to begin with literal text. This issue was reported Bruno Polaco
    -    in <a href="http://jira.qos.ch/browse/LOGBACK-782">LOGBACK-782</a>
    -    who also provided the relevant patch.
    -    </p>
    -    
    -    <p>In order to reduce unnecessary boilerplate several common types
    -    and packages are now <a
    -    href="manual/groovy.html#AutomaticImports">imported
    -    automatically</a>. This feature was contributed by Joris
    -    Kuipers.</p>
    -
    -    <p>Computation of caller information now takes into account Groovy
    -    frames (if nunning under Groovy). This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-811">LOGBACK-811</a>.</p>
    -
    -  
    -  
    -    <hr width="80%" align="center" />
    -
    -    <h3>December 5th, 2012 - Release of version 1.0.9</h3>
    -
    -    <p>Removed an erroneous compile-time dependency on Tomcat in the
    -    logback-classic module. This issue was reported by Arnaud
    -    Heritier.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>December 4th, 2012 - Release of version 1.0.8</h3>
    -
    -    <div class="breaking">
    -      <h4>logback-classic updated to use SLF4J version 1.7.2 instead
    -      of version 1.6.6.</h4>
    -      <p>The logback-classic module now uses SLF4J version 1.7.2
    -      instead of version 1.6.6. This only impacts projects running
    -      under an OSGi platform as SLF4J versions 1.7.x and 1.6.x are
    -      100% binary compatible.</p>
    -    </div>
    -
    -    <p>Fixed incorrect <code>BufferStream</code> assignment after
    -    recovery in <code>ResilientFileOutputStream</code>. This issue was
    -    reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-765">LOGBACK-765</a> by
    -    David Markwick.
    -    </p>
    -
    -    <p>Fixed incorrect parsing of variables in case of colon character
    -    followed by a dollar character. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-744">LOGBACK-744</a>.</p>
    -
    -    <p><code>SyslogAppender</code> now sends out the header line of
    -    stack traces. This issue was reported <a
    -    href="http://jira.qos.ch/browse/LOGBACK-411">LOGBACK-411</a> and
    -    separately as <a
    -    href="http://jira.qos.ch/browse/LOGBACK-750">LOGBACK-750</a>.</p>
    -
    -    <p>A <code>StatusListener</code> implementing
    -    <code>LifeCycle</code> interface was not started if specified via
    -    the <code>logback.statusListenerClass</code> system property. This
    -    issue was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-767">LOGBACK-767</a> by
    -    Adam Sokowicz.
    -    </p>
    -
    -    <p>In order to avoid duplication, automatic status printing will
    -    be disabled if the user explicitly registers a status
    -    listerner.</p>
    -
    -    <p>Added <code>OnErrorConsoleStatusListener</code> to print status
    -    messages on the error console, i.e. on System.err. This feature
    -    was requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-292">LOGBACK-292</a> by
    -    Abraham Lin. </p>
    -
    -    <p>Added <code>NopStatusListener</code> class which simply drops
    -    incoming status messages. By <a
    -    href="manual/configuration.html#logback.statusLC">explicitly
    -    registering</a> a <code>NopStatusListener</code> listener,
    -    automatic status printing can be turned off.
    -    </p>
    -
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>24th of August, 2012 - Release of version 1.0.7</h3>
    -
    -    <div class="breaking">
    -      <h4>Janino library upgraded to version 2.6.1.</h4>
    -
    -      <p>As of Janino version 2.6.0, in addition to
    -      <em>janino.jar</em>, <em>commons-compiler.jar</em> needs to be
    -      on the class path as well. Please see the <a
    -      href="setup.html#janino">Janino setup</a> instructions.</p>
    -    </div>
    -    
    -    <p>
    -    </p>
    -
    -    <div class="breaking">
    -      <h4>Groovy dependency upgraded to version 2.0.0.</h4>
    -
    -      <p>Please see the <a href="setup.html#groovy">Groovy setup</a>
    -      instructions.</p>
    -    </div>
    -
    -    <p>The code handling variable substitution has been completely
    -    re-written. As requested in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-729">LOGBACK-729</a>,
    -    variables can be now be nested arbitrarily, even within the
    -    default value section. For example,
    -    <code>${a${b:-c}:-${d:-e}}</code> is now a valid logback variable
    -    expression yielding "e", assuming variables with the keys 'd', 'b'
    -    and 'ac' are undefined. The new variable substitution code is
    -    designed to be backward compatible with existing configuration
    -    files.
    -    </p>
    -    
    -
    -    <p>Substitution properties are now correctly recognized by <span
    -    class="attr">scan</span> and <span class="attr">scanPeriod</span>
    -    attributes of <code>&lt;configuration></code> element in
    -    configuration files. This fixes <a
    -    href="http://jira.qos.ch/browse/LOGBACK-396">LOGBACK-396</a>
    -    reported by Oh Chin Boon</p>
    -
    -    <p>The color-related conversion words now set the default color
    -    correctly. Xu Huisheng provided the relavant patch.
    -    </p>
    -    
    -    <p>Fixed a race-condition in <code>AsyncAppender</code> and its
    -    worker thread. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LOGBACK-720">LOGBACK-720</a> by
    -    Arnd Hannemann who also supplied the relevant patch.</p>
    -
    -
    -    <p>If a <code>PropertyDefiner</code> implements
    -    <code>LifeCycle</code>, then its <code>start()</code> method will
    -    now be invoked.</p>
    -
    -    <p>Added the <span class="prop">includeCallerData</span> property
    -    in <code><a
    -    href="manual/appenders.html#smtpIncludeCallerData">SMTPAppender</a></code>
    -    to precompute caller data before storing events for future
    -    transmission. This property addresses <a
    -    href="http://jira.qos.ch/browse/LOGBACK-734">LOGBACK-734</a>
    -    reported by Patrick Hogarty.
    -    </p>
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>7th of June, 2012 - Release of version 1.0.6</h3>
    -
    -    <p><a
    -    href="manual/appenders.html#SMTPAppender"><code>SMTPAppender</code></a>
    -    now supports the retrieval of a <code>javax.mail.Session</code>
    -    resource from JNDI. This feature was requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-332">LBCLASSIC-332</a>
    -    by Hrotko Gabor.</p>
    -
    -    <p>Listeners passed to <code>statusListener()</code> method in
    -    <code>GafferConfigurator</code> (the groovy configurator) are now
    -    correctly started.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>6th of June, 2012 - Release of version 1.0.5</h3>
    -
    -    <p><a
    -    href="manual/appenders.html#ConsoleAppender"><code>ConsoleAppender</code></a>
    -    can now activate the <a href="http://jansi.fusesource.org">Jansi
    -    library</a> for ANSI color code support on Windows
    -    systems. Unix-based operating systems such as Linux and Mac OS X
    -    already support ANSI color codes by default.</p>
    -
    - 
    -    <p><code>PatternLayout</code> now supports <a
    -    href="manual/layouts.html#coloring">composite conversion
    -    specifiers for coloring</a>.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>31st of May, 2012 - Release of version 1.0.4</h3>
    -
    -    <p>Added <code>AsyncAppender</code> for asyncronous invocation of
    -    nested appenders. This was a long standing and popular request as
    -    attested by <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-177">LBCLASSIC-177</a>,
    -    <a href="http://jira.qos.ch/browse/LBCORE-92">LBCORE-92</a> and <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-242">LBCLASSIC-242</a>.</p>
    -
    -    <p><code>SMTPAppender</code> now admits the <span
    -    class="prop">asynchronousSending</span> property, set to 'true' by
    -    default. However, it can be set to 'false' for synchronous email
    -    transmission. This property was requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-323">LBCLASSIC-323</a>
    -    by Patrick Houk.</p>
    -
    -    <p>It is now possible to set a system property called <a
    -    href="manual/configuration.html#automaticStatusPrinting">"logback.debug"</a>
    -    in order to force printing of internal status messages on the
    -    console regardless of the value of the <span
    -    class="attr">debug</span> attribute found within the
    -    <code>&lt;configuration></code> element. This behavior was
    -    requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-225">LBCLASSIC-225</a>
    -    by Aaron Digulla with the relevant patch kindly provided by Antony
    -    Stubbs.
    -    </p>
    -
    -    <p><code>MarkerFilter</code> now correctly recognizes nested
    -    markers. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-295">LBCLASSIC-295</a>
    -    by Paolo Mazzoncini who also provided the relevant patch.</p>
    -
    -    <p>Fixed invalid multiple configuration warning when deployed
    -    under Weblogic. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-159">LBCLASSIC-159</a>
    -    by Hontv&aacute;ri J&oacute;zsef and with the appropriate patch
    -    provided by Derek Mahar.
    -    </p>
    -
    -    <p><a
    -    href="manual/appenders.html#SyslogAppender"><code>SyslogAppender</code></a>
    -    now admits the <span class="prop">throwableExcluded</span>
    -    property allowing the exclusion of throwable data if so
    -    desired. This solves <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-327">LBCLASSIC-327</a>
    -    reported by Don Faulkner.
    -    </p>
    -
    -    <p><a
    -    href="manual/appenders.html#SyslogAppender"><code>SyslogAppender</code></a>
    -    now admits the <span class="prop">stackTracePattern</span>
    -    property allowing the customization of the string appearing just
    -    before each stack trace line.  This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-333">LBCLASSIC-333</a>
    -    reported by Ingebrigt Berg who also provided the relavant patch.
    -    </p>
    -
    -    <p><code>DBAppender</code> now supports SQLite. This feature was
    -    requested in <a
    -    href="http://jira.qos.ch/browse/LBGENERAL-53">LBGENERAL-53</a> by
    -    Tony Trinh who also provided the relevant patch.</p>
    -   
    -
    -    <h3>4th of May, 2012 - Release of version 1.0.3</h3>
    -
    -    <p><code>PatternLayoutEncoder</code> class now admits the <span
    -    class="prop"><a
    -    href="manual/encoders.html#LayoutWrappingEncoder">immediateFlush</a></span>
    -    property (set to true by default). By setting this property to
    -    'false', logging throughput can be quintupled, although your
    -    mileage may vary. This enhancement was requested in <a
    -    href="http://jira.qos.ch/browse/LBCORE-243">LBCORE-243</a>.
    -    </p>
    -
    -    <p>In order to facilitate parsing of log files, logback can now <a
    -    href="manual/encoders.html#outputPatternAsHeader">output the
    -    pattern</a> used for the log output at the top of log files as a
    -    header. This feature was requested by James Strachan in <a
    -    href="http://jira.qos.ch/browse/LBCORE-234">LBCORE-234</a>.
    -    </p>
    -
    -    <p><code>SMTPAppender</code> failed to transmit messages under JDK
    -    1.5. After some investigation, Dave discovered that this was due to
    -    setting the <code>corePoolSize</code> parameter of the relevant
    -    <code>ThreadPoolExecutor</code> to 0. Setting
    -    <code>corePoolSize</code> to a higher value, e.g. 1, under JDK 1.5
    -    solves the problem and fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-323">LBCLASSIC-323</a>
    -    reported by Lutz Huehnken.
    -    </p>
    -
    -    <p><code>LevelChangePropagator</code> now retains references to
    -    j.u.l. loggers whose level it sets. This fixes garbage collection
    -    issues reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-256">LBCLASSIC-256</a>
    -    by Samuel Stanojevic who also provided the appropriate patch.
    -    </p>
    -    
    -
    -    <h3>26th of April, 2012 - Release of version 1.0.2</h3>
    -
    -    <div class="breaking">
    -
    -    <h4><span class="label">Breaking change partially reverted in
    -    1.0.3</span><br/>By default <code>PatternLayout</code> will now output
    -    its pattern at the top of log files</h4>
    -
    -    <p>This feature, although still available, is no longer enabled by
    -    default. See release notes for version 1.0.3 for details.
    -    </p>
    -
    -
    -    </div>
    -    
    -    <p><code>ReconfigureOnChangeFilter</code> will avoid excessive
    -    syncronization in case of CPU intensive applications. This fixes
    -    <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-234">LBCLASSIC-234</a>,
    -    a recalcitrant bug reported by Uri Unger who also provided the key
    -    steps for reproducing it.
    -    </p>
    -
    -    <p><code>RollingFileAppender</code> now creates missing
    -    directories for compressed archive files. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-169">LBCORE-169</a>
    -    reported by Tomasz Nurkiewicz with patches supplied by Paulo
    -    Andrade and Mats Henrikson.
    -    </p>
    -
    -    <p><code>SizeAndTimeBasedFNATP</code> will now remove files with
    -    indexes higher than 99. It will also correctly compute the highest
    -    index value when an application is restarted. This fixes bug <a
    -    href="http://jira.qos.ch/browse/LBCORE-221">LBCORE-221</a>
    -    reported by Dieter Mueller and Dawid Chodura.</p>
    -  
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>7th of March, 2012 - Release of version 1.0.1</h3>
    -
    -    <p>Setting the debug attribute to true in the
    -    <code>&lt;configuration></code> element now registers a
    -    <code>OnConsoleStatusListener</code> with the
    -    <code>StatusManager</code>.  Thus, problems occurring during the
    -    lifetime of your application, well after logback is initialized,
    -    can be reported when the <span class="attr">debug</span> attribute
    -    is set. This behavior is closer to what users expect.
    -    </p>
    -
    -    <p>Added new property the <span
    -    class="prop">cleanHistoryOnStart</span> to <a
    -    href="manual/appenders.html#TimeBasedRollingPolicy">TimeBasedRollingPolicy</a>.
    -    By setting this property to <code>true</code>, history removal
    -    will work as expected even in the case of short lived
    -    applications. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-226">LBCORE-226</a>
    -    reported by Bruce E. Irving.
    -    </p>
    -
    -    <p>It is now possible to specify multiple %d tokens in the file
    -    name pattern of <a
    -    href="manual/appenders.html#TimeBasedRollingPolicy">TimeBasedRollingPolicy</a>. Auxiliary
    -    %d tokens must be marked as such by passing the AUX
    -    parameter. This feature was requested in <a
    -    href="http://jira.qos.ch/browse/LBCORE-242">LBCORE-242</a> by
    -    Thomas Corte.</p>
    -
    -    <p>Logback now supports suppressed exceptions introduced in Java
    -    7. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-276">LBCLASSIC-276</a>.
    -    </p>
    -
    -    <p><code>SMTPAppender</code> clears the relevant cyclic buffer
    -    before asynchronous transmission. This corrects <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-221">LBCLASSIC-221</a>
    -    as reported by Chris Cheshire.
    -    </p>
    -
    -    <p><code>SiftingAppender</code> will consider stale and
    -    consequently remove nested appenders which are closed or
    -    improperly started. This caters for the use case described in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-316">LBCLASSIC-316</a>
    -    by Guus Bloemsma.</p>
    -
    -    <p><code>RequestLogImpl</code> can now lookup for the
    -    logback-access configuration file as a class path resource. This
    -    feature was requested in <a
    -    href="http://jira.qos.ch/browse/LBACCESS-26">LBACCESS-26</a> by
    -    Marshall Pierce.
    -    </p>
    -
    -    <p>Updated the Janino dependency to version 2.5.16. In addition,
    -    the code checking for the availability of Janino on the class path
    -    was updated to take Janino 2.6 into account, thus fixing <a
    -    href="http://jira.qos.ch/browse/LBCORE-210">LBCORE-210</a>.
    -    </p>
    -
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>1st of November, 2011 - Release of version 1.0.0</h3>
    -
    -    <p>The logback-access module now targets Tomcat 7.x and Jetty 7.x
    -    &amp; 8.x as its servlet container platforms. This fixes <a
    -    href="http://jira.qos.ch/browse/LBACCESS-17">LBACCESS-17</a>
    -    reported by Grzegorz Grzybek.</p>
    -
    -    <div class="breaking">
    -
    -    <h4>Breaking change: properties are no longer automatically
    -    inserted into the context</h4>
    -    
    -    <p>Properties now have a scope. Previously, the definition of a
    -    property added it to the context. As of version 1.0, properties
    -    are local, i.e. transient by default. For further details, please
    -    refer to the <a
    -    href="manual/configuration.html#variableSubstitution">documentation
    -    on properties</a>.</p>
    -    </div>
    -
    -    <p>Environment variables are now looked up during property
    -    substitution. This feature was requested in <a
    -    href="http://jira.qos.ch/browse/LBCORE-212">LBCORE-212</a> by
    -    Alexandre Garnier.
    -    </p>
    -
    -    <p><code>SMTPAppender</code> now sends emails asynchronously.</p>
    -
    -    <p>Investigation of <a
    -    href="http://jira.qos.ch/browse/LBCORE-224">LBCORE-224</a>
    -    reported by Cesar Alvarez Nunez points to bug in the JVM rather
    -    than in logback. A workaround has been found by using
    -    <code>CopyOnWriteArrayList</code> instead of the apparently buggy
    -    <code>ReadWriteLock</code>.</p>
    -
    -    <p>In STARTTLS mode, "mail.smtp.auth" property is no longer set
    -    automatically. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-225">LBCORE-225</a>
    -    reported by Mark Woon.</p>
    -
    -    <p>Logback-access now supports conditional configuration. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LBACCESS-27">LBACCESS-27</a>
    -    reported by Marshall Pierce.</p>
    -
    -    <p>Logback-access now supports inclusion of configuration
    -    files. See the <a
    -    href="manual/configuration.html#fileInclusion">chapter about
    -    configuration</a> in the logback's online manual for more
    -    information. </p>
    -
    -    <p>Added <code>ch.qos.logback.core.read</code> to "Import-Package"
    -    declaration in logback-classic's manifest file. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-131">LBCLASSIC-131</a>
    -    reported by Michal Prihoda and Thomas Jaeckle.</p>
    -
    -    <p>Fixed infinitely recursive calls in
    -    <code>AccessConverter#addError</code> methods as reported in <a
    -    href="http://jira.qos.ch/browse/LBACCESS-25">LBACCESS-25</a> by
    -    Pierre Queinnec.
    -    </p>
    -
    -    <p>Fixed incorrect switch fallthrough while selecting between the
    -    H2 and HSQL dialects as reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-218">LBCORE-218</a> by
    -    Pierre Queinnec.
    -    </p>
    -
    -    <h3>September 21st, 2011 - Release of version 0.9.30</h3>
    -
    -    <p>Archive removal can now deal with prolonged periods of
    -    application inactivity and application stop and restarts. The
    -    issue is described in <a
    -    href="http://jira.qos.ch/browse/LBCORE-147">LBCORE-147</a>
    -    reported by Rafael Diaz Maurin.
    -    </p>
    -
    -    <p>Logback-classic now supports printing stack traces "root cause
    -    first" instead of the standard "root cause last". See the
    -    documentation for <a
    -    href="manual/layouts.html#rootException">%rootException</a>
    -    converter for further details. The <code>%rootException</code>
    -    converter was contributed by Tomasz Nurkiewicz in relation with <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-217">LBCLASSIC-217</a>.
    -    </p>
    -
    -    <p>In the <code>ILoggingEvent</code> interface the
    -    <code>getMDC</code> method is now deprecated and replaced by
    -    <code>getMDCPropertyMap</code>. The latter method was already part
    -    of the <code>ILoggingEvent</code> interface in prior versions of
    -    logback-classic. Furthermore, the value returned by
    -    <code>ILoggingEvent.getMDCPropertyMap</code> can now be an empty
    -    map but never null.</p>
    -   
    -
    -    <p><code>LoggingEvent</code> no longer assumes that
    -    <code>LogbackMDCAdapter</code> is the only possible implementation
    -    of the <code>MDCAdapter</code> interface. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-275">LBCLASSIC-275</a>
    -    reported by Chris Dolan.
    -    </p>
    -
    -    <p><code>LogbackMDCAdapter</code> now synchronizes over its thread
    -    local map. This prevents
    -    <code>ConcurrentModificationException</code> from occurring while a
    -    child thread copies the map from the parent. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-289">LBCLASSIC-289</a>
    -    reported by Josh Oddman.</p>
    -
    -    <p>It is now possible to specify multiple destination addresses
    -    separated by commas in the the <span class="prop">to</span>
    -    property of <code>SMTPAppender</code>. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-213">LBCORE-213</a>
    -    reported by Alexandre Garnier who also provided the relevant
    -    patch. In previous versions of logback multiple destination
    -    addresses could only be specified by using multiple <span
    -    class="prop">to</span> properties. As of version 0.9.30 both
    -    comma separated addresses and multiple <span
    -    class="prop">to</span> properties are supported.
    -    </p>
    -
    -    <p>When debug attribute is set to true within the
    -    <code>&lt;configuration></code> element, status messages are
    -    printed on the console after Joran (re)configures
    -    logback. Previously, all status messages were printed. With this
    -    release, only status messages created during (re)configuration are
    -    printed. This change fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-273">LBCLASSIC-273</a>
    -    reported by Joern Huxhorn.
    -    </p>
    -
    -    <p>Single quotes within a date conversion specifier in filename
    -    patterns are now handled correctly. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-214">LBCORE-214</a>
    -    reported by Derek Libby.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>June 9th, 2011 - Release of version 0.9.29</h3>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-254">LBCLASSIC-254</a>,
    -    a performance issue in <code>LogbackMDCAdapter</code> as reported
    -    by Michael Franz.</p>
    -
    -    <p>Given that events reference a snapshot of the current MDC map,
    -    the snapshot should not be cleared upon invocation of the
    -    MDC.clear operation. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-253">LBCLASSIC-253</a>
    -    by Tommy Becker and independently fixed by the changes designated
    -    to fix LBCLASSIC-254.</p>
    -
    -    <p>As reported by Deepak Vadgama in <a
    -    href="http://jira.qos.ch/browse/LBCORE-199">LBCORE-199</a>, the
    -    entry in every zip file created by
    -    <code>FixedWindowRollingPolicy</code> had the same name. The issue
    -    was solved by adding a timestamp to the name of the entry in the
    -    zip files as suggested by a Benoit Xhenseval.</p> 
    -
    -    <p><code>ConfigurationWatchList</code> no longer emits an error
    -    message when it encounters a configuration file placed in a jar
    -    file, fixing <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-246">LBCLASSIC-246</a>
    -    as reported by Joern Huxhorn. This release also fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-247">LBCLASSIC-247</a>
    -    reported by Michael Franz.
    -    </p>
    -
    -
    -    <p>Logback now emits a clearer error message in case a conversion
    -    pattern misses a closing parenthesis or if an opening
    -    parenthesis is misplaced. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-193">LBCORE-193</a>
    -    reported by B. K. Oxley.</p>
    -
    -    <p>Fixed thread safety issue in LRUMessageCache reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-255">LBCLASSIC-255</a>
    -    by C&eacute;sar &Aacute;lvarez N&uacute;&ntilde;ez.
    -    </p>
    -
    -    <p>The AccessEvent class in logback-access module now implements
    -    the <code>IAccessEvent</code> interface. This fixes <a
    -    href="http://jira.qos.ch/browse/LBACCESS-12">LBACCESS-12</a>
    -    reported by Joern Huxhorn.</p>
    -    
    -    <hr width="80%" align="center" />
    -
    -    <h3>January 25th, 2011 - Release of version 0.9.28</h3>
    -
    -    <div class="breaking">
    - 
    -      <h4>Breaking change: In the Context interface, the previously
    -      misspelled property <code>bithTime</code> is now renamed as
    -      <code>birthTime</code>.</h4>
    -      
    -      <p>In the <a
    -      href="apidocs/ch/qos/logback/core/Context.html">Context</a>
    -      interface, the previously misspelled property
    -      <code>bithTime</code> is now renamed as
    -      <code>birthTime</code>. This is a backward-incompatible
    -      change. All pre-existing references to "bithTime" property now
    -      need to referenced as "birthTime".</p>
    -    </div>
    -
    -    <p>Fixed <a href="http://jira.qos.ch/browse/LBCLASSIC-238">issue
    -    238</a> reported by Robert Elliot. <code>GEventEvaluator</code>'s
    -    start method now correctly sets the state of the instance.
    -    </p>
    -
    -    <p><code>JaninoEventEvaluator</code> now expects Java blocks
    -    instead of boolean expressions. For even moderately complex
    -    logical statements, blocks can be more convenient to read or to
    -    write compared with boolean expressions. To ensure backward
    -    compatibility, existing boolean expressions are converted into
    -    blocks on the fly.
    -    </p>
    -
    -    <p>Corrected handling of null valued arguments by DBAppender. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-239">LBCLASSIC-239</a>
    -    reported by Artyom Kalita.</p>
    -
    -    <p>It is now possible to use the context birth time as the time
    -    reference for the <a
    -    href="manual/appenders.html#uniquelyNamed"><code>&lt;timestamp></code></a>
    -    element in configuration files.</p>
    -
    -    <p>Logback can now scan for included files as well. This solves <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-245">LBCLASSIC-245</a>.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>December 22nd, 2010 - Release of version 0.9.27</h3>
    -
    -    <p>Added a new section in the documentation about <a
    -    href="recipes/index.html">real-world inspired recipes</a>.</p>
    -
    -    <p><code>PatternLayout</code> now supports compound conversion
    -    words of which <a
    -    href="manual/layouts.html#replace">"%replace"</a> is one
    -    example. This change involved a significant re-write of the
    -    pattern tokenizer, the pattern parser and the pattern
    -    compiler. However, the changes should be backward compatible as far
    -    as users are concerned.
    -    </p>
    -
    -    <p>The <a href="manual/appenders.html#smtpTo"><span
    -    class="prop">to</span></a> option of SMTPAppender now admits a
    -    pattern and is evaluated dynamically.</p>
    -
    -    <p><code>TeeServletInputStream</code> no longer chokes on input
    -    over 8K in size. This fixes <a
    -    href="http://jira.qos.ch/browse/LBACCESS-10">LBACCESS-10</a> as
    -    reported by Augusto Rodriguez.
    -    </p>
    -
    -    <p><code>TeeFilter</code> now takes include and exclude parameters
    -    so that it can be enabled or disabled by host. This solves <a
    -    href="http://jira.qos.ch/browse/LBACCESS-15">LBACCESS-15</a>.</p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-231">LBCLASSIC-231</a>
    -    as reported by Jeff Skjonsby. When configuring
    -    <code>SizeAndTimeBasedFNATP</code>,
    -    <code>GaggerConfigurator</code> (Groovy) will no longer throw a
    -    <code>NullPointerException</code>.
    -    </p>
    -
    -    <p>Every context now returns its own name when asked for the
    -    property <code>CONTEXT_NAME</code>. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-178">LBCORE-178</a> as
    -    requested by Michael Osipov.
    -    </p>
    -
    -
    -    <h3>20th of October, 2010 - Release of version 0.9.26</h3>
    -
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-183">LBCLASSIC-183</a>
    -    as reported by Anthony Whitford. Upon application restart, Tomcat
    -    should no longer complain about ThreadLocal variables created
    -    internally by logback.
    -    </p>
    -
    -    <p><code>SMPTAppender</code> now lets users to set a custom buffer
    -    size for the outgoing messages. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-170">LBCORE-170</a> as
    -    requested by Peter Royal.
    -    </p>
    -
    -    <p>When stopped, <code>RollingFileAppender</code> now calls
    -    <code>stop()</code> on its policy objects, fixing <a
    -    href="http://jira.qos.ch/browse/LBCORE-114">LBCORE-114</a>
    -    reported by Anders Wallgren.</p>
    -
    -    <p>Added mapping for OFF and ALL levels in <code>JULHelper</code>
    -    as requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-226">LBCLASSIC-226</a>
    -    by Christian Brensing.
    -    </p>
    -
    -    <h3>October 13th, 2010 - Release of version 0.9.25</h3>
    -
    -    <p>Logback-classic now supports <a
    -    href="manual/configuration.html#LevelChangePropagator">propagation
    -    of level changes from logback-classic onto the
    -    j.u.l. framework</a>. This significantly reduces the performance
    -    impact of disabled log statements making it reasonable for
    -    real-world applications to use the <a
    -    href="http://slf4j.org/legacy.html#jul-to-slf4j">jul-to-slf4j
    -    bridge</a>.
    -    </p>
    -
    -    <p>The <code>&lt;appender-ref></code> element now supports
    -    <a href="manual/configuration.html#variableSubstitution">variable substitution</a>, thus fixing <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-224">LBCLASSIC-224</a>
    -    as reported by David Harrigan.
    -    </p>
    -
    -    <p>As reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-164">LBCORE-164</a>,
    -    <code>SizeAndTimeBasedFNATP</code> would cause previous logging
    -    files with indexes over nine to be overwritten on application
    -    restart. This complex bug was reported and precisely diagnosed by
    -    Dieter Mueller. He has also proposed a fix which was subsequently
    -    adopted as is.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>June 30th, 2010 - Release of version 0.9.24</h3>
    -
    -    <p>Fixed NullPointerException thrown by
    -    <code>MDCBasedDiscriminator</code> in case the MDC map is
    -    null.</p>
    -
    -    <p>Fixed issue with <code>SizeAndTimeBasedFNATP</code> in presence
    -    of application stop/restart and non-null file property. This issue
    -    was reported by Tom Liu in <a
    -    href="http://jira.qos.ch/browse/LBCORE-131">LBCORE-131</a>.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>June 29th, 2010 - Release of version 0.9.23</h3>
    -
    -    <p>Fixed Groovy runtime dependency problem with
    -    ReconfigureOnChangeFilter, i.e. the auto-scan filter, when using
    -    non-groovy configuration files. This problem was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-214">LBCLASSIC-214</a>
    -    by Alvin Chee.</p>
    -
    -    <p>Fixed <code>MDCBasedDiscriminator</code> so that it supports deferred
    -    processing. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-213">LBCLASSIC-213</a>
    -    by Torsten Juergeleit.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>June 21st, 2010 - Release of version 0.9.22</h3>
    -
    -    <p>Logback now supports <a href="manual/groovy.html">configuration
    -    files written in Groovy</a> which are more convenient than
    -    configuration files written in XML. We have also developed a tool
    -    to <a
    -    href="http://logback.qos.ch/translator/asGroovy.html">automatically
    -    migrate your logback.xml files to logback.groovy</a>.
    -    </p>
    -
    -    <p>Inspired by the functionality offered by
    -    <code>SiftingAppender</code>, <code>SMTPApppender</code> now can
    -    <a href="manual/appenders.html#smtpDiscriminator">track multiple
    -    buffers according to selection information returned by a
    -    discriminator</a>.
    -    </p>
    -
    -    <p>Fixed synchronization issue in <code>ConsoleAppender</code> as
    -    reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-158">LBCORE-158</a> by
    -    David Roussel who also supplied a corrective patch.</p>
    -
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>May 8th, 2010 - Release of version 0.9.21</h3>
    -
    -    <p>SLF4J dependency upgraded to 1.6.0</p>
    -
    -    <p>Fixed concurrency problem in <code>SiftingAppender</code> as
    -    reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-203">LBCLASSIC-203</a>
    -    by Ellen Strnod.
    -    </p>
    -
    -    <p>Added <a
    -    href="manual/filters.html#GEventEvaluator">GEventEvaluator</a>
    -    which processes boolean expression written in the Groovy language.
    -    </p>
    -
    -    <p>Fixed rollover problem occurring when the target directory was
    -    missing as reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-151">LBCORE-151</a> by
    -    Torsten Juergeleit. A closely related problem was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-167">LBCLASSIC-167</a>
    -    by Rick Janda.
    -    </p>
    -    
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>April 2nd, 2010 - Release of version 0.9.20</h3>
    -
    -    <p>Fixed issue related to charsets in
    -    <code>LayoutWrappingEncoder</code> not being recognized in config
    -    files.</p>
    -
    -    <p>Logback now supports <a
    -    href="manual/configuration.html#conditional">conditional
    -    processing</a> of configuration files.</p>
    -
    -    <p>A new extension point for <a
    -    href="manual/configuration.html#definingPropsOnTheFly">defining
    -    properties on the fly</a> has been added. The code for this
    -    enhancement was provided by Aleksey Didik in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-182">LBCLASSIC-182</a>.</p>
    -    
    -    <p>Logback now provides <a
    -    href="http://logback.qos.ch/manual/loggingSeparation.html">an
    -    answer</a> to the longstanding logging separation problem for
    -    static logger references in shared libraries. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-166">LBCLASSIC-166</a>
    -    and <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-87">LBCLASSIC-87</a>.
    -    </p>
    - 
    -    <p>Removed the previously deprecated <code>getFirstFilter</code>
    -    method in <code>FilterAttachable</code> interface.</p>
    -
    -    <!-- ============================================================== -->
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>March 24, 2010 - Release of version 0.9.19</h3>
    -
    -
    -    <div class="breaking">
    -
    -    <h4>Breaking change: FileAppender now expects an Encoder</h4>
    -
    -    <p>In response to <a
    -    href="http://jira.qos.ch/browse/LBCORE-128">LBCORE-128</a>,
    -    <code>WriterAppender</code> has been renamed as
    -    <code>OutputStreamAppender</code>, <code>FileAppender</code> now
    -    sub-classing the latter. <code>OutputStreamAppender</code> and
    -    sub-classes now take an <code>Encoder</code> instead of a
    -    <code>Layout</code>. 
    -    </p>
    -
    -    <p>Contrary to layouts which simply transform a logging event into
    -    a String, an encoder is responsible for writing the actual event
    -    or more accurately the representation of the event onto the output
    -    stream. The role of the enclosing appender is now limited to
    -    managing the output stream (opening/closing) but not writing
    -    actual contents onto the stream. This change will require <a
    -    href="codes.html#layoutInsteadOfEncoder"> modifications to
    -    existing configuration files</a>. We hope to make up for this
    -    inconvenience with cool new features which are only possible using
    -    encoders. <b>During a transition period, layouts passed as
    -    parameter will be automatically wrapped by an encoder so that
    -    configuration files in the old format (using a layout instead of
    -    encoder) will continue to work unmodified.</b>
    -    </p>
    -
    -    </div>
    -
    -    <br/>
    -
    -    <div class="breaking">
    -
    -
    -      <h4>Breaking change: DBAppender in logback-classic expects four
    -      additional columns
    -      </h4>
    -      
    -      <p>Instead of lumping log request arguments into a field,
    -      <code>DBAppender</code> (in logback-classic) now outputs up to
    -      four arguments in the logging request in separate columns, named
    -      <code>arg0</code>, <code>arg1</code>, <code>arg2</code> and
    -      <code>arg4</code>. This solves <a
    -      href="http://jira.qos.ch/browse/LBCLASSIC-187">LBCLASSIC-187</a>
    -      reported by Greg Thomas.</p>
    -
    -      <p>Existing databases can be migrated to the new table structure
    -      by altering the existing tables. Here is how it would be done
    -      in PostgreSQL and MySQL.</p>
    -
    -      <pre class="prettyprint">ALTER TABLE logging_event ADD COLUMN arg0 VARCHAR(254);
    -ALTER TABLE logging_event ADD COLUMN arg1 VARCHAR(254);
    -ALTER TABLE logging_event ADD COLUMN arg2 VARCHAR(254);
    -ALTER TABLE logging_event ADD COLUMN arg3 VARCHAR(254);</pre>
    -
    -    <p>In Oracle:</p>
    -
    -    <pre class="prettyprint">ALTER TABLE logging_event ADD arg0 VARCHAR(254);
    -ALTER TABLE logging_event ADD arg1 VARCHAR(254);
    -ALTER TABLE logging_event ADD arg2 VARCHAR(254);
    -ALTER TABLE logging_event ADD arg3 VARCHAR(254);</pre>
    -
    -      
    -    </div>
    -    <p><code>FileAppender</code> and derived classes can now
    -    gracefully deal with IO failures and recover quickly after the
    -    original cause of the IO failure is corrected. For example, if the
    -    log file is located on a <a
    -    href="http://en.wikipedia.org/wiki/Network-attached_storage">network-attached
    -    storage (NAS)</a>, and the connection to the NAS server is lost,
    -    <code>FileAppender</code> and derived classes will recover quickly
    -    after the connection to the server is re-established.
    -    </p>
    -
    -    <p>The packages under <code>ch.qos.logback.classic</code>
    -    namespace no longer depend on <code>org.slf4j.impl</code>. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-184">LBCLASSIC-184</a>
    -    reported by Gunnar Wagenknecht.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-193">LBCLASSIC-193</a>.
    -    <code>SyslogAppender</code> will no longer shutdown when messages
    -    over 256Kb cause an <code>IOException</code>.
    -    </p>
    -
    -    <p>Added <a
    -    href="manual/appenders.html#OnMarkerEvaluator">OnMarkerEvaluator</a>
    -    which evaluates to true in the presence of user-specified
    -    markers. When used with <code>SMTPAppender</code>, it can trigger
    -    an outgoing email message when an event matches user-specified
    -    marker.</p>
    -
    -    <p>Request header names in AccessEvent (logback-access) are now
    -    stored in a case insensitive way as specified in RFC 2616.  This
    -    prevents compatibility issues, in particular with recent versions
    -    of Tomcat which store request header names in lower-case.</p>
    -
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-134">LBCORE-134</a>
    -    reported by Michael Franz. While reading configuration files (in
    -    XML), the event handler will correctly process body text, in
    -    particular spaces, even when delivered in multiple chunks. The
    -    previous behavior of trimming leading and trailing white space has
    -    been preserved. Moreover, body text consisting of white space
    -    surrounding child elements is ignored.
    -    </p>
    -    
    -    <p><code>SiftingAppender</code> now exposes its
    -    <code>AppenderTracker</code>. Moreover, the
    -    <code>stopAndRemoveNow(String key)</code> method was added to
    -    <code>AppenderTracker</code>, allowing immediate removal of a
    -    nested appender.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-130">LBCORE-130</a>.
    -    <code>FileNamePattern</code> no longer treats parenthesis as
    -    special.
    -    </p>
    -
    -    <p>The <span class="prop">CharsetEncoder</span> property in
    -    <code>SMTPAppender</code> can now be accessed and set from
    -    configuration files. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-194">LBCLASSIC-194</a>.
    -    </p>
    -
    -    <p>Instead of sending its output to the System.out/err reference
    -    that was effective at initialization, <code>ConsoleAppender</code>
    -    will now seek the most current System.out/err reference and send
    -    its output there. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-143">LBCORE-143</a> as
    -    reported by Tom SH Liu.
    -    </p>
    -
    -    <p>DBAppender now outputs exception class and message. It will
    -    also output root causes, fixing
    -    <a href="http://jira.qos.ch/browse/LBCLASSIC-170">LBCLASSIC-170</a>
    -    reported by Tomasz Nurkiewicz.
    -    </p>
    -
    -    <p>You can now override table and column names in DBAppender. This
    -    feature was requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-188">LBCLASSIC-188</a>
    -    reported by Tomasz Nurkiewicz.
    -    </p>
    -
    -    <p>Fixed missing EVENT_ID column problem on PostgreSQL as reported
    -    in <a href="http://jira.qos.ch/browse/LBCORE-126">LBCORE-126</a>
    -    by Brian Edwards.</p>
    -
    -    <p>Event id values in DBAppender are now 64bit instead of 32. This
    -    change was requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-198">LBCLASSIC-198</a>
    -    by Michael Lynch.
    -    </p>
    -
    -    <p>Added Sybase SQLAnywhere support in DBAppender. This feature
    -    was requested by Michael Lynch in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-197">LBCLASSIC-197</a>.
    -    </p>
    -
    -    <p>Added H2 support in DBAppender.</p>
    -
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBSITE-36">LBSITE-36</a> reported
    -    by John Jimenez.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <!-- ======================================================================== -->
    -
    -    <h3>3rd of December 2009 - Release of version 0.9.18</h3>
    -
    -    <p>After a very long investigation resulting in somewhat a better
    -    understanding of licensing issues, logback is now <a
    -    href="license.html">dual-licensed</a> under the EPL 1.0 and LGPL
    -    2.1.
    -    </p>
    -
    -    <p>Due to a clerical error <a
    -    href="http://jira.qos.ch/browse/LBCORE-26">LBCORE-26</a> has
    -    re-raised its ugly head. It has now been tamed for good.</p>
    -
    -    <p>Fixed Private-Package and Export-Package sections in
    -    logback-classic.jar MANIFEST as reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-165">LBCLASSIC-165</a>
    -    and <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-121">LBCLASSIC-121</a>
    -    by applying the relevant patches supplied by Hugues Malphettes.
    -    </p>
    -
    -    <p>Made the OSGi import declarations for Janino and javax optional
    -    as proposed by David Varnes in <a
    -    href="http://jira.qos.ch/browse/LBCORE-101">LBCORE-101</a>.</p>
    -
    -    <p><code>ReconfigureOnChangeFilter</code> did not pick up changes
    -    for configuration files containing spaces. This issue was reported
    -    in <a href="http://jira.qos.ch/browse/LBCORE-119">LBCORE-119</a>
    -    by Anders Wallgren.
    -    </p>
    -
    -    <p>Added tests cases which run logback artifacts as bundles within
    -    Felix. This should solve a series of problems related to OSGi.</p>
    -
    -    <p>When ill-formed configuration files fragments were included in
    -    another configuration file, the included file was not closed
    -    correctly. This issue was reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-122">LBCORE-122</a> by
    -    Michael Franz.
    -    </p>
    -
    -    <p>JaninoEventEvaluator now passes the throwable associated with
    -    an event as a <code>java.lang.Throwable</code> instead of an
    -    <code>IThrowableProxy</code>. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-155">LBCLASSIC-155</a>
    -    as reported by Hontv&aacute;ri J&oacute;zsef. See <a
    -    href="manual/filters.html#evalutatorFilter">EvaluatorFilter
    -    documentation</a> for more details.</p>
    -
    -    <p>The name of the log file nested within .zip archives was
    -    incorrectly set to be that of the tmp file logback creates during
    -    compression. Anders Wallgren reported and posted an appropriate
    -    patch for this problem in <a
    -    href="http://jira.qos.ch/browse/LBCORE-98">LBCORE-98</a>.  The
    -    nested file is now named after the zip archive file. Analysis
    -    showed that this was an appropriate strategy in all the cases we
    -    considered.</p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-149">LBCLASSIC-149</a>. The
    -    ContextNameConverter now correctly uses the context name of the
    -    event instead of its own context name.
    -    </p>
    -
    -    <p>When a logger is named after an inner class, the '$' is used as
    -    a separator, instead of the usual '.'. This fixes the level
    -    inheritance issue described in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-102">LBCLASSIC-102</a>
    -    and as reported by Joern Huxhorn.</p>
    -
    -    <p>Fixed deadlock issue observed with appender which invoke
    -    loggers as reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-154">LBCLASSIC-154</a>
    -    by Andrew Perrine and debugged by Ralph Goers who also proposed
    -    the relevant patch.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>9th of August 2009 - Release of version 0.9.17</h3>
    -
    -    <p><code>PackagingDataCalculator</code> now correctly deals with
    -    <code>NoClassDefFoundError</code> thrown by class loaders. This
    -    fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-125">LBCLASSIC-125</a>
    -    reported by Roland Klein and independently by Didier Besset.
    -    </p>
    -
    -    <p>In configuration files, all tags names associated with explicit
    -    actions are now case-insensitive. This should diminish
    -    case-related errors users may make when writing configuration
    -    files. Tag names associated with implicit actions which are
    -    closely linked to the actual Java class being configured, still
    -    need to follow the camelCase convention. If you are unsure which
    -    case to use for a given tag name, just follow the camelCase
    -    convention for tag names which should be correct in most
    -    cases. </p>
    -
    -    <p>It is now possible to create <a
    -    href="manual/appenders.html#uniquelyNamed">uniquely named files by
    -    timestamp</a>. Such files are appropriate for log files associated
    -    with batch applications. This enhancement fulfills <a
    -    href="http://jira.qos.ch/browse/LBCORE-91">LBCORE-91</a> as
    -    requested by Rick Beton and Szel Zoltan.
    -    </p>
    -
    -    <p>Append mode is now mandatory in
    -    <code>RollingFileAppender</code>. This was already the default and
    -    truncation mode is unreasonable in most cases. For example, in
    -    truncate mode when an application stops and is quickly re-started,
    -    any pre-existing log file for the current period will be lost.</p>
    -
    -    <p>The <code>activeFile</code> field in
    -    <code>RollingFileAppender</code> is now properly updated, fixing
    -    <a href="http://jira.qos.ch/browse/LBCORE-90">LBCORE-90</a> as
    -    reported by Valery Shorin.</p>
    -
    -
    -    <p>The <code>TimeBasedTriggeringPolicy</code> has been heavily
    -    refactored. It is now possible to trigger rolling simultaneously
    -    by <a href="manual/appenders.html#SizeAndTimeBasedFNATP">time and
    -    by size</a>, fixing <a
    -    href="http://jira.qos.ch/browse/LBCORE-61">LBCORE-61</a>.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-145">LBCLASSIC-145</a>
    -    reported by Joern Huxhorn. The <code>LoggingEventVO</code> class
    -    now correctly populates its callerData field if the original
    -    <code>ILoggingEvent</code> already has it.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>15th of July 2009 - Release of version 0.9.16</h3>
    -
    -    <p>In addition to nifty new features, this release contains
    -    several important internal changes. Given the importance of some
    -    of those internal changes, <em>this release may be less stable
    -    than previous logback releases</em>.</p>
    -
    -    <p>In logback-classic, <a
    -    href="xref/ch/qos/logback/classic/spi/LoggingEvent.html"><code>LoggingEvent</code></a>
    -    now implements the <a
    -    href="xref/ch/qos/logback/classic/spi/ILoggingEvent.html"><code>ILoggingEvent</code></a>
    -    interface. All logback-classic components expect to process
    -    <code>ILoggingEvent</code> instances. Moreover, events which sent
    -    to a remote host are now of type <a
    -    href="xref/ch/qos/logback/classic/spi/LoggingEventVO.html"><code>LoggingEventVO</code></a>.
    -    </p>
    -
    -    <p>Logback-classic will now <a
    -    href="manual/configuration.html#autoScan">automatically
    -    re-configure itself</a> when its configuration file is
    -    modified. This enhancement was requested in <a
    -    href="http://jira.qos.ch/browse/LBCORE-59">LBCORE-59</a> by
    -    Michael Osipov.</p>
    -
    -    <p>FileAppender and its derived class RolllingFileAppender are now
    -    based on a finer-grain synchronization model. This change is aimed
    -    at reducing various synchronization related bugs, namely <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-135">LBCLASSIC-135</a>
    -    and its various sub-tasks.
    -    </p>
    -
    -    <p>Fixed a bug in the localLevelReset() method in the
    -    <code>Logger</code> class which inadvertently used the DEBUG_INT
    -    value in the org.slf4j.spi.LocationAwareLogger interface instead
    -    of the DEBUG_INT value in ch.qos.logback.classic.Level class. The
    -    issue was first identified by Andy Ruch <a
    -    href="http://qos.ch/pipermail/logback-user/2009-February/000955.html">in
    -    a message</a> to the logback-user list
    -    </p>
    -
    -    <p>If the logger name had 12 or more segments, a the logger
    -    conversion specified would throw an
    -    <code>ArrayIndexOfBounds</code> exception. Lukas Zapletal has
    -    kindly reported this problem in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-110">LBCLASSIC-110</a>
    -    and provided a test case.
    -    </p>
    -
    -    <p>A new discriminator class called "ContextBasedDiscriminator"
    -    allows SiftingAppender to separate logging based on the context
    -    name. This fits nicely with the requirements expressed in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-117">LBCLASSIC-117</a>
    -    by Rick Janda.
    -    </p>
    -    
    -    <p>When a context is reset, then its object and property maps are
    -    now cleared. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCORE-104">LBCORE-104</a>
    -    reported by Johan Bos.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-105">LBCORE-105</a>. Configuration
    -    files placed in jar files will no longer lock the jar file. This
    -    issue was initially reported for log4j in <a
    -    href="https://issues.apache.org/bugzilla/show_bug.cgi?id=47465">bug
    -    47465</a> Mark Thomas.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-94">LBCORE-94</a>. While
    -    configuring a RollingFileAppender, if the File property is
    -    declared after any rolling or triggering policies, then logback will
    -    now generate an error status message.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-134">LBCLASSIC-134</a>
    -    reported by Darryl Smith. <code>DuplicateMessageFilter</code> will
    -    now behave correctly when invoked via
    -    <code>Logger.isXXXEnabled</code> methods.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-107">LBCORE-107</a>,
    -    incorrectly set context information for the
    -    <code>NestedBasicPropertyIA</code> instance in Joran, reported by 
    -    L&oacute;r&aacute;nt Pint&eacute;r.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-109">LBCLASSIC-109</a>,
    -    reported by Andrew Ruch when a logger context was reset, the trace
    -    level was erroneously enabled.
    -    </p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>12th of February 2009 - Release of version 0.9.15</h3>
    -
    -    <p>When the reset() method in <code>LoggerContext</code> is
    -    called, registered turbo filters are first stopped before being
    -    unregistered. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-89">LBCLASSIC-89</a>
    -    reported by Alexis Morillo.
    -    </p>
    -
    -    <p>Added a servlet for viewing internal status messages as an HTML
    -    table. Two separate implementations are available; one for <a
    -    href="manual/configuration.html#viewingStatusMessages">logback-classic</a>
    -    and the other for <a
    -    href="access.html#viewingStatusMessages">logback-access</a>.</p>
    -
    -    <p>Added the <code>clear()</code> method to the
    -    <code>StatusManager</code> interface. After reconfiguration, it
    -    may be desirable to clear the status list maintained by a
    -    <code>StatusManager</code>. This change was requested by Alexis
    -    Morillo in <a
    -    href="http://jira.qos.ch/browse/LBCORE-77">LBCORE-77</a>.
    -    </p>
    -
    -    <p>Fixed problem <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-104">LBCLASSIC-104</a>
    -    related to loss of MDC information in deferred logging events.
    -    </p>
    -
    -    <p>Fixed issue <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-101">LBCLASSIC-101</a>. Logback
    -    jar files now include full OSGi bundle information in their
    -    manifests. This information is automatically added by Apache
    -    Felix' Maven bundle plug-in. The generated manifest files contain
    -    an appropriate Bundle-RequiredExecutionEnvironment directive,
    -    fixing <a
    -    href="http://jira.qos.ch/browse/LBGENERAL-8">LBGENERAL-8</a>.
    -    </p>
    -
    -    <p>The setter and getter methods for the layout property in
    -    <code>AppenderBase</code> class now have reasonable default
    -    implementations, instead of nop previously. This change was
    -    requested by Thilo Tanner in <a
    -    href="http://jira.qos.ch/browse/LBCORE-56">LBCORE-56</a>.
    -    </p>
    -    
    -
    -    <p>Added <em>log4j.dtd</em> compatible <a
    -    href="manual/layouts.html#log4jXMLLayout"><code>XMLLayout</code></a>
    -    as requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-22">LBCLASSIC-22</a>.
    -    </p>
    -
    -    <p>New chapter <a href="manual/onJoran.html">on Joran</a>.</p>
    -
    -    <hr width="80%" align="center" />
    -
    -    <h3>29th of December 2008 - Release of version 0.9.14</h3>
    -
    -    <p>Corrected a serious dead-lock problem occurring during
    -    configuration. It was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-81">LBCLASSIC-81</a> by
    -    Holger Mense.</p>
    -
    -    <p>Corrected thread leakage observed with
    -    <code>TimeBasedRollingPolicy</code>'s
    -    <code>AsynchronousCompressor</code>. This bug was reported in <a
    -    href="http://jira.qos.ch/browse/LBCORE-78">LBCORE-78</a> by Szel
    -    Zoltan.</p>
    -
    -    <p>Added <a
    -    href="manual/filters.html#DuplicateMessageFilter">DuplicateMessageFilter</a>,
    -    a turbo filter which detects duplicate messages, and beyond a
    -    certain number of repetitions, drops repeated messages.</p>
    -
    -    <p>Added a new appender called <a
    -    href="manual/appenders.html#SiftingAppender"><code>SiftingAppender</code></a>. As
    -    its name intimates, a <code><code>SiftingAppender</code></code>
    -    can be used to separate (or sift) logging according to a given
    -    runtime attribute. For example, <code>SiftingAppender</code> can
    -    separate logging events according to user sessions, so that the
    -    logs generated by each user go into distinct log files, one log
    -    file per user.
    -    </p>
    -    
    -    <p><code>BasicStatusManager</code>'s internal buffer is now split
    -    into two parts, the header part and the tail part. The header part
    -    stores the fist H status messages whereas the tail part stores the
    -    last T messages. At present time H=T=150, although these values may
    -    change in future releases.</p>
    -
    -    <p>Fixed <code>NullPointerException</code> thrown when calling
    -    <code>setContextMap</code> on a fresh MDC. <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-98">Issue was</a>
    -    reported by Francois Terrier.
    -    </p>
    -
    -
    -    <p>Evaluators are now wired into an appender automatically
    -    (implicit rules) without the help of
    -    <code>EvaluatorAction</code>. EvaluatorAction is now only needed
    -    for global evaluator elements.  It follows that name attribute is
    -    necessary only for global evaluators. Embedded evaluators no
    -    longer need a name. This change fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-100">LBCLASSIC-100</a>. In
    -    addition, <code>EvaluatorAction</code> has been modified to accept
    -    evaluators of any type and not just instances of
    -    <code>JaninoEvaluator</code>.    
    -    </p>
    -
    -    <p>Logback-classic default configuration process will now
    -    automatically print status data on the console in case of warnings
    -    as well as errors. Previously, status data was printed only in
    -    case of errors.</p>
    -
    -    <p><code>ContextInitializer</code> will now print the url of the
    -    configuration it is using. This should help reduce confusion when
    -    multiple config files are found on the class path.  This change
    -    resolves <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-55">LBCLASSIC-55</a>
    -    reported by Anton Tagunov and Henric Larsson.
    -    </p>
    -
    -    <p>In case multiple configuration files are found on the class
    -    path, <code>ContextInitializer</code> will now emit a
    -    warning. This fixes <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-85">LBCLASSIC-85</a>
    -    filed by Szel Zoltan.
    -    </p>
    -
    -    <p><code>HTMLLayout</code> now honors custom conversion words,
    -    thus fixing <a
    -    href="http://jira.qos.ch/browse/LBCORE-74">LBCORE-74</a> as
    -    reported by Natan Cox.</p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-69">LBCORE-69</a> as
    -    reported by Andrey Rybin. <code>SMTPAppenderBase</code> now has
    -    support for charset encodings so that email messages can contain
    -    characters beyond US-ASCII.
    -    </p>
    -
    -    <p><code>SMTPAppender</code> in logback-classic now defaults to
    -    <code>OnErrorEvaluator</code> instead of a janino-based
    -    evaluator. Thus, by default <code>SMTPAppender</code> no longer
    -    depends on Janino.</p>
    -
    -    <p>Added UnsynchronizedAppenderBase class based on Ralph Goers'
    -    contribution in <a
    -    href="http://jira.qos.ch/browse/LBCORE-58">LBCORE-58</a>. Note
    -    that <code>AppenderBase</code> remains unchanged. Appenders which
    -    need to handle synchronization on their own can do so by deriving
    -    from <code>UnsynchronizedAppenderBase</code>.</p>
    -
    -    <h3>5th of December 2008 - Release of version 0.9.13</h3>
    -
    -    <p>A <code>NullPointerException</code> was being thrown when a the
    -    level of a logger was set to null. The logger in question had to
    -    have children. This problem was reported in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-91">LBCLASSIC-91</a>
    -    by Mateusz Jedruch.
    -    </p>
    -
    -    <h3>4th of December 2008 - Release of version 0.9.12</h3>
    -    
    -    <p>Fixed <a href="http://jira.qos.ch/browse/LBCORE-26">bug
    -    LBCORE-26</a> reported by Tsutomu YANO and independently by
    -    Hontv&aacute;ri J&oacute;zsef and Gamaliel
    -    Amaudruz. <code>RollingFileAppender</code> when used in conjunction
    -    with <code>DateBasedRollingPolicy</code> will rollover existing log
    -    files at initialization if their timestamp warrants it.
    -    </p>
    -
    -    <p>Added support for file appending in <a
    -    href="manual/appenders.html#prudent">prudent mode</a>. Thus,
    -    multiple <code>FileAppender</code> instances running on multiple
    -    JVMs can safely write to the same log file. With certain
    -    limitations, prudent mode extends to
    -    <code>RollingFileAppender</code>.
    -    </p>
    -
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-83">LBCLASSIC-83</a>.
    -    It is now possible to set the level of a logger to null, even if
    -    it was previously set to a non-null level. Previously, a
    -    <code>NullPointerException</code> would be thrown.
    -    </p>
    -
    -    <p>In response to <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-61">LBCLASSIC-61</a>, <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-33">LBCLASSIC-33</a>, <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-14">LBCLASSIC-24</a> and
    -    <a href="http://jira.qos.ch/browse/LBCLASSIC-24">LBCLASSIC-14</a>
    -    <code>JMXConfigurator</code> has been redesigned.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBGENERAL-22">LBGENERAL-22</a>. The
    -    <a href="http://logback.qos.ch/translator/">log4j.properties
    -    translator</a> web-application has been significantly refactored.
    -    </p>
    -
    -    <p>Fixed improvement request <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-59">LBCLASSIC-59</a>
    -    relation to StatusListeners, originally submitted by Anton Tagunov.
    -    </p>
    -
    -    <p>The logger and class name converters now consider zero as
    -    having special meaning, and will return the simple class name,
    -    removing the package name prefix. This feature was asked by
    -    Silvano Maffeis.</p>
    -    
    -    <p>In response to <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-54"> LBCLASSIC-54</a>
    -    support for turbo filters has refactored. The present code is safe
    -    under concurrent access while still offering good performance.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCORE-43">LBCORE-43</a> reported
    -    by Bruno Navert. Configuration files can now look up property
    -    files from classpath resources.
    -    </p>
    -
    -    <p>Fixed <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-86">LBCLASSIC-86</a>
    -    related to <code>AccessControlException</code> thrown when run in
    -    a JVM with a <code>SecurityManager</code>.
    -    </p>
    -
    -    <p>Invoking the <code>LoggerContext.reset()</code> method resets
    -    logger levels to their default value, that is <code>DEBUG</code>
    -    for the root logger and <code>null</code> for all other
    -    loggers. This was requested in <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-90">LBCLASSIC-90</a> by
    -    Mateusz Jedruch.
    -    </p>
    -
    -    <!-- ======================== minor ================== -->
    -
    -    <p>Fixed <a href="http://jira.qos.ch/browse/LBCLASSIC-69">bug
    -    LBCLASSIC-69</a> reported by Anton Tagunov. The
    -    LevelToSyslogSeverity now correctly handles the TRACE level.
    -    </p>
    -
    -    <p>Fixed <a href="http://jira.qos.ch/browse/LBCLASSIC-57">bug
    -    LBCLASSIC-57</a> reported by Anton Tagunov. SyslogAppender could
    -    overwhelm the Syslog server with very large messages. SyslogAppender
    -    now limits its message size to 256K.
    -    </p>
    -
    -    <p>Logback now supports <a
    -    href="manual/configuration.html#contextName">setting the logger context
    -    name</a> as well as inserting variables <a
    -    href="manual/configuration.html#insertFromJNDI">stored in JNDI</a> as
    -    properties.
    -    </p>
    -
    -    <p>Fixed issue <a
    -    href="http://jira.qos.ch/browse/LBCLASSIC-49">LBCLASSIC-49</a>
    -    reported by Oliver Lietz. The getLogger() method in
    -    <code>LoggerContext</code> class will now throw an
    -    <code>IllegalArgumentException</code> when invoked with a null
    -    argument.
    -    </p>
    -
    -
    -  <hr width="80%" align="center" />
    -
    -  <h3>28th of October 2008 - Release of version 0.9.11</h3>
    -
    -  <p>Fixed <a
    -  href="http://jira.qos.ch/browse/LBCLASSIC-77">LBCLASSIC-77</a>
    -  reported independently by Gianni Doe, Yannick Haudry and Yossi Shaul.
    -  The childValue() method of <code>CopyOnInheritThreadLocal</code>
    -  class part of MDC code no longer throws a
    -  <code>NullPointerException</code>.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -
    -  <h3>27th of October 2008 - Release of version 0.9.10</h3>
    -
    -  <p>In case of errors during initialization, logback-classic now
    -  <em>automatically</em> prints out its internal status. This has been
    -  a frequently requested feature.
    -  </p>
    -
    -  <p>The LogbackValve in logback-access (ensuring integration with
    -  Tomcat), will now systematically print its internal status upon
    -  initialization, unless told to be quiet. This greatly helps
    -  troubleshooting the configuration of logback-access under Tomcat.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=147">bug
    -  147</a> which occurred when the user inadvertently attempted to set
    -  the layout of a <code>SyslogAppender</code>.  The code now actively
    -  prevents this. Documentation has been updated to reflect the change.
    -  </p>
    -
    -  <p>Fixed <a href="http://jira.qos.ch/browse/LBCLASSIC-56">bug
    -  LBCLASSIC-56</a> originally reported by Michel Colette. Backslash
    -  characters are now correctly interpreted in filename patterns.
    -  </p>
    -
    -  <p>The TurboFilterChain in a LoggerContext is <a
    -  href="http://svn.qos.ch/viewvc?view=rev&amp;revision=1678">now
    -  cleared</a> when the <code>reset</code>() method is called. This
    -  problem was reported on May 1st, 2008, by Julia Hu on the logback
    -  developers list.
    -  </p>
    -
    -  <!-- ========================== LB CORE ================== -->
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-48">LBCORE-48</a>.  During
    -  rollover, compression of large files would bring all logging to a
    -  halt. In this latest release, the rolled over file is first renamed,
    -  which is a relatively fast operation, and only then is the renamed
    -  file compressed asynchronously (in a separate thread).
    -  </p>
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-11">LBCORE-11</a>.  It is now
    -  possible to instruct TimeBasedRollingPolicy to delete old files,
    -  thus controlling then number of archived log files.</p>
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-42">LBCORE-42</a>. If the
    -  parent directory of the log file does not exist, then FileAppender
    -  will create it, including any necessary but nonexistent parent
    -  directories.
    -  </p>
    -
    -  <p>Fixed <a href="http://jira.qos.ch/browse/LBCORE-15">LBCORE-15</a>
    -  reported by Klaus Unger and others. DBAppender should now work with
    -  Oracle 9, 10g and 11g, regardless of the JDBC driver type used.
    -  </p>
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-17">LBCORE-17</a> reported by
    -  Thorbj&oslash;rn Ravn Andersen. Logback now relies on Geronimo JMS
    -  API specifications instead of Sun's JMS API specification, the
    -  latter requiring manual installation. With Geronimo JMS, logback can
    -  be built using Maven without needing to manually install any
    -  dependencies.
    -  </p>
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-23">LBCORE-23</a>. In
    -  PatternLayout, parenthesis can be used to group conversion
    -  patterns. It follows that the '(' and ')' carry special meaning and
    -  need to be escaped to be used as literals. This is now properly
    -  documented.
    -  </p>
    -
    -  <p>The location of the default configuration file can be specified
    -  by a system property. This feature was requested in <a
    -  href="http://jira.qos.ch/browse/LBCORE-32">LBCORE-32</a> by Ted
    -  Graham, Matt Fowles, Ivan Biddles and Ralph Goers.
    -  </p>
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-55">LBCORE-55</a> reported by
    -  Natan Cox. <code>WriterAppender</code> now outputs its presentation
    -  footer and file footer in the the correct order.
    -  </p>
    -
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCORE-27">LBCORE-27</a> reported by
    -  Peter Royal. <code>SMTPAppender</code> now outputs its presentation
    -  footer and file footer in the the correct order.
    -  </p>
    -
    -  <!-- ========================== LB CLASSIC ================== -->
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCLASSIC-67">LBCLASSIC-67</a>
    -  reported by Alessandro Fustini. SMTPAppender now correctly
    -  configures the layout used to format the subject line of the
    -  outgoing email. It no longer appends a lengthy stack trace to the
    -  subject line.
    -  </p>
    -
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCLASSIC-68">LBCLASSIC-68</a>
    -  reported by Gili Tzabari. In environments where the TCCL (Thread
    -  Context Class Loader) was not set, logback was unable to located its
    -  default configuration files. Logback now uses the class loader that
    -  loaded logback itself to locate its resources instead of the
    -  TCCL. This approach is simpler and deemed to cover more
    -  environments, i.e. more widely applicable.
    -  </p>
    -
    -
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBGENERAL-24">LBGENERAL-24</a>
    -  reported by Hung Tang. SMTPAppender now supports plain
    -  username/password authentication as well as both the STARTTLS
    -  command and SSL connections.
    -  </p>
    -
    - 
    -  <p>Fixed issue <a
    -  href="http://jira.qos.ch/browse/LBCLASSIC-48">LBCLASSIC-48</a>
    -  reported by Peter Royal. SyslogAppender now correctly outputs hours
    -  as 0-23 and not as 1-12 as previously.
    -  </p>
    -
    -  <p>Added a new TurboFilter called DynamicThresholdFilter which can
    -  filter events depending on MDC values based on a variety of criteria
    -  such as company name, product or the end user. This filter was
    -  contributed by Ralph Goers in <a
    -  href="http://jira.qos.ch/browse/LBCLASSIC-53">LBCLASSIC-53</a>.  
    -  </p>
    -
    -  
    -  <hr width="80%" align="center" />
    - 
    -
    -
    -  <h3>26th of March 2008 - Release of version 0.9.9</h3>
    -  
    -  <p>MDC data in now inherited by child threads. This behaviour was
    -  already specified in the javadocs. The issue was raised by Martin
    -  Benda in <a href="http://bugzilla.qos.ch/show_bug.cgi?id=64">bug
    -  64</a> and independently by Peter Huber.
    -  </p>
    -  
    -  <p>Logback no longer includes retro-weaver generated jars for JDK
    -  1.4. There seems to be little interest in JDK 1.4 builds. Calling
    -  retro-weaver increases logback's build time by a few seconds each
    -  time &ndash; seconds in which we can do more productive things.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=104">bug
    -  104</a>, silly but but nonetheless serious copy-and-paste errors in
    -  the c.q.l.classic.Logger class, reported by Joern Huxhorn.
    -  </p>
    -
    -  <p>Having been replaced by <code>SimpleSocketServer</code>, the
    -  <code>SocketServer</code> class has been removed.
    -  </p>
    -  
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=105">bug
    -  105</a>, sockets created by <code>SocketAppenderBase</code> is now
    -  closed, reported by Joern Huxhorn. More generally,
    -  <code>SimpleSocketServer</code> is much more careful to track open
    -  client connections and to close them.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=110">bug
    -  110</a> in relation with the <code>requestParameterMap</code> field
    -  in <code>AccessEvent</code> - reported by Joern Huxhorn.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.slf4j.org/show_bug.cgi?id=66">
    -  SLF4J bug 66</a> in relation with caller data when using
    -  log4j-over-slf4j - reported by Frnack Routier.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=129">bug
    -  129</a> reported by Michael Franz. As a result, Joran now supports
    -  nested as well as multiple file inclusions.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=100">bug
    -  100</a> reported by Joern Huxhorn. At serialization time, object
    -  array passed as parameter, when the LoggingEvent is are now
    -  serialized as strings.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=109">bug
    -  109</a> reported by Joern Huxhorn. There should no longer be any
    -  NullPointerExceptions thrown by deserialized
    -  <code>AccessEvent</code> instances.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=8">bug
    -  8</a> reported by Sebastien Pennec. The documentation has been
    -  updated to reflect the fact that that in the context of conversion
    -  patterns the percent sign carries special meaning, in order to
    -  include the percent sign as a literal, it must be escaped with a
    -  backslash.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=52">bug
    -  52</a> reported by Kenichi Masuko. The bug has been fixed on March
    -  8th, 2007. Starting with this release, Joran will support the
    -  injection of any enum type, not just <code>FilterReply</code>.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -
    -
    -  <h3>22th of August 2007 - Release of version 0.9.8</h3>
    -  
    -  <p>This version of logback synchronizes with SLF4J version 1.4.3. In
    -  particular, logback now natively supports SLF4J's MDC API introduced
    -  in SLF4J version 1.4.1. If you are using SLF4J version 1.4.1 or
    -  later please make sure upgrade to logback version 0.9.8 or later.
    -  </p>
    -
    -  <p>Fixed a number of documentation related bugs, in particular <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=90">bug 90</a> reported
    -  by Luc Maisonobe and <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=90">bug 88</a> reported
    -  by Sebastian Davids.
    -  </p>
    -
    -  <p>It is now possible to include configuration file fragments (in
    -  XML) as a resource. Previously, it was only possible to include a
    -  file by specifying a path to a file or a URL.  This feature was
    -  requested by Michael Newcomb in <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=89">bug 89</a>.
    -  </p>
    -
    -  <p>Fixed caller data extraction problem as reported in <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=78">bug 78</a> by Hans
    -  van der Meer.
    -  </p>
    -
    -  <p>The LoggingEvent class' constructor now correctly takes into
    -  account the argument array passed by the user. This problem was
    -  reported in <a href="http://bugzilla.qos.ch/show_bug.cgi?id=85">bug
    -  85</a> by Robert Christian.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -
    -
    -  <h3>29th of May 2007 - Release of version 0.9.7</h3>
    -
    -
    -  <p>This release corrects packaging bugs <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=75">75</a> and <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=76">76</a> related to
    -  the migration of logback to SLF4J version 1.4.0. There are no other
    -  changes.
    -  </p>
    -
    -
    -  <hr width="80%" align="center" />
    -
    -  <h3>23rd of May 2007 - Release of version 0.9.6</h3>
    -
    -
    -  <p>Logback is now aligned and compatible with SLF4J version 1.4.0,
    -  thus correcting <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=73">bug 73</a> as
    -  reported by Andy Gerweck.
    -  </p>
    -
    -  <p>Fixed <code>NoClassDefFoundError</code> problem when running
    -  under JDK 1.4 <a
    -  href="http://www.qos.ch/pipermail/logback-user/2007-April/000206.html">as
    -  reported</a> by Brian Suksomwong.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=63">bug
    -  63</a> as reported by La Canea Rosario. Calling log4j (bridge) with
    -  the trace level will no longer cause an IllegalStateException to be
    -  thrown.
    -  </p>
    -
    -  <p>Fixed <a href="http://bugzilla.qos.ch/show_bug.cgi?id=70">bug
    -  70</a> as reported by Dirk Ooms. The %throwable conversion word is
    -  now recognized as documented in the logback manual. Moreover, the
    -  manual now mentions the %nopex word which can be used to force
    -  <code>PatternLayout</code> to ignore the exception contained in the
    -  logging request.
    -  </p>
    -
    -  
    -  <p>As in most releases, the documentation has been improved.</p>
    -
    -  <hr width="80%" align="center" />
    -
    -
    -  <h3>April 2nd, 2007 - Release of version 0.9.5</h3>
    -
    -  <p>Fixed methods <code>isInfoEnabled</code>,
    -  <code>isWarnEnabled</code> and <code>isErrorEnabled</code> methods
    -  in <code>ch.qos.logback.classic.Logger</code> class which failed to
    -  work correctly. This bug was reported today by Pavel Kral on the
    -  slf4j-user list. 
    -  </p>
    -
    -  <p>Contrary to previous versions of logback, the various
    -  Logger.isXYZEnabled(Marker) methods now take into account the marker
    -  information passed as parameter.
    -  </p>
    -
    -
    -  <p>As discussed in <a
    -  href="http://bugzilla.qos.ch/show_bug.cgi?id=54">bug 54</a>, during
    -  automatic initialization, it makes better sense to first check for
    -  <em>logback-test.xml</em> file and only if that fails, to check for
    -  <em>logback.xml</em>. Maven2 will guarantee that the
    -  logback-test.xml file, if places under test/resources will not be
    -  included in the artifact it produces.
    -  </p>
    -
    -
    -  <hr width="80%" align="center" />
    -  
    -  <h3>March 29th, 2007 - Release of version 0.9.4</h3>
    -    
    -  <p>Significant bug fixes made to <code>c.q.l.access.TeeFilter</code>
    -  and Co. Images and other binary files are now intercepted and
    -  replayed correctly. As for "x-www-form-urlencoded" post requests,
    -  their input buffer is left untouched. In a best-effort attempt, the
    -  input buffer for "x-www-form-urlencoded" post requests is later
    -  reconstructed through the request parameters. However, it may differ
    -  from the original buffer.
    -  </p>
    -    
    -  <p>The logback team released today the first version of a plugin for
    -  Eclipse that allows developers to visualize logs generated by a
    -  running application.  It offers several nice features. Please check
    -  the <a href="consolePlugin.html">console plugin-in guide</a> for
    -  more details.
    -  </p>
    -
    -
    -  <h3>March 20th, 2007 - Release of version 0.9.3</h3>
    -  
    -  <p>Includes in configuration files are now supported by Joran,
    -  logback's configuration framework. A file can contain an
    -  <em>include</em> element that has a <em>file</em> or <em>url</em>
    -  attribute pointing to a configuration file.  See the <a
    -  href="manual/configuration.html#fileInclusion">chapter about
    -  configuration</a> in the logback's online manual for more
    -  information.
    -  </p>
    -    
    -  <p>Corrected bug 53 reported by Wilkins Poe. There is now a <a
    -  href="dependencies.html">dependencies page</a> that shows the
    -  requirements of each of logback's modules.
    -  </p>
    -    
    -  <p>After a <a
    -  href="http://www.slf4j.org/pipermail/user/2007-March/000297.html">
    -  discussion on the SLF4J mailing list</a> started by Franck Routier,
    -  a correction has been made when logging using the
    -  <em>JCL104-over-slf4j</em> module. Logback now correctly shows the
    -  caller location information.
    -  </p>
    -    
    -  <p>As in most logback releases, the documentation has been improved.
    -  </p>
    -    
    -
    -  <h3>March 5th, 2007 - Release of version 0.9.2</h3>
    -  
    -  <p>The documentation is now in the <em>docs/</em> directory to allow an
    -  easier access to the logback manual and website for offline viewing.
    -  </p>
    -  
    -  <h3>March 5th, 2007 - Release of version 0.9.1</h3>
    -  
    -  <p>Logback-class now depends on SLF4J version 1.3.0 instead of
    -  1.2.</p>
    -  
    -  <p>Numerous improvements to the documentation.</p>
    -  
    -  <p><a href="http://bugzilla.qos.ch/show_bug.cgi?id=46">Bug #46</a>
    -  reported by Mark Renyolds has been fixed. The
    -  <code>TimeUtilTest</code> should now run fine under any time
    -  zone.</p>
    -		
    -  <p><a href="http://bugzilla.qos.ch/show_bug.cgi?id=45">Bug
    -  #45</a>, also reported by Mark Reynolds, has been fixed. There
    -  should be no <code>ClassCastException</code> thrown anymore when
    -  passing an <code>Object</code> to the printing methods using the
    -  log4j-bridge module. </p>
    -  
    -  <hr width="80%" align="center" />
    -  
    -  <h3>January 31st, 2007 - Release of version 0.9</h3>
    -  
    -  <p>This version contains a new component, namely the
    -  <code>ContextSelector</code>, that provides context separation and
    -  management when logback is used by several web-apps running under
    -  the same server. A <a href="manual/contextSelector.html">new
    -  chapter</a> was added to the logback manual to detail the use of the
    -  <code>ContextSelector</code>, along with its associated components.
    -  </p>
    -    
    -  <p>The <code>JMXConfigurator</code> has been improved. It now shows
    -  the context's Status objects, which lets users check the internal
    -  state of logback.
    -  </p>
    -    
    -  <p>The logback manual's chapter 2, about <a
    -  href="manual/architecture.html">logback's architecture</a>, has been
    -  updated with two sections: Under the hood and Performance.
    -  </p>
    -  
    -  <hr width="80%" align="center" />
    -  
    -  <h3>January 23th, 2007 - Release of version 0.8.1</h3>
    -  
    -  <p>This version contains new components in the Access module,
    -  allowing users to display the full HttpServletRequest or
    -  HttpServletResponse of an access event.
    -  </p>
    -    
    -  <p>The documentation section has been updated. The short
    -  introduction was split into the chapter 1 and chapter 2 of the
    -  logback manual. The chapters about Appenders and Layouts have been
    -  updated to document new components of logback.
    -  </p>
    -    
    -  <p>A demonstration webApp presenting logback's major components is
    -  available.  A document explains how to run it, and provides a
    -  step-by-step visit of the demo.
    -  </p>
    -  
    -  <p>A first translation of logback jars to JDK1.4 is present in
    -  this release.
    -  </p>
    -  
    -  
    -  <hr width="80%" align="center" />
    -  
    -  <h3>January 12th, 2007 - Release of version 0.8</h3>
    -  
    -  <p>This version contains a whole new chapter, namely Chapter 3,
    -  about logback configuration. Several other documentation pages
    -  have been improved.
    -  </p>
    -  
    -  <p>Logback now uses Generics in many components.
    -  </p>
    -  
    -  <p>Several new components have been added to logback. A JMX
    -  Configurator now allows users to see and modify loggers or reload
    -  configuration among other possibilities.  A <a
    -  href="jmxConfig.html">document</a> about this configurator is
    -  available in the <a href="documentation.html">corresponding
    -  section</a> of the site. We'd like to thank Sebastian Davids for his
    -  ideas and contributions to this component.
    -  </p>
    -		
    -  <p>A JMSTopicAppender and JMSQueueAppender are now available, as
    -  well as two new filters: LevelFilter and ThresholdFilter. A
    -  refactoring was done in the filters objects to ease the
    -  implementation of custom filters.
    -  </p>
    -		
    -  <hr width="80%" align="center" />
    -
    -		
    -  <h3>December 19th, 2006 - Release of version 0.7.1</h3>
    -  
    -  <p>Version 0.7.1 of logback has been released.
    -  </p>
    -  
    -  <p>This version contains more detailed information about logback
    -  access module, and its JMX components. A <a
    -  href="access.html">dedicated page</a> explains how to configure and
    -  use logback access in Tomcat and Jetty, and access some of its
    -  components via JMX.
    -  </p>
    -		
    -  <hr width="80%" align="center" />
    -  
    -  <h3>December 18th, 2006 - Release of version 0.7</h3>
    -  
    -  <p>Version 0.7 of logback has been released.</p>
    -		
    -  <p>Logback now ships with a new module: <em>log4j-bridge</em>. This
    -  new module can be used to intercept log4j calls and redirects them
    -  to logback components.  More information about this module can be
    -  found in the corresponding <a href="bridge.html">documentation
    -  page</a>.
    -  </p>
    -		
    -  <p>The documentation has been vastly updated. Two new chapters,
    -  namely Filters and MDC, are available in the manual section.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -  
    -  <h3>November 30th, 2006 - Release of version 0.6</h3>
    -  
    -  <p>Version 0.6 of logback has been released.
    -  </p>
    -  
    -  <p>Logback classic now supports automatic configuration, allowing
    -  test and production environment
    -  configuration. <code>TurboFilters</code> make their first appearance
    -  in a logback release. They provide ultra-fast filtering
    -  possibilities.  The logging context now supports listeners which
    -  will be contacted each time the context is reset or
    -  started. <code>SMTPAppender</code> allows for much more flexible
    -  configuration than before.
    -  </p>
    -		
    -  <p>In logback access, new Appenders are available, namely
    -  <code>SocketAppender</code> and <code>DBAppender</code>.  Logback
    -  access now supports filtering and event evaluations. A
    -  <code>CountingFilter</code> has been added. It provides statistical
    -  views of server access, reachable via JMX.
    -  </p>
    -		
    -  <p>The documentation has also been improved. A complete new chapter
    -  has been added about Appenders, the short introduction to logback
    -  classic has been updated and a new module, containing many
    -  configuration examples has been added.
    -  </p>
    -		
    -  <p>Logback now uses continuous integration in its development.
    -  </p>
    -		
    -  <p>Tests have been improved, many new have been added.  This release
    -  also provides some bug fixes.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -  
    -  <h3>October 26th, 2006 - Release of version 0.5</h3>
    -  
    -  <p>Version 0.5 of logback has been released.
    -  </p>
    -		
    -  <p>This release offers a important improvements in Joran. In
    -  particular, Joran can now replay configuration elements.
    -  </p>
    -
    -  <p>As in the previous release, a major area of work is the
    -  documentation which is being continuously improved.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -  
    -  <h3>October 9th, 2006 - Release of version 0.4</h3>
    -  
    -  <p>Version 0.3 of logback has been released.
    -  </p>
    -		
    -  <p>This release includes an improved access module, with specific
    -  implementations for the Jetty and Tomcat servers. Documentation was
    -  also added to show how to integrate logback-access with Jetty.
    -  </p>
    -
    -  <p>As for the classic module, several appenders and layouts have
    -  been added or improved.  The error reporting of logback has also
    -  been enhanced, presenting the user with a link to an online page
    -  explaining possible reasons for the error.
    -  </p>
    -
    -  <p>Joran documentation was added, with examples in the core
    -  module.
    -  </p>
    -	
    -  <hr width="80%" align="center" />
    -  
    -  <h3>September 8th, 2006 - Release of version 0.3</h3>
    -  <p>
    -    Version 0.3 of logback has been released.
    -  </p>
    -  
    -  <p>This release offers several new Appenders, support for Mapped
    -  Diagnostic Context, improved tests and documentation<br />
    -  </p>
    -
    -  <p>In response to a bug report by Rickard Nilsson on the logback
    -  mailing list, a bug affecting parametrized logging was fixed.
    -  </p>
    -
    -  <p>We also released a <a
    -  href="http://logback.qos.ch/translator/">PropertiesTranslator</a>
    -  webapp that converts <em>log4j.properties</em> files to joran
    -  configuration files (in XML format).<br />
    -  </p>
    -	
    -  <hr width="80%" align="center" />
    -		
    -  <h3>August 23th, 2006 - Release of version 0.2.5</h3>
    -  
    -  <p> Version 0.2.5 of logback has been released. </p>
    -
    -  <p>This release offers better documentation, with a number of
    -  corrections made in the short introduction to logback-classic.
    -  </p>
    -  
    -  <hr width="80%" align="center" />
    -  
    -  
    -  <h3>August 15th, 2006 - Release of version 0.2</h3>
    -  
    -  <p>Version 0.2 of logback has been released.</p>
    -
    -  <p>It offers better tests, some more functionality, and enhanced
    -  documentation.  We also improved the site design to make it simpler
    -  and more efficient.
    -  </p>
    -
    -  <hr width="80%" align="center" />
    -  
    -  <h3>July 26th, 2006 - Release of version 0.1</h3>
    -  
    -  <p>Version 0.1 of logback has been released.</p>
    -  
    -  <hr width="80%" align="center" />
    -  
    -  <h3>February 9th, 2006 - Logback web-site goes live</h3>
    -  
    -  <p>The logback web-site goes live on the 9th of February. At its
    -  present state, it is pretty primitive but updates will follow.
    -  </p>
    -  
    -	
    -  <script src="templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/reasonsToSwitch.html b/logback-site/src/site/pages/reasonsToSwitch.html
    deleted file mode 100755
    index 7199a931f5..0000000000
    --- a/logback-site/src/site/pages/reasonsToSwitch.html
    +++ /dev/null
    @@ -1,283 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Reasons to prefer logback</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -
    -  </head>
    -  <body onload="decorate();">
    -    <script type="text/javascript">prefix='';</script>
    -    <script src="templates/header.js" type="text/javascript"></script>
    -    <script type="text/javascript" src="js/jquery-min.js"></script>
    -    <script type="text/javascript" src="js/decorator.js"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="right">
    -      <script src="templates/right.js" type="text/javascript"></script>
    -    </div>
    -
    -    <div id="content">
    -		
    -		<h2>Reasons to prefer logback over log4j</h2>
    -
    -    <p>Logback brings a very large number of improvements over log4j,
    -    big and small. They are too many to enumerate exhaustively.
    -    Nevertheless, here is a non-exhaustive list of reasons for
    -    switching to logback from log4j. Keep in mind that logback is
    -    conceptually very similar to log4j as both projects were founded
    -    by the same developer. If you are already familiar with log4j, you
    -    will quickly feel at home using logback. If you like log4j, you
    -    will probably love logback.</p>
    -
    -
    -    <h3 class="doAnchor" name="fasterImpl">Faster implementation</h3>
    -
    -    <p>Based on our previous work on log4j, logback internals have
    -    been re-written to perform about ten times faster on certain
    -    critical execution paths. Not only are logback components faster,
    -    they have a smaller memory footprint as well.</p>
    -
    -    <h3 class="doAnchor" name="tdd">Extensive battery of tests</h3>
    -
    -    <p>Logback comes with a very extensive battery of tests developed
    -    over the course of several years and untold hours of work. While
    -    log4j is also tested, logback takes testing to a completely
    -    different level. In our opinion, this is the single most important
    -    reason to prefer logback over log4j.  You want your logging
    -    framework to be rock solid and dependable even under adverse
    -    conditions.
    -    </p>
    -    
    -    <h3 class="doAnchor" name="slf4j">logback-classic speaks SLF4J
    -    natively</h3>
    -
    -    <p>Since the <code>Logger</code> class in logback-classic
    -    implements the SLF4J API natively, you incur zero overhead when
    -    invoking an SLF4J logger with logback-classic as the underlying
    -    implementation. Moreover, since logback-classic strongly
    -    encourages the use of SLF4J as its client API, if you need to
    -    switch to log4j or to j.u.l., you can do so by replacing one jar
    -    file with another. You will not need to touch your code logging
    -    via the SLF4J API. This can drastically reduce the work involved
    -    in switching logging frameworks.
    -    </p>
    -    
    -    <h3 class="doAnchor" name="docs">Extensive documentation</h3>
    -    
    -    <p>Logback ships with detailed and constantly updated
    -    documentation.</p>
    -
    -    <h3 class="doAnchor" name="DSL">Configuration files in XML or
    -    Groovy</h3>
    -
    -    <p>The traditional way of configuring logback is via an XML
    -    file. Most of the examples in the documentation use this XML
    -    syntax. However, as of logback version 0.9.22, <a
    -    href="manual/groovy.html">configuration files written in
    -    Groovy</a> are also supported. Compared to XML, Groovy-style
    -    configuration is more intuitive, consistent and has a shorter
    -    syntax.
    -    </p>
    -
    -    <p>
    -    There is also a <a
    -    href="http://logback.qos.ch/translator/asGroovy.html">tool to
    -    automatically migrate your logback.xml files to
    -    logback.groovy</a>.
    -    </p>
    -
    -    <h3 class="doAnchor" name="autoScan">Automatic reloading of
    -    configuration files</h3>
    -
    -    <p>Logback-classic can <a
    -    href="manual/configuration.html#autoScan">automatically reload its
    -    configuration file upon modification</a>. The scanning process is
    -    fast, contention-free, and dynamically scales to millions of
    -    invocations per second spread over hundreds of threads. It also
    -    plays well within application servers and more generally within
    -    the JEE environment as it does <em>not</em> involve the creation
    -    of a separate thread for scanning.
    -   </p>
    -
    -    <h3 class="doAnchor" name="grace">Graceful recovery from I/O
    -    failures</h3>
    -
    -    <p>Logback's <code>FileAppender</code> and all its sub-classes,
    -    including <code>RollingFileAppender</code>, can gracefully recover
    -    from I/O failures. Thus, if a file server fails temporarily, you
    -    no longer need to restart your application just to get logging
    -    working again. As soon as the file server comes back up, the
    -    relevant logback appender will transparently and quickly recover
    -    from the previous error condition.
    -    </p>
    -    
    -    <h3 class="doAnchor" name="maxHistory">Automatic removal of old
    -    log archives</h3>
    -
    -    <p>By setting the <span class="option">maxHistory</span> property
    -    of <a
    -    href="manual/appenders.html#TimeBasedRollingPolicy">TimeBasedRollingPolicy</a>
    -    or <a
    -    href="manual/appenders.html#SizeAndTimeBasedFNATP">SizeAndTimeBasedFNATP</a>,
    -    you can control the maximum number of archived files. If your
    -    rolling policy calls for monthly rollover and you wish to keep one
    -    year's worth of logs, simply set the <span
    -    class="option">maxHistory</span> property to 12. Archived log
    -    files older than 12 months will be automatically removed.
    -    </p>
    -
    -    <h3 class="doAnchor" name="compression">Automatic compression of
    -    archived log files</h3>
    -
    -    <p><a
    -    href="manual/appenders.html#RollingFileAppender">RollingFileAppender</a>
    -    can automatically compress archived log files during
    -    rollover. Compression always occurs asynchronously so that even
    -    for large log files, your application is not blocked for the
    -    duration of the compression.
    -    </p>
    -
    -    <h3 class="doAnchor" name="prudent">Prudent mode</h3>
    -
    -    <p>In <a href="manual/appenders.html#prudent">prudent mode</a>,
    -    multiple <code>FileAppender</code> instances running on multiple
    -    JVMs can safely write to the same log file. With certain
    -    limitations, prudent mode extends to
    -    <code>RollingFileAppender</code>.
    -    </p>
    -
    -    <h3 class="doAnchor" name="lilith">Lilith</h3>
    -
    -    <p><a href="http://lilith.huxhorn.de/">Lilith</a> is a logging and
    -    access event viewer for logback. It is comparable to log4j's
    -    chainsaw, except that Lilith is designed to handle large amounts of
    -    logging data without flinching.</p>
    -  
    -    <h3 class="doAnchor" name="conditional">Conditional processing of
    -    configuration files</h3>
    -
    -    <p>Developers often need to juggle between several logback
    -    configuration files targeting different environments such as
    -    development, testing and production. These configuration files
    -    have substantial parts in common, differing only in a few
    -    places. To avoid duplication, logback supports <a
    -    href="manual/configuration.html#conditional">conditional
    -    processing of configuration files</a> with the help of
    -    <code>&lt;if></code>, <code>&lt;then></code> and
    -    <code>&lt;else></code> elements so that a single configuration
    -    file can adequately target several environments.
    -    </p>
    -
    -
    -    <h3 class="doAnchor" name="filters">Filters</h3>
    -
    -    <p>Logback comes with a wide array of <a
    -    href="manual/filters.html">filtering capabilities</a> going much
    -    further than what log4j has to offer. For example, let's assume
    -    that you have a business-critical application deployed on a
    -    production server.  Given the large volume of transactions
    -    processed, logging level is set to WARN so that only warnings and
    -    errors are logged. Now imagine that you are confronted with a bug
    -    that can be reproduced on the production system but remains
    -    elusive on the test platform due to unspecified differences
    -    between those two environments (production/testing).
    -    </p>
    -
    -    <p>With log4j, your only choice is to lower the logging level to
    -    DEBUG on the production system in an attempt to identify the
    -    problem. Unfortunately, this will generate large volume of logging
    -    data, making analysis difficult. More importantly, extensive
    -    logging can impact the performance of your application on the
    -    production system.</p>
    -
    -    <p>With logback, you have the option of keeping logging at the
    -    WARN level for all users except for the one user, say Alice, who
    -    is responsible for identifying the problem. When Alice is logged
    -    on, she will be logging at level DEBUG while other users can
    -    continue to log at the WARN level. This feat can be accomplished
    -    by adding 4 lines of XML to your configuration file. Search for
    -    <code>MDCFilter</code> in the <a
    -    href="manual/filters.html#TurboFilter">relevant section</a> of the
    -    manual.
    -    </p>
    -    
    -
    -    <h3 class="doAnchor" name="sift">SiftingAppender</h3>
    -    
    -    <p><a
    -    href="manual/appenders.html#SiftingAppender">SiftingAppender</a>
    -    is an amazingly versatile appender. It can be used to separate (or
    -    sift) logging according to <b>any</b> given runtime attribute. For
    -    example, <code>SiftingAppender</code> can separate logging events
    -    according to user sessions, so that the logs generated by each
    -    user go into distinct log files, one log file per user.
    -    </p>
    -    
    -    <h3 class="doAnchor" name="packagingData">Stack traces with
    -    packaging data</h3>
    -
    -    <p>When logback prints an exception, the stack trace will include
    -    packaging data. Here is a sample stack trace generated by the <a
    -    href="demo.html">logback-demo</a> web-application.</p>
    -
    -    <pre>14:28:48.835 [btpool0-7] INFO  c.q.l.demo.prime.PrimeAction - 99 is not a valid value
    -java.lang.Exception: 99 is invalid
    -  at ch.qos.logback.demo.prime.PrimeAction.execute(PrimeAction.java:28) [classes/:na]
    -  at org.apache.struts.action.RequestProcessor.processActionPerform(RequestProcessor.java:431) [struts-1.2.9.jar:1.2.9]
    -  at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:236) [struts-1.2.9.jar:1.2.9]
    -  at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:432) [struts-1.2.9.jar:1.2.9]
    -  at javax.servlet.http.HttpServlet.service(HttpServlet.java:820) [servlet-api-2.5-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:502) [jetty-6.1.12.jar:6.1.12]
    -  at ch.qos.logback.demo.UserServletFilter.doFilter(UserServletFilter.java:44) [classes/:na]
    -  at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1115) [jetty-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:361) [jetty-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:417) [jetty-6.1.12.jar:6.1.12]
    -  at org.mortbay.jetty.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:230) [jetty-6.1.12.jar:6.1.12]</pre>
    -
    -    <p>From the above, you can recognize that the application is using
    -    Struts version 1.2.9 and was deployed under jetty version
    -    6.1.12. Thus, stack traces will quickly inform the reader about
    -    the classes intervening in the exception but also the package and
    -    package versions they belong to. When your customers send you a
    -    stack trace, as a developer you will no longer need to ask them
    -    to send you information about the versions of packages they are
    -    using. The information will be part of the stack trace. See <a
    -    href="manual/layouts.html#xThrowable">"%xThrowable" conversion
    -    word</a> for details.
    -    </p>
    -
    -    <p>This feature can be quite helpful to the point that some users
    -    mistakenly consider it a <a
    -    href="http://www.jetbrains.net/devnet/message/5259058">feature of
    -    their IDE</a>.
    -    </p>
    -
    -    <h3 class="doAnchor" name="logbackAccess">Logback-access,
    -    i.e. HTTP-access logging with brains, is an integral part of
    -    logback</h3>
    -
    -    <p>Last but not least, the logback-access module, part of the
    -    logback distribution, integrates with Servlet containers such as
    -    Jetty or Tomcat to provide rich and powerful HTTP-access log
    -    functionality. Since logback-access was part of the initial
    -    design, all the logback-classic features you love are
    -    available in logback-access as well.</p>
    -
    -    <h3 class="doAnchor" name="inSummary">In summary</h3>
    -
    -    <p>We have listed a number of reasons for preferring logback over
    -    log4j. Given that logback builds upon on our previous work on
    -    log4j, simply put, logback is just a better log4j.</p>
    -
    -    <script src="templates/footer.js" type="text/javascript"></script>	   
    -    </div>
    -
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/recipes/captureHttp.html b/logback-site/src/site/pages/recipes/captureHttp.html
    deleted file mode 100644
    index 35cd684fad..0000000000
    --- a/logback-site/src/site/pages/recipes/captureHttp.html
    +++ /dev/null
    @@ -1,260 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Capturing HTTP communications</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -
    -  </head>
    -  <body onload="prettyPrint()">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -      <h2>Capturing incoming HTTP requests and outgoing responses</h2>
    -
    -      <p>Logback ships with a module called logback-access which
    -      integrates with Servlet containers such as Jetty or Tomcat to
    -      provide rich and powerful HTTP-access log functionality.
    -      </p>
    -
    -      <p>When diagnosing issues in a web-application, it is very
    -      helpful to be able to capture incoming HTTP requests and the
    -      outgoing response. This is particularly true for a
    -      web-applications offering web-services using <a
    -      href="http://en.wikipedia.org/wiki/SOAP">SOAP</a>. Having access
    -      to the "raw" SOAP message makes it much easier to diagnose
    -      errors caused by misspelled namespaces or missing fields.
    -      </p>
    -
    -      <p>Certain Web-Service stacks can be easily configured to log
    -      SOAP traffic. For instance, with JBoss' Web-services stack, you
    -      can set the level of the
    -      <code>org.jboss.ws.core.MessageTrace</code> logger to
    -      <code>TRACE</code> in order to enable SOAP message tracing as <a
    -      href="http://community.jboss.org/wiki/JBossWS-Log4j">documented
    -      in the Jboss wiki</a>.
    -      </p>
    -
    -      <p>If you are using a different Web-Services stack, e.g. Metro,
    -      it might not be very convenient to enable SOAP message
    -      tracing. In that case, and assuming your application is deployed
    -      on Tomcat, you might want to enable <a
    -      href="http://tomcat.apache.org/tomcat-7.0-doc/config/filter.html#Request_Dumper_Filter">RequestDumperFilter</a>.
    -      Unfortunately, not only is <code>RequestDumperFilter</code>
    -      rather hard to install, it does not dump the payload of the
    -      incoming request nor the outgoing response.
    -      </p>
    -
    -      <p>With the help of <a href="../access.html#teeFilter">
    -      logback-access its TeeFilter</a> you can capture the full input
    -      and output for each request as explained below.
    -  
    -      </p>
    -
    -      <h3><a name="capturing" href="#capturing">Capturing</a></h3>
    -
    -      <p>The <code>TeeFilter</code>, as any other servlet filter,
    -      needs to be declared in your web-application's <em>web.xml</em>
    -      file. Here is the declaration to add to your web-application's
    -      <em>web.xml</em> file.
    -      </p>
    -
    -    <pre class="prettyprint source">&lt;filter&gt;
    -  &lt;filter-name&gt;TeeFilter&lt;/filter-name&gt;
    -  &lt;filter-class&gt;ch.qos.logback.access.servlet.TeeFilter&lt;/filter-class&gt;
    -&lt;/filter&gt;
    -
    -&lt;filter-mapping&gt;
    -  &lt;filter-name&gt;TeeFilter&lt;/filter-name&gt;
    -  &lt;url-pattern&gt;/*&lt;/url-pattern&gt;
    -&lt;/filter-mapping&gt;</pre>
    -
    -      <p>The <code>TeeFilter</code> requires the logback-access module
    -      to be installed in your web-server. The installation of
    -      logback-access is explained <a
    -      href="../access.html">elsewhere</a>. Once you have installed
    -      logback-access into your Servlet container, e.g. Tomcat or
    -      Jetty, you can configure logback-access according to your wishes
    -      with the help of a configuration file named
    -      <em>logback-access.xml</em>.
    -      </p>
    -      
    -      <p>Here is a sample <em>logback-access.xml</em> configuration
    -      file which will output the full contents of the request and
    -      response on the console.
    -      </p>
    -
    -  <pre class="prettyprint source">&lt;configuration&gt;
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"&gt;
    -    &lt;encoder&gt;      
    -      &lt;pattern&gt;<b>%fullRequest</b>%n%n<b>%fullResponse</b>&lt;/pattern&gt;
    -    &lt;/encoder&gt;
    -  &lt;/appender&gt;
    -	
    -  &lt;appender-ref ref="STDOUT" /&gt;
    -&lt;/configuration&gt;</pre>
    -      
    -     <p>For the list of conversion words supported by logback-access'
    -     <code>PatternLayout</code> please refer to <a
    -     href="manual/layouts.html#AccessPatternLayout">its documentation</a>.
    -     </p>
    -
    -     <p>Here is the output generated when accessing the <a
    -     href="../demo.html">logback-demo</a> application configured as shown
    -     above, yields:</p>
    -
    -     <p class="source"><b>GET /logback-demo/index.jsp HTTP/1.1</b>
    -Host: localhost:8080
    -User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8) Gecko/20070312 Firefox/1.5.0
    -Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8
    -Accept-Language: en-us,en;q=0.5
    -Accept-Encoding: gzip,deflate
    -Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
    -Keep-Alive: 300
    -Connection: keep-alive
    -Referer: http://localhost:8080/logback-demo/login.jsp
    -Cookie: JSESSIONID=15c7tqi9ehlwk;  OID324nkzcmr=null; OID32862zgoa=null; 
    -
    -
    -
    -<b>HTTP/1.1 200 OK</b>
    -Content-Type: text/html; charset=iso-8859-1
    -Expires: Thu, 01 Jan 1970 00:00:00 GMT
    -Set-Cookie: JSESSIONID=bgebt99ce9om;path=/logback-demo
    -
    -
    -&lt;html&gt;
    -&lt;head&gt;
    -        &lt;LINK REL=StyleSheet HREF="css/pk.css" /&gt;
    -&lt;/head&gt;
    -&lt;body&gt;
    -
    -&lt;h2&gt;Logback demo center&lt;/h2&gt;
    -
    -[snip, so that text is reasonably sized]</p>
    -
    -<p>&nbsp;</p>
    -
    -      <h3><a name="disabling" href="#disabling">Disabling
    -      <code>TeeFilter</code> in the production environment</a></h3>
    -
    -      <p>Due to its intrusive nature, <code>TeeFilter</code> can slow
    -      down performance. Moreover, although we have fixed all currently
    -      known bugs, <code>TeeFilter</code> has broken otherwise
    -      correctly behaving applications in the past. Thus, while
    -      extremely useful during problem hunting, we do not recommend
    -      having <code>TeeFilter</code> active in production systems. In
    -      order to avoid shipping different code for test and production
    -      environments, <code>TeeFilter</code> supports includes and
    -      excludes parameters. <code>TeeFilter</code> will be active if
    -      the current host is listed in the includes list and absent in
    -      the excludes list. By special convention, an empty
    -      <em>includes</em> list is interpreted as to contain all possible
    -      host names in the universe.
    -      </p>
    -      
    -      <p>Assume we wish to capture HTTP traffic on all hosts except on
    -      orion and gemini, the hostnames of the the production systems,
    -      we would write:</p>
    -
    -        <pre class="prettyprint source">&lt;filter>
    -  &lt;filter-name>TeeFilter&lt;/filter-name>
    -  &lt;filter-class>ch.qos.logback.access.servlet.TeeFilter&lt;/filter-class>
    -  &lt;init-param>
    -    <b>&lt;!-- exclude captures on production systems --></b>
    -    &lt;param-name><b>excludes</b>&lt;/param-name>
    -    &lt;param-value>orion, gemini&lt;/param-value>
    -  &lt;/init-param>
    -&lt;/filter>  </pre>
    -  
    -      <p>If it is easier to explicitly name the integration machines,
    -      you could list them in the includes list and omit the excludes
    -      list.
    -      </p>
    -
    -      <h3><a name="filtering" href="#filtering">Filtering captured
    -      requests</a></h3>
    -
    -      <p>Let assume that our web-application is deployed in a
    -      cluster. The cluster is located behind a load-balancer which
    -      probes each member of the cluster once or twice a second to
    -      check whether it is alive. Whenever a member becomes unavailable
    -      the load-balancer will immediately (well, after at most one
    -      second) divert traffic from that member.
    -      </p>
    -
    -      <p>While such a load-balancing strategy will ensure
    -      high-availability of your web-application, it will also
    -      seriously pollute the access-logs with the contents of each
    -      probe made by the load-balancer.
    -      </p>
    -
    -      <p>We need to a way to filter-out these probes so that they no
    -      longer contaminate the log output. In other words, we need to
    -      distinguish probes emanating from the load-balancer from other
    -      requests. The contents of the probes can give us clues about
    -      possible distinguishing criteria.</p>
    -
    -      <p>Here is a sample probe as logged by logback-access:</p>
    -
    -      <pre class="source"><b>HEAD</b> /myapp/<b>probe</b> HTTP/1.1
    -connection: Close
    -host: 192.168.1.1
    -
    -HTTP/1.1 200 OK
    -Expires: Thu, 01 Jan 1970 01:00:00 CET
    -X-Powered-By: Servlet 2.4;
    -Cache-Control: no-cache
    -Pragma: No-cache </pre>
    -
    -      <p>From the above, we can see that the load-balancer probes
    -      employ the <a
    -      href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.4">HTTP
    -      HEAD method</a> instead of the usual GET or POST. We can also
    -      see that the request URI for the probes contains the string
    -      "probe".</p>
    -
    -      <p>Here is a <em>logback-access.xml</em> configuration file
    -      which will deny any <code>AccessEvent</code> where the method is
    -      HEAD. Note that events are evaluated using
    -      <code>JaninoEventEvaluator</code> which requires Janino.</p>
    -
    -      <pre class="prettyprint source">&lt;configuration>
    -  &lt;!-- always a good idea to install OnConsoleStatusListener -->
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -
    -  &lt;appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    -    &lt;filter class="ch.qos.logback.core.filter.EvaluatorFilter">
    -      &lt;!-- in the absence of a class attribute the &lt;evaluator> element 
    -           defaults to ch.qos.logback.access.boolex.JaninoEventEvaluator --&gt;
    -      &lt;evaluator>
    -        &lt;expression><b>event.getMethod().equals("HEAD")</b>&lt;/expression>
    -      &lt;/evaluator>
    -      &lt;onMismatch>NEUTRAL&lt;/onMismatch>
    -      &lt;onMatch>DENY&lt;/onMatch>
    -    &lt;/filter>
    -
    -    &lt;encoder>
    -      &lt;pattern>%fullRequest%n%n%fullResponse&lt;/pattern>
    -    &lt;/encoder>
    -  &lt;/appender>
    -
    -  &lt;appender-ref ref="STDOUT" />
    -&lt;/configuration> </pre>
    -      
    -      <script src="../templates/footer.js" type="text/javascript"></script>	
    -    </div>
    -  </body>
    -</html>
    - 
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/recipes/emailPerTransaction.html b/logback-site/src/site/pages/recipes/emailPerTransaction.html
    deleted file mode 100644
    index 9b1989503e..0000000000
    --- a/logback-site/src/site/pages/recipes/emailPerTransaction.html
    +++ /dev/null
    @@ -1,445 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Email per transaction</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="../css/prettify.css" media="screen" />
    -
    -  </head>
    -  <body onload="prettyPrint()">
    -    <script type="text/javascript">prefix='../';</script>
    -    <script type="text/javascript" src="../js/prettify.js"></script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -      <h2>Triggering an email containing the isolated logs of selected
    -      transactions</h2>
    -      
    -      <p>Let Dave and Carol be software QA engineers working at a
    -      company called Fooware.com. As you might have guessed,
    -      Fooware.com sells foos. Yes, foos... Let <em>Buscrit</em> be a
    -      business critical backend system running at Fooware.com. Buscrit
    -      is called by a number of applications to make business-critical
    -      transactions of various types.
    -      </p>
    -
    -      <p>We would like to allow Carol to access the logging data
    -      generated by Buscrit as conveniently as possible. We could
    -      assume that Carol has access the log files directly on the
    -      server where Buscrit runs. However, let us assume that accessing
    -      the log files is somehow impractical because one or ore more of
    -      the following conditions holds true:
    -      </p>
    -
    -      <ol>
    -        <li>Buscrit runs on multiple hosts and it is difficult to
    -        identify the host where a particular transaction was
    -        executed</li>
    -        <li>Carol does not (or does not wish to) have access to the
    -        hosts where Buscrit runs
    -        </li>
    -        <li>Buscrit is tested by multiple testers, e.g. Dave and Carol
    -        and others, simultaneously so that it is hard to identify and
    -        track an individual transaction in the log files
    -        </li>
    -      </ol>
    -
    -      <p>Given the above circumstances, let us create a logback
    -      configuration so that Carol receives an email message at the end
    -      of every transaction. We will iteratively refine this
    -      configuration so that Carol will receive an email containing the
    -      logs of each transaction in isolation and only for the
    -      transactions she explicitly selects.
    -      </p>
    -
    -      <h3>Triggering an email message at the end of each transaction</h3>
    -
    -      <p>We will be using <code>SMTPAppender</code> to generate emails
    -      containing logging data. Please refer to the <a
    -      href="../manual/appenders.html#SMTPAppender">appropriate section
    -      of the manual</a> to familiarize yourself with
    -      <code>SMTPAppender</code> and its properties.
    -      </p> 
    -
    -      <p>The <a href="demo.html">logback-demo</a> project contains a
    -      Struts action called <a
    -      href="http://logback-demo.qos.ch/xref/ch/qos/logback/demo/prime/PrimeAction.html"><code>PrimeAction</code></a>. It
    -      can factorize integers. Here is the pertinent structure of
    -      <code>PrimeAction</code>'s code:</p>
    -
    -      <pre class="prettyprint source">package ch.qos.logback.demo.prime;
    -
    -import org.apache.struts.action.Action;
    -...
    -import javax.servlet.http.HttpServletRequest;
    -import javax.servlet.http.HttpServletResponse;
    -
    -public class PrimeAction extends Action {
    -
    -  Logger logger = LoggerFactory.getLogger(PrimeAction.class);
    -  static Marker SMTP_TRIGGER = MarkerFactory.getMarker("SMTP_TRIGGER");
    -
    -  public ActionForward execute(ActionMapping actionMapping,
    -                               ActionForm actionForm, HttpServletRequest request,
    -                               HttpServletResponse response) throws Exception {
    -
    -    PrimeForm form = (PrimeForm) actionForm;
    -
    -    Long number = form.getNumber();
    -    try {
    -      NumberCruncher nc = new NumberCruncherImpl();
    -      Long start = System.currentTimeMillis();
    -      Long[] result = nc.factor(number);
    -      Long duration = System.currentTimeMillis() - start;
    -      logger.info("Results computed in {} ms", duration);
    -
    -      ...
    -    } finally {
    -      <b>logger.info(SMTP_TRIGGER, "Prime computation ended");</b>
    -    }
    -  }
    -} </pre>
    -
    -      <p>In a real world application, a transaction would involve
    -      systems external to the application, e.g. a database or a
    -      messaging queue. For the sake of this example, let us consider
    -      each factorization request as a <em>transaction</em>.  At the
    -      end of each factorization request, i.e. each transaction, the
    -      logger of the <code>PrimeAction</code> instance is invoked with
    -      the SMTP_TRIGGER marker and the message "Prime computation
    -      ended".  We can capitalize on this logging request to clearly
    -      identify the end of every transaction in order to trigger an
    -      outgoing email message.
    -     </p>
    -
    -     <p>Here is a configuration file which uses
    -     <code>JaninoEventEvaluator</code> to trigger an outgoing email
    -     for logging event marked with SMTP_TRIGGER.
    -     </p> 
    -
    -    <pre class="prettyprint
    -     source">&lt;configuration scan="true" scanPeriod="3 seconds">
    -
    -  &lt;!-- always a good idea to have an OnConsoleStatusListener -->
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -
    -  &lt;appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>NAME_OF_SMTP_HOST&lt;/smtpHost>
    -    &lt;to>...&lt;/to>                                         
    -    &lt;from>...&lt;/from>
    -    &lt;subject>Prime - %mdc{number} by %mdc{userid} &lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -      &lt;pattern>%date%level%logger{24}%msg&lt;/pattern>
    -    &lt;/layout>
    -    
    -    <b>&lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator"></b>
    -      <b>&lt;expression></b>
    -        <b>marker != null  &amp;&amp; marker.contains("SMTP_TRIGGER")</b>
    -      <b>&lt;/expression></b>
    -    <b>&lt;/evaluator></b>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SMTP" /> 
    -  &lt;/root>
    -
    -&lt;/configuration></pre>
    -
    -      
    -
    -   <h3>Transaction isolation</h3>
    -
    -   <p>While the previous configuration file will trigger an outgoing
    -   email message whenever an event is marked with "SMTP_TRIGGER", the
    -   contents of the message will contain events generated by different
    -   transactions. With a little effort, we can actually separate events
    -   belonging to different transactions so that the outgoing email
    -   triggered at the end of the transaction contains logs from that
    -   transaction and only that transaction.
    -   </p>
    -
    -  <p>To isolate a given transaction, there must first be a way to
    -  distinguish it from other transactions. Typically this would be
    -  accomplished by putting the unique identifier of the transaction
    -  into the MDC.
    -  </p>
    -
    -  <pre class="prettyprint source">String transactionId = ...; // extract id from transaction 
    -MDC.put("txId", transactionId); </pre>
    -
    -  <p>In the <a
    -  href="http://logback-demo.qos.ch/xref/ch/qos/logback/demo/UserServletFilter.html"><code>UserServletFilter</code></a>
    -  class, this is done by retrieving the id of the session and putting
    -  it into the MDC under the key "txId".</p>
    -
    -
    -    <pre class="prettyprint source">public class UserServletFilter implements Filter {
    -
    -   public void doFilter(ServletRequest request, ServletResponse response,
    -                       FilterChain chain) throws IOException, ServletException {
    -
    -    HttpServletRequest req = (HttpServletRequest) request;
    -    HttpSession session = req.getSession();
    -    MDC.put("txId", session.getId());
    -    ...
    -    try {
    -      // invoke subsequent filters
    -      chain.doFilter(request, response);
    -    } finally {
    -      // always clear the MDC at the end of the request
    -      MDC.clear();
    -    }
    -  }
    -}</pre>
    -
    -
    -  <p>By setting an appropriate discriminator in SMTPAppender, you can
    -  can scatter incoming events into different buffers according to the
    -  value returned by the discriminator. Given that each request session
    -  is placed under the MDC key "txId", we can use an MDC-based
    -  discriminator to isolate the logs generated by each transaction.
    -  </p>
    -
    -  <pre class="prettyprint source">&lt;configuration scan="true" scanPeriod="3 seconds">
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
    -
    -  &lt;appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;smtpHost>NAME_OF_SMTP_HOST&lt;/smtpHost>
    -    &lt;to>...&lt;/to>                                         
    -    &lt;from>...&lt;/from>
    -
    -    &lt;smtpHost>NAME_OF_SMTP_HOST&lt;/smtpHost>
    -    &lt;to>name@some.smtp.host&lt;/to>
    -    &lt;from>testing@...&lt;/from>
    -    &lt;subject>Prime - %mdc{number} by %mdc{userid} &lt;/subject>
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -       &lt;pattern>%date%level%logger{24}%msg&lt;/pattern>
    -    &lt;/layout>
    -
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
    -      &lt;expression>
    -        marker != null &amp;&amp; marker.contains("SMTP_TRIGGER") 
    -      &lt;/expression>
    -    &lt;/evaluator>
    -
    -    <b>&lt;discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator"></b>
    -      <b>&lt;key>txId&lt;/key></b>
    -      <b>&lt;defaultValue>default&lt;/defaultValue></b>
    -    <b>&lt;/discriminator></b>
    -  &lt;/appender>  
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SMTP" /> 
    -  &lt;/root>
    -&lt;/configuration> </pre>
    -
    -   <p>After starting the logback-demo web-application with the above
    -   configuration file (with <span class="option">smtpHost</span> and
    -   <span class="option">to</span> options adapted for my environment)
    -   on localhost and then visiting the <a
    -   href="http://localhost:8070/logback-demo/prime.jsp">Prime
    -   number</a> page to factorize the number 123, I received the
    -   following email:
    -   </p>
    -
    -   <img src="images/factorEmail0.png" alt="selective email0"/>
    -      
    -   <p>Note that the above email contains the logs generated by the
    -   factorization of the number 123, without log pollution from any
    -   other "transaction".
    -   </p>
    -
    -  <h3>Selective triggering &amp; recipient addressing with transaction isolation</h3>
    -
    -  <p>In a real world scenario, receiving isolated transactions is not
    -  enough. You would need to trigger outgoing emails only for certain
    -  users, typically QA engineers such as Dave and Carol. Moreover, you
    -  would want the emails generated by transaction made by Carol to
    -  Carol's mailbox and those generated by Dave to Dave's mailbox.</p>
    -
    -  <p>Selective triggering and addressing are two distinct
    -  problems. Depending on the exact circumstances, there are many ways
    -  of tackling these two issues. However, for the sake of simplicity,
    -  let us assume that the SMTP server at Fooware.com accepts <a
    -  href="http://en.wikipedia.org/wiki/Email_address#Address_tags">address
    -  tags</a>. Such an SMTP server treats an incoming message sent to
    -  username+xyz@fooware.com as if it were addressed to
    -  username@fooware.com, effectively stripping the +xyz part.</p>
    -
    -  <p>Let us further assume that we can somehow extract the email
    -  address of the user from the contents of her transaction, via a
    -  database lookup or perhaps some other means. The extracted email
    -  addressed is placed into the MDC under the key "txEmail".
    -  </p>
    -
    -  <p>Upon a logging event marked as SMTP_TRIGGER, the following
    -  configuration file will trigger an email message addressed to the
    -  value of "%mdc{txEmail}" but only if it contains the string "+log".
    -  </p>
    -
    -  <pre class="prettyprint source">&lt;configuration scan="true" scanPeriod="3 seconds">
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;
    -
    -  &lt;appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;SMTPHost>NAME_OF_SMTP_HOST&lt;/SMTPHost>
    -    <b>&lt;to>%mdc{txEmail}&lt;/to></b>
    -    &lt;from>&lt;/from>
    -    &lt;subject>Prime - %mdc{number}&lt;/subject>
    -
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -       &lt;pattern>%date%level%logger{24}%msg&lt;/pattern>
    -    &lt;/layout>
    -
    -    &lt;discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator">
    -      &lt;key>txId&lt;/key>
    -      &lt;defaultValue>default&lt;/defaultValue>
    -    &lt;/discriminator>
    -
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
    -      &lt;expression>
    -        <b>(mdc != null &amp;amp;&amp;amp; mdc.get("txEmail") != null &amp;amp;&amp;amp; </b>
    -            <b>((String) mdc.get("txEmail")).contains("+log") )</b>
    -        &amp;amp;&amp;amp;
    -        (marker != null  &amp;&amp; marker.contains("SMTP_TRIGGER") )
    -      &lt;/expression>
    -    &lt;/evaluator>
    -  &lt;/appender>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SMTP" /> 
    -  &lt;/root>
    -&lt;/configuration>  </pre>
    -
    -     <p>If your particular SMTP server does not handle address tags,
    -     you can still use them within the evaluator but remove them in
    -     the recipient address with the help of the <a
    -     href="../manual/layouts.html#replace">%replace</a> conversion
    -     word. Here is the relevant configuration snippet:</p>
    -
    -     <pre class="prettyprint source">&lt;appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
    -  &lt;to>%replace(%mdc{txEmail}){'\+log', ''}&lt;/to>
    -  ...
    -&lt;/appender></pre>
    -
    -     <p>This replaces any occurrence of the string "+log" within the
    -     string returned by %mdc{txEmail} with the empty string,
    -     effectively erasing +log from the recipient address.</p>
    -
    -     <h3>Buffer management in very busy systems</h3>
    -
    -     <p>The solution described so far provides an amazingly flexible
    -     solution to the initial problem. Indeed, any QA-engineer at
    -     Fooware.com, say Carol, can have the logs generated by requests
    -     she makes to Buscrit sent to her automatically by email. All she
    -     has to do is to suffix the user part in her email address with
    -     "+log" when she registers with Buscrit.
    -     </p>
    -
    -     <p>By default <code>SMTPAppender</code> will cap the number of
    -     buffers it maintains to the value of the <span
    -     class="option">maxNumberOfBuffers</span> option (64 by default)
    -     and automatically discards buffers untouched for at least 30
    -     minutes. While this approach will work nicely in a test
    -     environment with few transactions, in a very busy production
    -     system, these buffer management mechanisms will cause Carol to
    -     receive <a
    -     href="../manual/appenders.html#bufferManagement">truncated log
    -     buffers</a>.  </p>
    -
    -     <p>To deal with this problem, we instruct SMTPAppender to discard
    -     the appropriate buffer at the end of each transaction. This is done by
    -     logging an event marked as "FINALIZE_SESSION". Here is a modified
    -     version of <code>PrimeAction</code> which marks the end of a
    -     transaction with "FINALIZE_SESSION".
    -     </p>
    -
    -     <pre class="prettyprint source">package ch.qos.logback.demo.prime;
    -
    -<b>import static ch.qos.logback.classic.ClassicConstants.FINALIZE_SESSION_MARKER;</b>
    -
    -public class PrimeAction extends Action {
    -
    -  Logger logger = LoggerFactory.getLogger(PrimeAction.class);
    -  static Marker SMTP_TRIGGER = MarkerFactory.getMarker("SMTP_TRIGGER");
    -  static {
    -     // markers can hold references to other markers
    -     <b>SMTP_TRIGGER.add(FINALIZE_SESSION_MARKER);</b>
    -  }
    -
    -  public ActionForward execute(ActionMapping actionMapping, ... ) throws Exception {
    -
    -    Long number = form.getNumber();
    -    try {
    -      ...
    -    } finally {
    -      <b>logger.info(SMTP_TRIGGER, "Prime computation ended");</b>
    -      MDC.put("txId", null); // clear txId asap to avoid accidental rebirth
    -    }
    -  }
    -} </pre>
    -
    -
    -   <p>Not that at the end of each transaction the appropriate buffer
    -   is discarded, we can increase the value of <span
    -   class="option">maxNumberOfBuffers</span> as shown in the next
    -   configuration file..
    -   </p>
    -
    -   <pre class="prettyprint   source">&lt;configuration scan="true" scanPeriod="3 seconds">
    -
    -  &lt;statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" /&gt;
    -
    -  &lt;appender name="SMTP" class="ch.qos.logback.classic.net.SMTPAppender">
    -    &lt;SMTPHost>NAME_OF_SMTP_HOST&lt;/SMTPHost>
    -    &lt;to>%mdc{txEmail}&lt;/to>
    -    &lt;from>&lt;/from>
    -    &lt;subject>Prime - %mdc{number}&lt;/subject>
    -
    -    &lt;layout class="ch.qos.logback.classic.html.HTMLLayout">
    -       &lt;pattern>%date%level%logger{24}%msg&lt;/pattern>
    -    &lt;/layout>
    -
    -    &lt;discriminator class="ch.qos.logback.classic.sift.MDCBasedDiscriminator">
    -      &lt;key>txId&lt;/key>
    -      &lt;defaultValue>default&lt;/defaultValue>
    -    &lt;/discriminator>
    -
    -    <b>&lt;cyclicBufferTracker class="ch.qos.logback.core.spi.CyclicBufferTracker"></b>
    -      <b>&lt;maxNumberOfBuffers>512&lt;/maxNumberOfBuffers></b>
    -    <b>&lt;/cyclicBufferTracker></b>
    -
    -    &lt;evaluator class="ch.qos.logback.classic.boolex.JaninoEventEvaluator">
    -      &lt;expression>
    -        (mdc != null &amp;amp;&amp;amp; mdc.get("txEmail") != null &amp;amp;&amp;amp;
    -            ((String) mdc.get("txEmail")).contains("+log") )
    -        &amp;amp;&amp;amp;
    -        (marker != null  &amp;&amp; marker.contains("SMTP_TRIGGER") )
    -      &lt;/expression>
    -    &lt;/evaluator>
    -
    -  &lt;root level="DEBUG">
    -    &lt;appender-ref ref="SMTP" /> 
    -  &lt;/root>
    -&lt;/configuration>  </pre>
    -
    -    <p>With these latest changes, we can selectively send isolated
    -    logs for selected transactions to the concerned recipient, even in
    -    very busy production systems without excessive memory consumption.
    -    </p>
    -
    -     <script src="../templates/footer.js" type="text/javascript"></script>	
    -    </div>
    -  </body>
    -</html>
    - 
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/recipes/index.html b/logback-site/src/site/pages/recipes/index.html
    deleted file mode 100644
    index 9988ee0b49..0000000000
    --- a/logback-site/src/site/pages/recipes/index.html
    +++ /dev/null
    @@ -1,43 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Logback Recipes</title>
    -    <link rel="stylesheet" type="text/css" href="../css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="../css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="../css/_print.css" media="print" />
    -
    -  </head>
    -  <body>
    -    <script type="text/javascript">prefix='../';</script>
    -    <script src="../templates/header.js" type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="../templates/left.js" type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -      <h2>Real-world inspired logback recipes</h2>
    -      
    -      <div>
    -        <p>Here is a list of logback-related recipes inspired by
    -        real-world use cases:</p>
    -
    -        <ul>
    -          <li><p><a href="emailPerTransaction.html">Triggering an
    -        email containing the isolated logs of selected
    -        transactions</a></p></li> </ul>
    -
    -
    -        <ul>
    -          <li><p><a href="captureHttp.html">Capture incoming HTTP
    -          requests and outgoing responses</a></p></li>
    -        </ul> 
    -      </div>
    -
    -      <script src="../templates/footer.js" type="text/javascript"></script>	
    -    </div>
    -  </body>
    -</html>
    diff --git a/logback-site/src/site/pages/repos.html b/logback-site/src/site/pages/repos.html
    deleted file mode 100644
    index 8778b4c0c9..0000000000
    --- a/logback-site/src/site/pages/repos.html
    +++ /dev/null
    @@ -1,47 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Repository</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -  </head>
    -  <body>
    -    <script  type="text/javascript">prefix='';</script>    
    -    <script src="templates/header.js"  type="text/javascript"></script>
    -    <div id="left">
    -      <noscript>Please turn on Javascript to view this menu</noscript>
    -      <script src="templates/left.js"  type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -	
    -	
    -		<div class="section">
    -			<h2>Source Repository</h2>
    -		</div>
    -
    -		<p>We store the project's source code in a revision control system
    -		called Git. Developers have write access to the repository,
    -		enabling them to make changes to the source code. Everyone else
    -		has read-access to the repository. Thus, anyone can check out the
    -		latest development version of the software. Note that the latest
    -		version in the repository may not work as expected. It may not
    -		even compile. If you are looking for a stable release, then
    -		download an official distribution.
    -    </p>
    -
    -    <p>The official logback source repository is located on github at
    -    <a href="http://github.com/qos-ch/logback">
    -    http://github.com/qos-ch/logback</a>.
    -    </p>
    -    
    -    <p>Anyone can clone logback's source repository. All you need is a
    -    <code>git</code> client. </p>
    -        
    -    <script src="templates/footer.js" type="text/javascript"></script>
    -    </div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/setup.html b/logback-site/src/site/pages/setup.html
    deleted file mode 100755
    index 81b3722df5..0000000000
    --- a/logback-site/src/site/pages/setup.html
    +++ /dev/null
    @@ -1,378 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Setup</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -    <link rel="stylesheet" type="text/css" href="css/prettify.css" media="screen" />
    -  </head>
    -  <body onload="prettyPrint(); decorate();">
    -    <script type="text/javascript">prefix='';	</script>
    -
    -    <script type="text/javascript" src="templates/header.js"></script>
    -    <script type="text/javascript" src="js/prettify.js"></script>
    -    <script type="text/javascript" src="js/jquery-min.js"></script>
    -    <script type="text/javascript" src="js/decorator.js"></script>
    -    
    -    <div id="left">
    -      <script type="text/javascript" src="templates/left.js"></script>
    -    </div>
    -  
    -    <div id="content">
    -	
    -	
    -    <h2 class="doAnchor">Classpath Setup</h2>
    -
    -    <p>In order to run the examples provided in the documentation, you
    -    need to add the following jars to your class path:
    -    </p>
    -
    -    <ul>
    -      <li>logback-core-${project.version}.jar</li>
    -      <li>logback-classic-${project.version}.jar</li>
    -      <li>logback-examples-${project.version}.jar</li>
    -      <li>slf4j-api-${slf4j.version}.jar</li>
    -    </ul>
    -    
    -    <p>The <em>logback-*.jar</em> files are part of the logback
    -    distribution whereas <em>slf4j-api-${slf4j.version}.jar</em> ships
    -    with <a href="http://www.slf4j.org">SLF4J</a>, a separate project.
    -    </p>
    -    
    -
    -    <h3 class="doAnchor" name="commandLine">Running from the command
    -    line</h3>
    -    
    -    <p>You can launch the first sample application,
    -    <em>chapters.introduction.HelloWord1</em> with the following
    -    command. This assumes that your current directory is
    -    <em>$LOGBACK_HOME/logback-examples</em>, where
    -    <em>$LOGBACK_HOME</em> stands for the directory where you
    -    installed logback:
    -    </p>
    -
    -    <p class="source">java -cp lib/slf4j-api-${slf4j.version}.jar;../logback-core-${project.version}.jar;\
    - ../logback-classic-${project.version}.jar;logback-examples-${project.version}.jar\
    - chapters.introduction.HelloWorld1</p>
    -
    -   <p>It is more convenient to set the CLASSPATH environment variable
    -   once and for all before running the examples.
    -   </p>
    -
    -   <p>The <em>setClasspath.cmd</em> script located in the
    -   $LOGBACK_HOME/logback-examples folder will configure the class path
    -   for the MS Windows platform. For Unix, you can use
    -   <em>setClasspath.sh</em>.
    -   </p>
    -
    -   <p>Please edit the script in order to adapt the <em>LB_HOME</em> variable 
    -   to match your local environment.</p>
    -   
    -   <p>Please be aware that many examples will launch Java classes
    -   along with configuration files. To access these files by using the
    -   same commands as written in the documentation, you will need to
    -   issue the commands from within the
    -   <em>$LOGBACK_HOME/logback-examples</em> directory.
    -   </p>
    -
    -    
    -   <h2 class="doAnchor" name="mavenBuild">Maven dependency
    -    declaration</h2>
    -
    -    <p>To use logback-classic in your Maven project, declare the
    -    following dependency in your project's <em>pom</em> file.</p>
    -
    -    <pre class="prettyprint source">&lt;dependency>
    -  &lt;groupId>ch.qos.logback&lt;/groupId>
    -  &lt;artifactId>logback-classic&lt;/artifactId>
    -  &lt;version>${project.version}&lt;/version>
    -&lt;/dependency></pre>
    -
    -    <p><span class="label notice">TRANSITIVITY</span> Note that in
    -    addition to <em>logback-classic.jar</em>, the above declaration
    -    will automatically pull-in <em>slf4j-api.jar</em> and
    -    <em>logback-core.jar</em> into your project by virtue of Maven's
    -    transitivity rules.</p>
    -
    -
    -   <p>To include logback-access in your Maven project, declare the following
    -    dependency in your project's <em>pom</em> file.</p>
    -
    -    <pre class="prettyprint source">&lt;dependency>
    -  &lt;groupId>ch.qos.logback&lt;/groupId>
    -  &lt;artifactId>logback-access&lt;/artifactId>
    -  &lt;version>${project.version}&lt;/version>
    -&lt;/dependency></pre>
    -
    -   <h2 class="doAnchor" name="optionalDeps">Optional dependencies</h2>
    -
    -   <h3 class="doAnchor" name="SMTP"><code>SMTPAppender</code> requires
    -   JavaMail API</h3>
    -
    -   <p><code>SMTPAppender</code> related examples require the JavaMail
    -   API version 1.4 or later. Once you <a
    -   href="http://java.sun.com/products/javamail/downloads/index.html">download
    -   JavaMail</a>, you need to place <em>mail.jar</em> on your class
    -   path.</p>
    -   
    -   <p>Here is the corresponding Maven dependency declaration for your
    -   convenience.</p>
    -
    -
    -
    -   <pre class="prettyprint source">&lt;!-- The javax.activation:activation:1.1 dependency will be --&gt;
    -&lt;!-- automatically pulled in by Maven's transitivity rules --&gt;
    -&lt;dependency>
    -  &lt;groupId>javax.mail&lt;/groupId>
    -  &lt;artifactId>mail&lt;/artifactId>
    -  &lt;version>${javax.mail.version}&lt;/version>
    -&lt;/dependency></pre>
    -
    -   <h3 class="doAnchor" name="groovy"><code>GEventEvaluator</code> and
    -   <em>logback.groovy</em> configuration files require the Groovy
    -   runtime</h3>
    -   
    -   <p><code>GEventEvaluator</code> depends on the Groovy runtime. It
    -   was tested with Groovy version ${groovy.version}. Similarly, as the
    -   name indicates <a href="manual/groovy.html">groovy
    -   configuration</a> files require the groovy runtime to be present on
    -   your class path.
    -   </p>
    -   
    -   <p>Here is the corresponding Maven dependency declaration for your
    -   convenience.
    -   </p>
    -
    -
    -   <pre class="prettyprint source">&lt;dependency>
    -  &lt;groupId>org.codehaus.groovy&lt;/groupId>
    -  &lt;artifactId>groovy-all&lt;/artifactId>
    -  &lt;version>${groovy.version}&lt;/version>
    -&lt;/dependency></pre>
    -
    -
    -   <h3 class="doAnchor" name="janino">Conditional processing and
    -   <code>JaninoEventEvaluator</code> require the Janino library</h3>
    -   
    -   <p><a href="manual/configuration.html#conditional">Conditional
    -   processing</a> in configuration files requires the <a
    -   href="http://docs.codehaus.org/display/JANINO/Home"><b>Janino
    -   library</b></a>. Moreover, the evaluator examples based on
    -   <code>JaninoEventEvaluator</code> require Janino as well.  Once you
    -   download Janino, simply place <em>commons-compiler.jar</em> and
    -   <em>janino.jar</em> on your application's class path.
    -   </p>
    -
    -   <p><span class="label notice">Don't forget</span> As of Janino
    -   version 2.6.0, in addition to <em>janino.jar</em>,
    -   <em>commons-compiler.jar</em> needs to be on the class path as well.</p>
    -
    -   <p>Here is the corresponding Maven dependency declaration for your
    -   convenience.
    -   </p>
    -
    -   <pre class="prettyprint source">&lt;!-- The org.codehaus.janino:commons-compiler:${janino.version} dependency --&gt;
    -&lt;!-- will be automatically pulled in by Maven's transitivity rules --&gt;
    -&lt;dependency>
    -  &lt;groupId>org.codehaus.janino&lt;/groupId>
    -  &lt;artifactId>janino&lt;/artifactId>
    -  &lt;version>${janino.version}&lt;/version>
    -&lt;/dependency></pre>
    -
    -
    -   <h2 class="doAnchor" name="ide">Building Logback with an IDE</h2>
    -
    -   <p class="big green">As of version 1.3.0, logback requires Java 9 to build. However,
    -   it can be run on Java 7 or later.</p>
    -   
    -   <p>If you wish to contribute to the project or just hack for fun,
    -   you will probably want to import logback as a project into your
    -   favorite IDE. Logback uses Maven as its build tool. Logback offers
    -   a Groovy-based configurator so there is a dependency on the Groovy
    -   language. It follows that your IDE should have plugins for Maven
    -   and Groovy in order to <em>build</em> logback from your within
    -   IDE. The Groovy dependency just mentioned is a <em>build-time</em>
    -   dependency. The only mandatory logback dependency at runrime is the
    -   JRE, unless of course you wish to use the Groovy configurator in
    -   which case Groovy runtime will be a required dependency as
    -   well. Also note that building from the command line is fairly
    -   trivial, the command 'mvn install' given from $LOGBACK_HOME folder
    -   should suffice. </p>
    -
    -   <p><span class="label">ask for help</span> Notwithstanding the
    -   instructions below, if you have trouble building logback from the
    -   sources, just ask for help on the logback-dev mailing list.</p>
    -
    -   <h3 class="doAnchor" name="idea">Building logback with IntelliJ
    -   IDEA</h3>
    -
    -   <p>Assuming you have the latest version of IntelliJ IDEA installed,
    -   no additional plugin installation is necessary. IntelliJ IDEA
    -   supports Maven as well as Groovy out of the box. You can import
    -   logback into IDEA by selecting File&rarr; New Project &rarr; Import
    -   from external model&rarr; Maven, then select $LOGBACK_HOME as the
    -   Root directory. The import should finish successfully in a few
    -   seconds.</p>
    -
    -   <h3 class="doAnchor" name="eclipse">Building with Eclipse and "mvn eclipse:eclipse"</h3>
    -
    -   <p>Building logback under Eclipse is a little trickier. Here are
    -   instructions for building logback under Eclipse using the maven
    -   eclipse:eclipse plugin.
    -   </p>
    -   
    -
    -   <p>The procedure outlines below assumes that M2Eclipse is not
    -   active.  If you have <code>m2eclipse</code> installed, you can
    -   disable it by removing the Maven Nature for a given project. In
    -   later versions of Eclipse, m2eclipse is installed by default. 
    -   </p>
    -
    -   <p>And without further ado here are the steps:
    -   </p>
    -   
    -
    -   <ol>
    -     <li>Ensure that the Groovy plugin for Eclipse is installed.
    -
    -       <ul>
    -         <li>
    -           <p>You first need to determine the update site appropriate
    -           for your version of Eclipse. The list of available update
    -           sites is available from <a
    -           href="https://github.com/groovy/groovy-eclipse/wiki">Groovy
    -           Eclipse Wiki</a>.
    -           </p>
    -         
    -         </li>
    -         
    -         <li>In Eclipse, select Help &rarr; Intall new Software &rarr;
    -         Work with the update site you chose in the previous step and
    -         then Select "Groovy-Eclipse Feature". 
    -         </li>
    -       </ul>
    -    </li>
    -
    -
    -
    -     <li><pre>cd $LOGBACK_HOME</pre>
    -
    -     where $LOGBACK_HOME stands for the location where you cloned the
    -     logback project from github </li>
    -
    -
    -     <li>From the command line, run <code>mvn eclipse:eclipse</code>
    -     in $LOGBACK_HOME</li>
    -
    -     <li>In Eclipse, import the logback project: Import&rarr;
    -     General&rarr; Existing Projects into Workspace, select
    -     $LOGBACK_HOME folder for the import
    -     </li>    
    -
    -     <li>In Eclipse, clean all projects in Eclipse (Project &rarr;
    -     Clean)
    -     </li>   
    -
    -     <li>In Eclipse, select logback-classic project and check that it has 
    -     "Groovy" nature. If not add it by right clicking on logback-classic project &rarr;
    -     Groovy &rarr; Convert Groovy to Project.
    -     </li>   
    -     
    -   </ol>
    -
    -
    -   <p>The above listed procedure has been last tested by the author
    -   using Eclipse Neon on March 17th, 2017.</p>
    -
    -   <h3>Building with Eclipse+m2eclipse</h3>
    -   
    -   <p>Building with Eclipse and m2eclipse is a bit more complicated
    -   due to the use of Groovy in logback-classic.
    -   </p>
    -
    -   <p>Here are the required steps:</p>
    -   
    -
    -   <ol>
    -     <li>Ensure that the Groovy plugin for Eclipse is installed. See
    -     above for instructions.</li>
    -
    -     <li>In Eclipse, import the logback project: Import&rarr;
    -     Maven&rarr; Existing Maven Projects, select
    -     $LOGBACK_HOME folder for the import
    -     </li>
    -
    -     <li>Install any discovered m2e connections,
    -     e.g. "maven-bundle-plugin".
    -     <p>
    -       <a href="images/setup/discoverM2EConnectors.png">
    -         <img src="images/setup/discoverM2EConnectors.png" alt="Click to enlarge" height="400"/>
    -       </a>
    -     </p>
    -
    -     <p>You may need to restart Eclipse.</p>.
    -
    -     </li>
    -
    -     <li>In Eclipse, select logback-classic project and check that it
    -     has "Groovy" nature. If not add it by right clicking on
    -     logback-classic project &rarr; Groovy &rarr; Convert Groovy to
    -     Project.
    -     </li>
    -     
    -     <li>At this stage Eclipse may complain about
    -     <code>GafferConfigurator</code> not being resolved. To fix these
    -     errors, select logback-classic project properties, select "Java
    -     Build Path" &rarr; Source &rarr; "logback-classic/src/main/groovy".
    -     Select "Excluded" and then click on Remove.
    -     
    -     <p>
    -       <a href="images/setup/removeGroovyExclusions.png">
    -         <img src="images/setup/removeGroovyExclusions.png" alt="Click to enlarge" height="400"/>
    -       </a>
    -     </p>
    -
    -     </li>
    -
    -     <li>
    -       <p>Project &rarr; Clean &rarr; Clean all projects.</p>     
    -     </li>
    -
    -     
    -     <li>To fix the errors in the logback-examples project, you need
    -     to instruct logback-classic to not export its Maven dependencies.
    -     Select logback-classic project properties, select "Java
    -     Build Path" &rarr; Order and Export and deselect "Maven Dependencies".
    -
    -     <p>
    -       <a href="images/setup/deselectMavenDependenciesExport.png">
    -         <img src="images/setup/deselectMavenDependenciesExport.png" alt="Click to enlarge" height="400"/>
    -       </a>
    -     </p>
    -     </li>
    -
    -     <li>
    -       <p>Project &rarr; Clean &rarr; Clean all projects.</p>     
    -     </li>
    -     
    -     
    -
    -   </ol>
    -   
    -   
    -   
    -   
    -   <p><span class="label notice">Call for volunteers</span> Given that
    -   many users prefer M2Eclipse for building projects under Eclipse
    -   IDE, we are looking for volunteers to help simplify the steps
    -   required for building logback with M2Eclipse.
    -   </p>
    -   
    -
    -   <script src="templates/footer.js" type="text/javascript"></script>
    -</div>
    -</body>
    -</html>
    diff --git a/logback-site/src/site/pages/support.html b/logback-site/src/site/pages/support.html
    deleted file mode 100644
    index 1074accffd..0000000000
    --- a/logback-site/src/site/pages/support.html
    +++ /dev/null
    @@ -1,56 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Professional support</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -    
    -  </head>
    -  <body>
    -    <script type="text/javascript">prefix='';</script>
    -    
    -    <script src="templates/header.js"  type="text/javascript"></script>
    -    <div id="left">
    -      <script src="templates/left.js"  type="text/javascript"></script>
    -    </div>
    -    <div id="content">
    -      
    -    <h2>Professional Support</h2>
    -
    -    <p>Although the quality of support in the various open source
    -    project mailing lists is quite good, these are operated on a
    -    best-effort basis. If you require authoritative responses and
    -    guaranteed response times, then you should consider our
    -    professional support package.
    -    </p>
    -
    -    <p>As the founders and/or current maintainers of the slf4j, log4j,
    -    logback and <a href="http://cal10n.qos.ch/">cal10n</a> projects,
    -    we can offer you timely and competent support. Choosing our
    -    professional support service is a good way to leverage our
    -    expertise. Moreover, if you require custom development we are
    -    likely to be of help.
    -    </p>
    -
    -    <p>For more information about Professional Support do not hesitate
    -    to contact us. We will be happy to discuss your needs.</p>
    -    
    -    <table>
    -      <tr>
    -        <td>email:</td>
    -        <td><img align="left" src="images/helpEMAIL.gif" alt=""/></td>
    -      </tr>
    -      <tr>
    -        <td>telephone:</td>
    -        <td> +41 21 312 32 26</td>
    -      </tr>
    -    </table> 
    -    
    -    </div>
    -
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/pages/templates/creative.js b/logback-site/src/site/pages/templates/creative.js
    deleted file mode 100755
    index 13c82124ad..0000000000
    --- a/logback-site/src/site/pages/templates/creative.js
    +++ /dev/null
    @@ -1,33 +0,0 @@
    -document.write('		<table style="margin-left: 0em; padding-top:0ex" cellpadding="0" ');
    -document.write('           cellspacing="0" width="70%">');
    -document.write('      <tr>');
    -document.write('        <td>        ');
    -document.write('          <p class="author">');
    -document.write('            Authors: Ceki G&#252;lc&#252;, S&#233;bastien Pennec, Carl Harris');
    -document.write('          <br/>');
    -document.write('          Copyright &#169; 2000-2017, QOS.ch</p>');
    -document.write('        </td>');
    -document.write('        <td>');
    -document.write('            <a rel="license"');
    -document.write('               href="http://creativecommons.org/licenses/by-nc-sa/2.5/">');
    -document.write('        <img alt="Creative Commons License"');
    -document.write('             style="border-width: 0; margin-left: 1em"');
    -document.write('             src="https://creativecommons.org/images/public/somerights20.png" />');
    -document.write('          </a>');
    -document.write('        </td>');
    -document.write('      </tr>');
    -document.write('      <tr>');
    -document.write('        <td>');
    -document.write('          <p>');
    -document.write('            This document is licensed under a');
    -document.write('            <a rel="license"');
    -document.write('               href="http://creativecommons.org/licenses/by-nc-sa/2.5/">');
    -document.write('              Creative Commons');
    -document.write('              Attribution-NonCommercial-ShareAlike 2.5');
    -document.write('              License');
    -document.write('            </a>');
    -document.write('          </p>');
    -document.write('        </td>');
    -document.write('        <td></td>');
    -document.write('			</tr>');
    -document.write('		</table>');
    diff --git a/logback-site/src/site/pages/templates/footer.js b/logback-site/src/site/pages/templates/footer.js
    deleted file mode 100755
    index 98493f86a9..0000000000
    --- a/logback-site/src/site/pages/templates/footer.js
    +++ /dev/null
    @@ -1,27 +0,0 @@
    -
    -document.write('<table class="footer" border="0">')
    -
    -document.write('<tr>')
    -
    -document.write('<td valign="top">Copyright &copy; 2018  <a href="http://www.qos.ch/">QOS.ch</a></td>')
    -
    -document.write('  <td rowspan="2">');
    -document.write('    <a href="http://twitter.com/qos_ch">');
    -document.write('      <img alt="Follow @qos_ch" src="https://www.slf4j.org/images/follow_us.png" />');
    -document.write('    </a>');
    -document.write('  </td>');
    -
    -
    -document.write('</tr>')
    -
    -AAT = '@'
    -DOOTT = '.'
    -document.write('<tr>') 
    -
    -document.write('<td align="left" colspan="1">') 
    -document.write('We are actively looking for volunteers to proofread the documentation. Please send your corrections or suggestions for improvement to "corrections' + AAT +'qos'+DOOTT+'ch". See also the <a href="http://articles.qos.ch/contributing.html">instructions for contributors</a>.');
    -document.write('</td>') 
    -document.write('  <td>&nbsp;</td>')
    -document.write('</tr>') 
    -
    -document.write('</table>')
    diff --git a/logback-site/src/site/pages/templates/header.js b/logback-site/src/site/pages/templates/header.js
    deleted file mode 100755
    index 8be6a5d644..0000000000
    --- a/logback-site/src/site/pages/templates/header.js
    +++ /dev/null
    @@ -1,13 +0,0 @@
    -document.write('<table width="100%" border="0"><tr>');
    -document.write('<td><a href="https://logback.qos.ch/">');
    -document.write('<img src="' + prefix + 'images/logos/lblogo.jpg" alt="" border="0"/>');
    -document.write('</a></td>')
    -
    -document.write('<td>&nbsp;</td>');
    -
    -document.write('</tr></table>')
    -
    -
    -document.write('<div id="headerLine"></div>');
    -
    -
    diff --git a/logback-site/src/site/pages/templates/left.js b/logback-site/src/site/pages/templates/left.js
    deleted file mode 100755
    index 14785d493b..0000000000
    --- a/logback-site/src/site/pages/templates/left.js
    +++ /dev/null
    @@ -1,40 +0,0 @@
    -
    -document.write('<div class="menuGroup">');
    -document.write('<p class="menu_header">Logback project</p>');
    -document.write('<p class="menu"><a href="' + prefix + 'index.html">Introduction</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'download.html">Download</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'documentation.html">Documentation</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'license.html">License</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'news.html">News</a></p>');
    -
    -document.write('<p class="menu_header">Support</p>');
    -document.write('<p class="menu"><a href="' + prefix + 'mailinglist.html">Mailing Lists</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'bugreport.html">Bug Report</a></p>');
    -document.write('<p class="menu"><a href="http://github.com/qos-ch/logback">Source Repository</a></p>');
    -document.write('<p class="menu"><a href="' + prefix + 'volunteer.html">Call for volunteers</a>');
    -document.write('<p class="menu"><a href="http://www.qos.ch/shop/products/professionalSupport">Support offerings</a>');
    -
    -//document.write('<p class="menu"><a href="http://www.qos.ch/shop/products/training">Training</a>');
    -
    -document.write('<p class="menu_header">Online Tools</p>');
    -document.write('<p class="menu"><a href="https://logback.qos.ch/translator/">log4j.properties Translator</a>');
    -document.write('<p class="menu"><a href="https://logback.qos.ch/translator/asGroovy.html">logback.XML to Groovy</a>');
    -
    -document.write('</p>');
    -document.write('</div>');
    -
    -document.write('<p>&nbsp;</p>');
    -document.write('<div class="jobadd"><p><a href="https://doodle.com/poll/s7n3wk59694pmnbs">Should logback 1.3.x series upgrade to Java 8 or remain with Java 7?</a></p></div>');
    -
    -document.write('<p>&nbsp;</p>');
    -
    -
    -document.write('<div class="pub">');
    -document.write('    <a href="http://twitter.com/qos_ch" style="">');
    -document.write('      <img alt="Follow @qos_ch" src="' + prefix + 'images/follow_us.png" />');
    -document.write('    </a>');
    -document.write('</div>');
    -
    -document.write('<p>&nbsp;</p>');
    -document.write('<div class="pub"><img src="https://travis-ci.org/qos-ch/logback.svg?branch=master"/></div>');
    -
    diff --git a/logback-site/src/site/pages/templates/right.js b/logback-site/src/site/pages/templates/right.js
    deleted file mode 100755
    index 91b217122e..0000000000
    --- a/logback-site/src/site/pages/templates/right.js
    +++ /dev/null
    @@ -1,11 +0,0 @@
    -document.write('      <script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>');
    -//      <!-- SLF4J -->
    -document.write('      <ins class="adsbygoogle"');
    -document.write('           style="display:block"');
    -document.write('           data-ad-client="ca-pub-7471410671306824"');
    -document.write('           data-ad-slot="6377851613"');
    -document.write('           data-ad-format="auto"></ins>');
    -document.write('      <script>');
    -document.write('        (adsbygoogle = window.adsbygoogle || []).push({});');
    -document.write('      </script>');
    -
    diff --git a/logback-site/src/site/pages/templates/setup.js b/logback-site/src/site/pages/templates/setup.js
    deleted file mode 100644
    index 732d434ef6..0000000000
    --- a/logback-site/src/site/pages/templates/setup.js
    +++ /dev/null
    @@ -1,8 +0,0 @@
    -
    -document.write('		<p class="highlight">');
    -document.write('      In order to run the examples in this chapter, you need');
    -document.write('      to make sure that certain jar files are present on the');
    -document.write('      classpath.');
    -document.write('    	Please refer to the <a href="../setup.html">setup page</a>');
    -document.write('    	for further details.');
    -document.write('    </p>');
    diff --git a/logback-site/src/site/pages/volunteer.html b/logback-site/src/site/pages/volunteer.html
    deleted file mode 100755
    index 48ce14dea5..0000000000
    --- a/logback-site/src/site/pages/volunteer.html
    +++ /dev/null
    @@ -1,141 +0,0 @@
    -<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    -  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    -
    -<html xmlns="http://www.w3.org/1999/xhtml">
    -  <head>
    -    <meta http-equiv="content-type" content="text/html; charset=iso-8859-1" />
    -    <title>Volunteers</title>
    -    <link rel="stylesheet" type="text/css" href="css/common.css" />
    -    <link rel="stylesheet" type="text/css" href="css/screen.css" media="screen" />
    -    <link rel="stylesheet" type="text/css" href="css/_print.css" media="print" />
    -  </head>
    -  <body>
    -	<script type="text/javascript">prefix='';</script>
    -
    -  <script src="templates/header.js" type="text/javascript"></script>
    -  <div id="left">
    -    <noscript>Please turn on Javascript to view this menu</noscript>
    -    <script src="templates/left.js" type="text/javascript"></script>
    -  </div>
    -  <div id="right">
    -      <script src="templates/right.js" type="text/javascript"></script>
    -  </div>
    -
    -  
    -  <div id="content">
    -  
    -    <h2>Call for volunteers</h2>
    -
    -    <p>We are looking for volunteers in the following areas.</p>
    -
    -    <ol>
    -      <li><span class="label">top priority</span> <b>logback in 10 minutes</b> 
    -      
    -      <p>We are looking for a volunteer, preferably a native English
    -      speaker, to write a short document describing logback for
    -      beginners, entitled say "logback in 10 minutes". This document
    -      is likely to be the most widely read document of the
    -      project. Writing such a document is an excellent opportunity to
    -      learn logback. Obviously, there would be plenty of editorial
    -      help and guidance coming from the logback developers.
    -      </p>
    -      </li>
    -
    -      <li><span class="label">high priority</span> <b>Proof reading the documentation</b>
    -      <p>We are always looking for volunteers to proof-read the
    -      documentation. Suggestions as to the design and look-and-feel of
    -      the site are also welcome.</p>
    -      </li>
    -
    -      <li><span class="label">medium priority</span> <b>Decoder:
    -      parse log files and transform them into logging events</b>
    -      
    -      <p>This effort has been started under the <a
    -      href="https://github.com/qos-ch/logback-decoder">logback-decoder</a>
    -      project but has stalled. This problem is technically
    -      interesting, has a well-defined scope and mostly independent of
    -      the logback framework.
    -      </p>
    -      
    -      </li>
    -
    -      <li><span class="label">medium priority</span> <b>Maintain the
    -      groovy configurator</b>
    -
    -      <p>The Groovy configurator, aka Gaffer, although pretty cool, is
    -      not getting the attention it deserves. The amount of code
    -      involved, although not completely trivial, is far from
    -      insurmountable. We are looking for a volunteer to take over
    -      Gaffer.
    -       </p>
    -      </li>
    -
    -      <li><b>Improve OSGi support</b>
    -      
    -      <p>We are looking for an OSGi expert to review our current
    -      practices and improve OSGi support in logback.  </p></li>
    -      
    -      <li><b>Fixing bugs</b>
    -
    -      <p>We are looking for volunteers for fixing logback
    -      bugs. Volunteering to solve bugs is a good way to learn about
    -      any project.
    -      </p> 
    -
    -      <p>For those looking for highly technical challenges with
    -      limited scope, have a look at various build failures observable
    -      on <a href="http://logback.qos.ch/jenkins/">our Jenkins
    -      instance</a>.
    -      </p>
    -
    -      <p>Our build is quite stable but failures occur from time to
    -      time on our Jenkins instance hosted on a relatively old
    -      computer.</p>
    -
    -      <ul>
    -
    -        <li><a
    -        href="http://logback.qos.ch/jenkins/job/logback/149/">build
    -        #149</a> with test failure in <a
    -        href="http://logback.qos.ch/jenkins/job/logback/ch.qos.logback$logback-classic/149/testReport/junit/ch.qos.logback.classic.joran/JoranConfiguratorTest/levelChangePropagator1/">JoranConfiguratorTest.levelChangePropagator1</a>
    -        probable cause: parallel execution, j.u.l. shared-state
    -        overridden by levelChangePropagator0 while levelChangePropagator1 is running
    -        </li>
    -
    -        <li><a
    -        href="http://logback.qos.ch/jenkins/job/logback/298/">build
    -        #298</a> with test failure in <a
    -        href="http://logback.qos.ch/jenkins/job/logback/ch.qos.logback$logback-core/298/testReport/junit/ch.qos.logback.core/AsyncAppenderBaseTest/workerShouldStopEvenIfInterruptExceptionConsumedWithinSubappender/">AsyncAppenderBaseTest</a>
    -        probable cause: race condition
    -        </li>
    -
    -
    -        <li><a
    -        href="http://logback.qos.ch/jenkins/job/logback/91/">build
    -        #91</a> with test failure in <a
    -        href="http://logback.qos.ch/jenkins/job/logback/ch.qos.logback$logback-classic/91/testReport/junit/ch.qos.logback.classic.net/SMTPAppender_GreenTest/testMultipleTo/">SMTPAppender_GreenTest</a>
    -        probable cause: race condition, GreenMail server not running
    -        at time of message transmission</li>
    -
    -
    -      </ul>
    -
    -      <p>These build failures are quite hard to reproduce. If you
    -      intend to work on these problems, you will probably first need
    -      to make changes to logback code so that the problem becomes
    -      easily reproducible. One the problem is identified and
    -      reproducible, solving it should be much easier.
    -      </p>
    -
    -    <h3>Setting up the project</h3>
    -    
    -    <p>If you wish to contribute to the project or just hack for fun,
    -    you will probably want to import logback as a project into your
    -    favorite IDE. See the instructions for <a
    -    href="setup.html#ide">building logback in Eclipse or IntelliJ
    -    IDEA</a> for details.</p>
    -
    -    <script src="templates/footer.js" type="text/javascript"></script>
    -  </div>
    -  </body>
    -</html>
    \ No newline at end of file
    diff --git a/logback-site/src/site/resources/beagle/images/beagleFeature.png b/logback-site/src/site/resources/beagle/images/beagleFeature.png
    deleted file mode 100644
    index faf6ab94a8..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/beagleFeature.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/beagleSample.png b/logback-site/src/site/resources/beagle/images/beagleSample.png
    deleted file mode 100644
    index 2d241751fc..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/beagleSample.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/beagleView0.png b/logback-site/src/site/resources/beagle/images/beagleView0.png
    deleted file mode 100644
    index 344e0a0445..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/beagleView0.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/callerData-jump.png b/logback-site/src/site/resources/beagle/images/callerData-jump.png
    deleted file mode 100644
    index f9f5ee77cc..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/callerData-jump.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/callerData.png b/logback-site/src/site/resources/beagle/images/callerData.png
    deleted file mode 100644
    index aa4ee7fb76..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/callerData.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/font.png b/logback-site/src/site/resources/beagle/images/font.png
    deleted file mode 100644
    index 2a0629dc0c..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/font.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/menu.png b/logback-site/src/site/resources/beagle/images/menu.png
    deleted file mode 100644
    index dd05f50c68..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/menu.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/nebulaFeature.png b/logback-site/src/site/resources/beagle/images/nebulaFeature.png
    deleted file mode 100644
    index 5af62959c1..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/nebulaFeature.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/play_doc.gif b/logback-site/src/site/resources/beagle/images/play_doc.gif
    deleted file mode 100644
    index 4d7e77ad3c..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/play_doc.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/preferences.png b/logback-site/src/site/resources/beagle/images/preferences.png
    deleted file mode 100644
    index 5d8e1cd742..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/preferences.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/sailing-ship-128x128.png b/logback-site/src/site/resources/beagle/images/sailing-ship-128x128.png
    deleted file mode 100644
    index 59aec6a89c..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/sailing-ship-128x128.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/sailing-ship-16x16.png b/logback-site/src/site/resources/beagle/images/sailing-ship-16x16.png
    deleted file mode 100644
    index 44e966337b..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/sailing-ship-16x16.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/sailing-ship-32x32.png b/logback-site/src/site/resources/beagle/images/sailing-ship-32x32.png
    deleted file mode 100644
    index 1b40e46f3a..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/sailing-ship-32x32.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/beagle/images/sailing-ship-48x48.png b/logback-site/src/site/resources/beagle/images/sailing-ship-48x48.png
    deleted file mode 100644
    index f7b9527fca..0000000000
    Binary files a/logback-site/src/site/resources/beagle/images/sailing-ship-48x48.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/css/anchor12.png b/logback-site/src/site/resources/css/anchor12.png
    deleted file mode 100644
    index 2cd97acb2b..0000000000
    Binary files a/logback-site/src/site/resources/css/anchor12.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/css/anchor16.png b/logback-site/src/site/resources/css/anchor16.png
    deleted file mode 100644
    index c0676f49d5..0000000000
    Binary files a/logback-site/src/site/resources/css/anchor16.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/css/anchor20.png b/logback-site/src/site/resources/css/anchor20.png
    deleted file mode 100644
    index 65c26c1e3a..0000000000
    Binary files a/logback-site/src/site/resources/css/anchor20.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/css/anchor24.png b/logback-site/src/site/resources/css/anchor24.png
    deleted file mode 100644
    index ad1a8c6950..0000000000
    Binary files a/logback-site/src/site/resources/css/anchor24.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/cyclicView.png b/logback-site/src/site/resources/images/cyclicView.png
    deleted file mode 100644
    index 98ceb3f46a..0000000000
    Binary files a/logback-site/src/site/resources/images/cyclicView.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/follow_us.png b/logback-site/src/site/resources/images/follow_us.png
    deleted file mode 100644
    index fe3a3888d1..0000000000
    Binary files a/logback-site/src/site/resources/images/follow_us.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/helpEMAIL.gif b/logback-site/src/site/resources/images/helpEMAIL.gif
    deleted file mode 100644
    index d42260a4fe..0000000000
    Binary files a/logback-site/src/site/resources/images/helpEMAIL.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/lbAccessStatus.jpg b/logback-site/src/site/resources/images/lbAccessStatus.jpg
    deleted file mode 100644
    index 3dbd271062..0000000000
    Binary files a/logback-site/src/site/resources/images/lbAccessStatus.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/logos/lblogo-150.jpg b/logback-site/src/site/resources/images/logos/lblogo-150.jpg
    deleted file mode 100644
    index 38a26060f6..0000000000
    Binary files a/logback-site/src/site/resources/images/logos/lblogo-150.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/logos/lblogo-200.jpg b/logback-site/src/site/resources/images/logos/lblogo-200.jpg
    deleted file mode 100644
    index d0edaf8f1e..0000000000
    Binary files a/logback-site/src/site/resources/images/logos/lblogo-200.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/logos/lblogo-40.jpg b/logback-site/src/site/resources/images/logos/lblogo-40.jpg
    deleted file mode 100644
    index 668c08c3bd..0000000000
    Binary files a/logback-site/src/site/resources/images/logos/lblogo-40.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/logos/lblogo.jpg b/logback-site/src/site/resources/images/logos/lblogo.jpg
    deleted file mode 100644
    index 29d016ac67..0000000000
    Binary files a/logback-site/src/site/resources/images/logos/lblogo.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/logos/qosLogo.png b/logback-site/src/site/resources/images/logos/qosLogo.png
    deleted file mode 100644
    index 52b216e916..0000000000
    Binary files a/logback-site/src/site/resources/images/logos/qosLogo.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/myjob.png b/logback-site/src/site/resources/images/myjob.png
    deleted file mode 100644
    index 40b50d4feb..0000000000
    Binary files a/logback-site/src/site/resources/images/myjob.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/buttons.gif b/logback-site/src/site/resources/images/plugin/buttons.gif
    deleted file mode 100644
    index 76ee9a510f..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/buttons.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/createFilter.gif b/logback-site/src/site/resources/images/plugin/createFilter.gif
    deleted file mode 100644
    index 19cfcd2c71..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/createFilter.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/filterWindow.gif b/logback-site/src/site/resources/images/plugin/filterWindow.gif
    deleted file mode 100644
    index ab7ac0cf76..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/filterWindow.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/prefs.gif b/logback-site/src/site/resources/images/plugin/prefs.gif
    deleted file mode 100644
    index 2902bcdbf0..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/prefs.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/sampleLogs.gif b/logback-site/src/site/resources/images/plugin/sampleLogs.gif
    deleted file mode 100644
    index 6a12fcadc4..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/sampleLogs.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/plugin/stackTrace.gif b/logback-site/src/site/resources/images/plugin/stackTrace.gif
    deleted file mode 100644
    index 0e50114f7a..0000000000
    Binary files a/logback-site/src/site/resources/images/plugin/stackTrace.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/setup/deselectMavenDependenciesExport.png b/logback-site/src/site/resources/images/setup/deselectMavenDependenciesExport.png
    deleted file mode 100755
    index 2c447478f2..0000000000
    Binary files a/logback-site/src/site/resources/images/setup/deselectMavenDependenciesExport.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/setup/discoverM2EConnectors.png b/logback-site/src/site/resources/images/setup/discoverM2EConnectors.png
    deleted file mode 100755
    index da52a866d3..0000000000
    Binary files a/logback-site/src/site/resources/images/setup/discoverM2EConnectors.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/setup/removeGroovyExclusions.png b/logback-site/src/site/resources/images/setup/removeGroovyExclusions.png
    deleted file mode 100755
    index 97e708a17f..0000000000
    Binary files a/logback-site/src/site/resources/images/setup/removeGroovyExclusions.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/setup/remove_evaluator_template.jpg b/logback-site/src/site/resources/images/setup/remove_evaluator_template.jpg
    deleted file mode 100755
    index be9455dd1e..0000000000
    Binary files a/logback-site/src/site/resources/images/setup/remove_evaluator_template.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/setup/remove_gen_src.jpg b/logback-site/src/site/resources/images/setup/remove_gen_src.jpg
    deleted file mode 100755
    index 7be5c95216..0000000000
    Binary files a/logback-site/src/site/resources/images/setup/remove_gen_src.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/images/turboFilterForMDC.png b/logback-site/src/site/resources/images/turboFilterForMDC.png
    deleted file mode 100644
    index 5c2d6f722e..0000000000
    Binary files a/logback-site/src/site/resources/images/turboFilterForMDC.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/logback-2011.ppt b/logback-site/src/site/resources/logback-2011.ppt
    deleted file mode 100644
    index aa3d289ab4..0000000000
    Binary files a/logback-site/src/site/resources/logback-2011.ppt and /dev/null differ
    diff --git a/logback-site/src/site/resources/logback.ppt b/logback-site/src/site/resources/logback.ppt
    deleted file mode 100644
    index 4593415287..0000000000
    Binary files a/logback-site/src/site/resources/logback.ppt and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/appender.uml b/logback-site/src/site/resources/manual/images/chapters/appenders/appender.uml
    deleted file mode 100644
    index 20ff4cfdc5..0000000000
    --- a/logback-site/src/site/resources/manual/images/chapters/appenders/appender.uml
    +++ /dev/null
    @@ -1,1179 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<XPD:PROJECT xmlns:XPD="http://www.staruml.com" version="1">
    -<XPD:HEADER>
    -<XPD:SUBUNITS>
    -</XPD:SUBUNITS>
    -<XPD:PROFILES>
    -<XPD:PROFILE>UMLStandard</XPD:PROFILE>
    -</XPD:PROFILES>
    -</XPD:HEADER>
    -<XPD:BODY>
    -<XPD:OBJ name="DocumentElement" type="UMLProject" guid="JEGKOL0wwE6O2Emy29kFuwAA">
    -<XPD:ATTR name="Title" type="string">Untitled</XPD:ATTR>
    -<XPD:ATTR name="#OwnedElements" type="integer">6</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLModel" guid="Nu7ucmhxa0aC5WTL/Hl5qgAA">
    -<XPD:ATTR name="Name" type="string">Use Case Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">useCaseModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLUseCaseDiagram" guid="AOpe9EpUuEqbJkhPg6UN+wAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">Nu7ucmhxa0aC5WTL/Hl5qgAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLUseCaseDiagramView" guid="jha7HHI2JEmss41Kcx5rmAAA">
    -<XPD:REF name="Diagram">AOpe9EpUuEqbJkhPg6UN+wAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[1]" type="UMLModel" guid="nzHC5ZSub0yOmXzqddVh7QAA">
    -<XPD:ATTR name="Name" type="string">Analysis Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">analysisModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLClassDiagram" guid="ucvrQjCCHEOMfGCAAc5zPQAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:ATTR name="DefaultDiagram" type="boolean">True</XPD:ATTR>
    -<XPD:ATTR name="DiagramType" type="string">RobustnessDiagram</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">nzHC5ZSub0yOmXzqddVh7QAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLClassDiagramView" guid="sxIjWZ8eT0KTmxDhcr84UAAA">
    -<XPD:REF name="Diagram">ucvrQjCCHEOMfGCAAc5zPQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[2]" type="UMLModel" guid="dQlYoesWY0yxg2VJCxHxOQAA">
    -<XPD:ATTR name="Name" type="string">Design Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">designModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLClassDiagram" guid="jdoFPKplx0W53YpEZi95TAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:ATTR name="DefaultDiagram" type="boolean">True</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLClassDiagramView" guid="oWGUMKWgOEus+/3Jqk/tpAAA">
    -<XPD:REF name="Diagram">jdoFPKplx0W53YpEZi95TAAA</XPD:REF>
    -<XPD:ATTR name="#OwnedViews" type="integer">15</XPD:ATTR>
    -<XPD:OBJ name="OwnedViews[0]" type="UMLClassView" guid="tSxGyrwfEkSgp1EcheSXaAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">496</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">140</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">161</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">69</XPD:ATTR>
    -<XPD:ATTR name="ShowProperty" type="boolean">True</XPD:ATTR>
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="4OJodmUjvU6Oqy3fe+ZfuQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="nTnCIikI3UOMxs9QtoE1sgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">Appender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="iOfigC5hM06553UhkG3o4gAA">
    -<XPD:ATTR name="Text" type="string">&lt;&lt;interface&gt;&gt;</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="gAAMY35/8kO92uV18LmCWAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="m+f/Nz96gEqfQeHGgfAxjwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="ViAxcLeYxEC/QDB9dCuwagAA">
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="gvxeAhX+WkGcvqw6KjlA7wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[1]" type="UMLClassView" guid="NaiNuZ1qLUu+5nZq2pgoJgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">448</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">384</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">257</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">69</XPD:ATTR>
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="3Qx0O28WLEC1wlpntM/QWQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="gLLmeDlS+Ua9NL1IKkZqBgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">OutputStreamAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="7awZOkK3cUS1dBI9YKP2TwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="LtVVLfcmbkq7TWeA6T7S4AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="H/1viIm6yE20XaYIV5XQmgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="/LiIyuskqE6jkbei5roLSgAA">
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="LFUOQyCzKkODyDltdenqSQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[2]" type="UMLClassView" guid="82N1NUXIMU2s0JVvIgA33wAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">460</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">248</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">233</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">95</XPD:ATTR>
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="MEOstStI0EC0RQoiT5DFLQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="CDkuaYXZDEC5+cMzdHsl8wAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">UnsynchronizedAppenderBase</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="N/VzkXk9vkGTb/Gq05Hm4gAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="Wbq2KwFqq0uOx2yShkE49QAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="MbkisNcJq06fy8rfNf//qgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="bHWlSSp0L0KsF9iS2K/VuAAA">
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="A3o0HBZQP0+GUAIDarVM5AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[3]" type="UMLClassView" guid="uk+yKXVJ4kGbJp9tS5jKTwAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">348</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">500</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">205</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">56</XPD:ATTR>
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="pd1JNpNdxkm4N3mMjABxcgAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="Jfgvt5uhf0aDXP4fvB8FuAAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">ConsoleAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="x915MlraB0+hK5agb0HxVwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="o72CPBdC2UGyraSt4vxsHQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="nH6UBhoeX0KbQRIkAzLiWAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="Jb94kigpgUqu4dlcc+CGdAAA">
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="QxduxAxohEmCZQGHeQsZcgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[4]" type="UMLRealizationView" guid="lL0hvfNl1UCp0ZckmzYqAAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">576,248;576,208</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:REF name="Head">tSxGyrwfEkSgp1EcheSXaAAA</XPD:REF>
    -<XPD:REF name="Tail">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="R+yIkrw1xkGRRmxo6cIv7AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="MHt2cHh8nkq4xztkXeNA1AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="1cFzyVYsZUexhz9n3LRU9AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[5]" type="UMLGeneralizationView" guid="fqotcKjEKUu0dHy5krCbbgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">576,384;576,342</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:REF name="Head">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:REF name="Tail">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="a5TKNXnsu0Cm1O3vWK2A5QAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="lfapsg0nvkOzLtKq21jKcQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="J/CqIqCm4k64OiOc5XyUFgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[6]" type="UMLGeneralizationView" guid="Byj7ulJD9UmN+tawKGwCEgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">481,500;537,452</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">uk+yKXVJ4kGbJp9tS5jKTwAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="HQiDhC6IA0iqlMhf1tdcJQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="npCChlHvbECPkeBWStHdGwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="DNi8lqQWi0GHkNrHlB+wdAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[7]" type="UMLClassView" guid="umRYCn2dek6PVCutQ7EHjgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">612</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">500</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">181</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">82</XPD:ATTR>
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="IY/ehYDmxki8kSqbCHHCfQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="lcA4v9eDHUugTZoPU740LAAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">FileAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="OVAuCrBMykaCfcii7gDdCwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="xiHCrB3qu0yBBg0U/q3EawAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="XJMRRQ4IvEKN76PkDbMKOQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="wZmHw439VE+zuefCYQQqVAAA">
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="PHqgEBQhpEmlVKbjkRedkAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[8]" type="UMLGeneralizationView" guid="xnvee9U5202YS/ztrwJSRAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">661,500;611,452</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="oASzcKN58EuYmztjk4WiEwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="O0OgQK6n40urO6JVc74njgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="bgKTOidFkEeRLn6bk0KEEgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[9]" type="UMLClassView" guid="rHFGD6C9T0y5SMWPFgS09QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">596</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">628</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">213</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">69</XPD:ATTR>
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="OuAPH3aKlk2rg4cWgwv2BQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="BEu6SCte7E6fslLxmD8brgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">RollingFileAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="diAZdqUjrUSnaEkyvki5HwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="HpL3GnQOO0KyNABCTyAsRwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="1hKuFZUojkuzeqX9AiWUlwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="g+GUofZEmU2lEM1VL3vLIQAA">
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="4HSOp+H3/ECJnOpzcIo3jwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[10]" type="UMLClassView" guid="0AQ8gdYUKk2Ms7nTdCyd3QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">788</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">372</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">137</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">95</XPD:ATTR>
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="h/ce/kieOUyyxRZ6c71fTQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="UZy1Qq+RWUWzR+BkMD2qBwAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">Encoder</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="A/4/paNKfEeyuIjaeXDosAAA">
    -<XPD:ATTR name="Text" type="string">&lt;&lt;interface&gt;&gt;</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="Y950eaGp5kKS5JcixC2pLAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="lyHSH0gj5USAm8DDiVvc1AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="A7cLwlK4RESapHusUAalugAA">
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="qQCEnWeXyUaictLrSH/4IgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[11]" type="UMLAssociationView" guid="lMmxZJude0e4iK5TJK4KmwAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">788,419;704,419</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">0AQ8gdYUKk2Ms7nTdCyd3QAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="Lpxx+3J+D0S223D5ODQq4wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="rhfNbKEGWUaK9KPGeno2AAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="5lfzJsxzLkS60zCpNz5+twAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadRoleNameLabel" type="EdgeLabelView" guid="w+U1pR05XkeMT1rnnS2TcgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailRoleNameLabel" type="EdgeLabelView" guid="G2JYoZaOKku0lwIKN7cKRQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadMultiplicityLabel" type="EdgeLabelView" guid="Wzm+qJkDL0+jgE0ioVsNvwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailMultiplicityLabel" type="EdgeLabelView" guid="h/GU+p9gHkyMIIzW5Tap4wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadPropertyLabel" type="EdgeLabelView" guid="MLngpvqsdU+i4Djdsali7wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailPropertyLabel" type="EdgeLabelView" guid="yN2upZx6REeZpBi/IRMnlgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadQualifierCompartment" type="UMLQualifierCompartmentView" guid="ZVjULz2utUKwdUcoGweZkgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailQualifierCompartment" type="UMLQualifierCompartmentView" guid="4leicQixUEWISYBF6P8LSgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[12]" type="UMLGeneralizationView" guid="VGws4gb7FEGKDPIpXKW13QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">702,628;702,581</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -<XPD:REF name="Head">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:REF name="Tail">rHFGD6C9T0y5SMWPFgS09QAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="2t9xQX0DaECER1G841x4ggAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="ZJ+e5yqWkkyJmgI5NCe9qQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="uWFySM7Jq026r7z2lz9/TgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[13]" type="UMLClassView" guid="XnLeT4tUAUivKFCoAi8mCgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">240</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">392</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">152</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">56</XPD:ATTR>
    -<XPD:REF name="Model">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="w2rmuF62nUmUAhOhrmqOyQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="irr7mImnC0OIf0QeBwSo3AAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">Filter</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="/ZkI9RE/EkCmMYBYxW9vSgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="/Seh5JPXJ0WqIDeUCCMxGQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="Y+K3bBi2DEiJlBzf/w+degAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="x12I7wVJHkKX8s9N/bVWLQAA">
    -<XPD:REF name="Model">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="ybd6Chc7bUaikqpMu2gydwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[14]" type="UMLAssociationView" guid="1GuVB976AUueN1l11+x/9AAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">391,419;448,419</XPD:ATTR>
    -<XPD:REF name="Model">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">XnLeT4tUAUivKFCoAi8mCgAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="rvc1lZzjJkyWMr6P/IPRnQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="bCA0jUpnukKQFQdvrUXC5gAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="okxxVKfDiESE7wFAiHiTwQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadRoleNameLabel" type="EdgeLabelView" guid="IPgYB1C27EyGv2x09901MgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">IA0uuG/DqUSPP93NTt7KPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailRoleNameLabel" type="EdgeLabelView" guid="mdaJom9Aq0CCnF2E8j5mfgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">rtqCfaDNzE6xCSqNvsGQmAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadMultiplicityLabel" type="EdgeLabelView" guid="QVrq+D0yCke4xdkp2ecVFgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">IA0uuG/DqUSPP93NTt7KPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailMultiplicityLabel" type="EdgeLabelView" guid="u6QeWosm+0GF06B6oeoA3AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">rtqCfaDNzE6xCSqNvsGQmAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadPropertyLabel" type="EdgeLabelView" guid="YoNg+kiZ/0Kk9BC8Hs4jBgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">IA0uuG/DqUSPP93NTt7KPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailPropertyLabel" type="EdgeLabelView" guid="TwLu6YMezke1PLwCqD9EKwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">rtqCfaDNzE6xCSqNvsGQmAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadQualifierCompartment" type="UMLQualifierCompartmentView" guid="j3F8XkYCU0K+Wb/6ACwHmAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">IA0uuG/DqUSPP93NTt7KPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailQualifierCompartment" type="UMLQualifierCompartmentView" guid="5M6UkF5vREiYn4A49eajJAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">rtqCfaDNzE6xCSqNvsGQmAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#OwnedElements" type="integer">28</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLInterface" guid="bL5DGTE1wkqFiYq+yJuTRAAA">
    -<XPD:ATTR name="Name" type="string">Bae, Rankyoung</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[1]" type="UMLInterface" guid="PfW7flMvskqKmFDofejCYQAA">
    -<XPD:ATTR name="Name" type="string">Jung, Yoontae</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[2]" type="UMLInterface" guid="XthOkDh0rk67+FyPgXCRgAAA">
    -<XPD:ATTR name="Name" type="string">Kim, Hyunsoo</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[3]" type="UMLInterface" guid="WhfjywuM1kqS7RtEmuvAlgAA">
    -<XPD:ATTR name="Name" type="string">Kim, Jeongil</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[4]" type="UMLClass" guid="Ec4V6xlK5USBInAfRT+kBgAA">
    -<XPD:ATTR name="Name" type="string">Appender</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">interface</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">tSxGyrwfEkSgp1EcheSXaAAA</XPD:REF>
    -<XPD:REF name="Views[1]">m+f/Nz96gEqfQeHGgfAxjwAA</XPD:REF>
    -<XPD:REF name="Views[2]">ViAxcLeYxEC/QDB9dCuwagAA</XPD:REF>
    -<XPD:REF name="Views[3]">gvxeAhX+WkGcvqw6KjlA7wAA</XPD:REF>
    -<XPD:ATTR name="#ClientDependencies" type="integer">1</XPD:ATTR>
    -<XPD:REF name="ClientDependencies[0]">kjMmekdMhUCZp7nT1+7m5QAA</XPD:REF>
    -<XPD:ATTR name="#SupplierDependencies" type="integer">2</XPD:ATTR>
    -<XPD:REF name="SupplierDependencies[0]">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:REF name="SupplierDependencies[1]">kjMmekdMhUCZp7nT1+7m5QAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="FRx7C/jgm0uUQ2KJbSES0QAA">
    -<XPD:ATTR name="Name" type="string">doAppend</XPD:ATTR>
    -<XPD:REF name="Owner">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="HEOzMeB7hUCVhMMCFiMU/QAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">FRx7C/jgm0uUQ2KJbSES0QAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="/QxGe2nRTke4OPZr6B8DjwAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">FRx7C/jgm0uUQ2KJbSES0QAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[5]" type="UMLClass" guid="wrUqRmysaUaTNG+EIldTAgAA">
    -<XPD:ATTR name="Name" type="string">Lee, Jangwoo</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[6]" type="UMLClass" guid="R4ueGs13U0uU7uA7GfQuwgAA">
    -<XPD:ATTR name="Name" type="string">Lee, Minkyu</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[7]" type="UMLClass" guid="cZJpDMIhLkaN+6380nTjCgAA">
    -<XPD:ATTR name="Name" type="string">Lim, Heejin</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[8]" type="UMLInterface" guid="KkLGJmfcO0y7keuSLh3xpQAA">
    -<XPD:ATTR name="Name" type="string">Bae, Rankyoung1</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[9]" type="UMLClass" guid="rzT262Z1jEeiLPfjq8YXAQAA">
    -<XPD:ATTR name="Name" type="string">OutputStreamAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Views[1]">H/1viIm6yE20XaYIV5XQmgAA</XPD:REF>
    -<XPD:REF name="Views[2]">/LiIyuskqE6jkbei5roLSgAA</XPD:REF>
    -<XPD:REF name="Views[3]">LFUOQyCzKkODyDltdenqSQAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="Y/8ny1CDlUmGw7XO+p1IVAAA">
    -<XPD:ATTR name="Name" type="string">setOutputStream</XPD:ATTR>
    -<XPD:REF name="Owner">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="o7mxtv1VFUuGMzTZ/PzpJQAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">Y/8ny1CDlUmGw7XO+p1IVAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="WiQnBPlZNUKAb1kDr8rYMAAA">
    -<XPD:ATTR name="Name" type="string">OutputStream</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">Y/8ny1CDlUmGw7XO+p1IVAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="LBTffoJKpkuteoCZOKUnaQAA">
    -<XPD:ATTR name="Name" type="string">setEncoder</XPD:ATTR>
    -<XPD:REF name="Owner">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="QBbT8+accEGiDOyIxemC6gAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">LBTffoJKpkuteoCZOKUnaQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="euJK1npBAkKQLyA/ixyf4gAA">
    -<XPD:ATTR name="Name" type="string">Encoder&lt;E&gt;</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">LBTffoJKpkuteoCZOKUnaQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#Associations" type="integer">3</XPD:ATTR>
    -<XPD:REF name="Associations[0]">CNBYSz/4s0OUBwR3Ujj7qAAA</XPD:REF>
    -<XPD:REF name="Associations[1]">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -<XPD:REF name="Associations[2]">IA0uuG/DqUSPP93NTt7KPwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[10]" type="UMLClass" guid="xVL+I0ePyEKePe2ybmIZqQAA">
    -<XPD:ATTR name="Name" type="string">UnsynchronizedAppenderBase</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:REF name="Views[1]">MbkisNcJq06fy8rfNf//qgAA</XPD:REF>
    -<XPD:REF name="Views[2]">bHWlSSp0L0KsF9iS2K/VuAAA</XPD:REF>
    -<XPD:REF name="Views[3]">A3o0HBZQP0+GUAIDarVM5AAA</XPD:REF>
    -<XPD:ATTR name="#ClientDependencies" type="integer">1</XPD:ATTR>
    -<XPD:REF name="ClientDependencies[0]">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">mt8sHMuW6k6vvkswchHN9AAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">mt8sHMuW6k6vvkswchHN9AAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">4</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="QrizX1w8Uk+lWkr80819WAAA">
    -<XPD:ATTR name="Name" type="string">doAppend</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="i3SFY3XjzkqfjCdeRItB0AAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QrizX1w8Uk+lWkr80819WAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="rcsyT8SmtEaJWAgOu29ifgAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QrizX1w8Uk+lWkr80819WAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="G5EvR3IAqUWyIUSQT5iKfAAA">
    -<XPD:ATTR name="Name" type="string">append</XPD:ATTR>
    -<XPD:ATTR name="Visibility" type="UMLVisibilityKind">vkProtected</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="BZo+omwID0Op8lvSMhrmCAAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">G5EvR3IAqUWyIUSQT5iKfAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="0mhi1Re0mUaekyTApQtYKgAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">G5EvR3IAqUWyIUSQT5iKfAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[2]" type="UMLOperation" guid="noZlW3WOpUSNYP7MIP2dIAAA">
    -<XPD:ATTR name="Name" type="string">addFilter</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="7JBY4nyhIES1n0jpwFzs0wAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">noZlW3WOpUSNYP7MIP2dIAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="Zd/8hSZIgUuOnnlNuMOW3QAA">
    -<XPD:ATTR name="Name" type="string">Filter&lt;E&gt; filter</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">noZlW3WOpUSNYP7MIP2dIAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[3]" type="UMLOperation" guid="+Hda0J6VTEGM7V6+m3aW7QAA">
    -<XPD:ATTR name="Name" type="string">getFilterChainDecision</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="b7Oh8zbH6kKEeryzkMlLQgAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">FilterReply</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">+Hda0J6VTEGM7V6+m3aW7QAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="QFIz2saC3kK9QRns9gVVIwAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">+Hda0J6VTEGM7V6+m3aW7QAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[11]" type="UMLClass" guid="yqvs3sZ9DUGqcZqP0JhEYQAA">
    -<XPD:ATTR name="Name" type="string">ConsoleAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">uk+yKXVJ4kGbJp9tS5jKTwAA</XPD:REF>
    -<XPD:REF name="Views[1]">nH6UBhoeX0KbQRIkAzLiWAAA</XPD:REF>
    -<XPD:REF name="Views[2]">Jb94kigpgUqu4dlcc+CGdAAA</XPD:REF>
    -<XPD:REF name="Views[3]">QxduxAxohEmCZQGHeQsZcgAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">3</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">KECSr2IcSEKuUekqxWFfQQAA</XPD:REF>
    -<XPD:REF name="Generalizations[1]">jfTGxiHUzUynYKDudsAtlgAA</XPD:REF>
    -<XPD:REF name="Generalizations[2]">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">KECSr2IcSEKuUekqxWFfQQAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">jfTGxiHUzUynYKDudsAtlgAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="QBjgNn4eI0Gb0Lu8uW8RgQAA">
    -<XPD:ATTR name="Name" type="string">setTarget</XPD:ATTR>
    -<XPD:REF name="Owner">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="D6Tvg4B6zk2nDCrvCSXiogAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QBjgNn4eI0Gb0Lu8uW8RgQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="5HN4co7SEkGYIIXb9P4IcwAA">
    -<XPD:ATTR name="Name" type="string">String</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QBjgNn4eI0Gb0Lu8uW8RgQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[12]" type="UMLRealization" guid="MLMgV5lk30+73KeQUlkJ1AAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Client">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:REF name="Supplier">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">lL0hvfNl1UCp0ZckmzYqAAAA</XPD:REF>
    -<XPD:REF name="Views[1]">R+yIkrw1xkGRRmxo6cIv7AAA</XPD:REF>
    -<XPD:REF name="Views[2]">MHt2cHh8nkq4xztkXeNA1AAA</XPD:REF>
    -<XPD:REF name="Views[3]">1cFzyVYsZUexhz9n3LRU9AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[13]" type="UMLRealization" guid="kjMmekdMhUCZp7nT1+7m5QAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Client">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:REF name="Supplier">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[14]" type="UMLGeneralization" guid="mt8sHMuW6k6vvkswchHN9AAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:REF name="Parent">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[15]" type="UMLGeneralization" guid="iGZQWONRSkmUZXwA2ctTYAAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:REF name="Parent">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">fqotcKjEKUu0dHy5krCbbgAA</XPD:REF>
    -<XPD:REF name="Views[1]">a5TKNXnsu0Cm1O3vWK2A5QAA</XPD:REF>
    -<XPD:REF name="Views[2]">lfapsg0nvkOzLtKq21jKcQAA</XPD:REF>
    -<XPD:REF name="Views[3]">J/CqIqCm4k64OiOc5XyUFgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[16]" type="UMLGeneralization" guid="KECSr2IcSEKuUekqxWFfQQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[17]" type="UMLGeneralization" guid="jfTGxiHUzUynYKDudsAtlgAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[18]" type="UMLGeneralization" guid="AjD3odwWRkqn7DLLtsqSMQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">Byj7ulJD9UmN+tawKGwCEgAA</XPD:REF>
    -<XPD:REF name="Views[1]">HQiDhC6IA0iqlMhf1tdcJQAA</XPD:REF>
    -<XPD:REF name="Views[2]">npCChlHvbECPkeBWStHdGwAA</XPD:REF>
    -<XPD:REF name="Views[3]">DNi8lqQWi0GHkNrHlB+wdAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[19]" type="UMLClass" guid="fbO+5cdSPEmSXuXSqE8gXgAA">
    -<XPD:ATTR name="Name" type="string">FileAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:REF name="Views[1]">XJMRRQ4IvEKN76PkDbMKOQAA</XPD:REF>
    -<XPD:REF name="Views[2]">wZmHw439VE+zuefCYQQqVAAA</XPD:REF>
    -<XPD:REF name="Views[3]">PHqgEBQhpEmlVKbjkRedkAAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">3</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="RGzx8sTCQ0+SvjEhj3FCpwAA">
    -<XPD:ATTR name="Name" type="string">setFile</XPD:ATTR>
    -<XPD:REF name="Owner">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="uxSNpKmGV0+K54EwgffqpgAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">RGzx8sTCQ0+SvjEhj3FCpwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="i90wHpacOEiHseZIDnpZAAAA">
    -<XPD:ATTR name="Name" type="string">String</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">RGzx8sTCQ0+SvjEhj3FCpwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="emxKAOn2sEGODzlrBwfBDQAA">
    -<XPD:ATTR name="Name" type="string">setPrudent</XPD:ATTR>
    -<XPD:REF name="Owner">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="Qzhwjji8FkawThwNpGWYlAAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">emxKAOn2sEGODzlrBwfBDQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="/c5zBe26tkWrpTH8RQJKDgAA">
    -<XPD:ATTR name="Name" type="string">boolean</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">emxKAOn2sEGODzlrBwfBDQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[2]" type="UMLOperation" guid="pzOru1aa1kKBEpPdw/x8HwAA">
    -<XPD:ATTR name="Name" type="string">setAppend</XPD:ATTR>
    -<XPD:REF name="Owner">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="xfe5Kz7eGUGt/qsdAVz1nwAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">pzOru1aa1kKBEpPdw/x8HwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="byGZtqglLkSMXqHvyAwzXQAA">
    -<XPD:ATTR name="Name" type="string">boolean</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">pzOru1aa1kKBEpPdw/x8HwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[20]" type="UMLGeneralization" guid="DwI/SvxrPEa+xlJeUNknVQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:REF name="Parent">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">xnvee9U5202YS/ztrwJSRAAA</XPD:REF>
    -<XPD:REF name="Views[1]">oASzcKN58EuYmztjk4WiEwAA</XPD:REF>
    -<XPD:REF name="Views[2]">O0OgQK6n40urO6JVc74njgAA</XPD:REF>
    -<XPD:REF name="Views[3]">bgKTOidFkEeRLn6bk0KEEgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[21]" type="UMLClass" guid="V4xOMUpxokyB4fxrQNJPxQAA">
    -<XPD:ATTR name="Name" type="string">RollingFileAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">rHFGD6C9T0y5SMWPFgS09QAA</XPD:REF>
    -<XPD:REF name="Views[1]">1hKuFZUojkuzeqX9AiWUlwAA</XPD:REF>
    -<XPD:REF name="Views[2]">g+GUofZEmU2lEM1VL3vLIQAA</XPD:REF>
    -<XPD:REF name="Views[3]">4HSOp+H3/ECJnOpzcIo3jwAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="zJE7QEhOYkGwavHKRi/e1gAA">
    -<XPD:ATTR name="Name" type="string">setRollingPolicy</XPD:ATTR>
    -<XPD:REF name="Owner">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="fBG8X0xpiU2QG4VhcArHXwAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">zJE7QEhOYkGwavHKRi/e1gAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="pokyvx+ik0yawdfYXl/EHQAA">
    -<XPD:ATTR name="Name" type="string">RollingPolicy</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">zJE7QEhOYkGwavHKRi/e1gAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="90I+pA0XnkiDcK8Um95kDwAA">
    -<XPD:ATTR name="Name" type="string">setTriggeringPolicy</XPD:ATTR>
    -<XPD:REF name="Owner">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="/rKEm7ax1kOVmGAMDsfXRgAA">
    -<XPD:ATTR name="Name" type="string">TriggeringPolicy&lt;E&gt;</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">90I+pA0XnkiDcK8Um95kDwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[22]" type="UMLClass" guid="F1lk6TogWUGfYFH4FvBLOAAA">
    -<XPD:ATTR name="Name" type="string">Encoder</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">interface</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">0AQ8gdYUKk2Ms7nTdCyd3QAA</XPD:REF>
    -<XPD:REF name="Views[1]">lyHSH0gj5USAm8DDiVvc1AAA</XPD:REF>
    -<XPD:REF name="Views[2]">A7cLwlK4RESapHusUAalugAA</XPD:REF>
    -<XPD:REF name="Views[3]">qQCEnWeXyUaictLrSH/4IgAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">3</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="KTkAdBDG1k6vBX6/3VDh3wAA">
    -<XPD:ATTR name="Name" type="string">init</XPD:ATTR>
    -<XPD:REF name="Owner">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="K1xInu1IW06VU8RAornDFgAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">KTkAdBDG1k6vBX6/3VDh3wAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="q0oNOzYnwkC00CgD5grrpgAA">
    -<XPD:ATTR name="Name" type="string">OutputStream</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">KTkAdBDG1k6vBX6/3VDh3wAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="0OIKQj9lz0epLF4VThStRgAA">
    -<XPD:ATTR name="Name" type="string">doEncode</XPD:ATTR>
    -<XPD:REF name="Owner">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="DMbvra8Y4E+d/ziodIGqyQAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">0OIKQj9lz0epLF4VThStRgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="v+KY7Q4t4kak92z8YJ0K7AAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">0OIKQj9lz0epLF4VThStRgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[2]" type="UMLOperation" guid="I6ieXuGhMkCcbz9MDCy1uAAA">
    -<XPD:ATTR name="Name" type="string">close</XPD:ATTR>
    -<XPD:REF name="Owner">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="XyFkApGcmESs4dwJgM1s8AAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">I6ieXuGhMkCcbz9MDCy1uAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#Associations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Associations[0]">1RTpKQO+4U+uqZahckvNEwAA</XPD:REF>
    -<XPD:REF name="Associations[1]">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[23]" type="UMLAssociation" guid="u9/9xY5vOU2kSfX9AmlMAwAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Connections" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Connections[0]" type="UMLAssociationEnd" guid="CNBYSz/4s0OUBwR3Ujj7qAAA">
    -<XPD:REF name="Association">u9/9xY5vOU2kSfX9AmlMAwAA</XPD:REF>
    -<XPD:REF name="Participant">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Connections[1]" type="UMLAssociationEnd" guid="1RTpKQO+4U+uqZahckvNEwAA">
    -<XPD:ATTR name="Aggregation" type="UMLAggregationKind">akComposite</XPD:ATTR>
    -<XPD:REF name="Association">u9/9xY5vOU2kSfX9AmlMAwAA</XPD:REF>
    -<XPD:REF name="Participant">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[24]" type="UMLAssociation" guid="kXyf6+RLrki8M9yh9qd9JwAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">lMmxZJude0e4iK5TJK4KmwAA</XPD:REF>
    -<XPD:REF name="Views[1]">Lpxx+3J+D0S223D5ODQq4wAA</XPD:REF>
    -<XPD:REF name="Views[2]">rhfNbKEGWUaK9KPGeno2AAAA</XPD:REF>
    -<XPD:REF name="Views[3]">5lfzJsxzLkS60zCpNz5+twAA</XPD:REF>
    -<XPD:ATTR name="#Connections" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Connections[0]" type="UMLAssociationEnd" guid="JFP7YnD8ak2COLWe8jfBSwAA">
    -<XPD:REF name="Association">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Participant">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">G2JYoZaOKku0lwIKN7cKRQAA</XPD:REF>
    -<XPD:REF name="Views[1]">h/GU+p9gHkyMIIzW5Tap4wAA</XPD:REF>
    -<XPD:REF name="Views[2]">yN2upZx6REeZpBi/IRMnlgAA</XPD:REF>
    -<XPD:REF name="Views[3]">4leicQixUEWISYBF6P8LSgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Connections[1]" type="UMLAssociationEnd" guid="KNPa5jL8m0SnxCPJDiLPEwAA">
    -<XPD:ATTR name="Aggregation" type="UMLAggregationKind">akComposite</XPD:ATTR>
    -<XPD:REF name="Association">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Participant">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">w+U1pR05XkeMT1rnnS2TcgAA</XPD:REF>
    -<XPD:REF name="Views[1]">Wzm+qJkDL0+jgE0ioVsNvwAA</XPD:REF>
    -<XPD:REF name="Views[2]">MLngpvqsdU+i4Djdsali7wAA</XPD:REF>
    -<XPD:REF name="Views[3]">ZVjULz2utUKwdUcoGweZkgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[25]" type="UMLGeneralization" guid="MAm8gr3YDE+lxocsUB9afAAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:REF name="Parent">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">VGws4gb7FEGKDPIpXKW13QAA</XPD:REF>
    -<XPD:REF name="Views[1]">2t9xQX0DaECER1G841x4ggAA</XPD:REF>
    -<XPD:REF name="Views[2]">ZJ+e5yqWkkyJmgI5NCe9qQAA</XPD:REF>
    -<XPD:REF name="Views[3]">uWFySM7Jq026r7z2lz9/TgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[26]" type="UMLClass" guid="ObD18j8uiUuqWEEES+U7VwAA">
    -<XPD:ATTR name="Name" type="string">Filter</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">XnLeT4tUAUivKFCoAi8mCgAA</XPD:REF>
    -<XPD:REF name="Views[1]">Y+K3bBi2DEiJlBzf/w+degAA</XPD:REF>
    -<XPD:REF name="Views[2]">x12I7wVJHkKX8s9N/bVWLQAA</XPD:REF>
    -<XPD:REF name="Views[3]">ybd6Chc7bUaikqpMu2gydwAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="h2ChKW0gZES/JuZQuxznnwAA">
    -<XPD:ATTR name="Name" type="string">decide</XPD:ATTR>
    -<XPD:REF name="Owner">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="en+rpIVeCkSoJAa812PoLwAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">FilterReply</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">h2ChKW0gZES/JuZQuxznnwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="uNlc9FZz80ancniLw7/8MgAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">h2ChKW0gZES/JuZQuxznnwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#Associations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Associations[0]">rtqCfaDNzE6xCSqNvsGQmAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[27]" type="UMLAssociation" guid="uh4HvYt2ZECJHo2VN0YtPwAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">1GuVB976AUueN1l11+x/9AAA</XPD:REF>
    -<XPD:REF name="Views[1]">rvc1lZzjJkyWMr6P/IPRnQAA</XPD:REF>
    -<XPD:REF name="Views[2]">bCA0jUpnukKQFQdvrUXC5gAA</XPD:REF>
    -<XPD:REF name="Views[3]">okxxVKfDiESE7wFAiHiTwQAA</XPD:REF>
    -<XPD:ATTR name="#Connections" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Connections[0]" type="UMLAssociationEnd" guid="rtqCfaDNzE6xCSqNvsGQmAAA">
    -<XPD:REF name="Association">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -<XPD:REF name="Participant">ObD18j8uiUuqWEEES+U7VwAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">mdaJom9Aq0CCnF2E8j5mfgAA</XPD:REF>
    -<XPD:REF name="Views[1]">TwLu6YMezke1PLwCqD9EKwAA</XPD:REF>
    -<XPD:REF name="Views[2]">u6QeWosm+0GF06B6oeoA3AAA</XPD:REF>
    -<XPD:REF name="Views[3]">5M6UkF5vREiYn4A49eajJAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Connections[1]" type="UMLAssociationEnd" guid="IA0uuG/DqUSPP93NTt7KPwAA">
    -<XPD:ATTR name="Aggregation" type="UMLAggregationKind">akAggregate</XPD:ATTR>
    -<XPD:REF name="Association">uh4HvYt2ZECJHo2VN0YtPwAA</XPD:REF>
    -<XPD:REF name="Participant">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">IPgYB1C27EyGv2x09901MgAA</XPD:REF>
    -<XPD:REF name="Views[1]">YoNg+kiZ/0Kk9BC8Hs4jBgAA</XPD:REF>
    -<XPD:REF name="Views[2]">QVrq+D0yCke4xdkp2ecVFgAA</XPD:REF>
    -<XPD:REF name="Views[3]">j3F8XkYCU0K+Wb/6ACwHmAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[3]" type="UMLModel" guid="UTbE66PthkuXloDrctmmwwAA">
    -<XPD:ATTR name="Name" type="string">Implementation Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">implementationModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLComponentDiagram" guid="UJbtRzsKuEa7sZAmr/tDGAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">UTbE66PthkuXloDrctmmwwAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLComponentDiagramView" guid="vo5SIJcJIUWJz7mqomMr4wAA">
    -<XPD:REF name="Diagram">UJbtRzsKuEa7sZAmr/tDGAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#OwnedElements" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLInterface" guid="naDG3jNwKEqkpiKwcQF/xgAA">
    -<XPD:ATTR name="Name" type="string">dsd</XPD:ATTR>
    -<XPD:REF name="Namespace">UTbE66PthkuXloDrctmmwwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[4]" type="UMLModel" guid="sbAEE91CXU+VbUQcRp4FwgAA">
    -<XPD:ATTR name="Name" type="string">Deployment Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">deploymentModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLDeploymentDiagram" guid="tzrsCrrbjka5umz88mdzIAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">sbAEE91CXU+VbUQcRp4FwgAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLDeploymentDiagramView" guid="OlRAEhzdxkCw+mUnxs9v2wAA">
    -<XPD:REF name="Diagram">tzrsCrrbjka5umz88mdzIAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[5]" type="UMLModel" guid="mC0A50UCikqRZT73f5NgTgAA">
    -<XPD:ATTR name="Name" type="string">Analysis Model1</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">analysisModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:BODY>
    -</XPD:PROJECT>
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/appender.~ml b/logback-site/src/site/resources/manual/images/chapters/appenders/appender.~ml
    deleted file mode 100644
    index 6533149397..0000000000
    --- a/logback-site/src/site/resources/manual/images/chapters/appenders/appender.~ml
    +++ /dev/null
    @@ -1,865 +0,0 @@
    -<?xml version="1.0" encoding="UTF-8"?>
    -<XPD:PROJECT xmlns:XPD="http://www.staruml.com" version="1">
    -<XPD:HEADER>
    -<XPD:SUBUNITS>
    -</XPD:SUBUNITS>
    -<XPD:PROFILES>
    -<XPD:PROFILE>UMLStandard</XPD:PROFILE>
    -</XPD:PROFILES>
    -</XPD:HEADER>
    -<XPD:BODY>
    -<XPD:OBJ name="DocumentElement" type="UMLProject" guid="JEGKOL0wwE6O2Emy29kFuwAA">
    -<XPD:ATTR name="Title" type="string">Untitled</XPD:ATTR>
    -<XPD:ATTR name="#OwnedElements" type="integer">6</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLModel" guid="Nu7ucmhxa0aC5WTL/Hl5qgAA">
    -<XPD:ATTR name="Name" type="string">Use Case Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">useCaseModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLUseCaseDiagram" guid="AOpe9EpUuEqbJkhPg6UN+wAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">Nu7ucmhxa0aC5WTL/Hl5qgAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLUseCaseDiagramView" guid="jha7HHI2JEmss41Kcx5rmAAA">
    -<XPD:REF name="Diagram">AOpe9EpUuEqbJkhPg6UN+wAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[1]" type="UMLModel" guid="nzHC5ZSub0yOmXzqddVh7QAA">
    -<XPD:ATTR name="Name" type="string">Analysis Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">analysisModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLClassDiagram" guid="ucvrQjCCHEOMfGCAAc5zPQAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:ATTR name="DefaultDiagram" type="boolean">True</XPD:ATTR>
    -<XPD:ATTR name="DiagramType" type="string">RobustnessDiagram</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">nzHC5ZSub0yOmXzqddVh7QAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLClassDiagramView" guid="sxIjWZ8eT0KTmxDhcr84UAAA">
    -<XPD:REF name="Diagram">ucvrQjCCHEOMfGCAAc5zPQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[2]" type="UMLModel" guid="dQlYoesWY0yxg2VJCxHxOQAA">
    -<XPD:ATTR name="Name" type="string">Design Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">designModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLClassDiagram" guid="jdoFPKplx0W53YpEZi95TAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:ATTR name="DefaultDiagram" type="boolean">True</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLClassDiagramView" guid="oWGUMKWgOEus+/3Jqk/tpAAA">
    -<XPD:REF name="Diagram">jdoFPKplx0W53YpEZi95TAAA</XPD:REF>
    -<XPD:ATTR name="#OwnedViews" type="integer">13</XPD:ATTR>
    -<XPD:OBJ name="OwnedViews[0]" type="UMLClassView" guid="tSxGyrwfEkSgp1EcheSXaAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">464</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">76</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">161</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">93</XPD:ATTR>
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="4OJodmUjvU6Oqy3fe+ZfuQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="nTnCIikI3UOMxs9QtoE1sgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">Appender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="iOfigC5hM06553UhkG3o4gAA">
    -<XPD:ATTR name="Text" type="string">&lt;&lt;interface&gt;&gt;</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="gAAMY35/8kO92uV18LmCWAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="m+f/Nz96gEqfQeHGgfAxjwAA">
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="ViAxcLeYxEC/QDB9dCuwagAA">
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="gvxeAhX+WkGcvqw6KjlA7wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[1]" type="UMLClassView" guid="NaiNuZ1qLUu+5nZq2pgoJgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">364</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">384</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">361</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">56</XPD:ATTR>
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="3Qx0O28WLEC1wlpntM/QWQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="gLLmeDlS+Ua9NL1IKkZqBgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">OutputStreamAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="7awZOkK3cUS1dBI9YKP2TwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="LtVVLfcmbkq7TWeA6T7S4AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="H/1viIm6yE20XaYIV5XQmgAA">
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="/LiIyuskqE6jkbei5roLSgAA">
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="LFUOQyCzKkODyDltdenqSQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[2]" type="UMLClassView" guid="82N1NUXIMU2s0JVvIgA33wAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">428</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">240</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">228</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">95</XPD:ATTR>
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="MEOstStI0EC0RQoiT5DFLQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="CDkuaYXZDEC5+cMzdHsl8wAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">UnsynchronizedAppenderBase</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="N/VzkXk9vkGTb/Gq05Hm4gAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="Wbq2KwFqq0uOx2yShkE49QAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="MbkisNcJq06fy8rfNf//qgAA">
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="bHWlSSp0L0KsF9iS2K/VuAAA">
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="A3o0HBZQP0+GUAIDarVM5AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[3]" type="UMLClassView" guid="uk+yKXVJ4kGbJp9tS5jKTwAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">308</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">548</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">205</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">43</XPD:ATTR>
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="pd1JNpNdxkm4N3mMjABxcgAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="Jfgvt5uhf0aDXP4fvB8FuAAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">ConsoleAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="x915MlraB0+hK5agb0HxVwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="o72CPBdC2UGyraSt4vxsHQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="nH6UBhoeX0KbQRIkAzLiWAAA">
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="Jb94kigpgUqu4dlcc+CGdAAA">
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="QxduxAxohEmCZQGHeQsZcgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[4]" type="UMLRealizationView" guid="lL0hvfNl1UCp0ZckmzYqAAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">542,240;543,168</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:REF name="Head">tSxGyrwfEkSgp1EcheSXaAAA</XPD:REF>
    -<XPD:REF name="Tail">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="R+yIkrw1xkGRRmxo6cIv7AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="MHt2cHh8nkq4xztkXeNA1AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="1cFzyVYsZUexhz9n3LRU9AAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[5]" type="UMLGeneralizationView" guid="fqotcKjEKUu0dHy5krCbbgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">543,384;542,334</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:REF name="Head">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:REF name="Tail">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="a5TKNXnsu0Cm1O3vWK2A5QAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="lfapsg0nvkOzLtKq21jKcQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="J/CqIqCm4k64OiOc5XyUFgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[6]" type="UMLGeneralizationView" guid="Byj7ulJD9UmN+tawKGwCEgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">428,548;520,439</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">uk+yKXVJ4kGbJp9tS5jKTwAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="HQiDhC6IA0iqlMhf1tdcJQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="npCChlHvbECPkeBWStHdGwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="DNi8lqQWi0GHkNrHlB+wdAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[7]" type="UMLClassView" guid="umRYCn2dek6PVCutQ7EHjgAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">628</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">552</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">181</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">43</XPD:ATTR>
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="IY/ehYDmxki8kSqbCHHCfQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="lcA4v9eDHUugTZoPU740LAAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">FileAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="OVAuCrBMykaCfcii7gDdCwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="xiHCrB3qu0yBBg0U/q3EawAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="XJMRRQ4IvEKN76PkDbMKOQAA">
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="wZmHw439VE+zuefCYQQqVAAA">
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="PHqgEBQhpEmlVKbjkRedkAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[8]" type="UMLGeneralizationView" guid="xnvee9U5202YS/ztrwJSRAAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">695,552;574,439</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="oASzcKN58EuYmztjk4WiEwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="O0OgQK6n40urO6JVc74njgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="bgKTOidFkEeRLn6bk0KEEgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[9]" type="UMLClassView" guid="rHFGD6C9T0y5SMWPFgS09QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">628</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">704</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">181</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">43</XPD:ATTR>
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="OuAPH3aKlk2rg4cWgwv2BQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="BEu6SCte7E6fslLxmD8brgAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">RollingFileAppender</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="diAZdqUjrUSnaEkyvki5HwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="HpL3GnQOO0KyNABCTyAsRwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="1hKuFZUojkuzeqX9AiWUlwAA">
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="g+GUofZEmU2lEM1VL3vLIQAA">
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="4HSOp+H3/ECJnOpzcIo3jwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[10]" type="UMLClassView" guid="0AQ8gdYUKk2Ms7nTdCyd3QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">820</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">380</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">85</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">56</XPD:ATTR>
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:OBJ name="NameCompartment" type="UMLNameCompartmentView" guid="h/ce/kieOUyyxRZ6c71fTQAA">
    -<XPD:OBJ name="NameLabel" type="LabelView" guid="UZy1Qq+RWUWzR+BkMD2qBwAA">
    -<XPD:ATTR name="FontStyle" type="integer">1</XPD:ATTR>
    -<XPD:ATTR name="Text" type="string">Encoder</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="LabelView" guid="A/4/paNKfEeyuIjaeXDosAAA">
    -<XPD:ATTR name="Text" type="string">&lt;&lt;interface&gt;&gt;</XPD:ATTR>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="LabelView" guid="Y950eaGp5kKS5JcixC2pLAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="AttributeCompartment" type="UMLAttributeCompartmentView" guid="lyHSH0gj5USAm8DDiVvc1AAA">
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OperationCompartment" type="UMLOperationCompartmentView" guid="A7cLwlK4RESapHusUAalugAA">
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TemplateParameterCompartment" type="UMLTemplateParameterCompartmentView" guid="qQCEnWeXyUaictLrSH/4IgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:REF name="Model">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[11]" type="UMLAssociationView" guid="lMmxZJude0e4iK5TJK4KmwAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">820,408;724,409</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Head">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Tail">0AQ8gdYUKk2Ms7nTdCyd3QAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="Lpxx+3J+D0S223D5ODQq4wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="rhfNbKEGWUaK9KPGeno2AAAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="5lfzJsxzLkS60zCpNz5+twAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadRoleNameLabel" type="EdgeLabelView" guid="w+U1pR05XkeMT1rnnS2TcgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailRoleNameLabel" type="EdgeLabelView" guid="G2JYoZaOKku0lwIKN7cKRQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadMultiplicityLabel" type="EdgeLabelView" guid="Wzm+qJkDL0+jgE0ioVsNvwAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailMultiplicityLabel" type="EdgeLabelView" guid="h/GU+p9gHkyMIIzW5Tap4wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.523598775598299</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">25</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadPropertyLabel" type="EdgeLabelView" guid="MLngpvqsdU+i4Djdsali7wAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epHead</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailPropertyLabel" type="EdgeLabelView" guid="yN2upZx6REeZpBi/IRMnlgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">0.785398163397448</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">40</XPD:ATTR>
    -<XPD:ATTR name="EdgePosition" type="EdgePositionKind">epTail</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="HeadQualifierCompartment" type="UMLQualifierCompartmentView" guid="ZVjULz2utUKwdUcoGweZkgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="TailQualifierCompartment" type="UMLQualifierCompartmentView" guid="4leicQixUEWISYBF6P8LSgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Left" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Top" type="integer">-1000</XPD:ATTR>
    -<XPD:ATTR name="Width" type="integer">50</XPD:ATTR>
    -<XPD:ATTR name="Height" type="integer">8</XPD:ATTR>
    -<XPD:REF name="Model">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedViews[12]" type="UMLGeneralizationView" guid="VGws4gb7FEGKDPIpXKW13QAA">
    -<XPD:ATTR name="LineColor" type="string">clMaroon</XPD:ATTR>
    -<XPD:ATTR name="FillColor" type="string">$00B9FFFF</XPD:ATTR>
    -<XPD:ATTR name="Points" type="Points">718,704;718,594</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -<XPD:REF name="Head">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:REF name="Tail">rHFGD6C9T0y5SMWPFgS09QAA</XPD:REF>
    -<XPD:OBJ name="NameLabel" type="EdgeLabelView" guid="2t9xQX0DaECER1G841x4ggAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="StereotypeLabel" type="EdgeLabelView" guid="ZJ+e5yqWkkyJmgI5NCe9qQAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">30</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="PropertyLabel" type="EdgeLabelView" guid="uWFySM7Jq026r7z2lz9/TgAA">
    -<XPD:ATTR name="Visible" type="boolean">False</XPD:ATTR>
    -<XPD:ATTR name="Alpha" type="real">-1.5707963267949</XPD:ATTR>
    -<XPD:ATTR name="Distance" type="real">15</XPD:ATTR>
    -<XPD:REF name="Model">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#OwnedElements" type="integer">26</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLInterface" guid="bL5DGTE1wkqFiYq+yJuTRAAA">
    -<XPD:ATTR name="Name" type="string">Bae, Rankyoung</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[1]" type="UMLInterface" guid="PfW7flMvskqKmFDofejCYQAA">
    -<XPD:ATTR name="Name" type="string">Jung, Yoontae</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[2]" type="UMLInterface" guid="XthOkDh0rk67+FyPgXCRgAAA">
    -<XPD:ATTR name="Name" type="string">Kim, Hyunsoo</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[3]" type="UMLInterface" guid="WhfjywuM1kqS7RtEmuvAlgAA">
    -<XPD:ATTR name="Name" type="string">Kim, Jeongil</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[4]" type="UMLClass" guid="Ec4V6xlK5USBInAfRT+kBgAA">
    -<XPD:ATTR name="Name" type="string">Appender</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">interface</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">tSxGyrwfEkSgp1EcheSXaAAA</XPD:REF>
    -<XPD:REF name="Views[1]">m+f/Nz96gEqfQeHGgfAxjwAA</XPD:REF>
    -<XPD:REF name="Views[2]">ViAxcLeYxEC/QDB9dCuwagAA</XPD:REF>
    -<XPD:REF name="Views[3]">gvxeAhX+WkGcvqw6KjlA7wAA</XPD:REF>
    -<XPD:ATTR name="#ClientDependencies" type="integer">1</XPD:ATTR>
    -<XPD:REF name="ClientDependencies[0]">kjMmekdMhUCZp7nT1+7m5QAA</XPD:REF>
    -<XPD:ATTR name="#SupplierDependencies" type="integer">2</XPD:ATTR>
    -<XPD:REF name="SupplierDependencies[0]">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:REF name="SupplierDependencies[1]">kjMmekdMhUCZp7nT1+7m5QAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="FRx7C/jgm0uUQ2KJbSES0QAA">
    -<XPD:ATTR name="Name" type="string">doAppend</XPD:ATTR>
    -<XPD:REF name="Owner">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="HEOzMeB7hUCVhMMCFiMU/QAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">FRx7C/jgm0uUQ2KJbSES0QAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="/QxGe2nRTke4OPZr6B8DjwAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">FRx7C/jgm0uUQ2KJbSES0QAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[5]" type="UMLClass" guid="wrUqRmysaUaTNG+EIldTAgAA">
    -<XPD:ATTR name="Name" type="string">Lee, Jangwoo</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[6]" type="UMLClass" guid="R4ueGs13U0uU7uA7GfQuwgAA">
    -<XPD:ATTR name="Name" type="string">Lee, Minkyu</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[7]" type="UMLClass" guid="cZJpDMIhLkaN+6380nTjCgAA">
    -<XPD:ATTR name="Name" type="string">Lim, Heejin</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[8]" type="UMLInterface" guid="KkLGJmfcO0y7keuSLh3xpQAA">
    -<XPD:ATTR name="Name" type="string">Bae, Rankyoung1</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[9]" type="UMLClass" guid="rzT262Z1jEeiLPfjq8YXAQAA">
    -<XPD:ATTR name="Name" type="string">OutputStreamAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">NaiNuZ1qLUu+5nZq2pgoJgAA</XPD:REF>
    -<XPD:REF name="Views[1]">H/1viIm6yE20XaYIV5XQmgAA</XPD:REF>
    -<XPD:REF name="Views[2]">/LiIyuskqE6jkbei5roLSgAA</XPD:REF>
    -<XPD:REF name="Views[3]">LFUOQyCzKkODyDltdenqSQAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="Y/8ny1CDlUmGw7XO+p1IVAAA">
    -<XPD:ATTR name="Name" type="string">setOutputStream</XPD:ATTR>
    -<XPD:REF name="Owner">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="o7mxtv1VFUuGMzTZ/PzpJQAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">Y/8ny1CDlUmGw7XO+p1IVAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="WiQnBPlZNUKAb1kDr8rYMAAA">
    -<XPD:ATTR name="Name" type="string">OutputStream</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">Y/8ny1CDlUmGw7XO+p1IVAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#Associations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Associations[0]">CNBYSz/4s0OUBwR3Ujj7qAAA</XPD:REF>
    -<XPD:REF name="Associations[1]">KNPa5jL8m0SnxCPJDiLPEwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[10]" type="UMLClass" guid="xVL+I0ePyEKePe2ybmIZqQAA">
    -<XPD:ATTR name="Name" type="string">UnsynchronizedAppenderBase</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">82N1NUXIMU2s0JVvIgA33wAA</XPD:REF>
    -<XPD:REF name="Views[1]">MbkisNcJq06fy8rfNf//qgAA</XPD:REF>
    -<XPD:REF name="Views[2]">bHWlSSp0L0KsF9iS2K/VuAAA</XPD:REF>
    -<XPD:REF name="Views[3]">A3o0HBZQP0+GUAIDarVM5AAA</XPD:REF>
    -<XPD:ATTR name="#ClientDependencies" type="integer">1</XPD:ATTR>
    -<XPD:REF name="ClientDependencies[0]">MLMgV5lk30+73KeQUlkJ1AAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">mt8sHMuW6k6vvkswchHN9AAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">mt8sHMuW6k6vvkswchHN9AAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">iGZQWONRSkmUZXwA2ctTYAAA</XPD:REF>
    -<XPD:ATTR name="#Operations" type="integer">4</XPD:ATTR>
    -<XPD:OBJ name="Operations[0]" type="UMLOperation" guid="QrizX1w8Uk+lWkr80819WAAA">
    -<XPD:ATTR name="Name" type="string">doAppend</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="i3SFY3XjzkqfjCdeRItB0AAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QrizX1w8Uk+lWkr80819WAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="rcsyT8SmtEaJWAgOu29ifgAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">QrizX1w8Uk+lWkr80819WAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[1]" type="UMLOperation" guid="G5EvR3IAqUWyIUSQT5iKfAAA">
    -<XPD:ATTR name="Name" type="string">append</XPD:ATTR>
    -<XPD:ATTR name="Visibility" type="UMLVisibilityKind">vkProtected</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="BZo+omwID0Op8lvSMhrmCAAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">G5EvR3IAqUWyIUSQT5iKfAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="0mhi1Re0mUaekyTApQtYKgAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">G5EvR3IAqUWyIUSQT5iKfAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[2]" type="UMLOperation" guid="noZlW3WOpUSNYP7MIP2dIAAA">
    -<XPD:ATTR name="Name" type="string">addFilter</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="7JBY4nyhIES1n0jpwFzs0wAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">void</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">noZlW3WOpUSNYP7MIP2dIAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="Zd/8hSZIgUuOnnlNuMOW3QAA">
    -<XPD:ATTR name="Name" type="string">Filter&lt;E&gt; filter</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">noZlW3WOpUSNYP7MIP2dIAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="Operations[3]" type="UMLOperation" guid="+Hda0J6VTEGM7V6+m3aW7QAA">
    -<XPD:ATTR name="Name" type="string">getFilterChainDecision</XPD:ATTR>
    -<XPD:REF name="Owner">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Parameters" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Parameters[0]" type="UMLParameter" guid="b7Oh8zbH6kKEeryzkMlLQgAA">
    -<XPD:ATTR name="DirectionKind" type="UMLParameterDirectionKind">pdkReturn</XPD:ATTR>
    -<XPD:ATTR name="TypeExpression" type="string">FilterReply</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">+Hda0J6VTEGM7V6+m3aW7QAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Parameters[1]" type="UMLParameter" guid="QFIz2saC3kK9QRns9gVVIwAA">
    -<XPD:ATTR name="Name" type="string">E event</XPD:ATTR>
    -<XPD:REF name="BehavioralFeature">+Hda0J6VTEGM7V6+m3aW7QAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[11]" type="UMLClass" guid="yqvs3sZ9DUGqcZqP0JhEYQAA">
    -<XPD:ATTR name="Name" type="string">ConsoleAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">uk+yKXVJ4kGbJp9tS5jKTwAA</XPD:REF>
    -<XPD:REF name="Views[1]">nH6UBhoeX0KbQRIkAzLiWAAA</XPD:REF>
    -<XPD:REF name="Views[2]">Jb94kigpgUqu4dlcc+CGdAAA</XPD:REF>
    -<XPD:REF name="Views[3]">QxduxAxohEmCZQGHeQsZcgAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">3</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">KECSr2IcSEKuUekqxWFfQQAA</XPD:REF>
    -<XPD:REF name="Generalizations[1]">jfTGxiHUzUynYKDudsAtlgAA</XPD:REF>
    -<XPD:REF name="Generalizations[2]">AjD3odwWRkqn7DLLtsqSMQAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">KECSr2IcSEKuUekqxWFfQQAA</XPD:REF>
    -<XPD:REF name="Specializations[1]">jfTGxiHUzUynYKDudsAtlgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[12]" type="UMLRealization" guid="MLMgV5lk30+73KeQUlkJ1AAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Client">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:REF name="Supplier">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">lL0hvfNl1UCp0ZckmzYqAAAA</XPD:REF>
    -<XPD:REF name="Views[1]">R+yIkrw1xkGRRmxo6cIv7AAA</XPD:REF>
    -<XPD:REF name="Views[2]">MHt2cHh8nkq4xztkXeNA1AAA</XPD:REF>
    -<XPD:REF name="Views[3]">1cFzyVYsZUexhz9n3LRU9AAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[13]" type="UMLRealization" guid="kjMmekdMhUCZp7nT1+7m5QAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Client">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -<XPD:REF name="Supplier">Ec4V6xlK5USBInAfRT+kBgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[14]" type="UMLGeneralization" guid="mt8sHMuW6k6vvkswchHN9AAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:REF name="Parent">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[15]" type="UMLGeneralization" guid="iGZQWONRSkmUZXwA2ctTYAAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:REF name="Parent">xVL+I0ePyEKePe2ybmIZqQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">fqotcKjEKUu0dHy5krCbbgAA</XPD:REF>
    -<XPD:REF name="Views[1]">a5TKNXnsu0Cm1O3vWK2A5QAA</XPD:REF>
    -<XPD:REF name="Views[2]">lfapsg0nvkOzLtKq21jKcQAA</XPD:REF>
    -<XPD:REF name="Views[3]">J/CqIqCm4k64OiOc5XyUFgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[16]" type="UMLGeneralization" guid="KECSr2IcSEKuUekqxWFfQQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[17]" type="UMLGeneralization" guid="jfTGxiHUzUynYKDudsAtlgAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[18]" type="UMLGeneralization" guid="AjD3odwWRkqn7DLLtsqSMQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">yqvs3sZ9DUGqcZqP0JhEYQAA</XPD:REF>
    -<XPD:REF name="Parent">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">Byj7ulJD9UmN+tawKGwCEgAA</XPD:REF>
    -<XPD:REF name="Views[1]">HQiDhC6IA0iqlMhf1tdcJQAA</XPD:REF>
    -<XPD:REF name="Views[2]">npCChlHvbECPkeBWStHdGwAA</XPD:REF>
    -<XPD:REF name="Views[3]">DNi8lqQWi0GHkNrHlB+wdAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[19]" type="UMLClass" guid="fbO+5cdSPEmSXuXSqE8gXgAA">
    -<XPD:ATTR name="Name" type="string">FileAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">umRYCn2dek6PVCutQ7EHjgAA</XPD:REF>
    -<XPD:REF name="Views[1]">XJMRRQ4IvEKN76PkDbMKOQAA</XPD:REF>
    -<XPD:REF name="Views[2]">wZmHw439VE+zuefCYQQqVAAA</XPD:REF>
    -<XPD:REF name="Views[3]">PHqgEBQhpEmlVKbjkRedkAAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">DwI/SvxrPEa+xlJeUNknVQAA</XPD:REF>
    -<XPD:ATTR name="#Specializations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Specializations[0]">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[20]" type="UMLGeneralization" guid="DwI/SvxrPEa+xlJeUNknVQAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:REF name="Parent">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">xnvee9U5202YS/ztrwJSRAAA</XPD:REF>
    -<XPD:REF name="Views[1]">oASzcKN58EuYmztjk4WiEwAA</XPD:REF>
    -<XPD:REF name="Views[2]">O0OgQK6n40urO6JVc74njgAA</XPD:REF>
    -<XPD:REF name="Views[3]">bgKTOidFkEeRLn6bk0KEEgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[21]" type="UMLClass" guid="V4xOMUpxokyB4fxrQNJPxQAA">
    -<XPD:ATTR name="Name" type="string">RollingFileAppender</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">rHFGD6C9T0y5SMWPFgS09QAA</XPD:REF>
    -<XPD:REF name="Views[1]">1hKuFZUojkuzeqX9AiWUlwAA</XPD:REF>
    -<XPD:REF name="Views[2]">g+GUofZEmU2lEM1VL3vLIQAA</XPD:REF>
    -<XPD:REF name="Views[3]">4HSOp+H3/ECJnOpzcIo3jwAA</XPD:REF>
    -<XPD:ATTR name="#Generalizations" type="integer">1</XPD:ATTR>
    -<XPD:REF name="Generalizations[0]">MAm8gr3YDE+lxocsUB9afAAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[22]" type="UMLClass" guid="F1lk6TogWUGfYFH4FvBLOAAA">
    -<XPD:ATTR name="Name" type="string">Encoder</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">interface</XPD:ATTR>
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">0AQ8gdYUKk2Ms7nTdCyd3QAA</XPD:REF>
    -<XPD:REF name="Views[1]">lyHSH0gj5USAm8DDiVvc1AAA</XPD:REF>
    -<XPD:REF name="Views[2]">A7cLwlK4RESapHusUAalugAA</XPD:REF>
    -<XPD:REF name="Views[3]">qQCEnWeXyUaictLrSH/4IgAA</XPD:REF>
    -<XPD:ATTR name="#Associations" type="integer">2</XPD:ATTR>
    -<XPD:REF name="Associations[0]">1RTpKQO+4U+uqZahckvNEwAA</XPD:REF>
    -<XPD:REF name="Associations[1]">JFP7YnD8ak2COLWe8jfBSwAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[23]" type="UMLAssociation" guid="u9/9xY5vOU2kSfX9AmlMAwAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Connections" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Connections[0]" type="UMLAssociationEnd" guid="CNBYSz/4s0OUBwR3Ujj7qAAA">
    -<XPD:REF name="Association">u9/9xY5vOU2kSfX9AmlMAwAA</XPD:REF>
    -<XPD:REF name="Participant">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Connections[1]" type="UMLAssociationEnd" guid="1RTpKQO+4U+uqZahckvNEwAA">
    -<XPD:ATTR name="Aggregation" type="UMLAggregationKind">akComposite</XPD:ATTR>
    -<XPD:REF name="Association">u9/9xY5vOU2kSfX9AmlMAwAA</XPD:REF>
    -<XPD:REF name="Participant">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[24]" type="UMLAssociation" guid="kXyf6+RLrki8M9yh9qd9JwAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">lMmxZJude0e4iK5TJK4KmwAA</XPD:REF>
    -<XPD:REF name="Views[1]">Lpxx+3J+D0S223D5ODQq4wAA</XPD:REF>
    -<XPD:REF name="Views[2]">rhfNbKEGWUaK9KPGeno2AAAA</XPD:REF>
    -<XPD:REF name="Views[3]">5lfzJsxzLkS60zCpNz5+twAA</XPD:REF>
    -<XPD:ATTR name="#Connections" type="integer">2</XPD:ATTR>
    -<XPD:OBJ name="Connections[0]" type="UMLAssociationEnd" guid="JFP7YnD8ak2COLWe8jfBSwAA">
    -<XPD:REF name="Association">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Participant">F1lk6TogWUGfYFH4FvBLOAAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">G2JYoZaOKku0lwIKN7cKRQAA</XPD:REF>
    -<XPD:REF name="Views[1]">h/GU+p9gHkyMIIzW5Tap4wAA</XPD:REF>
    -<XPD:REF name="Views[2]">yN2upZx6REeZpBi/IRMnlgAA</XPD:REF>
    -<XPD:REF name="Views[3]">4leicQixUEWISYBF6P8LSgAA</XPD:REF>
    -</XPD:OBJ>
    -<XPD:OBJ name="Connections[1]" type="UMLAssociationEnd" guid="KNPa5jL8m0SnxCPJDiLPEwAA">
    -<XPD:ATTR name="Aggregation" type="UMLAggregationKind">akComposite</XPD:ATTR>
    -<XPD:REF name="Association">kXyf6+RLrki8M9yh9qd9JwAA</XPD:REF>
    -<XPD:REF name="Participant">rzT262Z1jEeiLPfjq8YXAQAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">w+U1pR05XkeMT1rnnS2TcgAA</XPD:REF>
    -<XPD:REF name="Views[1]">Wzm+qJkDL0+jgE0ioVsNvwAA</XPD:REF>
    -<XPD:REF name="Views[2]">MLngpvqsdU+i4Djdsali7wAA</XPD:REF>
    -<XPD:REF name="Views[3]">ZVjULz2utUKwdUcoGweZkgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[25]" type="UMLGeneralization" guid="MAm8gr3YDE+lxocsUB9afAAA">
    -<XPD:REF name="Namespace">dQlYoesWY0yxg2VJCxHxOQAA</XPD:REF>
    -<XPD:REF name="Child">V4xOMUpxokyB4fxrQNJPxQAA</XPD:REF>
    -<XPD:REF name="Parent">fbO+5cdSPEmSXuXSqE8gXgAA</XPD:REF>
    -<XPD:ATTR name="#Views" type="integer">4</XPD:ATTR>
    -<XPD:REF name="Views[0]">VGws4gb7FEGKDPIpXKW13QAA</XPD:REF>
    -<XPD:REF name="Views[1]">2t9xQX0DaECER1G841x4ggAA</XPD:REF>
    -<XPD:REF name="Views[2]">ZJ+e5yqWkkyJmgI5NCe9qQAA</XPD:REF>
    -<XPD:REF name="Views[3]">uWFySM7Jq026r7z2lz9/TgAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[3]" type="UMLModel" guid="UTbE66PthkuXloDrctmmwwAA">
    -<XPD:ATTR name="Name" type="string">Implementation Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">implementationModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLComponentDiagram" guid="UJbtRzsKuEa7sZAmr/tDGAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">UTbE66PthkuXloDrctmmwwAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLComponentDiagramView" guid="vo5SIJcJIUWJz7mqomMr4wAA">
    -<XPD:REF name="Diagram">UJbtRzsKuEa7sZAmr/tDGAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:ATTR name="#OwnedElements" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedElements[0]" type="UMLInterface" guid="naDG3jNwKEqkpiKwcQF/xgAA">
    -<XPD:ATTR name="Name" type="string">dsd</XPD:ATTR>
    -<XPD:REF name="Namespace">UTbE66PthkuXloDrctmmwwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[4]" type="UMLModel" guid="sbAEE91CXU+VbUQcRp4FwgAA">
    -<XPD:ATTR name="Name" type="string">Deployment Model</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">deploymentModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -<XPD:ATTR name="#OwnedDiagrams" type="integer">1</XPD:ATTR>
    -<XPD:OBJ name="OwnedDiagrams[0]" type="UMLDeploymentDiagram" guid="tzrsCrrbjka5umz88mdzIAAA">
    -<XPD:ATTR name="Name" type="string">Main</XPD:ATTR>
    -<XPD:REF name="DiagramOwner">sbAEE91CXU+VbUQcRp4FwgAA</XPD:REF>
    -<XPD:OBJ name="DiagramView" type="UMLDeploymentDiagramView" guid="OlRAEhzdxkCw+mUnxs9v2wAA">
    -<XPD:REF name="Diagram">tzrsCrrbjka5umz88mdzIAAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -<XPD:OBJ name="OwnedElements[5]" type="UMLModel" guid="mC0A50UCikqRZT73f5NgTgAA">
    -<XPD:ATTR name="Name" type="string">Analysis Model1</XPD:ATTR>
    -<XPD:ATTR name="StereotypeProfile" type="string">UMLStandard</XPD:ATTR>
    -<XPD:ATTR name="StereotypeName" type="string">analysisModel</XPD:ATTR>
    -<XPD:REF name="Namespace">JEGKOL0wwE6O2Emy29kFuwAA</XPD:REF>
    -</XPD:OBJ>
    -</XPD:OBJ>
    -</XPD:BODY>
    -</XPD:PROJECT>
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/appenderClassDiagram.jpg b/logback-site/src/site/resources/manual/images/chapters/appenders/appenderClassDiagram.jpg
    deleted file mode 100644
    index cb6578208b..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/appenderClassDiagram.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLE.gif b/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLE.gif
    deleted file mode 100644
    index 5a3db97316..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLE.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEException.gif b/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEException.gif
    deleted file mode 100644
    index 5d18939ff6..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEException.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEProperty.gif b/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEProperty.gif
    deleted file mode 100644
    index 1b1cdec8ca..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/dbAppenderLEProperty.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/fileAppenderUML.png b/logback-site/src/site/resources/manual/images/chapters/appenders/fileAppenderUML.png
    deleted file mode 100644
    index 5ef73797c0..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/fileAppenderUML.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender1.jpg b/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender1.jpg
    deleted file mode 100644
    index 9c1bac6677..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender1.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender2.jpg b/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender2.jpg
    deleted file mode 100644
    index b8e6cb3d90..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/appenders/smtpAppender2.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2.gif b/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2.gif
    deleted file mode 100644
    index 5c916fae5a..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2_small.gif b/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2_small.gif
    deleted file mode 100644
    index d30ca06be4..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/architecture/underTheHoodSequence2_small.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/configuration/appenderSyntax.png b/logback-site/src/site/resources/manual/images/chapters/configuration/appenderSyntax.png
    deleted file mode 100644
    index dd2ed7b26d..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/configuration/appenderSyntax.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/configuration/basicSyntax.png b/logback-site/src/site/resources/manual/images/chapters/configuration/basicSyntax.png
    deleted file mode 100644
    index 9e88c644bc..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/configuration/basicSyntax.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/configuration/lbClassicStatus.jpg b/logback-site/src/site/resources/manual/images/chapters/configuration/lbClassicStatus.jpg
    deleted file mode 100644
    index 4522c264bd..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/configuration/lbClassicStatus.jpg and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/filters/countingFilter.png b/logback-site/src/site/resources/manual/images/chapters/filters/countingFilter.png
    deleted file mode 100644
    index f64f635855..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/filters/countingFilter.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/filters/filterChain.gif b/logback-site/src/site/resources/manual/images/chapters/filters/filterChain.gif
    deleted file mode 100644
    index 3a6d6f53b7..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/filters/filterChain.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_jetty.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_jetty.gif
    deleted file mode 100644
    index d24d1346d0..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_jetty.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_tomcat.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_tomcat.gif
    deleted file mode 100644
    index 8c930545ee..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jconsole15_tomcat.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jmxConfigurator.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jmxConfigurator.gif
    deleted file mode 100644
    index ddf9f4ad08..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/jmxConfigurator.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/multiple.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/multiple.gif
    deleted file mode 100644
    index 407f9f3386..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/multiple.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_jetty.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_jetty.gif
    deleted file mode 100644
    index 393820375e..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_jetty.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_tomcat.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_tomcat.gif
    deleted file mode 100644
    index d0612d6411..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/mx4j_tomcat.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/statusList.gif b/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/statusList.gif
    deleted file mode 100644
    index beb6b27310..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/jmxConfigurator/statusList.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout0.gif b/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout0.gif
    deleted file mode 100644
    index 54ef05b75c..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout0.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout1.png b/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout1.png
    deleted file mode 100644
    index 8969743ba2..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayout1.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayoutAccess.gif b/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayoutAccess.gif
    deleted file mode 100644
    index b2b684670e..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/layouts/htmlLayoutAccess.gif and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/receivers/serverSocketReceiver.png b/logback-site/src/site/resources/manual/images/chapters/receivers/serverSocketReceiver.png
    deleted file mode 100644
    index 4aaa5047ff..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/receivers/serverSocketReceiver.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/manual/images/chapters/receivers/socketReceiver.png b/logback-site/src/site/resources/manual/images/chapters/receivers/socketReceiver.png
    deleted file mode 100644
    index dbd79ec7d0..0000000000
    Binary files a/logback-site/src/site/resources/manual/images/chapters/receivers/socketReceiver.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/recipes/images/factorEmail0.png b/logback-site/src/site/resources/recipes/images/factorEmail0.png
    deleted file mode 100644
    index 06803674fc..0000000000
    Binary files a/logback-site/src/site/resources/recipes/images/factorEmail0.png and /dev/null differ
    diff --git a/logback-site/src/site/resources/slf4j-slides.vsd b/logback-site/src/site/resources/slf4j-slides.vsd
    deleted file mode 100644
    index 8351e186c9..0000000000
    Binary files a/logback-site/src/site/resources/slf4j-slides.vsd and /dev/null differ
    diff --git a/pom.xml b/pom.xml
    index 0408bbdbe2..babf08f721 100755
    --- a/pom.xml
    +++ b/pom.xml
    @@ -5,9 +5,10 @@
     
       <modelVersion>4.0.0</modelVersion>
     
    +
       <groupId>ch.qos.logback</groupId>
       <artifactId>logback-parent</artifactId>
    -  <version>1.3.0-alpha5-SNAPSHOT</version>
    +  <version>1.5.28-SNAPSHOT</version>
       <packaging>pom</packaging>
     
       <name>Logback-Parent</name>
    @@ -34,47 +35,79 @@
       </licenses>
     
       <scm>
    -    <url>https://github.com/ceki/logback</url>
    -    <connection>git@github.com:ceki/logback.git</connection>
    +    <url>https://github.com/qos-ch/logback</url>
    +    <connection>scm:git@github.com:qos-ch/logback.git</connection>
       </scm>
     
       <modules>
         <module>logback-core</module>
    +    <module>logback-core-blackbox</module>
         <module>logback-classic</module>
    +    <module>logback-classic-blackbox</module>
         <module>logback-access</module>
    -    <module>logback-site</module>
         <module>logback-examples</module>
       </modules>
     
       <properties>
    -    <jdk.version>1.8</jdk.version>
    -    <maven.compiler.source>${jdk.version}</maven.compiler.source>
    -    <maven.compiler.target>${jdk.version}</maven.compiler.target>
    +    <!-- yyyy-MM-dd'T'HH:mm:ss'Z' -->
    +    <project.build.outputTimestamp>2026-01-30T17:26:39Z</project.build.outputTimestamp>
    +
    +    <!-- minimal JDK version at runtime -->
    +    <jdk.version>11</jdk.version>
    +    <!-- See https://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-release.html -->
    +    <maven.compiler.release>${jdk.version}</maven.compiler.release>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    -    <junit.version>4.12</junit.version>
    -    <hamcrest.version>1.3</hamcrest.version>
    -    <javax.mail.version>1.6.0</javax.mail.version>
    -    <janino.version>3.0.6</janino.version>
    -    <groovy.version>2.4.13</groovy.version>
    +
    +    <junit-jupiter-api.version>5.9.1</junit-jupiter-api.version>
    +    <junit-vintage-engine.version>5.9.1</junit-vintage-engine.version>
    +    <junit-jupiter-params.version>5.9.1</junit-jupiter-params.version>
    +    <junit-platform.version>1.10.5</junit-platform.version>
    +    <assertj-core.version>3.27.7</assertj-core.version>
    +    <hamcrest.version>2.2</hamcrest.version>
    +    <jakarta.mail.version>2.1.5</jakarta.mail.version>
    +    <jakarta.activation.version>2.1.4</jakarta.activation.version>
    +    <jakarta.angus-mail.version>2.0.3</jakarta.angus-mail.version>
    +
    +    <jakarta.servlet.version>5.0.0</jakarta.servlet.version>
    +    <greenmail.version>2.1.7</greenmail.version>
    +
    +    <janino.version>3.1.8</janino.version>
         <!-- slf4j.version property is used below, in
    -         logback-classic/pom.xml and in setClasspath.cmd 
    -    -->    
    -    <slf4j.version>1.8.0-beta1</slf4j.version>
    +         logback-classic/pom.xml, /logback-examples/src/main/resources/setClasspath.cmd, download.html
    +    -->
    +    <slf4j.version>2.0.17</slf4j.version>
         <cal10n.version>0.8.1</cal10n.version>
         <consolePlugin.version>1.1.0</consolePlugin.version>
    -    <!--<tomcat.version>7.0.59</tomcat.version>-->
    -    <tomcat.version>8.5.9</tomcat.version>
    -    <!--<jetty.version>7.5.1.v20110908</jetty.version>-->
    -    <jetty.version>8.2.0.v20160908</jetty.version>
    -    <!--<jetty.version>9.2.9.v20150224</jetty.version>-->
    -    <compiler-plugin.version>3.7.0</compiler-plugin.version> <!-- 3.6.1, 3.7.0 -->
    -    <jansi.version>1.9</jansi.version>
    -    <javadoc.plugin.version>2.10.4</javadoc.plugin.version>
    -    <!--<javadoc.plugin.version>3.0.0</javadoc.plugin.version>-->
    -
    -    <site-plugin.version>3.6</site-plugin.version>
    +    <jackson.version>2.15.0</jackson.version>
    +    <xz.version>1.10</xz.version>
    +
    +    <!--<jansi.version>1.18</jansi.version>-->
    +    <jansi.version>2.4.0</jansi.version>
    +
    +    <mockito-core.version>4.8.0</mockito-core.version>
    +    <byte-buddy.version>1.12.14</byte-buddy.version>
    +
    +    <maven-compiler-plugin.version>3.10.1</maven-compiler-plugin.version>
    +    <maven-surefire-plugin.version>3.5.4</maven-surefire-plugin.version>
    +    <maven-site-plugin.version>3.7.1</maven-site-plugin.version>
    +    <maven-install-plugin.version>3.0.0-M1</maven-install-plugin.version>
    +    <maven-javadoc-plugin.version>3.3.0</maven-javadoc-plugin.version>
    +    <maven-source-plugin.version>3.2.0</maven-source-plugin.version>
    +    <maven-resources-plugin.version>3.1.0</maven-resources-plugin.version>
    +    <license-maven-plugin.version>3.0</license-maven-plugin.version>
    +    <maven-jar-plugin.version>3.2.2</maven-jar-plugin.version>
    +    <maven-jxr-plugin.version>3.6.0</maven-jxr-plugin.version>
    +    <maven-release-plugin.version>3.0.0-M4</maven-release-plugin.version>
    +    <maven-deploy-plugin.version>3.0.0-M1</maven-deploy-plugin.version>
    +    <maven-dependency-plugin.version>3.2.0</maven-dependency-plugin.version>
    +    <maven-bundle-plugin.version>5.1.9</maven-bundle-plugin.version>
    +    <maven-antrun-plugin.version>3.1.0</maven-antrun-plugin.version>
    +    <ant.version>1.10.13</ant.version>
         <cobertura.maven.plugin.version>2.7</cobertura.maven.plugin.version>
    -    <license-plugin.version>3.0</license-plugin.version>
    +    <jetty.version>12.0.13</jetty.version>
    +
    +    <central-publishing-maven-plugin.version>0.9.0</central-publishing-maven-plugin.version>
    +
       </properties>
     
       <developers>
    @@ -92,22 +125,47 @@
       </developers>
     
       <dependencies>
    +
         <dependency>
    -      <groupId>junit</groupId>
    -      <artifactId>junit</artifactId>
    -      <version>${junit.version}</version>
    +      <groupId>org.assertj</groupId>
    +      <artifactId>assertj-core</artifactId>
    +      <version>${assertj-core.version}</version>
           <scope>test</scope>
         </dependency>
         <dependency>
    -      <groupId>org.assertj</groupId>
    -      <artifactId>assertj-core</artifactId>
    -      <version>1.7.1</version>
    +      <groupId>org.junit.jupiter</groupId>
    +      <artifactId>junit-jupiter-api</artifactId>
    +      <version>${junit-jupiter-api.version}</version>
           <scope>test</scope>
         </dependency>
    +
    +    <dependency>
    +      <groupId>org.junit.jupiter</groupId>
    +      <artifactId>junit-jupiter-engine</artifactId>
    +      <version>${junit-jupiter-api.version}</version>
    +      <scope>test</scope>
    +    </dependency>
    +
    +    <dependency>
    +       <groupId>org.junit.jupiter</groupId>
    +       <artifactId>junit-jupiter-params</artifactId>
    +       <version>${junit-jupiter-api.version}</version>
    +       <scope>test</scope>
    +    </dependency>
    +
    +    <dependency>
    +      <groupId>org.hamcrest</groupId>
    +      <artifactId>hamcrest-library</artifactId>
    +      <version>${hamcrest.version}</version>
    +      <scope>test</scope>
    +    </dependency>
    +
       </dependencies>
     
       <dependencyManagement>
    +
         <dependencies>
    +
           <!-- start of dependencies duplicated from logback-bom/pom.xml -->
           <dependency>
             <groupId>ch.qos.logback</groupId>
    @@ -136,26 +194,21 @@
             <type>test-jar</type>
           </dependency>
           <!-- end of dependencies duplicated from logback-bom/pom.xml -->
    -    
    +
    +      <dependency>
    +        <groupId>org.tukaani</groupId>
    +        <artifactId>xz</artifactId>
    +        <version>${xz.version}</version>
    +      </dependency>
           <dependency>
             <groupId>org.codehaus.janino</groupId>
             <artifactId>janino</artifactId>
             <version>${janino.version}</version>
           </dependency>
           <dependency>
    -        <groupId>org.codehaus.groovy</groupId>
    -        <artifactId>groovy-all</artifactId>
    -        <version>${groovy.version}</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>org.codehaus.groovy</groupId>
    -        <artifactId>groovy</artifactId>
    -        <version>${groovy.version}</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>org.codehaus.groovy</groupId>
    -        <artifactId>groovy-test</artifactId>
    -        <version>${groovy.version}</version>
    +        <groupId>org.codehaus.janino</groupId>
    +        <artifactId>commons-compiler</artifactId>
    +        <version>${janino.version}</version>
           </dependency>
     
           <dependency>
    @@ -163,75 +216,61 @@
             <artifactId>jansi</artifactId>
             <version>${jansi.version}</version>
           </dependency>
    -      <dependency>
    -        <groupId>javax.mail</groupId>
    -        <artifactId>javax.mail-api</artifactId>
    -        <version>${javax.mail.version}</version>
    -      </dependency>
    -	  <dependency>
    -    	<groupId>com.sun.mail</groupId>
    -   		<artifactId>javax.mail</artifactId>
    -    	<version>${javax.mail.version}</version>
    -	  </dependency>
     
           <dependency>
    -        <groupId>org.dom4j</groupId>
    -        <artifactId>dom4j</artifactId>
    -        <version>2.0.2</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>org.hsqldb</groupId>
    -        <artifactId>hsqldb</artifactId>
    -        <version>2.3.4</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>com.h2database</groupId>
    -        <artifactId>h2</artifactId>
    -        <version>1.2.132</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>postgresql</groupId>
    -        <artifactId>postgresql</artifactId>
    -        <version>8.4-701.jdbc4</version>
    -      </dependency>
    -      <dependency>
    -        <groupId>mysql</groupId>
    -        <artifactId>mysql-connector-java</artifactId>
    -        <version>5.1.9</version>
    +        <groupId>jakarta.mail</groupId>
    +        <artifactId>jakarta.mail-api</artifactId>
    +        <version>${jakarta.mail.version}</version>
           </dependency>
    +
           <dependency>
    -        <groupId>org.apache.tomcat</groupId>
    -        <artifactId>tomcat-catalina</artifactId>
    -        <version>${tomcat.version}</version>
    +        <groupId>jakarta.activation</groupId>
    +        <artifactId>jakarta.activation-api</artifactId>
    +        <version>${jakarta.activation.version}</version>
           </dependency>
    +
           <dependency>
    -        <groupId>org.apache.tomcat</groupId>
    -        <artifactId>tomcat-coyote</artifactId>
    -        <version>${tomcat.version}</version>
    +        <groupId>org.eclipse.angus</groupId>
    +        <artifactId>angus-mail</artifactId>
    +        <version>${jakarta.angus-mail.version}</version>
           </dependency>
    +
    +<!--        <dependency>-->
    +<!--            <groupId>org.eclipse.angus</groupId>-->
    +<!--            <artifactId>angus-activation</artifactId>-->
    +<!--            <version>${jakarta.angus-activation.version}</version>-->
    +<!--        </dependency>-->
    +
           <dependency>
    -        <groupId>org.eclipse.jetty</groupId>
    -        <artifactId>jetty-server</artifactId>
    -        <version>${jetty.version}</version>
    +        <groupId>jakarta.servlet</groupId>
    +        <artifactId>jakarta.servlet-api</artifactId>
    +        <version>${jakarta.servlet.version}</version>
           </dependency>
     
           <dependency>
    -        <groupId>javax.servlet</groupId>
    -        <artifactId>javax.servlet-api</artifactId>
    -        <version>3.1.0</version>
    +        <groupId>com.icegreen</groupId>
    +        <artifactId>greenmail</artifactId>
    +        <version>${greenmail.version}</version>
           </dependency>
     
           <dependency>
    -        <groupId>joda-time</groupId>
    -        <artifactId>joda-time</artifactId>
    -        <version>2.9.2</version>
    +        <groupId>org.dom4j</groupId>
    +        <artifactId>dom4j</artifactId>
    +        <version>2.0.3</version>
           </dependency>
     
           <dependency>
             <groupId>org.mockito</groupId>
             <artifactId>mockito-core</artifactId>
    -        <version>2.7.9</version>
    +        <version>${mockito-core.version}</version>
    +      </dependency>
    +
    +      <dependency>
    +        <groupId>net.bytebuddy</groupId>
    +        <artifactId>byte-buddy</artifactId>
    +        <version>${byte-buddy.version}</version>
           </dependency>
    +
         </dependencies>
       </dependencyManagement>
     
    @@ -255,57 +294,70 @@
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-compiler-plugin</artifactId>
    -          <version>${compiler-plugin.version}</version>
    +          <version>${maven-compiler-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-deploy-plugin</artifactId>
    -          <version>2.8.2</version>
    +          <version>${maven-deploy-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-install-plugin</artifactId>
    -          <version>2.5.2</version>
    +          <version>${maven-install-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-resources-plugin</artifactId>
    -          <version>3.0.1</version>
    +          <version>${maven-resources-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-site-plugin</artifactId>
    -          <version>${site-plugin.version}</version>
    +          <version>${maven-site-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-surefire-plugin</artifactId>
    -          <version>2.19.1</version>
    +          <version>${maven-surefire-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-jar-plugin</artifactId>
    -          <version>2.3.1</version>
    +          <version>${maven-jar-plugin.version}</version>
    +          <configuration>
    +            <outputTimestamp>${project.build.outputTimestamp}</outputTimestamp>
    +            <archive>
    +              <index>true</index>
    +              <manifest>
    +                <!-- Add version information into the MANIFEST so it is accessible by
    +                     calling Package#getImplementationVersion()
    +                 -->
    +                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
    +                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
    +              </manifest>
    +            </archive>
    +          </configuration>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-javadoc-plugin</artifactId>
    -          <version>${javadoc.plugin.version}</version>
    +          <version>${maven-javadoc-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-dependency-plugin</artifactId>
    -          <version>2.10</version>
    +          <version>${maven-dependency-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-release-plugin</artifactId>
    -          <version>2.5.3</version>
    +          <version>${maven-release-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.apache.maven.plugins</groupId>
               <artifactId>maven-source-plugin</artifactId>
    -          <version>2.1.2</version>
    +          <version>${maven-source-plugin.version}</version>
             </plugin>
             <plugin>
               <groupId>org.codehaus.mojo</groupId>
    @@ -315,55 +367,9 @@
             <plugin>
               <groupId>org.apache.felix</groupId>
               <artifactId>maven-bundle-plugin</artifactId>
    -          <version>3.3.0</version>
    -        </plugin>
    -
    -        <plugin>
    -          <groupId>org.codehaus.mojo</groupId>
    -          <artifactId>animal-sniffer-maven-plugin</artifactId>
    -          <version>1.16</version>
    -          <configuration>
    -            <ignores>
    -              <ignore>sun.reflect.Reflection</ignore>
    -            </ignores>
    -            <signature>
    -              <groupId>org.codehaus.mojo.signature</groupId>
    -              <artifactId>java17</artifactId>
    -              <version>1.0</version>
    -            </signature>
    -          </configuration>
    +          <version>${maven-bundle-plugin.version}</version>
             </plugin>
    -
    -        <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself.-->
    -        <plugin>
    -        	<groupId>org.eclipse.m2e</groupId>
    -        	<artifactId>lifecycle-mapping</artifactId>
    -        	<version>1.0.0</version>
    -        	<configuration>
    -        		<lifecycleMappingMetadata>
    -        			<pluginExecutions>
    -        				<pluginExecution>
    -        					<pluginExecutionFilter>
    -        						<groupId>
    -        							org.apache.maven.plugins
    -        						</groupId>
    -        						<artifactId>
    -        							maven-toolchains-plugin
    -        						</artifactId>
    -        						<versionRange>[1.1,)</versionRange>
    -        						<goals>
    -        							<goal>toolchain</goal>
    -        						</goals>
    -        					</pluginExecutionFilter>
    -        					<action>
    -        						<ignore></ignore>
    -        					</action>
    -        				</pluginExecution>
    -        			</pluginExecutions>
    -        		</lifecycleMappingMetadata>
    -        	</configuration>
    -        </plugin>
    -      </plugins>
    +    </plugins>
     
         </pluginManagement>
     
    @@ -371,185 +377,98 @@
     
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-toolchains-plugin</artifactId>
    -        <version>1.1</version>
    -        <executions>
    -          <execution>
    -            <id>toolchain</id>
    -            <goals>
    -              <goal>toolchain</goal>
    -            </goals>
    -          </execution>
    -        </executions>
    +        <artifactId>maven-compiler-plugin</artifactId>
    +
             <configuration>
    -          <toolchains>
    -            <jdk>
    -              <version>9</version>
    -            </jdk>
    -            <jdk>
    -              <version>${jdk.version}</version>
    -            </jdk>            
    -          </toolchains>
    +          <release>${jdk.version}</release>
             </configuration>
           </plugin>
     
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-compiler-plugin</artifactId>
    +        <artifactId>maven-source-plugin</artifactId>
             <executions>
    -
               <execution>
    -            <id>base-compile</id>
    +            <id>attach-sources</id>
                 <goals>
    -              <goal>compile</goal>
    +              <goal>jar</goal>
    +              <goal>test-jar</goal>
                 </goals>
    -            <configuration>
    -              <excludes>
    -                <exclude>module-info.java</exclude>
    -              </excludes>
    -            </configuration>
    -          </execution> 
    -
    -          <execution>
    -            <id>default-compile</id>
    -            <configuration>
    -              <jdkToolchain>
    -                <version>9</version>
    -              </jdkToolchain>
    -              <release>9</release>
    -            </configuration>
               </execution>
             </executions>
    -        <configuration>
    -          <jdkToolchain>
    -            <version>[8,9)</version>
    -          </jdkToolchain>
    -          <source>${jdk.version}</source>
    -          <target>${jdk.version}</target>
    -        </configuration>
           </plugin>
     
    - 
           <plugin>
             <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-source-plugin</artifactId>
    +        <artifactId>maven-javadoc-plugin</artifactId>
             <executions>
               <execution>
    -            <id>attach-sources</id>
    +            <id>default-cli</id>
                 <goals>
    -              <goal>jar</goal>
    -              <goal>test-jar</goal>
    +              <goal>aggregate</goal>
                 </goals>
               </execution>
             </executions>
    -      </plugin>
     
    -      <plugin>
    -        <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-assembly-plugin</artifactId>
    -        <version>3.0.0</version>
             <configuration>
    -          <descriptors>
    -            <descriptor>src/main/assembly/dist.xml</descriptor>
    -          </descriptors>
    -          <finalName>logback-${project.version}</finalName>
    -          <appendAssemblyId>false</appendAssemblyId>
    -          <outputDirectory>target/site/dist/</outputDirectory>
    +          <linksource>true</linksource>
    +          <doclint>none</doclint>
    +          <skippedModules>
    +            logback-core-blackbox,logback-classic-blackbox,logback-examples
    +          </skippedModules>
    +          <additionalOptions>
    +            <additionalOption>-Xdoclint:none</additionalOption>
    +          </additionalOptions>
    +
    +          <links>
    +            <link>
    +                https://docs.oracle.com/en/java/javase/11/docs/api/
    +            </link>
    +          </links>
    +
    +          <groups>
    +            <group>
    +              <title>Logback Core</title>
    +              <packages>ch.qos.logback.core:ch.qos.logback.core.*
    +              </packages>
    +            </group>
    +            <group>
    +              <title>Logback Classic</title>
    +              <packages>
    +                ch.qos.logback:ch.qos.logback.classic:ch.qos.logback.classic.*
    +              </packages>
    +            </group>
    +          </groups>
             </configuration>
           </plugin>
     
           <plugin>
    -        <groupId>org.codehaus.mojo</groupId>
    -        <artifactId>findbugs-maven-plugin</artifactId>
    +        <groupId>org.apache.maven.plugins</groupId>
    +        <artifactId>maven-jxr-plugin</artifactId>
    +        <version>${maven-jxr-plugin.version}</version>
             <configuration>
    -          <threshold>High</threshold>
    -          <!--<trace>true</trace>-->
    -          <excludeFilterFile>findbugs-exclude.xml</excludeFilterFile>
    +          <aggregate>true</aggregate>
    +          <!--<javadocDir>target/site/apidocs/</javadocDir>-->
    +          <linkJavadoc>false</linkJavadoc>
             </configuration>
           </plugin>
     
    - 
    -
    +       <plugin>
    +         <groupId>org.sonatype.central</groupId>
    +         <artifactId>central-publishing-maven-plugin</artifactId>
    +         <version>${central-publishing-maven-plugin.version}</version>         
    +         <extensions>true</extensions>
    +         <configuration>
    +           <publishingServerId>central</publishingServerId>
    +           <excludeArtifacts>logback-core-blackbox,logback-classic-blackbox,logback-examples</excludeArtifacts>
    +         </configuration>
    +       </plugin>
     
    -      <!-- ================ site plugin ==================== -->
    -      <plugin>
    -        <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-site-plugin</artifactId>
    -        <configuration>
    -
    -          <reportPlugins>
    -
    -            <plugin>
    -              <groupId>org.apache.maven.plugins</groupId>
    -              <artifactId>maven-jxr-plugin</artifactId>
    -              <version>2.5</version>
    -              <configuration>
    -                <aggregate>true</aggregate>
    -                <javadocDir>target/site/apidocs/</javadocDir>
    -                <linkJavadoc>true</linkJavadoc>
    -              </configuration>
    -            </plugin>
    -
    -      
    -            <plugin>
    -              <groupId>org.apache.maven.plugins</groupId>
    -              <artifactId>maven-javadoc-plugin</artifactId>
    -              
    -              <configuration>                                
    -                <aggregate>true</aggregate>
    -                <linkJavadoc>true</linkJavadoc>
    -                <linksource>true</linksource>
    -                <additionalparam>-Xdoclint:none</additionalparam>
    -                <sourceFileExcludes>
    -                  <sourceFileExclude>**/module-info.java</sourceFileExclude>
    -                </sourceFileExcludes>
    -                <links>
    -                  <link>
    -                    http://docs.oracle.com/javase/7/docs/api/
    -                  </link>
    -                </links>
    -                
    -                <groups>
    -                  <group>
    -                    <title>Logback Core</title>
    -                    <packages>ch.qos.logback.core:ch.qos.logback.core.*
    -                    </packages>
    -                  </group>
    -                  <group>
    -                    <title>Logback Classic</title>
    -                    <packages>
    -                      ch.qos.logback:ch.qos.logback.classic:ch.qos.logback.classic.*
    -                    </packages>
    -                  </group>
    -                  <group>
    -                    <title>Logback Access</title>
    -                    <packages>ch.qos.logback.access:ch.qos.logback.access.*
    -                    </packages>
    -                  </group>
    -                  <group>
    -                    <title>Examples</title>
    -                    <packages>chapter*:joran*</packages>
    -                  </group>
    -                </groups>                  
    -              </configuration>
    -            </plugin>
    -
    -            
    -          </reportPlugins>
    -          
    -        </configuration>
    -        </plugin>
    -        
         </plugins>
       </build>
     
       <distributionManagement>
     
    -    <site>
    -      <id>qos_ch</id>
    -      <url>scp://yvo.qos.ch/var/www/logback.qos.ch/htdocs/</url>
    -    </site>
    -
         <repository>
           <id>sonatype-nexus-staging</id>
           <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    @@ -559,23 +478,9 @@
     
       <reporting>
         <plugins>
    -      <!--
    -      <plugin>
    -        <groupId>org.apache.maven.plugins</groupId>
    -        <artifactId>maven-jxr-plugin</artifactId>
    -        <version>2.5</version>
    -        <configuration>
    -          <aggregate>true</aggregate>
    -          <javadocDir>target/site/apidocs/</javadocDir>
    -          <linkJavadoc>true</linkJavadoc>
    -        </configuration>
    -      </plugin>
    -      -->
    -
    -            
         </plugins>
       </reporting>
    -  
    +
       <profiles>
         <profile>
           <id>testSkip</id>
    @@ -590,7 +495,7 @@
               <plugin>
                 <groupId>com.mycila</groupId>
                 <artifactId>license-maven-plugin</artifactId>
    -            <version>${license-plugin.version}</version>
    +            <version>${license-maven-plugin.version}</version>
                 <configuration>
                   <header>src/main/licenseHeader.txt</header>
                   <quiet>false</quiet>
    @@ -621,16 +526,17 @@
               <plugin>
                 <groupId>org.apache.maven.plugins</groupId>
                 <artifactId>maven-javadoc-plugin</artifactId>
    -            <version>${javadoc.plugin.version}</version>
    +            <version>${maven-javadoc-plugin.version}</version>
                 <executions>
                   <execution>
                     <id>attach-javadocs</id>
                     <goals>
                       <goal>jar</goal>
                     </goals>
    -              </execution>            
    +              </execution>
                 </executions>
                 <configuration>
    +              <doclint>none</doclint>
                   <sourceFileExcludes>
                     <sourceFileExclude>**/module-info.java</sourceFileExclude>
                   </sourceFileExcludes>
    @@ -662,60 +568,6 @@
           </build>
         </profile>
     
    -    <profile>
    -      <id>travis</id>
    -      <activation>
    -        <property>
    -          <name>env.TRAVIS</name>
    -          <value>true</value>
    -        </property>
    -      </activation>
    -      <build>
    -        <plugins>
    -          <plugin>
    -            <groupId>org.apache.maven.plugins</groupId>
    -            <artifactId>maven-toolchains-plugin</artifactId>
    -            <version>1.1</version>
    -            <executions>
    -              <execution>
    -                <id>toolchain</id>
    -                <phase>none</phase>
    -              </execution>
    -            </executions>
    -          </plugin>
    -        </plugins>
    -      </build>
    -    </profile>
    -     
    -    <profile>
    -      <id>cobertura</id>
    -      <build>
    -        <plugins>
    -          <plugin>
    -            <groupId>org.apache.maven.plugins</groupId>
    -            <artifactId>maven-site-plugin</artifactId>
    -            <configuration>
    -              <reportPlugins>
    -            
    -                <plugin>
    -                  <groupId>org.codehaus.mojo</groupId>
    -                  <artifactId>cobertura-maven-plugin</artifactId>
    -                  <version>${cobertura.maven.plugin.version}</version>
    -                  <configuration>
    -                    <formats>
    -                      <format>html</format>
    -                    </formats>
    -                    <aggregate>true</aggregate>
    -                  </configuration>
    -                </plugin>
    -              </reportPlugins>
    -            </configuration>
    -          </plugin>
    -        </plugins>
    -      </build>
    -    </profile>
    -
    -
       </profiles>
     
     </project>
    diff --git a/release.sh b/release.sh
    index 436efbe375..4638f1784e 100755
    --- a/release.sh
    +++ b/release.sh
    @@ -1,14 +1,30 @@
     mvn versions:set -DgenerateBackupPoms=false -DnewVersion=${VERSION_NUMBER} 
     
    +# mvn  -Dmaven.javadoc.skippedModules=logback-core-blackbox,logback-classic-blackbox,logback-examples javadoc:aggregate
    +
    +# mvn -Ddoclint=none -Dmaven.javadoc.skippedModules=slf4j-ext,log4j-over-slf4j,log4j-over-slf4j-blackbox,jul-to-slf4j-blackbox,slf4j-migrator,osgi-over-slf4j javadoc:aggregate
    +
    +#mvn -Ddoclint=none  -DXXadditionalparam=-Xdoclint:none -Dmaven.javadoc.skippedModules=osgi-over-slf4j,slf4j-ext,log4j-over-slf4j-blackbox,log4j-over-slf4j javadoc:aggregate
    +
    +
    +
    +
    +
     mvn clean
     mvn install
     mvn animal-sniffer:check
     mvn site:site
     
     #mvn javadoc:jar
    -mvn assembly:single
    +#mvn assembly:single
    +
    +export GPG_TTY=$(tty)
    +password
     mvn deploy -P javadocjar,sign-artifacts -Dgpg.passphrase=passwd
     
    +
    +
    +# cleanHistory dep
     #uncomment diffie-hellman support in /etc/ssh/sshd_config
     
     mvn site:deploy -N # with Java 8!!!
    diff --git a/src/main/assembly/dist.xml b/src/main/assembly/dist.xml
    deleted file mode 100755
    index 507e9fffb9..0000000000
    --- a/src/main/assembly/dist.xml
    +++ /dev/null
    @@ -1,230 +0,0 @@
    -<assembly>
    -	<id>dist</id>
    -	<formats>
    -		<format>zip</format>
    -		<format>tar.gz</format>
    -	</formats>
    -	<fileSets>
    -		<!-- Module POMs -->
    -		<fileSet>
    -			<directory>logback-core/</directory>
    -			<outputDirectory>logback-core/</outputDirectory>
    -			<includes>
    -				<include>
    -					pom.xml
    -				</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -    <fileSet>
    -      <directory>logback-classic/</directory>
    -			<outputDirectory>logback-classic/</outputDirectory>
    -			<includes>
    -				<include>pom.xml</include>
    -				<include>osgi-build.xml</include>
    -				<include>integration.xml</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-access/</directory>
    -			<outputDirectory>logback-access/</outputDirectory>
    -			<includes>
    -				<include>
    -					pom.xml
    -				</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-examples/</directory>
    -			<outputDirectory>logback-examples/</outputDirectory>
    -			<includes>
    -				<include>
    -					pom.xml
    -				</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-site/</directory>
    -			<outputDirectory>logback-site/</outputDirectory>
    -			<includes>
    -				<include>
    -					pom.xml
    -				</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -    <!--
    -    <fileSet>
    -			<directory>logback-bom/</directory>
    -			<outputDirectory>logback-bom/</outputDirectory>
    -			<includes>
    -				<include>
    -					pom.xml
    -				</include>
    -			</includes>
    -		</fileSet>
    -    -->
    -		<!-- Module Source directories -->
    -		<fileSet>
    -			<directory>logback-core/src/</directory>
    -			<outputDirectory>logback-core/src/</outputDirectory>
    -			<excludes>
    -				<exclude>
    -					test/output/
    -				</exclude>
    -			</excludes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-classic/src/</directory>
    -			<outputDirectory>logback-classic/src/</outputDirectory>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-access/src/</directory>
    -			<outputDirectory>logback-access/src/</outputDirectory>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-examples/src/</directory>
    -			<outputDirectory>logback-examples/src/</outputDirectory>
    -			<excludes>
    -				<exclude>
    -					main/resources/
    -				</exclude>
    -			</excludes>
    -		</fileSet>
    -		<!--fileSet>
    -			<directory>logback-examples/src/main/resources</directory>
    -			<outputDirectory>logback-examples/</outputDirectory>
    -		</fileSet -->
    -		<fileSet>
    -			<directory>logback-site/src/</directory>
    -			<outputDirectory>logback-site/src/</outputDirectory>
    -		</fileSet>
    -		<!-- We also include the classes of the examples module -->
    -		<fileSet>
    -			<directory>logback-examples/target/</directory>
    -			<outputDirectory>logback-examples/</outputDirectory>
    -      <includes>
    -				<include>logback-examples-${project.version}.jar</include>
    -      </includes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-examples/lib/</directory>
    -			<outputDirectory>logback-examples/lib</outputDirectory>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-examples/target/classes</directory>
    -			<outputDirectory>logback-examples/</outputDirectory>
    -      <includes>
    -				<include>setClasspath.sh</include>
    -				<include>setClasspath.cmd</include>
    -      </includes>
    -		</fileSet>
    - 		
    -		<!-- Module JARs -->
    -		<fileSet>
    -			<directory>logback-core/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-core-${project.version}.jar</include>
    -			</includes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-classic/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-classic-${project.version}.jar</include>
    -			</includes>
    -		</fileSet>		
    -		<fileSet>
    -			<directory>logback-access/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-access-${project.version}.jar</include>
    -			</includes>
    -		</fileSet>		
    -		<!-- Module Source JARs -->
    -		<fileSet>
    -			<directory>logback-core/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-core-${project.version}-sources.jar</include>
    -			</includes>
    -		</fileSet>
    -		<fileSet>
    -			<directory>logback-classic/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-classic-${project.version}-sources.jar</include>
    -			</includes>
    -		</fileSet>		
    -		<fileSet>
    -			<directory>logback-access/target/</directory>
    -			<outputDirectory>./</outputDirectory>
    -			<includes>
    -				<include>logback-access-${project.version}-sources.jar</include>
    -			</includes>
    -		</fileSet>	
    -
    -				 
    -		<!-- Website -->
    -		<fileSet>
    -			<directory>target/site</directory>
    -			<outputDirectory>docs/</outputDirectory>
    -      <excludes>
    -        <exclude>dist/*</exclude>
    -        <exclude>**/apidocs/**</exclude>
    -        <exclude>**/testapidocs/**</exclude>
    -        <exclude>**/xref/**</exclude>
    -        <exclude>**/xref-test/**</exclude>
    -      </excludes>
    -		</fileSet>
    -		
    -		<!-- Parent files -->
    -		<fileSet>
    -			<includes>
    -				<include>src/</include>
    -				<include>README*</include>
    -				<include>LICENSE*</include>
    -				<include>pom.xml</include>
    -			</includes>
    -			<excludes>
    -				<exclude>*.bak</exclude>
    -			</excludes>
    -		</fileSet>
    -	</fileSets>
    -	
    -	<!--
    -	<dependencySets>
    -		<dependencySet>
    -			<outputDirectory>/lib</outputDirectory>
    -			<unpack>false</unpack>
    -			<scope>test</scope>
    -			<excludes>
    -
    -					There is little documentation on this format.
    -					It is of the form of DefaultArtifact.getDependencyConflictId()
    -					which is <groupId>:<artifactId>:<type>:<classifier>
    -					or we can use the short form (hard coded into assembly) of
    -					<groupId>:<artifactId>
    -					This is a String equality match not a pattern match.
    -				
    -				<exclude>ch.qos.logback:logback-core</exclude>
    -				
    -			</excludes>
    -		</dependencySet>
    -	</dependencySets>
    -	-->
    -</assembly>
    \ No newline at end of file
    diff --git a/src/main/clas/signed-clas.txt b/src/main/clas/signed-clas.txt
    index e70e84db95..d365441803 100755
    --- a/src/main/clas/signed-clas.txt
    +++ b/src/main/clas/signed-clas.txt
    @@ -3,59 +3,68 @@ Here is a list of persons who have signed a CLA [1].
     
     Name                     Location              Date
     ----                     --------              ----
    -Maarten Bosteels         Heverlee, Belgium     2007-01-15
    -John E. Conlon           WI, USA               2007-02-08
    -Ralph W. Goers Jr.       CA, USA               2008-07-24
    -Matt Humphreys           London, UK            2008-08-16
    -Thorbjorn Ravn Andersen  Hammel, Denmark       2008-08-16
    -Chad LaVigne             MN, USA               2009-03-25
    -Joern Huxhorn            Pfungstadt, Germany   2009-04-29
    -Thorsten Moeller         Basel, Switzerland    2009-11-16
    -Robert Elliot            Twickenham, UK        2010-03-08
    -Tomasz Nurkiewicz        Wegrow, Poland        2010-03-19
    -Aleksey Didik            S.Putilovskay, Russia 2010-03-26
    -Heiko Seeberger          Schondorf , Germany   2010-09-06
    -Ryan Cogswell            MN, USA               2011-07-07
    -Pierre Queinnec          Paris, France         2011-07-20
    -Anthony Trinh            MA, USA               2012-05-10
    -Torsten Juergeleit       Liederbach, Germany   2012-05-13
    -Cedric Lime              Levallois-Perret, FR  2012-06-08
    -Christian Trutz          Mark, Germany         2012-06-14
    -Les Hazlewood            CA, USA               2012-09-10
    +Maarten Bosteels         Belgium               2007-01-15
    +John E. Conlon           USA                   2007-02-08
    +Ralph W. Goers Jr.       USA                   2008-07-24
    +Matt Humphreys           UK                    2008-08-16
    +Thorbjorn Ravn Andersen  Denmark               2008-08-16
    +Chad LaVigne             USA                   2009-03-25
    +Joern Huxhorn            Germany               2009-04-29
    +Thorsten Moeller         Switzerland           2009-11-16
    +Robert Elliot            United Kingdom        2010-03-08
    +Tomasz Nurkiewicz        Poland                2010-03-19
    +Aleksey Didik            Russia                2010-03-26
    +Heiko Seeberger          Germany               2010-09-06
    +Ryan Cogswell            USA                   2011-07-07
    +Pierre Queinnec          France                2011-07-20
    +Anthony Trinh            USA                   2012-05-10
    +Torsten Juergeleit       Germany               2012-05-13
    +Cedric Lime              France                2012-06-08
    +Christian Trutz          Germany               2012-06-14
    +Les Hazlewood            USA                   2012-09-10
     Libor Jelinek            Czech Republic        2012-09-17
    -Matthew Bishop           Vancouver, Canada     2012-09-18
    -Joris Kuipers            Amstelveen, NL        2013-02-11
    -Andrey Korzhevskiy       Skryabina Akademika, Russia 2013-03-26 
    -Carl E. Harris Jr.       VA, USA               2013-03-27
    -Matthew R. Bertolini     NJ, USA               2013-04-23
    -Gregory A. Denton        WA, USA               2013-05-02
    -Mikhail Mazursky         Bashkortostan,Russia  2013-10-02
    -Marvin B. Lillehaug      Trondheim, Norway     2014-01-20
    -Chetan Mehrotra          Uttar Pradesh, India  2014-02-04
    -Eric Dahl                NE, USA               2014-02-04
    -Adam Gent                MA, USA               2014-04-01
    -Sebastian Groebler       Berlin, Germany       2014-07-17
    -Alexander Dorokhine      CA, USA               2014-12-07
    -Yuji Okazawa             Tokyo, Japan          2015-04-01
    -Lukasz Cwik              WA, USA               2015-06-11
    -Juan Pablo Santos        Madrid, Spain         2015-06-15
    -Gareth Davis             Cambridge, UK         2015-07-15
    -Marek Szalik             Warszawa, Poland      2015-11-25
    -Pavel Boldyrev           Ontario, Canada       2015-12-22
    -Christoph Zauner         Ennsdorf, Austria     2016-01-11
    -David Roberge            Maine, USA            2016-02-14
    -Adam Batkin              Connecticut, USA      2016-02-14
    -Ville Koskela            Washington, USA       2016-02-19
    +Matthew Bishop           Canada                2012-09-18
    +Atilla Kiraly            Hungary               2013-01-07
    +Joris Kuipers            Netherlands           2013-02-11
    +Andrey Korzhevskiy       Russia                2013-03-26 
    +Carl E. Harris Jr.       USA                   2013-03-27
    +Matthew R. Bertolini     USA                   2013-04-23
    +Gregory A. Denton        USA                   2013-05-02
    +Mikhail Mazursky         Russia                2013-10-02
    +Marvin B. Lillehaug      Norway                2014-01-20
    +Chetan Mehrotra          India                 2014-02-04
    +Eric Dahl                USA                   2014-02-04
    +Adam Gent                USA                   2014-04-01
    +Sebastian Groebler       Germany               2014-07-17
    +Alexander Dorokhine      USA                   2014-12-07
    +Yuji Okazawa             Japan                 2015-04-01
    +Lukasz Cwik              USA                   2015-06-11
    +Juan Pablo Santos        Spain                 2015-06-15
    +Gareth Davis             United Kingdom        2015-07-15
    +Marek Szalik             Poland                2015-11-25
    +Pavel Boldyrev           Canada                2015-12-22
    +Christoph Zauner         Austria               2016-01-11
    +David Roberge            USA                   2016-02-14
    +Adam Batkin              USA                   2016-02-14
    +Ville Koskela            USA                   2016-02-19
     Vedran Pavic             Croatia               2016-02-23
    -Max Urech                AG, Switzerland       2016-03-17
    -Kenneth Gendron          CA, USA               2016-04-01
    +Max Urech                Switzerland           2016-03-17
    +Tsuyoshi Yoshizawa       Japan                 2016-03-29 
    +Kenneth Gendron          USA                   2016-04-01
     Espen A. Fossen          Norway                2016-04-07
    -Scott Babcock            WA, USA               2016-12-22
    +Scott Babcock            USA                   2016-12-22
     Olivier Bourgain         France                2017-01-31
     Nicolas Maupu            France                2017-01-31
     Friso Vrolijken          Netherlands           2017-02-15
    -Jan Engehausen           ZH, Switzerland       2017-07-07
    -Christian Lorenz         Berlin, Germany       2017-11-28
    +Jan Engehausen           Switzerland           2017-07-07
    +Christian Lorenz         Germany               2017-11-28
    +Federico Fissore         Italy                 2019-02-18
    +Wessel van Norel         Netherlands           2019-08-12
    +Alexandre Dutra          France                2019-10-10
    +Nicolai Parlog           Germany               2019-11-25
    +Antonio Tomac            Croatia               2020-08-11 
    +Matthias Kiefer          Germany               2024-01-14
    +Duncan Jauncey           United Kingdom        2026-01-15
     
     Justification for CLAs
     ----------------------
    @@ -83,10 +92,9 @@ been performed (due diligence).
     
     A more detailed discussion of CLAs can be found at [2].
     
    -BTW, the CLA can be sent to Ceki directly by email. A scanned copy on
    -two separate pages is fine but please make sure that the images render
    -reasonably well when printed on a laser printer. Images taken by
    -cellphone cameras often render as a large gray smudge when printed.
    +A scanned copy of the signed CLA can be sent to Ceki directly by email
    +(ceki at qos.ch) in PDF format. Please make sure that the copy renders
    +reasonably well when printed on a laser printer.
     
     [1] http://logback.qos.ch/cla.txt
     [2] http://www.oss-watch.ac.uk/resources/cla.xml