Skip to content

Commit a37d551

Browse files
committed
Add CustomColorConverter, re #10
1 parent 201e743 commit a37d551

File tree

1 file changed

+178
-0
lines changed

1 file changed

+178
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/* Copyright (c) 2025 LibJ
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* You should have received a copy of The MIT License (MIT) along with this
14+
* program. If not, see <http://opensource.org/licenses/MIT/>.
15+
*/
16+
17+
package org.libj.logging;
18+
19+
import java.util.ArrayList;
20+
import java.util.Arrays;
21+
import java.util.List;
22+
import java.util.Map;
23+
24+
import ch.qos.logback.classic.spi.ILoggingEvent;
25+
import ch.qos.logback.core.Context;
26+
import ch.qos.logback.core.pattern.CompositeConverter;
27+
28+
/**
29+
* A {@link CompositeConverter} that allows for the conversion of a log pattern to be displayed in colors configurable via
30+
* {@code <variable>} tags.
31+
* <p>
32+
* Example:
33+
*
34+
* <pre>
35+
* &lt;variable scope="context" name="cc:com.example.foo" value="[38;5;21m"/>
36+
* &lt;variable scope="context" name="cc:com.example.bar" value="[38;5;219m"/>
37+
* &lt;conversionRule conversionWord="customColor" converterClass="org.libj.logging.CustomColorConverter"/>
38+
* &lt;appender>
39+
* &lt;encoder>
40+
* &lt;pattern>%date{yyyy-MM-dd HH:mm:ss.SSS} %customColor(%logger){loggerName,cc} %color(%level %msg%n)&lt;/pattern>
41+
* &lt;/encoder>
42+
* &lt;/appender>
43+
* </pre>
44+
* <p>
45+
* In the provided example, two options are specified:
46+
* <ol>
47+
* <li>loggerName: The event property to match. Can be one of {level, loggerName, marker, message, threadName}.</li>
48+
* <li>cc: The variable prefix to be referenced when parsing defined variables.</li>
49+
* </ol>
50+
* <p>
51+
* The following two variables are also defined:
52+
* <ol>
53+
* <li>cc:com.example.foo: The key with "cc:" prefix to be matched resulting in the color specified as the value when matching
54+
* "com.example.foo" as the "loggerName".</li>
55+
* <li>cc:com.example.bar: The key with "cc:" prefix to be matched resulting in the color specified as the value when matching
56+
* "com.example.bar" as the "loggerName"</li>
57+
* </ol>
58+
*/
59+
public class CustomColorConverter extends CompositeConverter<ILoggingEvent> {
60+
private static final String esc = String.valueOf((char)0x1b);
61+
62+
private final ArrayList<String> keys = new ArrayList<>();
63+
private final ArrayList<String> values = new ArrayList<>();
64+
65+
private enum Property {
66+
LEVEL("level") {
67+
@Override
68+
String getValue(final ILoggingEvent event) {
69+
return event.getLevel().toString();
70+
}
71+
},
72+
LOGGER_NAME("loggerName") {
73+
@Override
74+
String getValue(final ILoggingEvent event) {
75+
return event.getLoggerName();
76+
}
77+
},
78+
MARKER("marker") {
79+
@Override
80+
String getValue(final ILoggingEvent event) {
81+
return event.getMarker().toString();
82+
}
83+
},
84+
MESSAGE("message") {
85+
@Override
86+
String getValue(final ILoggingEvent event) {
87+
return event.getMessage();
88+
}
89+
},
90+
THREAD_NAME("threadName") {
91+
@Override
92+
String getValue(final ILoggingEvent event) {
93+
return event.getThreadName();
94+
}
95+
};
96+
97+
private final String name;
98+
99+
private Property(final String name) {
100+
this.name = name;
101+
}
102+
103+
abstract String getValue(ILoggingEvent event);
104+
105+
@Override
106+
public String toString() {
107+
return name;
108+
}
109+
110+
private static Property fromString(final String s) {
111+
for (final Property property : values()) // [A]
112+
if (property.name.equals(s))
113+
return property;
114+
115+
return null;
116+
}
117+
}
118+
119+
private Property property;
120+
121+
@Override
122+
public void start() {
123+
final Context context = getContext();
124+
if (context == null)
125+
return;
126+
127+
final List<String> optionList = super.getOptionList();
128+
if (optionList == null || optionList.size() != 2) {
129+
addError("Options {<EVENT_PROPERTY>,<VARIABLE_PREFIX>} are not specified for conversionRule with class " + getClass().getName());
130+
return;
131+
}
132+
133+
final String propertyName = optionList.get(0);
134+
this.property = Property.fromString(propertyName);
135+
if (this.property == null) {
136+
addError("EVENT_PROPERTY (" + propertyName + ") does not match: " + Arrays.toString(Property.values()));
137+
return;
138+
}
139+
140+
final String prefix = optionList.get(1) + ":";
141+
final int len = prefix.length();
142+
for (final Map.Entry<String,String> entry : context.getCopyOfPropertyMap().entrySet()) {
143+
if (entry.getKey().startsWith(prefix)) {
144+
final String key = entry.getKey().substring(len);
145+
keys.add(key);
146+
values.add(entry.getValue());
147+
}
148+
}
149+
150+
super.start();
151+
}
152+
153+
private String match(final ILoggingEvent event) {
154+
final String loggerName = event.getLoggerName();
155+
final int length = loggerName.length();
156+
for (int i = 0, i$ = keys.size(); i < i$; ++i) { // [RA]
157+
final String key = keys.get(i);
158+
final int len = key.length();
159+
final String value = values.get(i);
160+
if (len == length) {
161+
if (loggerName.equals(value))
162+
return value;
163+
}
164+
else if (len < length) {
165+
if (loggerName.startsWith(key) && loggerName.charAt(len) == '.')
166+
return value;
167+
}
168+
}
169+
170+
return null;
171+
}
172+
173+
@Override
174+
protected String transform(final ILoggingEvent event, final String in) {
175+
final String value = match(event);
176+
return value != null ? esc + value + in + esc + "[0m" : in;
177+
}
178+
}

0 commit comments

Comments
 (0)