Skip to content

Commit 4867237

Browse files
ppkarwaszvy
andcommitted
Move compression into new log4j-compress module
We move the code that depends on Commons Compress into a new `log4j-compress` module. At the same time, we perform the following behavioral changes: - Compression is performed by the `CompressActionFactoryProvider`, with the maximum order. In the current setting, if `log4j-compress` is on the classpath, it is used for everything, even GZ and ZIP. - The `compressionLevel` parameter, specific to the GZ and ZIP compression factories in the JRE, is replaced by a more generic `compressionOptions` map. This will allow adding more compression options in the future if users request it. - The available list of compression algorithms is no longer hardcoded, but depends entirely on the list of compressors supported by Apache Commons Compress. > [!NOTE] > Currently Commons Compress does not offer an algorithm-independent > interface to supply additional compression options. > However, each algorithm does provide such a way. Co-authored-by: Volkan Yazıcı <volkan@yazi.ci>
1 parent feebf52 commit 4867237

File tree

212 files changed

+1684
-1236
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

212 files changed

+1684
-1236
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
This file is here to activate the `plugin-processing` Maven profile.

log4j-compress/pom.xml

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to you under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ the License. You may obtain a copy of the License at
9+
~
10+
~ http://www.apache.org/licenses/LICENSE-2.0
11+
~
12+
~ Unless required by applicable law or agreed to in writing, software
13+
~ distributed under the License is distributed on an "AS IS" BASIS,
14+
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
~ See the License for the specific language governing permissions and
16+
~ limitations under the License.
17+
-->
18+
<project xmlns="http://maven.apache.org/POM/4.0.0" 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">
19+
<modelVersion>4.0.0</modelVersion>
20+
<parent>
21+
<groupId>org.apache.logging.log4j</groupId>
22+
<artifactId>log4j</artifactId>
23+
<version>${revision}</version>
24+
<relativePath>../log4j-parent</relativePath>
25+
</parent>
26+
27+
<artifactId>log4j-compress</artifactId>
28+
<name>Apache Log4j Core: Extended Compression Support</name>
29+
<description>Add additional compression formats to Log4j Core.</description>
30+
31+
<properties>
32+
<commons-compress.version>1.27.1</commons-compress.version>
33+
<jimfs.version>1.3.0</jimfs.version>
34+
<xz.version>1.10</xz.version>
35+
<zstd.version>1.5.6-5</zstd.version>
36+
</properties>
37+
38+
<dependencyManagement>
39+
<dependencies>
40+
41+
<!-- Dummy dependency to force Dependabot to upgrade this entry -->
42+
<dependency>
43+
<groupId>org.tukaani</groupId>
44+
<artifactId>xz</artifactId>
45+
<version>${xz.version}</version>
46+
</dependency>
47+
48+
<!-- Dummy dependency to force Dependabot to upgrade this entry -->
49+
<dependency>
50+
<groupId>com.github.luben</groupId>
51+
<artifactId>zstd-jni</artifactId>
52+
<version>${zstd.version}</version>
53+
</dependency>
54+
55+
</dependencies>
56+
</dependencyManagement>
57+
58+
<dependencies>
59+
60+
<dependency>
61+
<groupId>org.apache.commons</groupId>
62+
<artifactId>commons-compress</artifactId>
63+
<version>${commons-compress.version}</version>
64+
</dependency>
65+
66+
<dependency>
67+
<groupId>org.apache.logging.log4j</groupId>
68+
<artifactId>log4j-api</artifactId>
69+
</dependency>
70+
71+
<dependency>
72+
<groupId>org.apache.logging.log4j</groupId>
73+
<artifactId>log4j-core</artifactId>
74+
</dependency>
75+
76+
<dependency>
77+
<groupId>org.apache.logging.log4j</groupId>
78+
<artifactId>log4j-plugins</artifactId>
79+
</dependency>
80+
81+
<dependency>
82+
<groupId>org.assertj</groupId>
83+
<artifactId>assertj-core</artifactId>
84+
<scope>test</scope>
85+
</dependency>
86+
87+
<dependency>
88+
<groupId>org.junit.jupiter</groupId>
89+
<artifactId>junit-jupiter-api</artifactId>
90+
<scope>test</scope>
91+
</dependency>
92+
93+
<dependency>
94+
<groupId>org.junit.jupiter</groupId>
95+
<artifactId>junit-jupiter-params</artifactId>
96+
<scope>test</scope>
97+
</dependency>
98+
99+
<dependency>
100+
<groupId>com.google.jimfs</groupId>
101+
<artifactId>jimfs</artifactId>
102+
<version>${jimfs.version}</version>
103+
<scope>test</scope>
104+
</dependency>
105+
106+
<dependency>
107+
<groupId>org.apache.logging.log4j</groupId>
108+
<artifactId>log4j-api-test</artifactId>
109+
<scope>test</scope>
110+
</dependency>
111+
112+
</dependencies>
113+
114+
<build>
115+
<plugins>
116+
117+
<plugin>
118+
<groupId>org.apache.maven.plugins</groupId>
119+
<artifactId>maven-surefire-plugin</artifactId>
120+
<executions>
121+
<execution>
122+
<id>default-test</id>
123+
<configuration>
124+
<excludes>
125+
<exclude>org/apache/logging/log4j/compress/commons/xz/*</exclude>
126+
<exclude>org/apache/logging/log4j/compress/commons/zstd/*</exclude>
127+
</excludes>
128+
</configuration>
129+
</execution>
130+
<!--
131+
~ The XZ algorithm requires additional Commons Compress dependencies
132+
-->
133+
<execution>
134+
<id>test-xz</id>
135+
<goals>
136+
<goal>test</goal>
137+
</goals>
138+
<configuration>
139+
<additionalClasspathDependencies>
140+
<dependency>
141+
<groupId>org.tukaani</groupId>
142+
<artifactId>xz</artifactId>
143+
<version>${xz.version}</version>
144+
</dependency>
145+
</additionalClasspathDependencies>
146+
<includes>
147+
<include>org/apache/logging/log4j/compress/commons/xz/*Test.class</include>
148+
</includes>
149+
</configuration>
150+
</execution>
151+
<!--
152+
~ The XZ algorithm requires additional Commons Compress dependencies
153+
-->
154+
<execution>
155+
<id>test-zstd</id>
156+
<goals>
157+
<goal>test</goal>
158+
</goals>
159+
<configuration>
160+
<additionalClasspathDependencies>
161+
<dependency>
162+
<groupId>com.github.luben</groupId>
163+
<artifactId>zstd-jni</artifactId>
164+
<version>${zstd.version}</version>
165+
</dependency>
166+
</additionalClasspathDependencies>
167+
<includes>
168+
<include>org/apache/logging/log4j/compress/commons/zstd/*Test.class</include>
169+
</includes>
170+
</configuration>
171+
</execution>
172+
</executions>
173+
</plugin>
174+
175+
</plugins>
176+
</build>
177+
</project>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.compress.commons;
18+
19+
import java.io.BufferedOutputStream;
20+
import java.io.IOException;
21+
import java.io.OutputStream;
22+
import java.nio.file.Path;
23+
import java.util.Objects;
24+
import org.apache.commons.compress.compressors.CompressorException;
25+
import org.apache.commons.compress.compressors.CompressorStreamProvider;
26+
import org.apache.logging.log4j.core.appender.rolling.action.AbstractCompressAction;
27+
28+
/**
29+
* Compresses a file using bzip2 compression.
30+
*/
31+
final class CommonsCompressAction extends AbstractCompressAction {
32+
33+
private final CompressorStreamProvider provider;
34+
35+
/**
36+
* Compressor name. One of "gz", "bzip2", "xz", "pack200" or "deflate".
37+
*/
38+
private final String name;
39+
40+
/**
41+
* Creates new instance of Bzip2CompressAction.
42+
*
43+
* @param name The compressor name. One of "gz", "bzip2", "xz", "pack200", or "deflate".
44+
* @param source The file to compress, may not be null.
45+
* @param destination The compressed file, may not be null.
46+
*/
47+
CommonsCompressAction(
48+
final CompressorStreamProvider provider, final String name, final Path source, final Path destination) {
49+
super(source, destination);
50+
this.provider = Objects.requireNonNull(provider);
51+
this.name = Objects.requireNonNull(name, "name");
52+
}
53+
54+
@Override
55+
protected OutputStream wrapOutputStream(OutputStream stream) throws IOException {
56+
try {
57+
return new BufferedOutputStream(provider.createCompressorOutputStream(name, stream), BUF_SIZE);
58+
} catch (final CompressorException error) {
59+
final String message = String.format("failed to wrap the output stream with the `%s` compressor", name);
60+
throw new IOException(message, error);
61+
}
62+
}
63+
64+
@Override
65+
protected String getAlgorithmName() {
66+
return name;
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.compress.commons;
18+
19+
import java.nio.file.Path;
20+
import java.util.Map;
21+
import org.apache.commons.compress.compressors.CompressorStreamProvider;
22+
import org.apache.logging.log4j.core.appender.rolling.action.Action;
23+
import org.apache.logging.log4j.core.appender.rolling.action.CompressActionFactory;
24+
import org.jspecify.annotations.NullMarked;
25+
26+
@NullMarked
27+
final class CommonsCompressActionFactory implements CompressActionFactory {
28+
29+
private final CompressorStreamProvider provider;
30+
31+
private final String name;
32+
33+
CommonsCompressActionFactory(final String name, final CompressorStreamProvider provider) {
34+
this.name = name;
35+
this.provider = provider;
36+
}
37+
38+
@Override
39+
public Action createCompressAction(final Path source, final Path destination, final Map<String, String> ignored) {
40+
return new CommonsCompressAction(provider, name, source, destination);
41+
}
42+
43+
@Override
44+
public String getAlgorithmName() {
45+
return name;
46+
}
47+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.compress.commons;
18+
19+
import static org.apache.logging.log4j.util.Strings.toRootLowerCase;
20+
import static org.apache.logging.log4j.util.Strings.toRootUpperCase;
21+
22+
import org.apache.commons.compress.compressors.CompressorStreamFactory;
23+
import org.apache.commons.compress.compressors.CompressorStreamProvider;
24+
import org.apache.commons.compress.compressors.lzma.LZMAUtils;
25+
import org.apache.commons.compress.compressors.xz.XZUtils;
26+
import org.apache.commons.compress.compressors.zstandard.ZstdUtils;
27+
import org.apache.logging.log4j.Logger;
28+
import org.apache.logging.log4j.core.appender.rolling.action.CompressActionFactory;
29+
import org.apache.logging.log4j.core.appender.rolling.action.CompressActionFactoryProvider;
30+
import org.apache.logging.log4j.plugins.Namespace;
31+
import org.apache.logging.log4j.plugins.Ordered;
32+
import org.apache.logging.log4j.plugins.Plugin;
33+
import org.apache.logging.log4j.status.StatusLogger;
34+
import org.jspecify.annotations.Nullable;
35+
36+
@Plugin
37+
@Namespace(CompressActionFactoryProvider.NAMESPACE)
38+
@Ordered(0)
39+
public final class CommonsCompressActionFactoryProvider implements CompressActionFactoryProvider {
40+
41+
private static final Logger LOGGER = StatusLogger.getLogger();
42+
private static final String COMMONS_COMPRESS_DOCUMENTATION =
43+
"See https://commons.apache.org/proper/commons-compress/index.html for more information.";
44+
45+
private final CompressorStreamFactory factory = new CompressorStreamFactory();
46+
47+
private static void missingDependencyWarning(final String algorithm) {
48+
LOGGER.warn(
49+
"{} compression is not available due to a missing dependency. {}",
50+
algorithm,
51+
COMMONS_COMPRESS_DOCUMENTATION);
52+
}
53+
54+
private @Nullable CompressorStreamProvider getCompressorStreamProvider(String algorithm) {
55+
switch (toRootLowerCase(algorithm)) {
56+
case CompressorStreamFactory.LZMA:
57+
if (!LZMAUtils.isLZMACompressionAvailable()) {
58+
missingDependencyWarning("LZMA");
59+
return null;
60+
}
61+
break;
62+
case CompressorStreamFactory.PACK200:
63+
LOGGER.warn("Pack200 compression is not suitable for log files and will not be used.");
64+
return null;
65+
case CompressorStreamFactory.XZ:
66+
if (!XZUtils.isXZCompressionAvailable()) {
67+
missingDependencyWarning("XZ");
68+
return null;
69+
}
70+
break;
71+
case CompressorStreamFactory.ZSTANDARD:
72+
if (!ZstdUtils.isZstdCompressionAvailable()) {
73+
missingDependencyWarning("Zstd");
74+
return null;
75+
}
76+
break;
77+
}
78+
// Commons Compress uses upper case keys.
79+
return CompressorStreamFactory.findAvailableCompressorOutputStreamProviders()
80+
.get(toRootUpperCase(algorithm));
81+
}
82+
83+
@Override
84+
public @Nullable CompressActionFactory createFactoryForAlgorithm(String algorithm) {
85+
CompressorStreamProvider provider = getCompressorStreamProvider(algorithm);
86+
if (provider != null) {
87+
return new CommonsCompressActionFactory(algorithm, provider);
88+
}
89+
return null;
90+
}
91+
}

0 commit comments

Comments
 (0)