Skip to content

Commit c977798

Browse files
committed
Merge branch 'fix/2363_add_logback_throwable_consuming_semantics' into 2.x
2 parents 06d9695 + 42b5ebb commit c977798

File tree

9 files changed

+809
-0
lines changed

9 files changed

+809
-0
lines changed
Lines changed: 187 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,187 @@
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.slf4j.message;
18+
19+
import java.util.Arrays;
20+
import org.apache.logging.log4j.message.Message;
21+
import org.apache.logging.log4j.message.MessageFactory2;
22+
import org.apache.logging.log4j.message.ObjectMessage;
23+
import org.apache.logging.log4j.message.ParameterizedMessage;
24+
import org.apache.logging.log4j.message.SimpleMessage;
25+
26+
/**
27+
* A message factory that eagerly removes a trailing throwable argument.
28+
* <p>
29+
* This factory implements the algorithm used by Logback 1.1.x or later to determine
30+
* {@link Message#getThrowable()}: if the last argument is a {@link Throwable} and there are less arguments than
31+
* the number of placeholders incremented by one, the last argument will be used as
32+
* {@link Message#getThrowable()} and will <strong>not</strong> appear in the parameterized message.
33+
* </p>
34+
* <p>
35+
* The usual Log4j semantic only looks for throwables once <strong>all</strong> the placeholders have been filled.
36+
* </p>
37+
* @since 2.24.0
38+
*/
39+
public final class ThrowableConsumingMessageFactory implements MessageFactory2 {
40+
41+
private Message newParameterizedMessage(final Object throwable, final String pattern, final Object... args) {
42+
return new ParameterizedMessage(pattern, args, (Throwable) throwable);
43+
}
44+
45+
@Override
46+
public Message newMessage(final Object message) {
47+
return new ObjectMessage(message);
48+
}
49+
50+
@Override
51+
public Message newMessage(final String message) {
52+
return new SimpleMessage(message);
53+
}
54+
55+
@Override
56+
public Message newMessage(final String message, final Object... params) {
57+
if (params != null && params.length > 0) {
58+
final Object lastArg = params[params.length - 1];
59+
return lastArg instanceof Throwable
60+
? newParameterizedMessage(lastArg, message, Arrays.copyOf(params, params.length - 1))
61+
: newParameterizedMessage(null, message, params);
62+
}
63+
return new SimpleMessage(message);
64+
}
65+
66+
@Override
67+
public Message newMessage(final CharSequence charSequence) {
68+
return new SimpleMessage(charSequence);
69+
}
70+
71+
@Override
72+
public Message newMessage(final String message, final Object p0) {
73+
return p0 instanceof Throwable
74+
? newParameterizedMessage(p0, message)
75+
: newParameterizedMessage(null, message, p0);
76+
}
77+
78+
@Override
79+
public Message newMessage(final String message, final Object p0, final Object p1) {
80+
return p1 instanceof Throwable
81+
? newParameterizedMessage(p1, message, p0)
82+
: newParameterizedMessage(null, message, p0, p1);
83+
}
84+
85+
@Override
86+
public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {
87+
return p2 instanceof Throwable
88+
? newParameterizedMessage(p2, message, p0, p1)
89+
: newParameterizedMessage(null, message, p0, p1, p2);
90+
}
91+
92+
@Override
93+
public Message newMessage(
94+
final String message, final Object p0, final Object p1, final Object p2, final Object p3) {
95+
return p3 instanceof Throwable
96+
? newParameterizedMessage(p3, message, p0, p1, p2)
97+
: newParameterizedMessage(null, message, p0, p1, p2, p3);
98+
}
99+
100+
@Override
101+
public Message newMessage(
102+
final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {
103+
return p4 instanceof Throwable
104+
? newParameterizedMessage(p4, message, p0, p1, p2, p3)
105+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4);
106+
}
107+
108+
@Override
109+
public Message newMessage(
110+
final String message,
111+
final Object p0,
112+
final Object p1,
113+
final Object p2,
114+
final Object p3,
115+
final Object p4,
116+
final Object p5) {
117+
return p5 instanceof Throwable
118+
? newParameterizedMessage(p5, message, p0, p1, p2, p3, p4)
119+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4, p5);
120+
}
121+
122+
@Override
123+
public Message newMessage(
124+
final String message,
125+
final Object p0,
126+
final Object p1,
127+
final Object p2,
128+
final Object p3,
129+
final Object p4,
130+
final Object p5,
131+
final Object p6) {
132+
return p6 instanceof Throwable
133+
? newParameterizedMessage(p6, message, p0, p1, p2, p3, p4, p5)
134+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4, p5, p6);
135+
}
136+
137+
@Override
138+
public Message newMessage(
139+
final String message,
140+
final Object p0,
141+
final Object p1,
142+
final Object p2,
143+
final Object p3,
144+
final Object p4,
145+
final Object p5,
146+
final Object p6,
147+
final Object p7) {
148+
return p7 instanceof Throwable
149+
? newParameterizedMessage(p7, message, p0, p1, p2, p3, p4, p5, p6)
150+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4, p5, p6, p7);
151+
}
152+
153+
@Override
154+
public Message newMessage(
155+
final String message,
156+
final Object p0,
157+
final Object p1,
158+
final Object p2,
159+
final Object p3,
160+
final Object p4,
161+
final Object p5,
162+
final Object p6,
163+
final Object p7,
164+
final Object p8) {
165+
return p8 instanceof Throwable
166+
? newParameterizedMessage(p8, message, p0, p1, p2, p3, p4, p5, p6, p7)
167+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8);
168+
}
169+
170+
@Override
171+
public Message newMessage(
172+
final String message,
173+
final Object p0,
174+
final Object p1,
175+
final Object p2,
176+
final Object p3,
177+
final Object p4,
178+
final Object p5,
179+
final Object p6,
180+
final Object p7,
181+
final Object p8,
182+
final Object p9) {
183+
return p9 instanceof Throwable
184+
? newParameterizedMessage(p9, message, p0, p1, p2, p3, p4, p5, p6, p7, p8)
185+
: newParameterizedMessage(null, message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
186+
}
187+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
@Export
18+
@Version("2.24.0")
19+
package org.apache.logging.slf4j.message;
20+
21+
import org.osgi.annotation.bundle.Export;
22+
import org.osgi.annotation.versioning.Version;
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
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.slf4j.message;
18+
19+
import static org.assertj.core.api.Assertions.assertThat;
20+
21+
import org.apache.logging.log4j.message.Message;
22+
import org.apache.logging.log4j.message.MessageFactory2;
23+
import org.junit.jupiter.api.Test;
24+
25+
public class ThrowableConsumingMessageFactoryTest {
26+
27+
private static final String MESSAGE = "MESSAGE";
28+
private static final Object P0 = new Object();
29+
private static final Object P1 = new Object();
30+
private static final Object P2 = new Object();
31+
private static final Object P3 = new Object();
32+
private static final Object P4 = new Object();
33+
private static final Object P5 = new Object();
34+
private static final Object P6 = new Object();
35+
private static final Object P7 = new Object();
36+
private static final Object P8 = new Object();
37+
private static final Object P9 = new Object();
38+
private static final Object P10 = new Object();
39+
private static final Object THROWABLE = new Throwable();
40+
41+
@Test
42+
void should_not_consume_last_object_parameter() {
43+
final MessageFactory2 factory = new ThrowableConsumingMessageFactory();
44+
assertThat(factory.newMessage(MESSAGE, P0))
45+
.extracting(m -> m.getParameters().length, Message::getThrowable)
46+
.as("checking parameter count and throwable")
47+
.containsExactly(1, null);
48+
assertThat(factory.newMessage(MESSAGE, P0, P1))
49+
.extracting(m -> m.getParameters().length, Message::getThrowable)
50+
.as("checking parameter count and throwable")
51+
.containsExactly(2, null);
52+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2))
53+
.extracting(m -> m.getParameters().length, Message::getThrowable)
54+
.as("checking parameter count and throwable")
55+
.containsExactly(3, null);
56+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3))
57+
.extracting(m -> m.getParameters().length, Message::getThrowable)
58+
.as("checking parameter count and throwable")
59+
.containsExactly(4, null);
60+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4))
61+
.extracting(m -> m.getParameters().length, Message::getThrowable)
62+
.as("checking parameter count and throwable")
63+
.containsExactly(5, null);
64+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5))
65+
.extracting(m -> m.getParameters().length, Message::getThrowable)
66+
.as("checking parameter count and throwable")
67+
.containsExactly(6, null);
68+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6))
69+
.extracting(m -> m.getParameters().length, Message::getThrowable)
70+
.as("checking parameter count and throwable")
71+
.containsExactly(7, null);
72+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7))
73+
.extracting(m -> m.getParameters().length, Message::getThrowable)
74+
.as("checking parameter count and throwable")
75+
.containsExactly(8, null);
76+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, P8))
77+
.extracting(m -> m.getParameters().length, Message::getThrowable)
78+
.as("checking parameter count and throwable")
79+
.containsExactly(9, null);
80+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9))
81+
.extracting(m -> m.getParameters().length, Message::getThrowable)
82+
.as("checking parameter count and throwable")
83+
.containsExactly(10, null);
84+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, P10))
85+
.extracting(m -> m.getParameters().length, Message::getThrowable)
86+
.as("checking parameter count and throwable")
87+
.containsExactly(11, null);
88+
}
89+
90+
@Test
91+
void should_consume_last_throwable_parameter() {
92+
final MessageFactory2 factory = new ThrowableConsumingMessageFactory();
93+
assertThat(factory.newMessage(MESSAGE, THROWABLE))
94+
.extracting(m -> m.getParameters().length, Message::getThrowable)
95+
.as("checking parameter count and throwable")
96+
.containsExactly(0, THROWABLE);
97+
assertThat(factory.newMessage(MESSAGE, P0, THROWABLE))
98+
.extracting(m -> m.getParameters().length, Message::getThrowable)
99+
.as("checking parameter count and throwable")
100+
.containsExactly(1, THROWABLE);
101+
assertThat(factory.newMessage(MESSAGE, P0, P1, THROWABLE))
102+
.extracting(m -> m.getParameters().length, Message::getThrowable)
103+
.as("checking parameter count and throwable")
104+
.containsExactly(2, THROWABLE);
105+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, THROWABLE))
106+
.extracting(m -> m.getParameters().length, Message::getThrowable)
107+
.as("checking parameter count and throwable")
108+
.containsExactly(3, THROWABLE);
109+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, THROWABLE))
110+
.extracting(m -> m.getParameters().length, Message::getThrowable)
111+
.as("checking parameter count and throwable")
112+
.containsExactly(4, THROWABLE);
113+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, THROWABLE))
114+
.extracting(m -> m.getParameters().length, Message::getThrowable)
115+
.as("checking parameter count and throwable")
116+
.containsExactly(5, THROWABLE);
117+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, THROWABLE))
118+
.extracting(m -> m.getParameters().length, Message::getThrowable)
119+
.as("checking parameter count and throwable")
120+
.containsExactly(6, THROWABLE);
121+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, THROWABLE))
122+
.extracting(m -> m.getParameters().length, Message::getThrowable)
123+
.as("checking parameter count and throwable")
124+
.containsExactly(7, THROWABLE);
125+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, THROWABLE))
126+
.extracting(m -> m.getParameters().length, Message::getThrowable)
127+
.as("checking parameter count and throwable")
128+
.containsExactly(8, THROWABLE);
129+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, P8, THROWABLE))
130+
.extracting(m -> m.getParameters().length, Message::getThrowable)
131+
.as("checking parameter count and throwable")
132+
.containsExactly(9, THROWABLE);
133+
assertThat(factory.newMessage(MESSAGE, P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, THROWABLE))
134+
.extracting(m -> m.getParameters().length, Message::getThrowable)
135+
.as("checking parameter count and throwable")
136+
.containsExactly(10, THROWABLE);
137+
}
138+
}

0 commit comments

Comments
 (0)