Browse Source

日志优化

mxd 3 years ago
parent
commit
7b9175912c

+ 275 - 0
magic-api/src/main/java/org/ssssssss/magicapi/logging/Formatter.java

@@ -0,0 +1,275 @@
+package org.ssssssss.magicapi.logging;
+
+import ch.qos.logback.classic.ClassicConstants;
+import ch.qos.logback.core.CoreConstants;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+public class Formatter {
+
+	private final static String[] SPACES = {" ", "  ", "    ", "        ", // 1,2,4,8
+			// spaces
+			"                ", // 16 spaces
+			"                                "}; // 32 spaces
+
+	private final static CachingDateFormatter CACHING_DATE_FORMATTER = new CachingDateFormatter("yyyy-MM-dd HH:mm:ss.SSS");
+
+	private final static TargetLengthBasedClassNameAbbreviator ABBREVIATOR = new TargetLengthBasedClassNameAbbreviator(39);
+
+	private StringBuilder buf = new StringBuilder();
+
+	private Formatter() {
+	}
+
+
+	public static Formatter create(){
+		return new Formatter();
+	}
+
+	public Formatter timestamp(long timestamp){
+		buf.append(CACHING_DATE_FORMATTER.format(timestamp));
+		return this;
+	}
+
+	public Formatter space(){
+		buf.append(SPACES[0]);
+		return this;
+	}
+
+	public Formatter value(String value) {
+		buf.append(value);
+		return this;
+	}
+	public Formatter newline() {
+		buf.append("\n");
+		return this;
+	}
+
+	public Formatter thread(String value) {
+		return alignment(value, 15, 15, true, true);
+	}
+
+	public Formatter level(String value) {
+		return alignment(value, 5, 2147483647, true, true);
+	}
+
+	public Formatter loggerName(String value) {
+		return alignment(ABBREVIATOR.abbreviate(value), 40, 40, true, false);
+	}
+
+	@Override
+	public String toString() {
+		return buf.toString();
+	}
+
+	public Formatter throwable(Throwable throwable){
+		if(throwable != null){
+			this.newline();
+			StringWriter sw = new StringWriter(1024);
+			PrintWriter writer = new PrintWriter(sw);
+			throwable.printStackTrace(writer);
+			writer.close();
+			buf.append(sw.getBuffer());
+			this.newline();
+		}
+		return this;
+	}
+
+	private Formatter alignment(String value, int min, int max, boolean leftTruncate, boolean leftPad){
+		if (value == null) {
+			if (0 < min) {
+				spacePad(buf, min);
+			}
+		} else {
+			int len = value.length();
+			if (len > max) {
+				if (leftTruncate) {
+					buf.append(value.substring(len - max));
+				} else {
+					buf.append(value, 0, max);
+				}
+			} else if (len < min) {
+				if (leftPad) {
+					leftPad(buf, value, min);
+				} else {
+					rightPad(buf, value, min);
+				}
+			} else {
+				buf.append(value);
+			}
+		}
+		return this;
+	}
+
+	private static void leftPad(StringBuilder buf, String s, int desiredLength) {
+		int actualLen = 0;
+		if (s != null) {
+			actualLen = s.length();
+		}
+		if (actualLen < desiredLength) {
+			spacePad(buf, desiredLength - actualLen);
+		}
+		if (s != null) {
+			buf.append(s);
+		}
+	}
+
+	private static void rightPad(StringBuilder buf, String s, int desiredLength) {
+		int actualLen = 0;
+		if (s != null) {
+			actualLen = s.length();
+		}
+		if (s != null) {
+			buf.append(s);
+		}
+		if (actualLen < desiredLength) {
+			spacePad(buf, desiredLength - actualLen);
+		}
+	}
+
+	/**
+	 * Fast space padding method.
+	 */
+	private static void spacePad(StringBuilder sbuf, int length) {
+		while (length >= 32) {
+			sbuf.append(SPACES[5]);
+			length -= 32;
+		}
+
+		for (int i = 4; i >= 0; i--) {
+			if ((length & (1 << i)) != 0) {
+				sbuf.append(SPACES[i]);
+			}
+		}
+	}
+
+	private static class CachingDateFormatter {
+
+		long lastTimestamp = -1;
+		String cachedStr = null;
+		final SimpleDateFormat sdf;
+
+		public CachingDateFormatter(String pattern) {
+			sdf = new SimpleDateFormat(pattern);
+		}
+
+		public final String format(long now) {
+
+			// SimpleDateFormat is not thread safe.
+
+			// See also the discussion in http://jira.qos.ch/browse/LBCLASSIC-36
+			// DateFormattingThreadedThroughputCalculator and SelectiveDateFormattingRunnable
+			// are also noteworthy
+
+			// The now == lastTimestamp guard minimizes synchronization
+			synchronized (this) {
+				if (now != lastTimestamp) {
+					lastTimestamp = now;
+					cachedStr = sdf.format(new Date(now));
+				}
+				return cachedStr;
+			}
+		}
+	}
+
+	private static class TargetLengthBasedClassNameAbbreviator {
+
+		final int targetLength;
+
+		public TargetLengthBasedClassNameAbbreviator(int targetLength) {
+			this.targetLength = targetLength;
+		}
+
+		public String abbreviate(String fqClassName) {
+			StringBuilder buf = new StringBuilder(targetLength);
+			if (fqClassName == null) {
+				throw new IllegalArgumentException("Class name may not be null");
+			}
+
+			int inLen = fqClassName.length();
+			if (inLen < targetLength) {
+				return fqClassName;
+			}
+
+			int[] dotIndexesArray = new int[ClassicConstants.MAX_DOTS];
+			// a.b.c contains 2 dots but 2+1 parts.
+			// see also http://jira.qos.ch/browse/LBCLASSIC-110
+			int[] lengthArray = new int[ClassicConstants.MAX_DOTS + 1];
+
+			int dotCount = computeDotIndexes(fqClassName, dotIndexesArray);
+
+			// System.out.println();
+			// System.out.println("Dot count for [" + className + "] is " + dotCount);
+			// if there are not dots than abbreviation is not possible
+			if (dotCount == 0) {
+				return fqClassName;
+			}
+			// printArray("dotArray: ", dotArray);
+			computeLengthArray(fqClassName, dotIndexesArray, lengthArray, dotCount);
+			// printArray("lengthArray: ", lengthArray);
+			for (int i = 0; i <= dotCount; i++) {
+				if (i == 0) {
+					buf.append(fqClassName.substring(0, lengthArray[i] - 1));
+				} else {
+					buf.append(fqClassName.substring(dotIndexesArray[i - 1], dotIndexesArray[i - 1] + lengthArray[i]));
+				}
+				// System.out.println("i=" + i + ", buf=" + buf);
+			}
+
+			return buf.toString();
+		}
+
+		int computeDotIndexes(final String className, int[] dotArray) {
+			int dotCount = 0;
+			int k = 0;
+			while (true) {
+				// ignore the $ separator in our computations. This is both convenient
+				// and sensible.
+				k = className.indexOf(CoreConstants.DOT, k);
+				if (k != -1 && dotCount < ClassicConstants.MAX_DOTS) {
+					dotArray[dotCount] = k;
+					dotCount++;
+					k++;
+				} else {
+					break;
+				}
+			}
+			return dotCount;
+		}
+
+		void computeLengthArray(final String className, int[] dotArray, int[] lengthArray, int dotCount) {
+			int toTrim = className.length() - targetLength;
+			// System.out.println("toTrim=" + toTrim);
+
+			// int toTrimAvarage = 0;
+
+			int len;
+			for (int i = 0; i < dotCount; i++) {
+				int previousDotPosition = -1;
+				if (i > 0) {
+					previousDotPosition = dotArray[i - 1];
+				}
+				int available = dotArray[i] - previousDotPosition - 1;
+				// System.out.println("i=" + i + ", available = " + available);
+
+				len = (available < 1) ? available : 1;
+				// System.out.println("i=" + i + ", toTrim = " + toTrim);
+
+				if (toTrim > 0) {
+					len = (available < 1) ? available : 1;
+				} else {
+					len = available;
+				}
+				toTrim -= (available - len);
+				lengthArray[i] = len + 1;
+			}
+
+			int lastDotIndex = dotCount - 1;
+			lengthArray[dotCount] = className.length() - dotArray[lastDotIndex];
+		}
+
+	}
+}

