Skip to the content.

Home / cs-notes / Architecture / Components / Log / Log4j2 / 日志格式是如何解析的

log4j-core-2.6.2.jar

PatternLayout

org.apache.logging.log4j.core.layout

    public static Serializer createSerializer(final Configuration configuration, final RegexReplacement replace,
            final String pattern, final String defaultPattern, final PatternSelector patternSelector,
            final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi) {
        if (Strings.isEmpty(pattern) && Strings.isEmpty(defaultPattern)) {
            return null;
        }
        if (patternSelector == null) {
            try {
                final PatternParser parser = createPatternParser(configuration);
                final List<PatternFormatter> list = parser.parse(pattern == null ? defaultPattern : pattern,
                        alwaysWriteExceptions, noConsoleNoAnsi);
                final PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]);
                return new PatternSerializer(formatters, replace);
            } catch (final RuntimeException ex) {
                throw new IllegalArgumentException("Cannot parse pattern '" + pattern + "'", ex);
            }
        }
        return new PatternSelectorSerializer(patternSelector, replace);
    }

PatternParser

    public List<PatternFormatter> parse(final String pattern) {
        return parse(pattern, false, false);
    }

    public List<PatternFormatter> parse(final String pattern, final boolean alwaysWriteExceptions,
            final boolean noConsoleNoAnsi) {
        final List<PatternFormatter> list = new ArrayList<>();
        final List<PatternConverter> converters = new ArrayList<>();
        final List<FormattingInfo> fields = new ArrayList<>();

        parse(pattern, converters, fields, noConsoleNoAnsi, true);

        final Iterator<FormattingInfo> fieldIter = fields.iterator();
        boolean handlesThrowable = false;

        for (final PatternConverter converter : converters) {
            if (converter instanceof NanoTimePatternConverter) {
                // LOG4J2-1074 Switch to actual clock if nanosecond timestamps are required in config.
                // LOG4J2-1248 set config nanoclock
                if (config != null) {
                    config.setNanoClock(new SystemNanoClock());
                }
            }
            LogEventPatternConverter pc;
            if (converter instanceof LogEventPatternConverter) {
                pc = (LogEventPatternConverter) converter;
                handlesThrowable |= pc.handlesThrowable();
            } else {
                pc = new LiteralPatternConverter(config, Strings.EMPTY, true);
            }

            FormattingInfo field;
            if (fieldIter.hasNext()) {
                field = fieldIter.next();
            } else {
                field = FormattingInfo.getDefault();
            }
            list.add(new PatternFormatter(pc, field));
        }
        if (alwaysWriteExceptions && !handlesThrowable) {
            final LogEventPatternConverter pc = ExtendedThrowablePatternConverter.newInstance(null);
            list.add(new PatternFormatter(pc, FormattingInfo.getDefault()));
        }
        return list;
    }

    /**
     * Parse a format specifier.
     *
     * @param pattern
     *            pattern to parse.
     * @param patternConverters
     *            list to receive pattern converters.
     * @param formattingInfos
     *            list to receive field specifiers corresponding to pattern converters.
     * @param noConsoleNoAnsi
     *            TODO
     * @param convertBackslashes if {@code true}, backslash characters are treated as escape characters and character
     *            sequences like "\" followed by "t" (backslash+t) are converted to special characters like '\t' (tab).
     */
    public void parse(final String pattern, final List<PatternConverter> patternConverters,
            final List<FormattingInfo> formattingInfos, final boolean noConsoleNoAnsi,
            final boolean convertBackslashes) {
        Objects.requireNonNull(pattern, "pattern");

        final StringBuilder currentLiteral = new StringBuilder(BUF_SIZE);

        final int patternLength = pattern.length();
        ParserState state = ParserState.LITERAL_STATE;
        char c;
        int i = 0;
        FormattingInfo formattingInfo = FormattingInfo.getDefault();

        while (i < patternLength) {
            c = pattern.charAt(i++);

            switch (state) {
            case LITERAL_STATE:

                // In literal state, the last char is always a literal.
                if (i == patternLength) {
                    currentLiteral.append(c);

                    continue;
                }

                if (c == ESCAPE_CHAR) {
                    // peek at the next char.
                    switch (pattern.charAt(i)) {
                    case ESCAPE_CHAR:
                        currentLiteral.append(c);
                        i++; // move pointer

                        break;

                    default:

                        if (currentLiteral.length() != 0) {
                            patternConverters.add(new LiteralPatternConverter(config, currentLiteral.toString(),
                                    convertBackslashes));
                            formattingInfos.add(FormattingInfo.getDefault());
                        }

                        currentLiteral.setLength(0);
                        currentLiteral.append(c); // append %
                        state = ParserState.CONVERTER_STATE;
                        formattingInfo = FormattingInfo.getDefault();
                    }
                } else {
                    currentLiteral.append(c);
                }

                break;

            case CONVERTER_STATE:
                currentLiteral.append(c);

                switch (c) {
                case '-':
                    formattingInfo = new FormattingInfo(true, formattingInfo.getMinLength(),
                            formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
                    break;

                case '.':
                    state = ParserState.DOT_STATE;
                    break;

                default:

                    if (c >= '0' && c <= '9') {
                        formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), c - '0',
                                formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
                        state = ParserState.MIN_STATE;
                    } else {
                        i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules,
                                patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes);

                        // Next pattern is assumed to be a literal.
                        state = ParserState.LITERAL_STATE;
                        formattingInfo = FormattingInfo.getDefault();
                        currentLiteral.setLength(0);
                    }
                } // switch

                break;

            case MIN_STATE:
                currentLiteral.append(c);

                if (c >= '0' && c <= '9') {
                    // Multiply the existing value and add the value of the number just encountered.
                    formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength()
                            * DECIMAL + c - '0', formattingInfo.getMaxLength(), formattingInfo.isLeftTruncate());
                } else if (c == '.') {
                    state = ParserState.DOT_STATE;
                } else {
                    i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules,
                            patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes);
                    state = ParserState.LITERAL_STATE;
                    formattingInfo = FormattingInfo.getDefault();
                    currentLiteral.setLength(0);
                }

                break;

            case DOT_STATE:
                currentLiteral.append(c);
                switch (c) {
                case '-':
                    formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
                            formattingInfo.getMaxLength(),false);
                    break;

                default:

	                if (c >= '0' && c <= '9') {
	                    formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
	                            c - '0', formattingInfo.isLeftTruncate());
	                    state = ParserState.MAX_STATE;
	                } else {
	                    LOGGER.error("Error occurred in position " + i + ".\n Was expecting digit, instead got char \"" + c
	                            + "\".");

	                    state = ParserState.LITERAL_STATE;
	                }
                }

                break;

            case MAX_STATE:
                currentLiteral.append(c);

                if (c >= '0' && c <= '9') {
                    // Multiply the existing value and add the value of the number just encountered.
                    formattingInfo = new FormattingInfo(formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
                            formattingInfo.getMaxLength() * DECIMAL + c - '0', formattingInfo.isLeftTruncate());
                } else {
                    i = finalizeConverter(c, pattern, i, currentLiteral, formattingInfo, converterRules,
                            patternConverters, formattingInfos, noConsoleNoAnsi, convertBackslashes);
                    state = ParserState.LITERAL_STATE;
                    formattingInfo = FormattingInfo.getDefault();
                    currentLiteral.setLength(0);
                }

                break;
            } // switch
        }

        // while
        if (currentLiteral.length() != 0) {
            patternConverters.add(new LiteralPatternConverter(config, currentLiteral.toString(), convertBackslashes));
            formattingInfos.add(FormattingInfo.getDefault());
        }
    }