Skip to content

Commit ee61a8b

Browse files
committed
Adapt ExtendedStackTraceElement to Java 9+
1 parent 6dfba56 commit ee61a8b

File tree

10 files changed

+606
-24
lines changed

10 files changed

+606
-24
lines changed

log4j-core-java9/pom.xml

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
<log4jParentDir>${basedir}/..</log4jParentDir>
3131
<docLabel>Log4j Implementation Documentation</docLabel>
3232
<projectDir>/core</projectDir>
33+
<maven.compiler.release>9</maven.compiler.release>
3334
<maven.deploy.skip>true</maven.deploy.skip>
3435
</properties>
3536
<dependencies>
@@ -38,6 +39,14 @@
3839
<groupId>org.apache.logging.log4j</groupId>
3940
<artifactId>log4j-api</artifactId>
4041
</dependency>
42+
<dependency>
43+
<groupId>com.fasterxml.jackson.core</groupId>
44+
<artifactId>jackson-annotations</artifactId>
45+
</dependency>
46+
<dependency>
47+
<groupId>com.fasterxml.jackson.dataformat</groupId>
48+
<artifactId>jackson-dataformat-xml</artifactId>
49+
</dependency>
4150
<dependency>
4251
<groupId>org.apache.maven</groupId>
4352
<artifactId>maven-core</artifactId>
@@ -65,7 +74,6 @@
6574
</execution>
6675
</executions>
6776
</plugin>
68-
6977
<plugin>
7078
<groupId>org.apache.maven.plugins</groupId>
7179
<artifactId>maven-compiler-plugin</artifactId>
@@ -77,13 +85,6 @@
7785
</goals>
7886
<phase>compile</phase>
7987
</execution>
80-
<execution>
81-
<id>default-test-compile</id>
82-
<goals>
83-
<goal>testCompile</goal>
84-
</goals>
85-
<phase>test-compile</phase>
86-
</execution>
8788
</executions>
8889
</plugin>
8990
</plugins>

