001package com.credibledoc.substitution.doc.module.substitution.logmessage; 002 003import org.springframework.stereotype.Service; 004 005import java.util.ArrayList; 006import java.util.Arrays; 007import java.util.List; 008 009/** 010 * This service helps to parse log messages. 011 * 012 * @author Kyrylo Semenko 013 */ 014@Service 015public class LogMessageService { 016 017 public static final String LINE_SEPARATOR = System.lineSeparator(); 018 public static final String LOG_SEPARATOR = " - "; 019 public static final String DOT = "."; 020 public static final String FOUR_SPACES = " "; 021 private static final String WORDS_SEPARATOR = " "; 022 private static final String BACKWARD_SLASH = "\\"; 023 private static final List<String> FORBIDDEN_SUFFIXES = Arrays.asList("~;", "/"); 024 025 /** 026 * Parse a message from a log line and split it to rows. 027 * @param line for example 028 * <pre>04.03.2019 18:41:13.658|main|INFO |com.credibledoc.substitution.core.configuration.ConfigurationService - Properties loaded by ClassLoader from the resource: file..</pre> 029 * @param maxRowLength maximal number of characters in a row 030 * @return For example 031 * <pre> 032 * Properties loaded by ClassLoa<br> 033 * der from the resource: file.. 034 * </pre> 035 */ 036 public String parseMessage(String line, int maxRowLength) { 037 int separatorIndex = line.indexOf(LOG_SEPARATOR); 038 String[] tokens = line.substring(separatorIndex + LOG_SEPARATOR.length()).split("\\s"); 039 tokens = splitLongTokens(tokens, maxRowLength); 040 StringBuilder result = new StringBuilder(line.length()); 041 StringBuilder row = new StringBuilder(line.length()); 042 for (int i = 0; i < tokens.length; i++) { 043 if ("}".equals(tokens[i].trim())) { 044 row.insert(row.length() - 1, tokens[i].trim()); 045 tokens[i] = ""; 046 } 047 String escapedToken = escapeToken(tokens[i]); 048 boolean hasMoreTokens = i + 1 < tokens.length; 049 if (hasMoreTokens) { 050 escapedToken = moveForbiddenSuffixToNextLine(tokens, i, escapedToken); 051 } 052 boolean isShortRow = row.length() + escapedToken.length() + WORDS_SEPARATOR.length() < maxRowLength; 053 if (isShortRow) { 054 appendEscapedToRow(maxRowLength, row, escapedToken, hasMoreTokens); 055 } else { 056 if (row.toString().endsWith(BACKWARD_SLASH)) { 057 row.replace(row.length() - 1, row.length(), ""); 058 escapedToken = BACKWARD_SLASH + escapedToken; 059 } 060 result.append(row); 061 result.append(LINE_SEPARATOR); 062 row = new StringBuilder(line.length()).append(FOUR_SPACES); 063 appendEscapedToRow(maxRowLength, row, escapedToken, hasMoreTokens); 064 } 065 } 066 result.append(row); 067 return result.toString(); 068 } 069 070 private String moveForbiddenSuffixToNextLine(String[] tokens, int i, String escapedToken) { 071 int notAllowedSuffixIndex = getForbiddenSuffixIndex(escapedToken); 072 if (notAllowedSuffixIndex > -1) { 073 // PlantUML comment line cannot be ended with this sequence. Move this sequence to the next line. 074 String suffix = FORBIDDEN_SUFFIXES.get(notAllowedSuffixIndex); 075 if (tokens.length > i + 2) { 076 tokens[i + 1] = suffix + tokens[i + 1]; 077 } 078 escapedToken = escapedToken.substring(0, escapedToken.length() - suffix.length()); 079 } 080 return escapedToken; 081 } 082 083 private int getForbiddenSuffixIndex(String token) { 084 for (int i = 0; i < FORBIDDEN_SUFFIXES.size(); i++) { 085 if (token.endsWith(FORBIDDEN_SUFFIXES.get(i))) { 086 return i; 087 } 088 } 089 return -1; 090 } 091 092 private String[] splitLongTokens(String[] tokens, int maxRowLength) { 093 List<String> result = new ArrayList<>(); 094 for (String token : tokens) { 095 if (token.length() <= maxRowLength) { 096 result.add(token); 097 } else { 098 while (token.length() > maxRowLength) { 099 result.add(token.substring(0, maxRowLength)); 100 token = token.substring(maxRowLength); 101 } 102 if (token.length() > 0) { 103 result.add(token); 104 } 105 } 106 } 107 return result.toArray(new String[0]); 108 } 109 110 private String escapeToken(String token) { 111 return token 112 .replaceAll(";", "~;") 113 .replaceAll("'", ""); 114 } 115 116 private void appendEscapedToRow(int maxRowLength, StringBuilder row, String replaced, boolean hasMoreTokens) { 117 row.append(replaced); 118 if (row.length() < maxRowLength && hasMoreTokens) { 119 row.append(WORDS_SEPARATOR); 120 } 121 } 122}