+ 19 - 15
magic-api/src/main/java/org/ssssssss/magicapi/logging/Log4j2LoggerContext.java

@@ -6,7 +6,6 @@ import org.apache.logging.log4j.core.Layout;
 import org.apache.logging.log4j.core.LogEvent;
 import org.apache.logging.log4j.core.LoggerContext;
 import org.apache.logging.log4j.core.appender.AbstractAppender;
-import org.apache.logging.log4j.core.appender.ConsoleAppender;
 import org.apache.logging.log4j.core.config.Configuration;
 import org.apache.logging.log4j.core.config.LoggerConfig;
 import org.apache.logging.log4j.core.config.Property;
@@ -26,16 +25,11 @@ public class Log4j2LoggerContext implements MagicLoggerContext {
 		LoggerContext context = (LoggerContext) LogManager.getContext(false);
 		Configuration configuration = context.getConfiguration();
 		LoggerConfig logger = configuration.getRootLogger();
-		Layout<String> layout = logger.getAppenders().values()
-				.stream()
-				.filter(it -> it instanceof ConsoleAppender)
-				.map(it -> (Layout<String>)it.getLayout())
-				.findFirst()
-				.orElseGet(() -> PatternLayout.newBuilder()
-						.withCharset(StandardCharsets.UTF_8)
-						.withConfiguration(configuration)
-						.withPattern(PATTERN)
-						.build());
+		PatternLayout layout = PatternLayout.newBuilder()
+				.withCharset(StandardCharsets.UTF_8)
+				.withConfiguration(configuration)
+				.withPattern("%d %t %p %X{TracingMsg} %c - %m%n")
+				.build();
 		MagicLog4j2Appender appender = new MagicLog4j2Appender("Magic", logger.getFilter(), layout);
 		appender.start();
 		configuration.addAppender(appender);
@@ -45,16 +39,26 @@ public class Log4j2LoggerContext implements MagicLoggerContext {
 
 	static class MagicLog4j2Appender extends AbstractAppender {
 
-		private Layout<String> layout;
-
 		MagicLog4j2Appender(String name, Filter filter, Layout<String> layout) {
 			super(name, filter, layout, true, Property.EMPTY_ARRAY);
-			this.layout = layout;
 		}
 
 		@Override
 		public void append(LogEvent event) {
-			MagicLoggerContext.println(this.layout.toSerializable(event));
+			String message = Formatter.create()
+					.timestamp(event.getTimeMillis())
+					.space()
+					.level(event.getLevel().toString())
+					.value(" --- [")
+					.thread(event.getThreadName())
+					.value("] ")
+					.loggerName(event.getLoggerName())
+					.value(": ")
+					.value(event.getMessage().getFormattedMessage())
+					.newline()
+					.throwable(event.getThrown())
+					.toString();
+			MagicLoggerContext.println(message);
 		}
 	}
 }

+ 14 - 1
magic-api/src/main/java/org/ssssssss/magicapi/logging/Log4jLoggerContext.java

@@ -27,7 +27,20 @@ public class Log4jLoggerContext implements MagicLoggerContext {
 
 		@Override
 		protected void append(LoggingEvent event) {
-			MagicLoggerContext.println(getLayout().format(event));
+			String message = Formatter.create()
+					.timestamp(event.getTimeStamp())
+					.space()
+					.level(event.getLevel().toString())
+					.value(" --- [")
+					.thread(event.getThreadName())
+					.value("] ")
+					.loggerName(event.getLoggerName())
+					.value(": ")
+					.value(event.getRenderedMessage())
+					.newline()
+					.throwable(event.getThrowableInformation() == null ? null : event.getThrowableInformation().getThrowable())
+					.toString();
+			MagicLoggerContext.println(message);
 		}
 
 		@Override

+ 19 - 13
magic-api/src/main/java/org/ssssssss/magicapi/logging/LogbackLoggerContext.java

@@ -1,8 +1,9 @@
 package org.ssssssss.magicapi.logging;
 
 import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.PatternLayout;
 import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.IThrowableProxy;
+import ch.qos.logback.classic.spi.ThrowableProxy;
 import ch.qos.logback.core.UnsynchronizedAppenderBase;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -18,11 +19,7 @@ public class LogbackLoggerContext implements MagicLoggerContext {
 	public void generateAppender() {
 		LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();
 		ch.qos.logback.classic.Logger logger = context.getLogger(Logger.ROOT_LOGGER_NAME);
-		PatternLayout layout = new PatternLayout();
-		layout.setContext(context);
-		layout.setPattern(PATTERN);
-		layout.start();
-		MagicLogbackAppender appender = new MagicLogbackAppender(layout);
+		MagicLogbackAppender appender = new MagicLogbackAppender();
 		appender.setContext(context);
 		appender.setName(LOGGER_NAME);
 		appender.start();
@@ -31,15 +28,24 @@ public class LogbackLoggerContext implements MagicLoggerContext {
 
 	static class MagicLogbackAppender extends UnsynchronizedAppenderBase<ILoggingEvent> {
 
-		private PatternLayout layout;
-
-		public MagicLogbackAppender(PatternLayout layout) {
-			this.layout = layout;
-		}
-
 		@Override
 		protected void append(ILoggingEvent event) {
-			MagicLoggerContext.println(layout.doLayout(event));
+			Formatter formatter = Formatter.create()
+					.timestamp(event.getTimeStamp())
+					.space()
+					.level(event.getLevel().toString())
+					.value(" --- [")
+					.thread(event.getThreadName())
+					.value("] ")
+					.loggerName(event.getLoggerName())
+					.value(": ")
+					.value(event.getFormattedMessage())
+					.newline();
+			IThrowableProxy proxy = event.getThrowableProxy();
+			if(proxy instanceof ThrowableProxy){
+				formatter.throwable(((ThrowableProxy) proxy).getThrowable());
+			}
+			MagicLoggerContext.println(formatter.toString());
 		}
 	}
 }

+ 2 - 3
magic-api/src/main/java/org/ssssssss/magicapi/logging/MagicLoggerContext.java

@@ -14,13 +14,12 @@ public interface MagicLoggerContext {
 
 	String LOGGER_NAME = "magic";
 
-	String PATTERN = "%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%5p) %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n%wEx";
-
 	ThreadLocal<String> SESSION = new InheritableThreadLocal<>();
 
 	/**
 	 * 打印日志
-	 *re
+	 * re
+	 *
 	 * @param logInfo 日志信息
 	 */
 	static void println(String logInfo) {

+ 30 - 0
magic-editor/src/console/src/assets/index.css

@@ -113,6 +113,14 @@
     --suggest-hover-color: #000;
     --statusbar-em-color: #007f31;
     --run-log-background: #fff;
+    /* 日志级别颜色 */
+    --log-color-info: #00cd00;
+    --log-color-warn: #A66F00;
+    --log-color-debug: #00cccc;
+    --log-color-error: #cd0000;
+    --log-color-trace: #0000EE;
+    --log-color-cyan: #00CCCC;
+    --log-color-link: #006DCC;
     scrollbar-color: var(--scollbar-color) var(--scollbar-color);
     scrollbar-width: thin;
     outline: 0;
@@ -323,6 +331,28 @@
     font-weight: bold;
 }
 
+.ma-log pre span.log-INFO{
+    color: var(--log-color-info);
+}
+.ma-log pre span.log-DEBUG{
+    color: var(--log-color-debug);
+}
+.ma-log pre span.log-ERROR{
+    color: var(--log-color-error);
+}
+.ma-log pre span.log-WARN{
+    color: var(--log-color-warn);
+}
+.ma-log pre span.log-TRACE{
+    color: var(--log-color-trace);
+}
+.ma-log pre span.log-cyan{
+    color: var(--log-color-cyan);
+}
+.ma-log pre a.log-link{
+    color: var(--log-color-link);
+}
+
 /** 旋转特效 **/
 @keyframes rotate {
     from {

+ 8 - 8
magic-editor/src/console/src/components/layout/magic-log.vue

@@ -11,7 +11,6 @@
 
 <script>
 import bus from "@/scripts/bus";
-import Anser from 'anser'
 
 export default {
   name: "MagicLog",
@@ -32,9 +31,14 @@ export default {
     },
     onLogReceived(row){
       let text = row[0]
-      let html = Anser.linkify(Anser.ansiToHtml(Anser.escapeForHtml(text)));
-      // 替换链接为新标签页打开
-      html = html.replace(/<a /g,'<a target="blank" ');
+      // escape
+      let html = text.replace(/[&<>]/gm, function (str) {
+        return str === "&" ? "&amp;" : str === "<" ? "&lt;" : str === ">" ? "&gt;" : "";
+      });
+      html = html.replace(/(\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}.\d{3}\s+)([^\s]+)( --- \[)(.{15})(] )(.{40})/gm,'$1 <span class="log-$2">$2</span>$3$4$5<span class="log-cyan">$6</span>')
+      // 替换链接
+      html = html.replace(/(https?:\/\/[^\s]+)/gm, '<a class="log-link" href="$1" target="blank">$1</a>')
+      // 处理异常里的 at
       html = html.replace(/(\tat .*\()(.*?:\d+)(\).*?[\r\n])/g,'$1<span style="color:#808080;text-decoration: underline;">$2</span>$3')
       let lines = text.split('\n').length;
       this.logs.push({
@@ -79,10 +83,6 @@ export default {
 .ma-log > div.multiple.more pre{
   max-height: none;
 }
-.ma-log >>> pre span{
-  opacity: 1 !important;
-}
-
 .ma-log span.multiple{
   opacity: 0.5;
   font-size: 13px;

+ 8 - 1
magic-editor/src/console/src/scripts/editor/dark-theme.js

@@ -101,6 +101,13 @@ export default {
         'suggest-hover-background': '#113A5C',
         'suggest-hover-color': '#fff',
         'statusbar-em-color': '#68dd9a',
-        'run-log-background': '#2b2b2b'
+        'run-log-background': '#2b2b2b',
+        'log-level-info': '#ABC023',
+        'log-level-error': '#CC666E',
+        'log-level-debug': '#299999',
+        'log-level-warn': 'unset',
+        'log-level-trace': '#5394EC',
+        'log-color-cyan': '#009191',
+        'log-color-link': '#287BDE'
     }
 };