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