This is an automated email from the ASF dual-hosted git repository.
wusheng pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/skywalking.git
The following commit(s) were added to refs/heads/master by this push:
new 9442a51 JsonLogger implementation (#5356) (#5357)
9442a51 is described below
commit 9442a51c112d8c3908e936224f30757bcbfb3a25
Author: Lu Jiajing <[email protected]>
AuthorDate: Sat Aug 29 23:17:29 2020 +0800
JsonLogger implementation (#5356) (#5357)
---
apm-commons/apm-datacarrier/pom.xml | 8 +
apm-commons/apm-util/pom.xml | 8 +
.../skywalking/apm/util/ConfigInitializer.java | 2 +-
apm-sniffer/apm-agent-core/pom.xml | 5 +
.../skywalking/apm/agent/core/conf/Config.java | 7 +
.../agent/core/conf/SnifferConfigInitializer.java | 18 +-
.../agent/core/logging/core/AbstractLogger.java | 185 +++++++++++++++++++++
.../apm/agent/core/logging/core/Converter.java | 4 +-
.../core/{Converter.java => JsonLogResolver.java} | 19 ++-
.../apm/agent/core/logging/core/JsonLogger.java | 80 +++++++++
.../apm/agent/core/logging/core/Parser.java | 2 +-
.../apm/agent/core/logging/core/PatternLogger.java | 148 +----------------
.../core/{Converter.java => ResolverType.java} | 9 +-
.../AgentNameConverter.java | 7 +-
.../{coverts => converters}/ClassConverter.java | 7 +-
.../{coverts => converters}/DateConverter.java | 7 +-
.../{coverts => converters}/LevelConverter.java | 7 +-
.../{coverts => converters}/LiteralConverter.java | 7 +-
.../{coverts => converters}/MessageConverter.java | 7 +-
.../{coverts => converters}/ThreadConverter.java | 7 +-
.../ThrowableConverter.java | 7 +-
.../agent/core/logging/core/JsonLoggerTest.java | 183 ++++++++++++++++++++
.../agent/core/logging/core/LoggingBenchmark.java | 96 +++++++++++
.../agent/core/logging/core/PatternLoggerTest.java | 49 +++++-
.../skywalking/apm/agent/SkyWalkingAgent.java | 34 ++--
.../apm-sdk-plugin/solrj-7.x-plugin/pom.xml | 6 +
apm-sniffer/apm-test-tools/pom.xml | 5 +
docs/en/setup/service-agent/java-agent/README.md | 3 +-
pom.xml | 11 +-
29 files changed, 754 insertions(+), 184 deletions(-)
diff --git a/apm-commons/apm-datacarrier/pom.xml
b/apm-commons/apm-datacarrier/pom.xml
index 7fb14f4..2ba5a72 100644
--- a/apm-commons/apm-datacarrier/pom.xml
+++ b/apm-commons/apm-datacarrier/pom.xml
@@ -27,4 +27,12 @@
<artifactId>apm-datacarrier</artifactId>
+ <dependencies>
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
</project>
diff --git a/apm-commons/apm-util/pom.xml b/apm-commons/apm-util/pom.xml
index 0a1a6df..93967be 100644
--- a/apm-commons/apm-util/pom.xml
+++ b/apm-commons/apm-util/pom.xml
@@ -33,4 +33,12 @@
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
</project>
diff --git
a/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
b/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
index 163f46b..d313ba8 100644
---
a/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
+++
b/apm-commons/apm-util/src/main/java/org/apache/skywalking/apm/util/ConfigInitializer.java
@@ -149,7 +149,7 @@ public class ConfigInitializer {
String propertyStringKey = propertyKey.toString();
if (propertyStringKey.startsWith(prefix) &&
propertyStringKey.endsWith(suffix)) {
String itemKey = propertyStringKey.substring(
- prefix.length(), propertyStringKey.length() -
suffix.length());
+ prefix.length(), propertyStringKey.length() -
suffix.length());
Object keyObj;
Object valueObj;
diff --git a/apm-sniffer/apm-agent-core/pom.xml
b/apm-sniffer/apm-agent-core/pom.xml
index 375a94b..0d1d27f 100644
--- a/apm-sniffer/apm-agent-core/pom.xml
+++ b/apm-sniffer/apm-agent-core/pom.xml
@@ -104,6 +104,11 @@
<version>${ststem-rules.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<dependencyManagement>
<dependencies>
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
index e43f116..47b8ebe 100755
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/Config.java
@@ -20,9 +20,11 @@ package org.apache.skywalking.apm.agent.core.conf;
import java.util.HashMap;
import java.util.Map;
+
import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
import org.apache.skywalking.apm.agent.core.logging.core.LogLevel;
import org.apache.skywalking.apm.agent.core.logging.core.LogOutput;
+import org.apache.skywalking.apm.agent.core.logging.core.ResolverType;
import org.apache.skywalking.apm.agent.core.logging.core.WriterFactory;
import org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ClassCacheMode;
import org.apache.skywalking.apm.util.Length;
@@ -250,6 +252,11 @@ public class Config {
public static LogOutput OUTPUT = LogOutput.FILE;
/**
+ * The log resolver type. Default is PATTERN which will create
PatternLogResolver later.
+ */
+ public static ResolverType RESOLVER = ResolverType.PATTERN;
+
+ /**
* The log patten. Default is "%level %timestamp %thread %class : %msg
%throwable". Each conversion specifiers
* starts with a percent sign '%' and fis followed by conversion word.
There are some default conversion
* specifiers: %thread = ThreadName %level = LogLevel {@link
LogLevel} %timestamp = The now() who format is
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java
index d5fbd6a..06b82c4 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/conf/SnifferConfigInitializer.java
@@ -32,6 +32,8 @@ import
org.apache.skywalking.apm.agent.core.boot.AgentPackageNotFoundException;
import org.apache.skywalking.apm.agent.core.boot.AgentPackagePath;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
+import org.apache.skywalking.apm.agent.core.logging.core.JsonLogResolver;
+import org.apache.skywalking.apm.agent.core.logging.core.PatternLogResolver;
import org.apache.skywalking.apm.util.ConfigInitializer;
import org.apache.skywalking.apm.util.PropertyPlaceholderHelper;
import org.apache.skywalking.apm.util.StringUtil;
@@ -40,7 +42,7 @@ import org.apache.skywalking.apm.util.StringUtil;
* The <code>SnifferConfigInitializer</code> initializes all configs in
several way.
*/
public class SnifferConfigInitializer {
- private static final ILog LOGGER =
LogManager.getLogger(SnifferConfigInitializer.class);
+ private static ILog LOGGER =
LogManager.getLogger(SnifferConfigInitializer.class);
private static final String SPECIFIED_CONFIG_PATH = "skywalking_config";
private static final String DEFAULT_CONFIG_FILE_NAME =
"/config/agent.config";
private static final String ENV_KEY_PREFIX = "skywalking.";
@@ -90,6 +92,9 @@ public class SnifferConfigInitializer {
}
initializeConfig(Config.class);
+ // reconfigure logger after config initialization
+ configureLogger();
+ LOGGER = LogManager.getLogger(SnifferConfigInitializer.class);
if (StringUtil.isEmpty(Config.Agent.SERVICE_NAME)) {
throw new ExceptionInInitializerError("`agent.service_name` is
missing.");
@@ -207,4 +212,15 @@ public class SnifferConfigInitializer {
}
throw new ConfigNotFoundException("Failed to load agent.config.");
}
+
+ static void configureLogger() {
+ switch (Config.Logging.RESOLVER) {
+ case JSON:
+ LogManager.setLogResolver(new JsonLogResolver());
+ break;
+ case PATTERN:
+ default:
+ LogManager.setLogResolver(new PatternLogResolver());
+ }
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java
new file mode 100644
index 0000000..d58abf4
--- /dev/null
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/AbstractLogger.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.skywalking.apm.agent.core.logging.core;
+
+import org.apache.skywalking.apm.agent.core.conf.Config;
+import org.apache.skywalking.apm.agent.core.logging.api.ILog;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.AgentNameConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.ClassConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.DateConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.LevelConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.MessageConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.ThreadConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.ThrowableConverter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+
+/**
+ * An abstract class to simplify the real implementation of the loggers.
+ * It hold the class name of the logger, and is responsible for log level
check,
+ * message interpolation, etc.
+ */
+public abstract class AbstractLogger implements ILog {
+ public static final Map<String, Class<? extends Converter>>
DEFAULT_CONVERTER_MAP = new HashMap<>();
+ protected List<Converter> converters = new ArrayList<>();
+
+ static {
+ DEFAULT_CONVERTER_MAP.put("thread", ThreadConverter.class);
+ DEFAULT_CONVERTER_MAP.put("level", LevelConverter.class);
+ DEFAULT_CONVERTER_MAP.put("agent_name", AgentNameConverter.class);
+ DEFAULT_CONVERTER_MAP.put("timestamp", DateConverter.class);
+ DEFAULT_CONVERTER_MAP.put("msg", MessageConverter.class);
+ DEFAULT_CONVERTER_MAP.put("throwable", ThrowableConverter.class);
+ DEFAULT_CONVERTER_MAP.put("class", ClassConverter.class);
+ }
+
+ protected final String targetClass;
+
+ public AbstractLogger(String targetClass) {
+ this.targetClass = targetClass;
+ }
+
+ @Override
+ public void info(String message) {
+ if (this.isInfoEnable()) {
+ this.logger(LogLevel.INFO, message, null);
+ }
+ }
+
+ @Override
+ public void info(String message, Object... objects) {
+ if (this.isInfoEnable()) {
+ this.logger(LogLevel.INFO, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void warn(String message, Object... objects) {
+ if (this.isWarnEnable()) {
+ this.logger(LogLevel.WARN, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void warn(Throwable throwable, String message, Object... objects) {
+ if (this.isWarnEnable()) {
+ this.logger(LogLevel.WARN, replaceParam(message, objects),
throwable);
+ }
+ }
+
+ @Override
+ public void error(String message, Throwable throwable) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, message, throwable);
+ }
+ }
+
+ @Override
+ public void error(Throwable throwable, String message, Object... objects) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, replaceParam(message, objects),
throwable);
+ }
+ }
+
+ @Override
+ public void error(String message) {
+ if (this.isErrorEnable()) {
+ this.logger(LogLevel.ERROR, message, null);
+ }
+ }
+
+ @Override
+ public void debug(String message) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, message, null);
+ }
+ }
+
+ @Override
+ public void debug(String message, Object... objects) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, replaceParam(message, objects), null);
+ }
+ }
+
+ @Override
+ public void debug(Throwable throwable, String message, Object... objects) {
+ if (this.isDebugEnable()) {
+ this.logger(LogLevel.DEBUG, replaceParam(message, objects),
throwable);
+ }
+ }
+
+ public boolean isDebugEnable() {
+ return LogLevel.DEBUG.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ public boolean isInfoEnable() {
+ return LogLevel.INFO.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ public boolean isWarnEnable() {
+ return LogLevel.WARN.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ public boolean isErrorEnable() {
+ return LogLevel.ERROR.compareTo(Config.Logging.LEVEL) >= 0;
+ }
+
+ protected String replaceParam(String message, Object... parameters) {
+ if (message == null) {
+ return message;
+ }
+ int startSize = 0;
+ int parametersIndex = 0;
+ int index;
+ String tmpMessage = message;
+ while ((index = message.indexOf("{}", startSize)) != -1) {
+ if (parametersIndex >= parameters.length) {
+ break;
+ }
+ /**
+ * @Fix the Illegal group reference issue
+ */
+ tmpMessage = tmpMessage.replaceFirst("\\{\\}",
Matcher.quoteReplacement(String.valueOf(parameters[parametersIndex++])));
+ startSize = index + 2;
+ }
+ return tmpMessage;
+ }
+
+ protected void logger(LogLevel level, String message, Throwable e) {
+ WriterFactory.getLogWriter().write(this.format(level, message, e));
+ }
+
+ /**
+ * The abstract method left for real loggers.
+ * Any implementation MUST return string, which will be directly
transferred to log destination,
+ * i.e. log files OR stdout
+ *
+ * @param level log level
+ * @param message log message, which has been interpolated with
user-defined parameters.
+ * @param e throwable if exists
+ * @return string representation of the log, for example, raw json string
for {@link JsonLogger}
+ */
+ protected abstract String format(LogLevel level, String message, Throwable
e);
+
+}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
index 504d816..88fa58e 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
@@ -19,10 +19,12 @@
package org.apache.skywalking.apm.agent.core.logging.core;
/**
- * The Converter, It is used to convert the LogEvent to the String.
+ * The Converter, it is used to convert the LogEvent to the String.
+ * For JsonLogger, the `getKey()` method is used to generate the key for json.
*/
public interface Converter {
String convert(LogEvent logEvent);
+ String getKey();
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogResolver.java
similarity index 64%
copy from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
copy to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogResolver.java
index 504d816..44dd8e2 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogResolver.java
@@ -18,11 +18,20 @@
package org.apache.skywalking.apm.agent.core.logging.core;
-/**
- * The Converter, It is used to convert the LogEvent to the String.
- */
-public interface Converter {
+import com.google.gson.Gson;
+import org.apache.skywalking.apm.agent.core.logging.api.ILog;
+import org.apache.skywalking.apm.agent.core.logging.api.LogResolver;
+
+public class JsonLogResolver implements LogResolver {
+ private static final Gson GSON = new Gson();
- String convert(LogEvent logEvent);
+ @Override
+ public ILog getLogger(Class<?> aClass) {
+ return new JsonLogger(aClass, GSON);
+ }
+ @Override
+ public ILog getLogger(String s) {
+ return new JsonLogger(s, GSON);
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogger.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogger.java
new file mode 100644
index 0000000..cffd493
--- /dev/null
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLogger.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.skywalking.apm.agent.core.logging.core;
+
+import com.google.gson.Gson;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.LiteralConverter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * An alternative logger for the SkyWalking agent. The default layout is
+ * {
+ * "@timestamp": "", // timestamp
+ * "logger": "", // name of the Logger
+ * "level": "", // info|debug|warn|error
+ * "thread": "", // thread where the log method is called
+ * "message": "", // your log message
+ * "throwable": "",
+ * "agent_name" "service_name"
+ * }
+ */
+public class JsonLogger extends AbstractLogger {
+ private final Gson gson;
+
+ public JsonLogger(Class<?> targetClass, Gson gson) {
+ this(targetClass.getSimpleName(), gson);
+ }
+
+ /**
+ * In the Constructor, the instances of converters are created,
+ * except those {@link LiteralConverter} since this class is used
+ * only the literals in {@link PatternLogger} ,
+ * and thus should not be added to the json log.
+ *
+ * @param targetClass the logger class
+ * @param gson instance of Gson works as json serializer
+ */
+ public JsonLogger(String targetClass, Gson gson) {
+ super(targetClass);
+ this.gson = gson;
+ for (Map.Entry<String, Class<? extends Converter>> entry :
DEFAULT_CONVERTER_MAP.entrySet()) {
+ final Class<? extends Converter> converterClass = entry.getValue();
+ try {
+ if (converters instanceof LiteralConverter) {
+ continue;
+ }
+ converters.add(converterClass.newInstance());
+ } catch (IllegalAccessException | InstantiationException e) {
+ throw new IllegalStateException("Create Converter error.
Class: " + converterClass, e);
+ }
+ }
+ }
+
+ @Override
+ protected String format(LogLevel level, String message, Throwable e) {
+ LogEvent logEvent = new LogEvent(level, message, e, this.targetClass);
+ Map<String, String> log = new HashMap<>();
+ for (Converter converter : this.converters) {
+ log.put(converter.getKey(), converter.convert(logEvent));
+ }
+ return this.gson.toJson(log);
+ }
+}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Parser.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Parser.java
index 8f3fa9b..28b10f3 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Parser.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Parser.java
@@ -18,7 +18,7 @@
package org.apache.skywalking.apm.agent.core.logging.core;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.LiteralConverter;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.LiteralConverter;
import java.util.ArrayList;
import java.util.List;
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLogger.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLogger.java
index 9bad76e..6ebb002 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLogger.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLogger.java
@@ -18,53 +18,25 @@
package org.apache.skywalking.apm.agent.core.logging.core;
-import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.logging.api.ILog;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.AgentNameConverter;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.ClassConverter;
-import org.apache.skywalking.apm.agent.core.logging.core.coverts.DateConverter;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.LevelConverter;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.MessageConverter;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.ThreadConverter;
-import
org.apache.skywalking.apm.agent.core.logging.core.coverts.ThrowableConverter;
import org.apache.skywalking.apm.util.StringUtil;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-
/**
* A flexible Logger configurable with pattern string. This is default
implementation of {@link ILog} This can parse a
* pattern to the List of converter with Parser. We package LogEvent with
message, level,timestamp ..., passing around
* to the List of converter to concat actually Log-String.
*/
-public class PatternLogger implements ILog {
-
- public static final Map<String, Class<? extends Converter>>
DEFAULT_CONVERTER_MAP = new HashMap<String, Class<? extends Converter>>();
-
- static {
- DEFAULT_CONVERTER_MAP.put("thread", ThreadConverter.class);
- DEFAULT_CONVERTER_MAP.put("level", LevelConverter.class);
- DEFAULT_CONVERTER_MAP.put("agent_name", AgentNameConverter.class);
- DEFAULT_CONVERTER_MAP.put("timestamp", DateConverter.class);
- DEFAULT_CONVERTER_MAP.put("msg", MessageConverter.class);
- DEFAULT_CONVERTER_MAP.put("throwable", ThrowableConverter.class);
- DEFAULT_CONVERTER_MAP.put("class", ClassConverter.class);
- }
-
+public class PatternLogger extends AbstractLogger {
public static final String DEFAULT_PATTERN = "%level %timestamp %thread
%class : %msg %throwable";
private String pattern;
- private List<Converter> converters;
- private String targetClass;
- public PatternLogger(Class targetClass, String pattern) {
+ public PatternLogger(Class<?> targetClass, String pattern) {
this(targetClass.getSimpleName(), pattern);
}
public PatternLogger(String targetClass, String pattern) {
- this.targetClass = targetClass;
+ super(targetClass);
this.setPattern(pattern);
}
@@ -77,122 +49,14 @@ public class PatternLogger implements ILog {
pattern = DEFAULT_PATTERN;
}
this.pattern = pattern;
- converters = new Parser(pattern, DEFAULT_CONVERTER_MAP).parse();
- }
-
- protected void logger(LogLevel level, String message, Throwable e) {
- WriterFactory.getLogWriter().write(format(level, message, e));
- }
-
- private String replaceParam(String message, Object... parameters) {
- if (message == null) {
- return message;
- }
- int startSize = 0;
- int parametersIndex = 0;
- int index;
- String tmpMessage = message;
- while ((index = message.indexOf("{}", startSize)) != -1) {
- if (parametersIndex >= parameters.length) {
- break;
- }
- /**
- * @Fix the Illegal group reference issue
- */
- tmpMessage = tmpMessage.replaceFirst("\\{\\}",
Matcher.quoteReplacement(String.valueOf(parameters[parametersIndex++])));
- startSize = index + 2;
- }
- return tmpMessage;
- }
-
- @Override
- public void info(String format) {
- if (isInfoEnable())
- logger(LogLevel.INFO, format, null);
- }
-
- @Override
- public void info(String format, Object... arguments) {
- if (isInfoEnable())
- logger(LogLevel.INFO, replaceParam(format, arguments), null);
- }
-
- @Override
- public void warn(String format, Object... arguments) {
- if (isWarnEnable())
- logger(LogLevel.WARN, replaceParam(format, arguments), null);
- }
-
- @Override
- public void warn(Throwable e, String format, Object... arguments) {
- if (isWarnEnable())
- logger(LogLevel.WARN, replaceParam(format, arguments), e);
- }
-
- @Override
- public void error(String format, Throwable e) {
- if (isErrorEnable())
- logger(LogLevel.ERROR, format, e);
- }
-
- @Override
- public void error(Throwable e, String format, Object... arguments) {
- if (isErrorEnable())
- logger(LogLevel.ERROR, replaceParam(format, arguments), e);
- }
-
- @Override
- public boolean isDebugEnable() {
- return LogLevel.DEBUG.compareTo(Config.Logging.LEVEL) >= 0;
- }
-
- @Override
- public boolean isInfoEnable() {
- return LogLevel.INFO.compareTo(Config.Logging.LEVEL) >= 0;
- }
-
- @Override
- public boolean isWarnEnable() {
- return LogLevel.WARN.compareTo(Config.Logging.LEVEL) >= 0;
- }
-
- @Override
- public boolean isErrorEnable() {
- return LogLevel.ERROR.compareTo(Config.Logging.LEVEL) >= 0;
- }
-
- @Override
- public void debug(String format) {
- if (isDebugEnable()) {
- logger(LogLevel.DEBUG, format, null);
- }
- }
-
- @Override
- public void debug(String format, Object... arguments) {
- if (isDebugEnable()) {
- logger(LogLevel.DEBUG, replaceParam(format, arguments), null);
- }
+ this.converters = new Parser(pattern, DEFAULT_CONVERTER_MAP).parse();
}
@Override
- public void error(String format) {
- if (isErrorEnable()) {
- logger(LogLevel.ERROR, format, null);
- }
- }
-
- @Override
- public void debug(final Throwable t, final String format, final Object...
arguments) {
- if (isDebugEnable()) {
- logger(LogLevel.DEBUG, replaceParam(format, arguments), t);
- }
- }
-
- String format(LogLevel level, String message, Throwable t) {
+ protected String format(LogLevel level, String message, Throwable t) {
LogEvent logEvent = new LogEvent(level, message, t, targetClass);
StringBuilder stringBuilder = new StringBuilder();
- for (Converter converter : converters) {
+ for (Converter converter : this.converters) {
stringBuilder.append(converter.convert(logEvent));
}
return stringBuilder.toString();
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/ResolverType.java
similarity index 85%
copy from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
copy to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/ResolverType.java
index 504d816..25548b4 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/Converter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/ResolverType.java
@@ -18,11 +18,6 @@
package org.apache.skywalking.apm.agent.core.logging.core;
-/**
- * The Converter, It is used to convert the LogEvent to the String.
- */
-public interface Converter {
-
- String convert(LogEvent logEvent);
-
+public enum ResolverType {
+ JSON, PATTERN
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/AgentNameConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/AgentNameConverter.java
similarity index 88%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/AgentNameConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/AgentNameConverter.java
index 0c690a1..67d74bb 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/AgentNameConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/AgentNameConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.conf.Config;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
@@ -27,4 +27,9 @@ public class AgentNameConverter implements Converter {
public String convert(LogEvent logEvent) {
return Config.Agent.SERVICE_NAME;
}
+
+ @Override
+ public String getKey() {
+ return "agent_name";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ClassConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ClassConverter.java
similarity index 88%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ClassConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ClassConverter.java
index 3bcf5a2..150be54 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ClassConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ClassConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -30,4 +30,9 @@ public class ClassConverter implements Converter {
public String convert(LogEvent logEvent) {
return logEvent.getTargetClass();
}
+
+ @Override
+ public String getKey() {
+ return "logger";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/DateConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/DateConverter.java
similarity index 89%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/DateConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/DateConverter.java
index 7487556..8bfcdc6 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/DateConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/DateConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -33,4 +33,9 @@ public class DateConverter implements Converter {
public String convert(LogEvent logEvent) {
return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS").format(new
Date());
}
+
+ @Override
+ public String getKey() {
+ return "@timestamp";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LevelConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LevelConverter.java
similarity index 88%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LevelConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LevelConverter.java
index f016cf3..2614b44 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LevelConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LevelConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -29,4 +29,9 @@ public class LevelConverter implements Converter {
public String convert(LogEvent logEvent) {
return logEvent.getLevel().name();
}
+
+ @Override
+ public String getKey() {
+ return "level";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LiteralConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LiteralConverter.java
similarity index 90%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LiteralConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LiteralConverter.java
index 411b31a..fd9d035 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/LiteralConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/LiteralConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -36,4 +36,9 @@ public class LiteralConverter implements Converter {
public String convert(LogEvent logEvent) {
return literal;
}
+
+ @Override
+ public String getKey() {
+ return "";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/MessageConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/MessageConverter.java
similarity index 88%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/MessageConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/MessageConverter.java
index 106c266..97a3f6f 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/MessageConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/MessageConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -29,4 +29,9 @@ public class MessageConverter implements Converter {
public String convert(LogEvent logEvent) {
return logEvent.getMessage();
}
+
+ @Override
+ public String getKey() {
+ return "message";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThreadConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThreadConverter.java
similarity index 88%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThreadConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThreadConverter.java
index 45e421d..888c237 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThreadConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThreadConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
import org.apache.skywalking.apm.agent.core.logging.core.LogEvent;
@@ -29,4 +29,9 @@ public class ThreadConverter implements Converter {
public String convert(LogEvent logEvent) {
return Thread.currentThread().getName();
}
+
+ @Override
+ public String getKey() {
+ return "thread";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThrowableConverter.java
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThrowableConverter.java
similarity index 92%
rename from
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThrowableConverter.java
rename to
apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThrowableConverter.java
index 1067829..f8cc316 100644
---
a/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/coverts/ThrowableConverter.java
+++
b/apm-sniffer/apm-agent-core/src/main/java/org/apache/skywalking/apm/agent/core/logging/core/converters/ThrowableConverter.java
@@ -16,7 +16,7 @@
*
*/
-package org.apache.skywalking.apm.agent.core.logging.core.coverts;
+package org.apache.skywalking.apm.agent.core.logging.core.converters;
import org.apache.skywalking.apm.agent.core.conf.Constants;
import org.apache.skywalking.apm.agent.core.logging.core.Converter;
@@ -46,4 +46,9 @@ public class ThrowableConverter implements Converter {
}
return Constants.LINE_SEPARATOR + expMessage;
}
+
+ @Override
+ public String getKey() {
+ return "throwable";
+ }
}
diff --git
a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLoggerTest.java
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLoggerTest.java
new file mode 100644
index 0000000..1747d2e
--- /dev/null
+++
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/JsonLoggerTest.java
@@ -0,0 +1,183 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.skywalking.apm.agent.core.logging.core;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.reflect.TypeToken;
+import org.apache.skywalking.apm.agent.core.conf.Config;
+import
org.apache.skywalking.apm.agent.core.logging.core.converters.ThrowableConverter;
+import org.hamcrest.Description;
+import org.hamcrest.TypeSafeDiagnosingMatcher;
+import org.junit.Assert;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Type;
+import java.util.Map;
+
+import static org.mockito.Mockito.times;
+
+public class JsonLoggerTest {
+ @BeforeClass
+ public static void initAndHoldOut() {
+ Config.Agent.SERVICE_NAME = "testAppFromConfig";
+ }
+
+ @Test
+ public void testLog() {
+ final IWriter output = Mockito.mock(IWriter.class);
+ JsonLogger logger = new JsonLogger(JsonLoggerTest.class, new Gson()) {
+ @Override
+ protected void logger(LogLevel level, String message, Throwable e)
{
+ output.write(format(level, message, e));
+ }
+ };
+
+ Assert.assertTrue(logger.isDebugEnable());
+ Assert.assertTrue(logger.isInfoEnable());
+ Assert.assertTrue(logger.isWarnEnable());
+ Assert.assertTrue(logger.isErrorEnable());
+
+ ArgumentCaptor<String> argument =
ArgumentCaptor.forClass(String.class);
+ logger.debug("hello world");
+ Mockito.verify(output, times(1)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"DEBUG", null));
+ logger.debug("hello {}", "world");
+ Mockito.verify(output, times(2)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"DEBUG", null));
+ logger.info("hello world");
+ Mockito.verify(output, times(3)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"INFO", null));
+ logger.info("hello {}", "world");
+ Mockito.verify(output, times(4)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"INFO", null));
+
+ logger.warn("hello {}", "world");
+ Mockito.verify(output, times(5)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"WARN", null));
+ logger.warn("hello world");
+ Mockito.verify(output, times(6)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"WARN", null));
+ logger.error("hello world");
+ Mockito.verify(output, times(7)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"ERROR", null));
+ Throwable t = new NullPointerException();
+ logger.error("hello world", t);
+ Mockito.verify(output, times(8)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"ERROR", t));
+ logger.error(t, "hello {}", "world");
+ Mockito.verify(output, times(9)).write(argument.capture());
+ Assert.assertThat(argument.getValue(), createMatcher("hello world",
"ERROR", t));
+ }
+
+ private static class LogMatcher extends TypeSafeDiagnosingMatcher<String> {
+ private static final Gson GSON = new Gson();
+ private static final Type MAP_TYPE = new TypeToken<Map<String,
String>>() {
+ }.getType();
+
+ private final String agent;
+ private final String logger;
+ private final String message;
+ private final String level;
+ private final Throwable t;
+
+ public LogMatcher(String agent, String logger, String message, String
level, Throwable t) {
+ this.agent = agent;
+ this.logger = logger;
+ this.message = message;
+ this.level = level;
+ this.t = t;
+ }
+
+ @Override
+ protected boolean matchesSafely(String item, Description
mismatchDescription) {
+ try {
+ Map<String, String> logMap = GSON.fromJson(item, MAP_TYPE);
+ if (!logMap.containsKey("agent_name")) {
+ mismatchDescription.appendText("agent_name did not exist");
+ return false;
+ }
+ if (!this.agent.equals(logMap.get("agent_name"))) {
+ mismatchDescription.appendText("agent_name was " +
this.agent);
+ return false;
+ }
+ if (!logMap.containsKey("message")) {
+ mismatchDescription.appendText("agent_name did not exist");
+ return false;
+ }
+ if (!this.message.equals(logMap.get("message"))) {
+ mismatchDescription.appendText("message was " +
this.message);
+ return false;
+ }
+
+ if (!logMap.containsKey("@timestamp")) {
+ mismatchDescription.appendText("@timestamp did not exist");
+ return false;
+ }
+
+ if (!logMap.containsKey("level")) {
+ mismatchDescription.appendText("level did not exist");
+ return false;
+ }
+ if (!this.level.equals(logMap.get("level"))) {
+ mismatchDescription.appendText("level was " + this.level);
+ return false;
+ }
+
+ if (!logMap.containsKey("logger")) {
+ mismatchDescription.appendText("logger did not exist");
+ return false;
+ }
+ if (!this.logger.equals(logMap.get("logger"))) {
+ mismatchDescription.appendText("logger was " +
this.logger);
+ return false;
+ }
+
+ if (!logMap.containsKey("throwable")) {
+ mismatchDescription.appendText("throwable did not exist");
+ return false;
+ }
+ final String tStr = this.t == null ? "" :
ThrowableConverter.format(this.t);
+ if (!tStr.equals(logMap.get("throwable"))) {
+ mismatchDescription.appendText("throwable was " + tStr);
+ return false;
+ }
+
+ } catch (JsonSyntaxException ex) {
+ mismatchDescription.appendText("not a valid json string");
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public void describeTo(Description expectation) {
+ expectation.appendText(
+ String.format("a valid json log format with
{message=%s,level=%s,logger=%s,@timestamp=*,agent=%s}",
+ message, level, logger, agent));
+ }
+ }
+
+ public static LogMatcher createMatcher(String message, String level,
Throwable t) {
+ return new LogMatcher("testAppFromConfig",
JsonLoggerTest.class.getSimpleName(), message, level, t);
+ }
+}
diff --git
a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/LoggingBenchmark.java
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/LoggingBenchmark.java
new file mode 100644
index 0000000..5e0b53f
--- /dev/null
+++
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/LoggingBenchmark.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.skywalking.apm.agent.core.logging.core;
+
+import com.google.gson.Gson;
+import org.openjdk.jmh.annotations.Benchmark;
+import org.openjdk.jmh.annotations.BenchmarkMode;
+import org.openjdk.jmh.annotations.Fork;
+import org.openjdk.jmh.annotations.Mode;
+import org.openjdk.jmh.annotations.OutputTimeUnit;
+
+import java.util.concurrent.TimeUnit;
+
+public class LoggingBenchmark {
+ private static final PatternLogger PATTERN_LOGGER = new
PatternLogger(LoggingBenchmark.class, PatternLogger.DEFAULT_PATTERN) {
+ @Override
+ protected void logger(LogLevel level, String message, Throwable e) {
+ format(level, message, e);
+ }
+ };
+
+ private static final JsonLogger JSON_LOGGER = new
JsonLogger(LoggingBenchmark.class, new Gson()) {
+ @Override
+ protected void logger(LogLevel level, String message, Throwable e) {
+ format(level, message, e);
+ }
+ };
+
+ @Benchmark
+ @Fork(value = 1, warmups = 1)
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.SampleTime)
+ public void jsonLogger() {
+ JSON_LOGGER.info("Hello World");
+ }
+
+ @Benchmark
+ @Fork(value = 1, warmups = 1)
+ @OutputTimeUnit(TimeUnit.NANOSECONDS)
+ @BenchmarkMode(Mode.SampleTime)
+ public void patternLogger() {
+ PATTERN_LOGGER.info("Hello World");
+ }
+
+ public static void main(String[] args) throws Exception {
+ org.openjdk.jmh.Main.main(args);
+ }
+
+ /**
+ * # JMH version: 1.21
+ * # VM version: JDK 1.8.0_265, OpenJDK 64-Bit Server VM, 25.265-b01
+ * # VM invoker:
/Library/Java/JavaVirtualMachines/adoptopenjdk-8.jdk/Contents/Home/jre/bin/java
+ * # VM options: -javaagent:/Applications/IntelliJ
IDEA.app/Contents/lib/idea_rt.jar=61104:/Applications/IntelliJ
IDEA.app/Contents/bin -Dfile.encoding=UTF-8
+ * # Warmup: 5 iterations, 10 s each
+ * # Measurement: 5 iterations, 10 s each
+ * # Timeout: 10 min per iteration
+ * # Threads: 1 thread, will synchronize iterations
+ * # Benchmark mode: Sampling time
+ *
+ * Benchmark Mode Cnt
Score Error Units
+ * LoggingBenchmark.jsonLogger sample 1400812
2305.088 ± 19.119 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.00 sample
1988.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.50 sample
2156.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.90 sample
2280.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.95 sample
2388.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.99 sample
3576.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.999 sample
18688.000 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p0.9999 sample
78072.717 ns/op
+ * LoggingBenchmark.jsonLogger:jsonLogger·p1.00 sample
1183744.000 ns/op
+ * LoggingBenchmark.patternLogger sample 1100999
1522.612 ± 23.720 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.00 sample
1270.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.50 sample
1378.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.90 sample
1452.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.95 sample
1548.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.99 sample
2188.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.999 sample
17696.000 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p0.9999 sample
589721.600 ns/op
+ * LoggingBenchmark.patternLogger:patternLogger·p1.00 sample
1259520.000 ns/op
+ */
+}
diff --git
a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLoggerTest.java
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLoggerTest.java
index 933cbca..db58485 100644
---
a/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLoggerTest.java
+++
b/apm-sniffer/apm-agent-core/src/test/java/org/apache/skywalking/apm/agent/core/logging/core/PatternLoggerTest.java
@@ -25,11 +25,11 @@ import org.hamcrest.core.StringContains;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
+import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
import java.util.List;
-import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.times;
public class PatternLoggerTest {
@@ -57,18 +57,38 @@ public class PatternLoggerTest {
Assert.assertTrue(logger.isWarnEnable());
Assert.assertTrue(logger.isErrorEnable());
+ ArgumentCaptor<String> argument =
ArgumentCaptor.forClass(String.class);
logger.debug("hello world");
+ Mockito.verify(output, times(1)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("DEBUG [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
logger.debug("hello {}", "world");
+ Mockito.verify(output, times(2)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("DEBUG [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
+
logger.info("hello world");
+ Mockito.verify(output, times(3)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("INFO [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
logger.info("hello {}", "world");
+ Mockito.verify(output, times(4)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("INFO [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
logger.warn("hello {}", "world");
+ Mockito.verify(output, times(5)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("WARN [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
logger.warn("hello world");
+ Mockito.verify(output, times(6)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("WARN [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
+
logger.error("hello world");
+ Mockito.verify(output, times(7)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("ERROR [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
logger.error("hello world", new NullPointerException());
+ Mockito.verify(output, times(8)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("ERROR [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello world"));
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("java.lang.NullPointerException"));
logger.error(new NullPointerException(), "hello {}", "world");
-
- Mockito.verify(output, times(9)).write(anyString());
+ Mockito.verify(output, times(9)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("java.lang.NullPointerException"));
}
@Test
@@ -86,18 +106,37 @@ public class PatternLoggerTest {
Assert.assertTrue(logger.isWarnEnable());
Assert.assertTrue(logger.isErrorEnable());
+ ArgumentCaptor<String> argument =
ArgumentCaptor.forClass(String.class);
logger.debug("$^!@#*()");
+ Mockito.verify(output, times(1)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("DEBUG [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 $^!@#*()"));
logger.debug("hello {}", "!@#$%^&*(),./[]:;");
+ Mockito.verify(output, times(2)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("DEBUG [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello !@#$%^&*(),./[]:;"));
+
logger.info("{}{}");
+ Mockito.verify(output, times(3)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("INFO [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 {}{}"));
logger.info("hello {}", "{}{}");
+ Mockito.verify(output, times(4)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("INFO [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello {}{}"));
logger.warn("hello {}", "\\");
+ Mockito.verify(output, times(5)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("WARN [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello \\"));
logger.warn("hello \\");
+ Mockito.verify(output, times(6)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("WARN [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello \\"));
+
logger.error("hello <>..");
+ Mockito.verify(output, times(7)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("ERROR [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello <>.."));
logger.error("hello ///\\\\", new NullPointerException());
+ Mockito.verify(output, times(8)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("ERROR [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello ///\\\\"));
logger.error(new NullPointerException(), "hello {}", "&&&**%%");
-
- Mockito.verify(output, times(9)).write(anyString());
+ Mockito.verify(output, times(9)).write(argument.capture());
+ Assert.assertThat(argument.getValue(),
StringContains.containsString("ERROR [testAppFromConfig,,,] [main]
PatternLoggerTest:-1 hello &&&**%%"));
}
@Test
diff --git
a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
index 7167d29..b327089 100644
---
a/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
+++
b/apm-sniffer/apm-agent/src/main/java/org/apache/skywalking/apm/agent/SkyWalkingAgent.java
@@ -20,6 +20,7 @@ package org.apache.skywalking.apm.agent;
import java.lang.instrument.Instrumentation;
import java.util.List;
+
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.description.NamedElement;
@@ -53,7 +54,7 @@ import static net.bytebuddy.matcher.ElementMatchers.not;
* The main entrance of sky-walking agent, based on javaagent mechanism.
*/
public class SkyWalkingAgent {
- private static final ILog LOGGER =
LogManager.getLogger(SkyWalkingAgent.class);
+ private static ILog LOGGER = LogManager.getLogger(SkyWalkingAgent.class);
/**
* Main entrance. Use byte-buddy transform to enhance all classes, which
define in plugins.
@@ -62,7 +63,17 @@ public class SkyWalkingAgent {
final PluginFinder pluginFinder;
try {
SnifferConfigInitializer.initializeCoreConfig(agentArgs);
+ } catch (Exception e) {
+ // try to resolve a new logger, and use the new logger to write
the error log here
+ LogManager.getLogger(SkyWalkingAgent.class)
+ .error(e, "SkyWalking agent initialized failure. Shutting
down.");
+ return;
+ } finally {
+ // refresh logger again after initialization finishes
+ LOGGER = LogManager.getLogger(SkyWalkingAgent.class);
+ }
+ try {
pluginFinder = new PluginFinder(new
PluginBootstrap().loadPlugins());
} catch (AgentPackageNotFoundException ape) {
LOGGER.error(ape, "Locate agent.jar failure. Shutting down.");
@@ -75,14 +86,15 @@ public class SkyWalkingAgent {
final ByteBuddy byteBuddy = new
ByteBuddy().with(TypeValidation.of(Config.Agent.IS_OPEN_DEBUGGING_CLASS));
AgentBuilder agentBuilder = new AgentBuilder.Default(byteBuddy).ignore(
- nameStartsWith("net.bytebuddy.").or(nameStartsWith("org.slf4j."))
- .or(nameStartsWith("org.groovy."))
- .or(nameContains("javassist"))
- .or(nameContains(".asm."))
- .or(nameContains(".reflectasm."))
- .or(nameStartsWith("sun.reflect"))
-
.or(allSkyWalkingAgentExcludeToolkit())
-
.or(ElementMatchers.isSynthetic()));
+ nameStartsWith("net.bytebuddy.")
+ .or(nameStartsWith("org.slf4j."))
+ .or(nameStartsWith("org.groovy."))
+ .or(nameContains("javassist"))
+ .or(nameContains(".asm."))
+ .or(nameContains(".reflectasm."))
+ .or(nameStartsWith("sun.reflect"))
+ .or(allSkyWalkingAgentExcludeToolkit())
+ .or(ElementMatchers.isSynthetic()));
JDK9ModuleExporter.EdgeClasses edgeClasses = new
JDK9ModuleExporter.EdgeClasses();
try {
@@ -121,7 +133,7 @@ public class SkyWalkingAgent {
}
Runtime.getRuntime()
- .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown,
"skywalking service shutdown thread"));
+ .addShutdownHook(new Thread(ServiceManager.INSTANCE::shutdown,
"skywalking service shutdown thread"));
}
private static class Transformer implements AgentBuilder.Transformer {
@@ -142,7 +154,7 @@ public class SkyWalkingAgent {
EnhanceContext context = new EnhanceContext();
for (AbstractClassEnhancePluginDefine define : pluginDefines) {
DynamicType.Builder<?> possibleNewBuilder = define.define(
- typeDescription, newBuilder, classLoader, context);
+ typeDescription, newBuilder, classLoader, context);
if (possibleNewBuilder != null) {
newBuilder = possibleNewBuilder;
}
diff --git a/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml
b/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml
index 153d5f4..fb7983f 100644
--- a/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/solrj-7.x-plugin/pom.xml
@@ -42,6 +42,12 @@
<version>${solr-solrj.version}</version>
<scope>provided</scope>
</dependency>
+
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
\ No newline at end of file
diff --git a/apm-sniffer/apm-test-tools/pom.xml
b/apm-sniffer/apm-test-tools/pom.xml
index f13ce0f..dc25263 100644
--- a/apm-sniffer/apm-test-tools/pom.xml
+++ b/apm-sniffer/apm-test-tools/pom.xml
@@ -47,5 +47,10 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
diff --git a/docs/en/setup/service-agent/java-agent/README.md
b/docs/en/setup/service-agent/java-agent/README.md
index cbdf94e..44cb374 100755
--- a/docs/en/setup/service-agent/java-agent/README.md
+++ b/docs/en/setup/service-agent/java-agent/README.md
@@ -96,7 +96,8 @@ property key | Description | Default |
`logging.file_name`|Log file name.|`skywalking-api.log`|
`logging.output`| Log output. Default is FILE. Use CONSOLE means output to
stdout. |`FILE`|
`logging.dir`|Log files directory. Default is blank string, means, use
"{theSkywalkingAgentJarDir}/logs " to output logs. {theSkywalkingAgentJarDir}
is the directory where the skywalking agent jar file is located |`""`|
-`logging.pattern `|logging format. There are all conversion specifiers:
<br> * `%level` means log level. <br> * `%timestamp`
means now of time with format `yyyy-MM-dd HH:mm:ss:SSS`.<br> *
`%thread` means name of current thread.<br> * `%msg` means some
message which user logged. <br> * `%class` means SimpleName of
TargetClass. <br> * `%throwable` means a throwable which user
called. <br> * `%agent_name` m [...]
+`logging.resolver`|Logger resolver: `PATTERN` or `JSON`. The default is
`PATTERN`, which uses `logging.pattern` to print traditional text logs. `JSON`
resolver prints logs in JSON format. |`PATTERN`|
+`logging.pattern `|Logging format. There are all conversion specifiers:
<br> * `%level` means log level. <br> * `%timestamp`
means now of time with format `yyyy-MM-dd HH:mm:ss:SSS`.<br> *
`%thread` means name of current thread.<br> * `%msg` means some
message which user logged. <br> * `%class` means SimpleName of
TargetClass. <br> * `%throwable` means a throwable which user
called. <br> * `%agent_name` m [...]
`logging.max_file_size`|The max size of log file. If the size is bigger than
this, archive the current file, and write into a new file.|`300 * 1024 * 1024`|
`logging.max_history_files`|The max history log files. When rollover happened,
if log files exceed this number,then the oldest file will be delete. Negative
or zero means off, by default.|`-1`|
`jvm.buffer_size`|The buffer size of collected JVM info.|`60 * 10`|
diff --git a/pom.xml b/pom.xml
index cad5f0d..667fdcd 100755
--- a/pom.xml
+++ b/pom.xml
@@ -250,6 +250,7 @@
<version>${jmh.version}</version>
<scope>test</scope>
</dependency>
+
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
@@ -290,6 +291,12 @@
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.openjdk.jmh</groupId>
+ <artifactId>jmh-generator-annprocess</artifactId>
+ <version>${jmh.version}</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</dependencyManagement>
@@ -588,7 +595,9 @@
org/apache/skywalking/oap/server/core/remote/grpc/proto/*.java,
org/apache/skywalking/oal/rt/grammar/*.java,
org/apache/skywalking/oap/server/exporter/grpc/*.java,
-
org/apache/skywalking/oap/server/configuration/service/*.java
+
org/apache/skywalking/oap/server/configuration/service/*.java,
+ **/generated/*_jmhType*.java,
+ **/generated/*_jmhTest.java
</excludes>
<propertyExpansion>
import.control=apm-checkstyle/importControl.xml