/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch licenses this file to you under * the Apache License, Version 2.0 (the "License"); you may * not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.elasticsearch.common.logging; import java.util.HashSet; import java.util.Set; /** * Format string for Elasticsearch log messages. */ public class LoggerMessageFormat { static final char DELIM_START = '{'; static final char DELIM_STOP = '}'; static final String DELIM_STR = "{}"; private static final char ESCAPE_CHAR = '\\'; public static String format(final String messagePattern, final Object... argArray) { return format(null, messagePattern, argArray); } public static String format(final String prefix, final String messagePattern, final Object... argArray) { if (messagePattern == null) { return null; } if (argArray == null) { if (prefix == null) { return messagePattern; } else { return prefix + messagePattern; } } int i = 0; int j; final StringBuilder sbuf = new StringBuilder(messagePattern.length() + 50); if (prefix != null) { sbuf.append(prefix); } for (int L = 0; L < argArray.length; L++) { j = messagePattern.indexOf(DELIM_STR, i); if (j == -1) { // no more variables if (i == 0) { // this is a simple string return messagePattern; } else { // add the tail string which contains no variables and return // the result. sbuf.append(messagePattern.substring(i, messagePattern.length())); return sbuf.toString(); } } else { if (isEscapedDelimiter(messagePattern, j)) { if (!isDoubleEscaped(messagePattern, j)) { L--; // DELIM_START was escaped, thus should not be incremented sbuf.append(messagePattern.substring(i, j - 1)); sbuf.append(DELIM_START); i = j + 1; } else { // The escape character preceding the delimiter start is // itself escaped: "abc x:\\{}" // we have to consume one backward slash sbuf.append(messagePattern.substring(i, j - 1)); deeplyAppendParameter(sbuf, argArray[L], new HashSet<Object[]>()); i = j + 2; } } else { // normal case sbuf.append(messagePattern.substring(i, j)); deeplyAppendParameter(sbuf, argArray[L], new HashSet<Object[]>()); i = j + 2; } } } // append the characters following the last {} pair. sbuf.append(messagePattern.substring(i, messagePattern.length())); return sbuf.toString(); } static boolean isEscapedDelimiter(String messagePattern, int delimiterStartIndex) { if (delimiterStartIndex == 0) { return false; } char potentialEscape = messagePattern.charAt(delimiterStartIndex - 1); if (potentialEscape == ESCAPE_CHAR) { return true; } else { return false; } } static boolean isDoubleEscaped(String messagePattern, int delimiterStartIndex) { if (delimiterStartIndex >= 2 && messagePattern.charAt(delimiterStartIndex - 2) == ESCAPE_CHAR) { return true; } else { return false; } } private static void deeplyAppendParameter(StringBuilder sbuf, Object o, Set<Object[]> seen) { if (o == null) { sbuf.append("null"); return; } if (!o.getClass().isArray()) { safeObjectAppend(sbuf, o); } else { // check for primitive array types because they // unfortunately cannot be cast to Object[] if (o instanceof boolean[]) { booleanArrayAppend(sbuf, (boolean[]) o); } else if (o instanceof byte[]) { byteArrayAppend(sbuf, (byte[]) o); } else if (o instanceof char[]) { charArrayAppend(sbuf, (char[]) o); } else if (o instanceof short[]) { shortArrayAppend(sbuf, (short[]) o); } else if (o instanceof int[]) { intArrayAppend(sbuf, (int[]) o); } else if (o instanceof long[]) { longArrayAppend(sbuf, (long[]) o); } else if (o instanceof float[]) { floatArrayAppend(sbuf, (float[]) o); } else if (o instanceof double[]) { doubleArrayAppend(sbuf, (double[]) o); } else { objectArrayAppend(sbuf, (Object[]) o, seen); } } } private static void safeObjectAppend(StringBuilder sbuf, Object o) { try { String oAsString = o.toString(); sbuf.append(oAsString); } catch (Exception e) { sbuf.append("[FAILED toString()]"); } } private static void objectArrayAppend(StringBuilder sbuf, Object[] a, Set<Object[]> seen) { sbuf.append('['); if (!seen.contains(a)) { seen.add(a); final int len = a.length; for (int i = 0; i < len; i++) { deeplyAppendParameter(sbuf, a[i], seen); if (i != len - 1) sbuf.append(", "); } // allow repeats in siblings seen.remove(a); } else { sbuf.append("..."); } sbuf.append(']'); } private static void booleanArrayAppend(StringBuilder sbuf, boolean[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void byteArrayAppend(StringBuilder sbuf, byte[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void charArrayAppend(StringBuilder sbuf, char[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void shortArrayAppend(StringBuilder sbuf, short[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void intArrayAppend(StringBuilder sbuf, int[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void longArrayAppend(StringBuilder sbuf, long[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void floatArrayAppend(StringBuilder sbuf, float[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } private static void doubleArrayAppend(StringBuilder sbuf, double[] a) { sbuf.append('['); final int len = a.length; for (int i = 0; i < len; i++) { sbuf.append(a[i]); if (i != len - 1) sbuf.append(", "); } sbuf.append(']'); } }