001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache license, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the license for the specific language governing permissions and
015 * limitations under the license.
016 */
017package org.apache.logging.log4j.core.pattern;
018
019import java.util.Locale;
020
021import org.apache.logging.log4j.core.LogEvent;
022import org.apache.logging.log4j.core.config.Configuration;
023import org.apache.logging.log4j.core.config.plugins.Plugin;
024import org.apache.logging.log4j.core.util.ArrayUtils;
025import org.apache.logging.log4j.core.util.Constants;
026import org.apache.logging.log4j.core.util.Loader;
027import org.apache.logging.log4j.message.Message;
028import org.apache.logging.log4j.message.MultiformatMessage;
029import org.apache.logging.log4j.status.StatusLogger;
030import org.apache.logging.log4j.util.MultiFormatStringBuilderFormattable;
031import org.apache.logging.log4j.util.PerformanceSensitive;
032import org.apache.logging.log4j.util.StringBuilderFormattable;
033
034/**
035 * Returns the event's rendered message in a StringBuilder.
036 */
037@Plugin(name = "MessagePatternConverter", category = PatternConverter.CATEGORY)
038@ConverterKeys({ "m", "msg", "message" })
039@PerformanceSensitive("allocation")
040public final class MessagePatternConverter extends LogEventPatternConverter {
041
042    private static final String NOLOOKUPS = "nolookups";
043
044    private final String[] formats;
045    private final Configuration config;
046    private final TextRenderer textRenderer;
047
048    /**
049     * Private constructor.
050     *
051     * @param options
052     *            options, may be null.
053     */
054    private MessagePatternConverter(final Configuration config, final String[] options) {
055        super("Message", "message");
056        this.formats = options;
057        this.config = config;
058        final int noLookupsIdx = loadNoLookups(options);
059        this.textRenderer = loadMessageRenderer(noLookupsIdx >= 0 ? ArrayUtils.remove(options, noLookupsIdx) : options);
060    }
061
062    private int loadNoLookups(final String[] options) {
063        if (options != null) {
064            for (int i = 0; i < options.length; i++) {
065                final String option = options[i];
066                if (NOLOOKUPS.equalsIgnoreCase(option)) {
067                    return i;
068                }
069            }
070        }
071        return -1;
072    }
073
074    private TextRenderer loadMessageRenderer(final String[] options) {
075        if (options != null) {
076            for (final String option : options) {
077                switch (option.toUpperCase(Locale.ROOT)) {
078                case "ANSI":
079                    if (Loader.isJansiAvailable()) {
080                        return new JAnsiTextRenderer(options, JAnsiTextRenderer.DefaultMessageStyleMap);
081                    }
082                    StatusLogger.getLogger()
083                            .warn("You requested ANSI message rendering but JANSI is not on the classpath.");
084                    return null;
085                case "HTML":
086                    return new HtmlTextRenderer(options);
087                }
088            }
089        }
090        return null;
091    }
092
093    /**
094     * Obtains an instance of pattern converter.
095     *
096     * @param config
097     *            The Configuration.
098     * @param options
099     *            options, may be null.
100     * @return instance of pattern converter.
101     */
102    public static MessagePatternConverter newInstance(final Configuration config, final String[] options) {
103        return new MessagePatternConverter(config, options);
104    }
105
106    /**
107     * {@inheritDoc}
108     */
109    @Override
110    public void format(final LogEvent event, final StringBuilder toAppendTo) {
111        final Message msg = event.getMessage();
112        if (msg instanceof StringBuilderFormattable) {
113
114            final boolean doRender = textRenderer != null;
115            final StringBuilder workingBuilder = doRender ? new StringBuilder(80) : toAppendTo;
116
117            if (msg instanceof MultiFormatStringBuilderFormattable) {
118                ((MultiFormatStringBuilderFormattable) msg).formatTo(formats, workingBuilder);
119            } else {
120                ((StringBuilderFormattable) msg).formatTo(workingBuilder);
121            }
122
123            if (doRender) {
124                textRenderer.render(workingBuilder, toAppendTo);
125            }
126            return;
127        }
128        if (msg != null) {
129            String result;
130            if (msg instanceof MultiformatMessage) {
131                result = ((MultiformatMessage) msg).getFormattedMessage(formats);
132            } else {
133                result = msg.getFormattedMessage();
134            }
135            if (result != null) {
136                toAppendTo.append(result);
137            } else {
138                toAppendTo.append("null");
139            }
140        }
141    }
142}