log4j-core-java9/src/assembly/java9.xml

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,10 @@
2626
<directory>${project.build.outputDirectory}</directory>
2727
<outputDirectory>/classes/META-INF/versions/9</outputDirectory>
2828
<includes>
29-
<include>**/*.class</include>
30-
</includes>
31-
<excludes>
32-
<exclude>module-info.class</exclude>
33-
<exclude>**/Dummy.class</exclude>
34-
<exclude>**/util/Clock.class</exclude>
35-
<exclude>**/time/Instant.class</exclude>
36-
<exclude>**/time/MutableInstant.class</exclude>
37-
<exclude>**/time/PreciseClock.class</exclude>
38-
</excludes>
39-
</fileSet>
40-
<fileSet>
41-
<directory>${project.build.outputDirectory}</directory>
42-
<outputDirectory>/classes</outputDirectory>
43-
<includes>
44-
<include>module-info.class</include>
29+
<include>org/apache/logging/log4j/core/impl/ExtendedStackTraceElement.class</include>
30+
<include>org/apache/logging/log4j/core/jackson/ExtendedStackTraceElementMixIn.class</include>
31+
<include>org/apache/logging/log4j/core/jackson/Log4jStackTraceElementDeserializer.class</include>
32+
<include>org/apache/logging/log4j/core/util/SystemClock.class</include>
4533
</includes>
4634
</fileSet>
4735
</fileSets>
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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.core.impl;
18+
19+
import java.io.Serializable;
20+
21+
import org.apache.logging.log4j.core.pattern.TextRenderer;
22+
23+
/**
24+
* Dummy class to let ExtendedStackTracElement to compile.
25+
*/
26+
public class ExtendedClassInfo implements Serializable {
27+
28+
/**
29+
* Constructs a new instance.
30+
*
31+
* @param exact
32+
* @param location
33+
* @param version
34+
*/
35+
public ExtendedClassInfo(final boolean exact, final String location, final String version) {
36+
}
37+
38+
public boolean getExact() {
39+
return false;
40+
}
41+
42+
public String getLocation() {
43+
return null;
44+
}
45+
46+
public String getVersion() {
47+
return null;
48+
}
49+
50+
public void renderOn(final StringBuilder output, final TextRenderer textRenderer) {
51+
}
52+
53+
}
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
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.core.impl;
18+
19+
import java.io.Serializable;
20+
import java.lang.module.ModuleReference;
21+
import java.lang.module.ResolvedModule;
22+
import java.lang.reflect.Method;
23+
import java.util.HashSet;
24+
import java.util.Objects;
25+
import java.util.Optional;
26+
import java.util.Set;
27+
28+
import org.apache.logging.log4j.core.pattern.PlainTextRenderer;
29+
import org.apache.logging.log4j.core.pattern.TextRenderer;
30+
import org.apache.logging.log4j.status.StatusLogger;
31+
import org.apache.logging.log4j.util.Strings;
32+
33+
/**
34+
* Wraps and extends the concept of the JRE's final class {@link StackTraceElement} by adding more location information.
35+
* <p>
36+
* Complements a StackTraceElement with:
37+
* </p>
38+
* <ul>
39+
* <li>exact: whether the class was obtained via {@link sun.reflect.Reflection#getCallerClass(int)}</li>
40+
* <li>location: a classpath element or a jar</li>
41+
* <li>version</li>
42+
* </ul>
43+
*/
44+
public final class ExtendedStackTraceElement implements Serializable {
45+
46+
static final ExtendedStackTraceElement[] EMPTY_ARRAY = {};
47+
48+
private static final Set<String> MODULES_IGNORE_VERSION = modulesIgnoreVersion();
49+
50+
private static final long serialVersionUID = -2171069569241280505L;
51+
52+
private final ExtendedClassInfo extraClassInfo;
53+
54+
private final StackTraceElement stackTraceElement;
55+
56+
/**
57+
* Modules for which {@link StackTraceElement} does not print the version
58+
* number.
59+
* @see StackTraceElement
60+
*/
61+
private static Set<String> modulesIgnoreVersion() {
62+
final Optional<ResolvedModule> resolvedModule = ModuleLayer.boot().configuration().findModule("java.base");
63+
final ModuleReference mref = resolvedModule.get().reference();
64+
try {
65+
final Method recordedHashesMethod = mref.getClass().getMethod("recordedHashes");
66+
final Object hashes = recordedHashesMethod.invoke(mref);
67+
final Method namesMethod = hashes.getClass().getMethod("names");
68+
@SuppressWarnings("unchecked")
69+
final Set<String> names = new HashSet<>((Set<String>) namesMethod.invoke(hashes));
70+
names.add("java.base");
71+
return names;
72+
} catch (ReflectiveOperationException e) {
73+
StatusLogger.getLogger().debug("Can not retrieve list of JDK non-upgradeable modules.", e);
74+
}
75+
return Set.of("java.base");
76+
}
77+
78+
public ExtendedStackTraceElement(final StackTraceElement stackTraceElement,
79+
final ExtendedClassInfo extraClassInfo) {
80+
this.stackTraceElement = stackTraceElement;
81+
this.extraClassInfo = extraClassInfo;
82+
}
83+
84+
85+
/**
86+
* Called from Jackson for XML and JSON IO.
87+
*/
88+
public ExtendedStackTraceElement(final String classLoaderName, final String moduleName, final String moduleVersion,
89+
final String declaringClass, final String methodName, final String fileName, final int lineNumber,
90+
final boolean exact, final String location, final String version) {
91+
this(new StackTraceElement(classLoaderName, moduleName, moduleVersion, declaringClass, methodName, fileName,
92+
lineNumber), new ExtendedClassInfo(exact, location, version));
93+
}
94+
95+
@Override
96+
public boolean equals(final Object obj) {
97+
if (this == obj) {
98+
return true;
99+
}
100+
if (obj == null) {
101+
return false;
102+
}
103+
if (!(obj instanceof ExtendedStackTraceElement)) {
104+
return false;
105+
}
106+
final ExtendedStackTraceElement other = (ExtendedStackTraceElement) obj;
107+
if (!Objects.equals(this.extraClassInfo, other.extraClassInfo)) {
108+
return false;
109+
}
110+
if (!Objects.equals(this.stackTraceElement, other.stackTraceElement)) {
111+
return false;
112+
}
113+
return true;
114+
}
115+
116+
117+
public String getModuleName() {
118+
return stackTraceElement.getModuleName();
119+
}
120+
121+
public String getModuleVersion() {
122+
return stackTraceElement.getModuleVersion();
123+
}
124+
125+
public String getClassLoaderName() {
126+
return stackTraceElement.getClassLoaderName();
127+
}
128+
129+
public String getClassName() {
130+
return stackTraceElement.getClassName();
131+
}
132+
133+
public boolean getExact() {
134+
return extraClassInfo.getExact();
135+
}
136+
137+
public ExtendedClassInfo getExtraClassInfo() {
138+
return extraClassInfo;
139+
}
140+
141+
public String getFileName() {
142+
return stackTraceElement.getFileName();
143+
}
144+
145+
public int getLineNumber() {
146+
return stackTraceElement.getLineNumber();
147+
}
148+
149+
public String getLocation() {
150+
return extraClassInfo.getLocation();
151+
}
152+
153+
public String getMethodName() {
154+
return stackTraceElement.getMethodName();
155+
}
156+
157+
public StackTraceElement getStackTraceElement() {
158+
return stackTraceElement;
159+
}
160+
161+
public String getVersion() {
162+
return extraClassInfo.getVersion();
163+
}
164+
165+
@Override
166+
public int hashCode() {
167+
return Objects.hash(extraClassInfo, stackTraceElement);
168+
}
169+
170+
public boolean isNativeMethod() {
171+
return stackTraceElement.isNativeMethod();
172+
}
173+
174+
void renderOn(final StringBuilder output, final TextRenderer textRenderer) {
175+
render(stackTraceElement, output, textRenderer);
176+
textRenderer.render(" ", output, "Text");
177+
extraClassInfo.renderOn(output, textRenderer);
178+
}
179+
180+
private void render(final StackTraceElement stElement, final StringBuilder output, final TextRenderer textRenderer) {
181+
final String classLoaderName = getClassLoaderName();
182+
if (Strings.isNotEmpty(classLoaderName)) {
183+
switch (classLoaderName) {
184+
case "app":
185+
case "boot":
186+
case "platform":
187+
break;
188+
default:
189+
textRenderer.render(classLoaderName, output, "StackTraceElement.ClassLoaderName");
190+
textRenderer.render("/", output, "StackTraceElement.ClassLoaderSeparator");
191+
}
192+
}
193+
final String moduleName = getModuleName();
194+
if (Strings.isNotEmpty(moduleName)) {
195+
textRenderer.render(moduleName, output, moduleName);
196+
final String moduleVersion = getModuleVersion();
197+
if (!MODULES_IGNORE_VERSION.contains(moduleName) && Strings.isNotEmpty(moduleVersion)) {
198+
textRenderer.render("@", output, "StackTraceElement.ModuleVersionSeparator");
199+
textRenderer.render(moduleVersion, output, "StackTraceElement.ModuleVersion");
200+
}
201+
textRenderer.render("/", output, "StackTraceElement.ModuleNameSeparator");
202+
}
203+
final String fileName = stElement.getFileName();
204+
final int lineNumber = stElement.getLineNumber();
205+
textRenderer.render(getClassName(), output, "StackTraceElement.ClassName");
206+
textRenderer.render(".", output, "StackTraceElement.ClassMethodSeparator");
207+
textRenderer.render(stElement.getMethodName(), output, "StackTraceElement.MethodName");
208+
if (stElement.isNativeMethod()) {
209+
textRenderer.render("(Native Method)", output, "StackTraceElement.NativeMethod");
210+
} else if (fileName != null && lineNumber >= 0) {
211+
textRenderer.render("(", output, "StackTraceElement.Container");
212+
textRenderer.render(fileName, output, "StackTraceElement.FileName");
213+
textRenderer.render(":", output, "StackTraceElement.ContainerSeparator");
214+
textRenderer.render(Integer.toString(lineNumber), output, "StackTraceElement.LineNumber");
215+
textRenderer.render(")", output, "StackTraceElement.Container");
216+
} else if (fileName != null) {
217+
textRenderer.render("(", output, "StackTraceElement.Container");
218+
textRenderer.render(fileName, output, "StackTraceElement.FileName");
219+
textRenderer.render(")", output, "StackTraceElement.Container");
220+
} else {
221+
textRenderer.render("(", output, "StackTraceElement.Container");
222+
textRenderer.render("Unknown Source", output, "StackTraceElement.UnknownSource");
223+
textRenderer.render(")", output, "StackTraceElement.Container");
224+
}
225+
}
226+
227+
@Override
228+
public String toString() {
229+
final StringBuilder sb = new StringBuilder();
230+
renderOn(sb, PlainTextRenderer.getInstance());
231+
return sb.toString();
232+
}
233+
234+
}

0 commit comments

Comments
 (0)