This is an automated email from the ASF dual-hosted git repository.
iluo pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-dubbo.git
The following commit(s) were added to refs/heads/master by this push:
new 5146f6d Acesslog dateformat enhancemnet (#3274)
5146f6d is described below
commit 5146f6d65d62eda5db67273e17f3de61af5e67b1
Author: Imteyaz Ahmed Khan <[email protected]>
AuthorDate: Sat Feb 2 08:01:09 2019 +0530
Acesslog dateformat enhancemnet (#3274)
* #3026 Access log related changed
* Reviwe comment incorporated given by @satansk and removed unuded method
* Incorporated @beiwei30 review comment, incorporated common-lang3 time
package modified version
* Added rat entry for common lang3 FastDateFormat related java files
* switch back to jdk's dateformatter
---
.../apache/dubbo/rpc/filter/AccessLogFilter.java | 221 ++++++++---------
.../apache/dubbo/rpc/support/AccessLogData.java | 266 +++++++++++++++++++++
2 files changed, 372 insertions(+), 115 deletions(-)
diff --git
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
index 2690dbf..dd95d3f 100644
---
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
+++
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/filter/AccessLogFilter.java
@@ -20,7 +20,6 @@ import org.apache.dubbo.common.Constants;
import org.apache.dubbo.common.extension.Activate;
import org.apache.dubbo.common.logger.Logger;
import org.apache.dubbo.common.logger.LoggerFactory;
-import org.apache.dubbo.common.utils.ArrayUtils;
import org.apache.dubbo.common.utils.ConcurrentHashSet;
import org.apache.dubbo.common.utils.ConfigUtils;
import org.apache.dubbo.common.utils.NamedThreadFactory;
@@ -30,21 +29,20 @@ import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.Result;
import org.apache.dubbo.rpc.RpcContext;
import org.apache.dubbo.rpc.RpcException;
-
-import com.alibaba.fastjson.JSON;
+import org.apache.dubbo.rpc.support.AccessLogData;
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
+import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
@@ -68,88 +66,42 @@ public class AccessLogFilter implements Filter {
private static final String ACCESS_LOG_KEY = "dubbo.accesslog";
- private static final String FILE_DATE_FORMAT = "yyyyMMdd";
-
- private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
-
private static final int LOG_MAX_BUFFER = 5000;
private static final long LOG_OUTPUT_INTERVAL = 5000;
- private final ConcurrentMap<String, Set<String>> logQueue = new
ConcurrentHashMap<String, Set<String>>();
+ private static final String FILE_DATE_FORMAT = "yyyyMMdd";
- private final ScheduledExecutorService logScheduled =
Executors.newScheduledThreadPool(2, new NamedThreadFactory("Dubbo-Access-Log",
true));
+ // It's safe to declare it as singleton since it runs on single thread only
+ private static final DateFormat FILE_NAME_FORMATTER = new
SimpleDateFormat(FILE_DATE_FORMAT);
- private volatile ScheduledFuture<?> logFuture = null;
+ private static final Map<String, Set<AccessLogData>> logEntries = new
ConcurrentHashMap<String, Set<AccessLogData>>();
- private void init() {
- if (logFuture == null) {
- synchronized (logScheduled) {
- if (logFuture == null) {
- logFuture = logScheduled.scheduleWithFixedDelay(new
LogTask(), LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
- }
- }
- }
- }
+ private static final ScheduledExecutorService logScheduled =
Executors.newSingleThreadScheduledExecutor(new
NamedThreadFactory("Dubbo-Access-Log", true));
- private void log(String accesslog, String logmessage) {
- init();
- Set<String> logSet = logQueue.get(accesslog);
- if (logSet == null) {
- logQueue.putIfAbsent(accesslog, new ConcurrentHashSet<String>());
- logSet = logQueue.get(accesslog);
- }
- if (logSet.size() < LOG_MAX_BUFFER) {
- logSet.add(logmessage);
- }
+ /**
+ * Default constructor initialize demon thread for writing into access log
file with names with access log key
+ * defined in url <b>accesslog</b>
+ */
+ public AccessLogFilter() {
+ logScheduled.scheduleWithFixedDelay(this::writeLogToFile,
LOG_OUTPUT_INTERVAL, LOG_OUTPUT_INTERVAL, TimeUnit.MILLISECONDS);
}
+ /**
+ * This method logs the access log for service method invocation call.
+ *
+ * @param invoker service
+ * @param inv Invocation service method.
+ * @return Result from service method.
+ * @throws RpcException
+ */
@Override
public Result invoke(Invoker<?> invoker, Invocation inv) throws
RpcException {
try {
- String accesslog =
invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
- if (ConfigUtils.isNotEmpty(accesslog)) {
- RpcContext context = RpcContext.getContext();
- String serviceName = invoker.getInterface().getName();
- String version =
invoker.getUrl().getParameter(Constants.VERSION_KEY);
- String group =
invoker.getUrl().getParameter(Constants.GROUP_KEY);
- StringBuilder sn = new StringBuilder();
- sn.append("[").append(new
SimpleDateFormat(MESSAGE_DATE_FORMAT).format(new Date())).append("]
").append(context.getRemoteHost()).append(":").append(context.getRemotePort())
- .append(" ->
").append(context.getLocalHost()).append(":").append(context.getLocalPort())
- .append(" - ");
- if (null != group && group.length() > 0) {
- sn.append(group).append("/");
- }
- sn.append(serviceName);
- if (null != version && version.length() > 0) {
- sn.append(":").append(version);
- }
- sn.append(" ");
- sn.append(inv.getMethodName());
- sn.append("(");
- Class<?>[] types = inv.getParameterTypes();
- if (types != null && types.length > 0) {
- boolean first = true;
- for (Class<?> type : types) {
- if (first) {
- first = false;
- } else {
- sn.append(",");
- }
- sn.append(type.getName());
- }
- }
- sn.append(") ");
- Object[] args = inv.getArguments();
- if (ArrayUtils.isNotEmpty(args)) {
- sn.append(JSON.toJSONString(args));
- }
- String msg = sn.toString();
- if (ConfigUtils.isDefault(accesslog)) {
- LoggerFactory.getLogger(ACCESS_LOG_KEY + "." +
invoker.getInterface().getName()).info(msg);
- } else {
- log(accesslog, msg);
- }
+ String accessLogKey =
invoker.getUrl().getParameter(Constants.ACCESS_LOG_KEY);
+ if (ConfigUtils.isNotEmpty(accessLogKey)) {
+ AccessLogData logData = buildAccessLogData(invoker, inv);
+ log(accessLogKey, logData);
}
} catch (Throwable t) {
logger.warn("Exception in AcessLogFilter of service(" + invoker +
" -> " + inv + ")", t);
@@ -157,52 +109,91 @@ public class AccessLogFilter implements Filter {
return invoker.invoke(inv);
}
- private class LogTask implements Runnable {
- @Override
- public void run() {
- try {
- if (logQueue != null && logQueue.size() > 0) {
- for (Map.Entry<String, Set<String>> entry :
logQueue.entrySet()) {
- try {
- String accesslog = entry.getKey();
- Set<String> logSet = entry.getValue();
- File file = new File(accesslog);
- File dir = file.getParentFile();
- if (null != dir && !dir.exists()) {
- dir.mkdirs();
- }
- if (logger.isDebugEnabled()) {
- logger.debug("Append log to " + accesslog);
- }
- if (file.exists()) {
- String now = new
SimpleDateFormat(FILE_DATE_FORMAT).format(new Date());
- String last = new
SimpleDateFormat(FILE_DATE_FORMAT).format(new Date(file.lastModified()));
- if (!now.equals(last)) {
- File archive = new
File(file.getAbsolutePath() + "." + last);
- file.renameTo(archive);
- }
- }
- FileWriter writer = new FileWriter(file, true);
- try {
- for (Iterator<String> iterator =
logSet.iterator();
- iterator.hasNext();
- iterator.remove()) {
- writer.write(iterator.next());
- writer.write("\r\n");
- }
- writer.flush();
- } finally {
- writer.close();
- }
- } catch (Exception e) {
- logger.error(e.getMessage(), e);
+ private void log(String accessLog, AccessLogData accessLogData) {
+ Set<AccessLogData> logSet = logEntries.computeIfAbsent(accessLog, k ->
new ConcurrentHashSet<>());
+
+ if (logSet.size() < LOG_MAX_BUFFER) {
+ logSet.add(accessLogData);
+ } else {
+ //TODO we needs use force writing to file so that buffer gets
clear and new log can be written.
+ logger.warn("AccessLog buffer is full skipping buffer ");
+ }
+ }
+
+ private void writeLogToFile() {
+ if (!logEntries.isEmpty()) {
+ for (Map.Entry<String, Set<AccessLogData>> entry :
logEntries.entrySet()) {
+ try {
+ String accessLog = entry.getKey();
+ Set<AccessLogData> logSet = entry.getValue();
+ if (ConfigUtils.isDefault(accessLog)) {
+ processWithServiceLogger(logSet);
+ } else {
+ File file = new File(accessLog);
+ createIfLogDirAbsent(file);
+ if (logger.isDebugEnabled()) {
+ logger.debug("Append log to " + accessLog);
}
+ renameFile(file);
+ processWithAccessKeyLogger(logSet, file);
}
+
+ } catch (Exception e) {
+ logger.error(e.getMessage(), e);
}
- } catch (Exception e) {
- logger.error(e.getMessage(), e);
}
}
}
+ private void processWithAccessKeyLogger(Set<AccessLogData> logSet, File
file) throws IOException {
+ try (FileWriter writer = new FileWriter(file, true)) {
+ for (Iterator<AccessLogData> iterator = logSet.iterator();
+ iterator.hasNext();
+ iterator.remove()) {
+ writer.write(iterator.next().getLogMessage());
+ writer.write("\r\n");
+ }
+ writer.flush();
+ }
+ }
+
+ private AccessLogData buildAccessLogData(Invoker<?> invoker, Invocation
inv) {
+ RpcContext context = RpcContext.getContext();
+ AccessLogData logData = AccessLogData.newLogData();
+ logData.setServiceName(invoker.getInterface().getName());
+ logData.setMethodName(inv.getMethodName());
+
logData.setVersion(invoker.getUrl().getParameter(Constants.VERSION_KEY));
+ logData.setGroup(invoker.getUrl().getParameter(Constants.GROUP_KEY));
+ logData.setInvocationTime(new Date());
+ logData.setTypes(inv.getParameterTypes());
+ logData.setArguments(inv.getArguments());
+ return logData;
+ }
+
+ private void processWithServiceLogger(Set<AccessLogData> logSet) {
+ for (Iterator<AccessLogData> iterator = logSet.iterator();
+ iterator.hasNext();
+ iterator.remove()) {
+ AccessLogData logData = iterator.next();
+ LoggerFactory.getLogger(ACCESS_LOG_KEY + "." +
logData.getServiceName()).info(logData.getLogMessage());
+ }
+ }
+
+ private void createIfLogDirAbsent(File file) {
+ File dir = file.getParentFile();
+ if (null != dir && !dir.exists()) {
+ dir.mkdirs();
+ }
+ }
+
+ private void renameFile(File file) {
+ if (file.exists()) {
+ String now = FILE_NAME_FORMATTER.format(new Date());
+ String last = FILE_NAME_FORMATTER.format(new
Date(file.lastModified()));
+ if (!now.equals(last)) {
+ File archive = new File(file.getAbsolutePath() + "." + last);
+ file.renameTo(archive);
+ }
+ }
+ }
}
diff --git
a/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
new file mode 100644
index 0000000..b7d1096
--- /dev/null
+++
b/dubbo-rpc/dubbo-rpc-api/src/main/java/org/apache/dubbo/rpc/support/AccessLogData.java
@@ -0,0 +1,266 @@
+/*
+ * 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.dubbo.rpc.support;
+
+import org.apache.dubbo.common.utils.StringUtils;
+import org.apache.dubbo.rpc.RpcContext;
+
+import com.alibaba.fastjson.JSON;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * AccessLogData is a container for log event data. In internally uses map and
store each filed of log as value. It
+ * does not generate any dynamic value e.g. time stamp, local jmv machine host
address etc. It does not allow any null
+ * or empty key.
+ *
+ * Note: since its date formatter is a singleton, make sure to run it in
single thread only.
+ */
+public final class AccessLogData {
+
+ private static final String MESSAGE_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ private static final DateFormat MESSAGE_DATE_FORMATTER = new
SimpleDateFormat(MESSAGE_DATE_FORMAT);
+
+ private static final String VERSION = "version";
+ private static final String GROUP = "group";
+ private static final String SERVICE = "service";
+ private static final String METHOD_NAME = "method-name";
+ private static final String INVOCATION_TIME = "invocation-time";
+ private static final String TYPES = "types";
+ private static final String ARGUMENTS = "arguments";
+ private static final String REMOTE_HOST = "remote-host";
+ private static final String REMOTE_PORT = "remote-port";
+ private static final String LOCAL_HOST = "localhost";
+ private static final String LOCAL_PORT = "local-port";
+
+ /**
+ * This is used to store log data in key val format.
+ */
+ private Map<String, Object> data;
+
+ /**
+ * Default constructor.
+ */
+ private AccessLogData() {
+ RpcContext context = RpcContext.getContext();
+ data = new HashMap<>();
+ setLocalHost(context.getLocalHost());
+ setLocalPort(context.getLocalPort());
+ setRemoteHost(context.getRemoteHost());
+ setRemotePort(context.getRemotePort());
+ }
+
+ /**
+ * Get new instance of log data.
+ *
+ * @return instance of AccessLogData
+ */
+ public static AccessLogData newLogData() {
+ return new AccessLogData();
+ }
+
+
+ /**
+ * Add version information.
+ *
+ * @param version
+ */
+ public void setVersion(String version) {
+ set(VERSION, version);
+ }
+
+ /**
+ * Add service name.
+ *
+ * @param serviceName
+ */
+ public void setServiceName(String serviceName) {
+ set(SERVICE, serviceName);
+ }
+
+ /**
+ * Add group name
+ *
+ * @param group
+ */
+ public void setGroup(String group) {
+ set(GROUP, group);
+ }
+
+ /**
+ * Set the invocation date. As an argument it accept date string.
+ *
+ * @param invocationTime
+ */
+ public void setInvocationTime(Date invocationTime) {
+ set(INVOCATION_TIME, invocationTime);
+ }
+
+ /**
+ * Set caller remote host
+ *
+ * @param remoteHost
+ */
+ private void setRemoteHost(String remoteHost) {
+ set(REMOTE_HOST, remoteHost);
+ }
+
+ /**
+ * Set caller remote port.
+ *
+ * @param remotePort
+ */
+ private void setRemotePort(Integer remotePort) {
+ set(REMOTE_PORT, remotePort);
+ }
+
+ /**
+ * Set local host
+ *
+ * @param localHost
+ */
+ private void setLocalHost(String localHost) {
+ set(LOCAL_HOST, localHost);
+ }
+
+ /**
+ * Set local port of exported service
+ *
+ * @param localPort
+ */
+ private void setLocalPort(Integer localPort) {
+ set(LOCAL_PORT, localPort);
+ }
+
+ /**
+ * Set target method name.
+ *
+ * @param methodName
+ */
+ public void setMethodName(String methodName) {
+ set(METHOD_NAME, methodName);
+ }
+
+ /**
+ * Set invocation's method's input parameter's types
+ *
+ * @param types
+ */
+ public void setTypes(Class[] types) {
+ set(TYPES, types != null ? Arrays.copyOf(types, types.length) : null);
+ }
+
+ /**
+ * Sets invocation arguments
+ *
+ * @param arguments
+ */
+ public void setArguments(Object[] arguments) {
+ set(ARGUMENTS, arguments != null ? Arrays.copyOf(arguments,
arguments.length) : null);
+ }
+
+ /**
+ * Return gthe service of access log entry
+ *
+ * @return
+ */
+ public String getServiceName() {
+ return get(SERVICE).toString();
+ }
+
+
+ public String getLogMessage() {
+ StringBuilder sn = new StringBuilder();
+
+ sn.append("[")
+ .append(MESSAGE_DATE_FORMATTER.format(getInvocationTime()))
+ .append("] ")
+ .append(get(REMOTE_HOST))
+ .append(":")
+ .append(get(REMOTE_PORT))
+ .append(" -> ")
+ .append(get(LOCAL_HOST))
+ .append(":")
+ .append(get(LOCAL_PORT))
+ .append(" - ");
+
+ String group = get(GROUP) != null ? get(GROUP).toString() : "";
+ if (StringUtils.isNotEmpty(group.toString())) {
+ sn.append(group).append("/");
+ }
+
+ sn.append(get(SERVICE));
+
+ String version = get(VERSION) != null ? get(VERSION).toString() : "";
+ if (StringUtils.isNotEmpty(version.toString())) {
+ sn.append(":").append(version);
+ }
+
+ sn.append(" ");
+ sn.append(get(METHOD_NAME));
+
+ sn.append("(");
+ Class<?>[] types = get(TYPES) != null ? (Class<?>[]) get(TYPES) : new
Class[0];
+ boolean first = true;
+ for (Class<?> type : types) {
+ if (first) {
+ first = false;
+ } else {
+ sn.append(",");
+ }
+ sn.append(type.getName());
+ }
+ sn.append(") ");
+
+
+ Object[] args = get(ARGUMENTS) != null ? (Object[]) get(ARGUMENTS) :
null;
+ if (args != null && args.length > 0) {
+ sn.append(JSON.toJSONString(args));
+ }
+
+ return sn.toString();
+ }
+
+ private Date getInvocationTime() {
+ return (Date)get(INVOCATION_TIME);
+ }
+ /**
+ * Return value of key
+ *
+ * @param key
+ * @return
+ */
+ private Object get(String key) {
+ return data.get(key);
+ }
+
+ /**
+ * Add log key along with his value.
+ *
+ * @param key Any not null or non empty string
+ * @param value Any object including null.
+ */
+ private void set(String key, Object value) {
+ data.put(key, value);
+ }
+
+}