com.jcabi.log.MulticolorLayout Maven / Gradle / Ivy
Show all versions of driver-cql-shaded Show documentation
/**
* Copyright (c) 2012-2014, jcabi.com
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met: 1) Redistributions of source code must retain the above
* copyright notice, this list of conditions and the following
* disclaimer. 2) Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution. 3) Neither the name of the jcabi.com nor
* the names of its contributors may be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT
* NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcabi.log;
import java.util.Locale;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.apache.log4j.EnhancedPatternLayout;
import org.apache.log4j.Level;
import org.apache.log4j.spi.LoggingEvent;
/**
* Multi-color layout for LOG4J.
*
* Use it in your LOG4J configuration:
*
*
log4j.rootLogger=INFO, CONSOLE
* log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
* log4j.appender.CONSOLE.layout=com.jcabi.log.MulticolorLayout
* log4j.appender.CONSOLE.layout.ConversionPattern=[%color{%-5p}] %c: %m%n
*
* The part of the message wrapped with {@code %color{...}}
* will change its color according to the logging level of the event. Without
* this highlighting the behavior of the layout is identical to
* {@link EnhancedPatternLayout}. You can use {@code %color-red{...}} if you
* want to use specifically red color for the wrapped piece of text. Supported
* colors are: {@code red}, {@code blue}, {@code yellow}, {@code cyan},
* {@code black}, and {@code white}.
*
*
Besides that you can specify any ANSI color you like with
* {@code %color-;;{...}}, where
* {@code } is a binary mask of attributes,
* {@code } is a background color, and
* {@code } is a foreground color. Read more about
* ANSI escape code.
*
* This class or its parents are not serializable.
*
*
Maven dependency for this class is
* (see How
* to use with Maven instructions):
*
*
<dependency>
* <groupId>com.jcabi</groupId>
* <artifactId>jcabi-log</artifactId>
* </dependency>
*
* @author Yegor Bugayenko ([email protected])
* @version $Id$
* @since 0.1.10
* @see ANSI escape code
* @see PatternLayout from LOG4J
* @see How to use with Maven
*/
@ToString
@EqualsAndHashCode(callSuper = false)
@SuppressWarnings("PMD.NonStaticInitializer")
public final class MulticolorLayout extends EnhancedPatternLayout {
/**
* Control sequence indicator.
*/
private static final String CSI = "\u001b[";
/**
* To split strings with javascript like map syntax.
*/
private static final String SPLIT_ITEMS = ",";
/**
* To split key:value pairs.
*/
private static final String SPLIT_VALUES = ":";
/**
* Regular expression for all matches.
*/
private static final Pattern METAS = Pattern.compile(
"%color(?:-([a-z]+|[0-9]{1,3};[0-9]{1,3};[0-9]{1,3}))?\\{(.*?)\\}"
);
/**
* Colors with names.
*/
private final transient ConcurrentMap colors =
MulticolorLayout.colorMap();
/**
* Colors of levels.
*/
private final transient ConcurrentMap levels =
MulticolorLayout.levelMap();
/**
* Store original conversation pattern to be able
* to recalculate it, if new colors are provided.
*/
private transient String base;
@Override
public void setConversionPattern(final String pattern) {
this.base = pattern;
final Matcher matcher = MulticolorLayout.METAS.matcher(pattern);
final StringBuffer buf = new StringBuffer(0);
while (matcher.find()) {
matcher.appendReplacement(buf, "");
buf.append(MulticolorLayout.CSI)
.append(this.ansi(matcher.group(1)))
.append('m')
.append(matcher.group(2))
.append(MulticolorLayout.CSI)
.append('m');
}
matcher.appendTail(buf);
super.setConversionPattern(buf.toString());
}
/**
* Allow to overwrite or specify new ANSI color names
* in a javascript map like format.
*
* @param cols JavaScript like map of color names
* @since 0.9
*/
public void setColors(final String cols) {
for (final String item : cols.split(MulticolorLayout.SPLIT_ITEMS)) {
final String[] values = item.split(MulticolorLayout.SPLIT_VALUES);
this.colors.put(values[0], values[1]);
}
/**
* If setConversionPattern was called before me must call again
* to be sure to replace all custom color constants with
* new values.
*/
if (this.base != null) {
this.setConversionPattern(this.base);
}
}
/**
* Allow to overwrite the ANSI color values for the log levels
* in a javascript map like format.
*
* @param lev JavaScript like map of levels
* @since 0.9
*/
public void setLevels(final String lev) {
for (final String item : lev.split(MulticolorLayout.SPLIT_ITEMS)) {
final String[] values = item.split(MulticolorLayout.SPLIT_VALUES);
final String level = values[0].toUpperCase(Locale.ENGLISH);
if (Level.toLevel(level, null) == null) {
throw new IllegalArgumentException(
String.format(Locale.ENGLISH, "Unknown level '%s'", level)
);
}
this.levels.put(level, values[1]);
}
}
@Override
public String format(final LoggingEvent event) {
return super.format(event).replace(
String.format("%s?m", MulticolorLayout.CSI),
String.format(
"%s%sm",
MulticolorLayout.CSI,
this.levels.get(event.getLevel().toString())
)
);
}
/**
* Convert our text to ANSI color.
* @param meta Meta text
* @return ANSI color
*/
private String ansi(final String meta) {
final String ansi;
if (meta == null) {
ansi = "?";
} else if (meta.matches("[a-z]+")) {
ansi = this.colors.get(meta);
if (ansi == null) {
throw new IllegalArgumentException(
String.format("unknown color '%s'", meta)
);
}
} else {
ansi = meta;
}
return ansi;
}
/**
* Color map.
* @return Map of colors
*/
private static ConcurrentMap colorMap() {
final ConcurrentMap map =
new ConcurrentHashMap();
map.put("black", "30");
map.put("blue", "34");
map.put("cyan", "36");
map.put("green", "32");
map.put("magenta", "35");
map.put("red", "31");
map.put("yellow", "33");
map.put("white", "37");
return map;
}
/**
* Level map.
* @return Map of levels
*/
private static ConcurrentMap levelMap() {
final ConcurrentMap map =
new ConcurrentHashMap();
map.put(Level.TRACE.toString(), "2;33");
map.put(Level.DEBUG.toString(), "2;37");
map.put(Level.INFO.toString(), "0;37");
map.put(Level.WARN.toString(), "0;33");
map.put(Level.ERROR.toString(), "0;31");
map.put(Level.FATAL.toString(), "0;35");
return map;
}
}