├── src └── main │ ├── resources │ └── META-INF │ │ ├── druid-filter.properties │ │ ├── services │ │ └── fun.pxyc.onelogger.log.LoggerFormatterHandler │ │ ├── dubbo │ │ └── org.apache.dubbo.rpc.Filter │ │ └── spring.factories │ └── java │ └── fun │ └── pxyc │ └── onelogger │ ├── log │ ├── LoggerFormatterHandler.java │ ├── LoggerFilter.java │ ├── JsonLoggerFormatterHandler.java │ ├── IndexLoggerFilter.java │ ├── PlainLoggerFormatterHandler.java │ ├── IgnoreLoggerFilter.java │ ├── Action.java │ ├── Spis.java │ └── LoggerFormatter.java │ ├── web │ ├── WebDynamicPointcut.java │ └── TraceIdInterceptor.java │ ├── db │ ├── DbMsgNameHolder.java │ ├── MySqlDriverInfo.java │ ├── JdbcAuditLogAspect.java │ ├── DbInstrument.java │ ├── MySqlDriver8.java │ └── DbDyeing.java │ ├── redis │ ├── RedisConfig.java │ ├── RedisLogFormat.java │ └── RedisAuditLogAspect.java │ ├── utils │ ├── NetUtils.java │ ├── ReflectionUtils.java │ ├── JdbcUrlUtil.java │ ├── TypeSafeMap.java │ ├── TypeSafe.java │ └── JsonUtil.java │ ├── autoconfig │ ├── GlobalConfig.java │ ├── RabbitMqAutoConfiguration.java │ ├── KonisGraphConfiguration.java │ ├── KafkaAutoConfiguration.java │ ├── JdbcAutoConfiguration.java │ ├── ElasticSearchConfiguration.java │ ├── MybatisAutoConfiguration.java │ ├── BizAopConfiguration.java │ ├── WebControllerConfiguration.java │ ├── RedisAutoConfiguration.java │ ├── RestTemplateWithPoolConfiguration.java │ ├── EnvAutoConfiguration.java │ └── RestTemplateConfiguration.java │ ├── http │ ├── HttpCallInfo.java │ ├── HttpClientProperties.java │ ├── HttpsClientRequestFactory.java │ ├── HttpAuditLogInterceptor.java │ └── RestTemplateAspect.java │ ├── trace │ ├── SpanIds.java │ ├── ServerLogContextData.java │ ├── TraceIds.java │ ├── Metric.java │ ├── adpater │ │ ├── TraceAdapter.java │ │ └── DefaultTraceAdapter.java │ ├── TraceContext.java │ ├── Event.java │ ├── TraceData.java │ ├── Span.java │ ├── MetaData.java │ ├── ContextData.java │ └── Trace.java │ ├── kafka │ ├── KafkaErrorCounter.java │ ├── KafkaInstrument.java │ ├── KafkaAuditLogListenerAspect.java │ └── KafkaAuditLogSendAspect.java │ ├── bizlog │ ├── BizAdvice.java │ └── BizServiceInterceptor.java │ ├── NamedThreadFactory.java │ ├── ServerContextData.java │ ├── auditlog │ └── AsyncAuditLogPool.java │ ├── config │ └── EnvConfig.java │ ├── dubbo │ ├── ConsumerTraceFilter.java │ └── ProviderTraceFilter.java │ ├── rabbitmq │ ├── RabbitMqAuditSendAspect.java │ └── RabbitMqAuditRecvAspect.java │ └── elasticsearch │ └── ElasticSearchAuditLogAspect.java ├── doc ├── assert │ ├── benchmark_image01.png │ ├── benchmark_image02.png │ ├── benchmark_image03.png │ ├── benchmark_image04.png │ ├── benchmark_image05.png │ ├── benchmark_image06.png │ ├── benchmark_image07.png │ ├── benchmark_image08.png │ ├── benchmark_image09.png │ ├── benchmark_image10.png │ ├── benchmark_image11.png │ ├── benchmark_image12.png │ ├── benchmark_image13.png │ ├── benchmark_image14.png │ ├── benchmark_image15.png │ └── benchmark_image16.png └── benchmark.md └── .gitignore /src/main/resources/META-INF/druid-filter.properties: -------------------------------------------------------------------------------- 1 | druid.filters.dbauditlog=fun.pxyc.onelogger.db.DruidAuditLogFilter -------------------------------------------------------------------------------- /doc/assert/benchmark_image01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image01.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image02.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image03.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image04.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image05.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image06.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image07.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image08.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image09.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image10.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image11.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image12.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image13.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image14.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image15.png -------------------------------------------------------------------------------- /doc/assert/benchmark_image16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xiaoyuuuuuupeng/one-logger/HEAD/doc/assert/benchmark_image16.png -------------------------------------------------------------------------------- /src/main/resources/META-INF/services/fun.pxyc.onelogger.log.LoggerFormatterHandler: -------------------------------------------------------------------------------- 1 | fun.pxyc.onelogger.log.JsonLoggerFormatterHandler 2 | fun.pxyc.onelogger.log.PlainLoggerFormatterHandler -------------------------------------------------------------------------------- /src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.Filter: -------------------------------------------------------------------------------- 1 | consumerFilter=fun.pxyc.onelogger.dubbo.ConsumerTraceFilter 2 | providerFilter=fun.pxyc.onelogger.dubbo.ProviderTraceFilter 3 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/LoggerFormatterHandler.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import java.util.Map; 4 | 5 | public interface LoggerFormatterHandler { 6 | 7 | String logStr(Map params); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/web/WebDynamicPointcut.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.web; 2 | 3 | import org.springframework.aop.aspectj.AspectJExpressionPointcut; 4 | 5 | public class WebDynamicPointcut extends AspectJExpressionPointcut {} 6 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/DbMsgNameHolder.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | public class DbMsgNameHolder { 4 | public static final ThreadLocal msgName = new ThreadLocal(); 5 | 6 | public DbMsgNameHolder() {} 7 | } 8 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/MySqlDriverInfo.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | import java.sql.Connection; 4 | 5 | public interface MySqlDriverInfo { 6 | String getDatabase(Connection connection); 7 | 8 | String getHostPort(Connection connection); 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/LoggerFilter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import java.util.Map; 4 | 5 | public interface LoggerFilter { 6 | 7 | Map beforeBuildLogStr(Map logParams); 8 | 9 | String afterBuildLogStr(String logStr); 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/redis/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.redis; 2 | 3 | import java.util.concurrent.atomic.AtomicInteger; 4 | 5 | public class RedisConfig { 6 | 7 | public static final String LOG_TYPE = "REDIS."; 8 | public static AtomicInteger seq = new AtomicInteger(0); 9 | 10 | public RedisConfig() {} 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/NetUtils.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import java.net.InetAddress; 4 | 5 | public class NetUtils { 6 | public static String getHostAddress() { 7 | try { 8 | InetAddress ia = InetAddress.getLocalHost(); 9 | return ia.getHostAddress(); 10 | } catch (Exception e) { 11 | return "0.0.0.0"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/JsonLoggerFormatterHandler.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import fun.pxyc.onelogger.utils.JsonUtil; 4 | import java.util.Map; 5 | 6 | public class JsonLoggerFormatterHandler implements LoggerFormatterHandler { 7 | 8 | @Override 9 | public String logStr(Map params) { 10 | if (params == null || params.isEmpty()) { 11 | return ""; 12 | } 13 | return JsonUtil.toJson(params); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/GlobalConfig.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.utils.NetUtils; 4 | 5 | public class GlobalConfig { 6 | public static final String sourceIp = NetUtils.getHostAddress(); 7 | public static String globalProfile = ""; 8 | public static String globalGitName = ""; 9 | public static int globalServiceId = 999; 10 | public static String globalServiceName = ""; 11 | public static Integer dubboPort = 0; 12 | 13 | public GlobalConfig() {} 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/http/HttpCallInfo.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.http; 2 | 3 | import java.net.URI; 4 | import java.util.Map; 5 | 6 | public class HttpCallInfo { 7 | public static ThreadLocal instance = new ThreadLocal<>(); 8 | public URI uri; 9 | public String method; 10 | public Map reqHeaders; 11 | public String reqBodyStr; 12 | public int httpCode; 13 | public Map resHeaders; 14 | public String resBodyStr; 15 | 16 | public HttpCallInfo() {} 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea 8 | *.iws 9 | *.iml 10 | *.ipr 11 | 12 | ### Eclipse ### 13 | .apt_generated 14 | .classpath 15 | .factorypath 16 | .project 17 | .settings 18 | .springBeans 19 | .sts4-cache 20 | 21 | ### NetBeans ### 22 | /nbproject/private/ 23 | /nbbuild/ 24 | /dist/ 25 | /nbdist/ 26 | /.nb-gradle/ 27 | build/ 28 | !**/src/main/**/build/ 29 | !**/src/test/**/build/ 30 | 31 | ### VS Code ### 32 | .vscode/ 33 | 34 | ### Mac OS ### 35 | .DS_Store -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/SpanIds.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class SpanIds { 4 | private String parentSpanId; 5 | private String spanId; 6 | 7 | public SpanIds(String parentSpanId, String spanId) { 8 | this.parentSpanId = parentSpanId; 9 | this.spanId = spanId; 10 | } 11 | 12 | public String getParentSpanId() { 13 | return this.parentSpanId; 14 | } 15 | 16 | public void setParentSpanId(String parentSpanId) { 17 | this.parentSpanId = parentSpanId; 18 | } 19 | 20 | public String getSpanId() { 21 | return this.spanId; 22 | } 23 | 24 | public void setSpanId(String spanId) { 25 | this.spanId = spanId; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ 2 | fun.pxyc.onelogger.autoconfig.EnvAutoConfiguration,\ 3 | fun.pxyc.onelogger.autoconfig.RestTemplateConfiguration,\ 4 | fun.pxyc.onelogger.autoconfig.RabbitMqAutoConfiguration,\ 5 | fun.pxyc.onelogger.autoconfig.RedisAutoConfiguration,\ 6 | fun.pxyc.onelogger.autoconfig.WebControllerConfiguration,\ 7 | fun.pxyc.onelogger.autoconfig.JdbcAutoConfiguration,\ 8 | fun.pxyc.onelogger.autoconfig.MybatisAutoConfiguration,\ 9 | fun.pxyc.onelogger.autoconfig.BizAopConfiguration,\ 10 | fun.pxyc.onelogger.autoconfig.KonisGraphConfiguration,\ 11 | fun.pxyc.onelogger.autoconfig.ElasticSearchConfiguration,\ 12 | fun.pxyc.onelogger.autoconfig.KafkaAutoConfiguration -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/kafka/KafkaErrorCounter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.kafka; 2 | 3 | import java.util.HashMap; 4 | 5 | public class KafkaErrorCounter { 6 | private static final Object lock = new Object(); 7 | private static HashMap errorCount = new HashMap(); 8 | 9 | public KafkaErrorCounter() {} 10 | 11 | public static void addErrorCount(String addr) { 12 | synchronized (lock) { 13 | int n = errorCount.getOrDefault(addr, 0); 14 | ++n; 15 | errorCount.put(addr, n); 16 | } 17 | } 18 | 19 | public static HashMap getErrorCount() { 20 | synchronized (lock) { 21 | HashMap t = errorCount; 22 | errorCount = new HashMap(); 23 | return t; 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/ServerLogContextData.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | import fun.pxyc.onelogger.ServerContextData; 4 | 5 | public class ServerLogContextData { 6 | static ThreadLocal tlData = new ThreadLocal(); 7 | 8 | public ServerLogContextData() {} 9 | 10 | public static ServerContextData get() { 11 | return tlData.get(); 12 | } 13 | 14 | public static void set(ServerContextData data) { 15 | tlData.set(data); 16 | Trace.restoreContext(data.getTraceContext()); 17 | } 18 | 19 | public static void remove() { 20 | tlData.remove(); 21 | } 22 | 23 | public static void logVar(String key, Object value) { 24 | ServerContextData data = tlData.get(); 25 | if (data != null) { 26 | data.setAttribute("var:" + key, value); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/ReflectionUtils.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import java.lang.reflect.Constructor; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class ReflectionUtils { 8 | private static final Logger log = LoggerFactory.getLogger(ReflectionUtils.class); 9 | 10 | public static Class getClass(String s) { 11 | try { 12 | return Class.forName(s); 13 | } catch (Throwable e) { 14 | return null; 15 | } 16 | } 17 | 18 | public static Object newObject(String clsName) { 19 | try { 20 | Class cls = Class.forName(clsName); 21 | Constructor cons = cls.getDeclaredConstructor(); 22 | return cons.newInstance(); 23 | } catch (Throwable e) { 24 | throw new RuntimeException("newObject exception", e); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/RabbitMqAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.rabbitmq.RabbitMqAuditRecvAspect; 4 | import fun.pxyc.onelogger.rabbitmq.RabbitMqAuditSendAspect; 5 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @ConditionalOnClass({RabbitTemplate.class}) 12 | public class RabbitMqAutoConfiguration { 13 | public RabbitMqAutoConfiguration() {} 14 | 15 | @Bean 16 | RabbitMqAuditSendAspect rabbitMqAuditSendAspect() { 17 | return new RabbitMqAuditSendAspect(); 18 | } 19 | 20 | @Bean 21 | RabbitMqAuditRecvAspect rabbitMqAuditRecvAspect() { 22 | return new RabbitMqAuditRecvAspect(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/KonisGraphConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.konisgraph.KonisGraphAuditLogAspect; 4 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 6 | import org.springframework.boot.context.properties.ConfigurationProperties; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | @Configuration 11 | @ConditionalOnProperty(prefix = "graph.config", name = "address") 12 | @ConfigurationProperties(prefix = "graph.config") 13 | public class KonisGraphConfiguration { 14 | 15 | @Bean 16 | @ConditionalOnMissingBean(name = "konisGraphAuditLogAspect") 17 | public KonisGraphAuditLogAspect konisGraphAuditLogAspect() { 18 | return new KonisGraphAuditLogAspect(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/KafkaAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.kafka.KafkaAuditLogListenerAspect; 4 | import fun.pxyc.onelogger.kafka.KafkaAuditLogSendAspect; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.kafka.annotation.EnableKafka; 9 | import org.springframework.kafka.core.KafkaTemplate; 10 | 11 | @Configuration 12 | @EnableKafka 13 | @ConditionalOnClass({KafkaTemplate.class}) 14 | public class KafkaAutoConfiguration { 15 | public KafkaAutoConfiguration() {} 16 | 17 | @Bean 18 | KafkaAuditLogListenerAspect kafkaAuditLogListenerAspect() { 19 | return new KafkaAuditLogListenerAspect(); 20 | } 21 | 22 | @Bean 23 | KafkaAuditLogSendAspect kafkaAuditSendAspect() { 24 | return new KafkaAuditLogSendAspect(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/TraceIds.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class TraceIds { 4 | private String traceId; 5 | private String parentSpanId; 6 | private String spanId; 7 | 8 | public TraceIds(String traceId, String parentSpanId, String spanId) { 9 | this.traceId = traceId; 10 | this.parentSpanId = parentSpanId; 11 | this.spanId = spanId; 12 | } 13 | 14 | public String getParentSpanId() { 15 | return this.parentSpanId; 16 | } 17 | 18 | public void setParentSpanId(String parentSpanId) { 19 | this.parentSpanId = parentSpanId; 20 | } 21 | 22 | public String getSpanId() { 23 | return this.spanId; 24 | } 25 | 26 | public void setSpanId(String spanId) { 27 | this.spanId = spanId; 28 | } 29 | 30 | public String getTraceId() { 31 | return this.traceId; 32 | } 33 | 34 | public void setTraceId(String traceId) { 35 | this.traceId = traceId; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/IndexLoggerFilter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import fun.pxyc.onelogger.utils.JsonUtil; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | public class IndexLoggerFilter implements LoggerFilter { 8 | 9 | private List logIndex = null; 10 | 11 | @Override 12 | public Map beforeBuildLogStr(Map logParams) { 13 | // 处理索引提取 14 | if (logIndex != null && !logIndex.isEmpty()) { 15 | String json = JsonUtil.toJson(logParams); 16 | Map indexValue = JsonUtil.findJsonFieldValue(json, logIndex); 17 | if (!indexValue.isEmpty()) { 18 | logParams.putAll(indexValue); 19 | } 20 | } 21 | return logParams; 22 | } 23 | 24 | @Override 25 | public String afterBuildLogStr(String logStr) { 26 | return logStr; 27 | } 28 | 29 | public void setLogIndex(List logIndex) { 30 | this.logIndex = logIndex; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/Metric.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class Metric { 4 | public static final int COUNT = 1; 5 | public static final int QUANTITY = 2; 6 | public static final int SUM = 3; 7 | public static final int QUANTITY_AND_SUM = 4; 8 | private String key; 9 | private int type; 10 | private String value; 11 | 12 | public Metric(String key, int type, String value) { 13 | this.key = key; 14 | this.type = type; 15 | this.value = value; 16 | } 17 | 18 | public int getType() { 19 | return this.type; 20 | } 21 | 22 | public void setType(int type) { 23 | this.type = type; 24 | } 25 | 26 | public String getValue() { 27 | return this.value; 28 | } 29 | 30 | public void setValue(String value) { 31 | this.value = value; 32 | } 33 | 34 | public String getKey() { 35 | return this.key; 36 | } 37 | 38 | public void setKey(String key) { 39 | this.key = key; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/adpater/TraceAdapter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace.adpater; 2 | 3 | import fun.pxyc.onelogger.trace.*; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public interface TraceAdapter { 7 | 8 | default TraceContext newTraceContext() { 9 | return new DefaultTraceContext(); 10 | } 11 | 12 | default TraceContext newTraceContext(TraceData trace, boolean restoreFlag) { 13 | return new DefaultTraceContext(trace, restoreFlag); 14 | } 15 | 16 | default boolean needThreadInfo() { 17 | return false; 18 | } 19 | 20 | default boolean useCtxSubCalls() { 21 | return false; 22 | } 23 | 24 | void inject(TraceContext ctx, Span span, TraceData traceData); 25 | 26 | SpanIds restore(String parentSpanId, String spanId); 27 | 28 | TraceIds newStartTraceIds(boolean isServer); 29 | 30 | SpanIds newChildSpanIds(String spanId, AtomicInteger subCalls); 31 | 32 | void send(TraceContext ctx, Span span); 33 | 34 | boolean hasError(int retCode); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/web/TraceIdInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.web; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.trace.Trace; 5 | import fun.pxyc.onelogger.trace.TraceData; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | import org.slf4j.MDC; 9 | import org.springframework.web.servlet.HandlerInterceptor; 10 | 11 | public class TraceIdInterceptor implements HandlerInterceptor { 12 | 13 | @Override 14 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 15 | throws Exception { 16 | // 生成web的traceId和span 17 | String remoteAddr = request.getRemoteAddr(); 18 | TraceData t = AsyncAuditLog.newTrace(remoteAddr); 19 | Trace.setCurrentContext(Trace.getAdapter().newTraceContext(t, false)); 20 | // 返回给客户端 21 | MDC.put("TRACE_ID", t.getTraceId()); 22 | response.setHeader("TRACEID", t.getTraceId()); 23 | return true; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/JdbcAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.db.DbDyeing; 4 | import fun.pxyc.onelogger.db.JdbcAuditLogAspect; 5 | import javax.annotation.PostConstruct; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.jdbc.core.JdbcTemplate; 11 | 12 | @Configuration 13 | @ConditionalOnClass({JdbcTemplate.class}) 14 | public class JdbcAutoConfiguration { 15 | 16 | @Value("${envconfig.db.prefix:t_,v_}") 17 | String tablePrefix; 18 | 19 | public JdbcAutoConfiguration() {} 20 | 21 | @PostConstruct 22 | void init() { 23 | String[] ss = this.tablePrefix.split(","); 24 | DbDyeing.init(ss); 25 | } 26 | 27 | @Bean 28 | JdbcAuditLogAspect jdbcAuditLogAspect() { 29 | return new JdbcAuditLogAspect(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/bizlog/BizAdvice.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.bizlog; 2 | 3 | import java.lang.reflect.Method; 4 | import org.aopalliance.aop.Advice; 5 | import org.aopalliance.intercept.MethodInterceptor; 6 | import org.springframework.aop.Advisor; 7 | import org.springframework.aop.AfterReturningAdvice; 8 | import org.springframework.aop.MethodBeforeAdvice; 9 | import org.springframework.aop.framework.adapter.AdvisorAdapter; 10 | 11 | public class BizAdvice extends BizServiceInterceptor 12 | implements MethodBeforeAdvice, AfterReturningAdvice, AdvisorAdapter { 13 | 14 | @Override 15 | public void before(Method method, Object[] args, Object target) throws Throwable {} 16 | 17 | @Override 18 | public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {} 19 | 20 | @Override 21 | public boolean supportsAdvice(Advice advice) { 22 | return true; 23 | } 24 | 25 | @Override 26 | public MethodInterceptor getInterceptor(Advisor advisor) { 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/PlainLoggerFormatterHandler.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.utils.JsonUtil; 5 | import java.util.Map; 6 | import java.util.Set; 7 | 8 | public class PlainLoggerFormatterHandler implements LoggerFormatterHandler { 9 | @Override 10 | public String logStr(Map params) { 11 | StringBuilder stringBuilder = new StringBuilder(); 12 | Set> entries = params.entrySet(); 13 | for (Map.Entry entry : entries) { 14 | Object v = entry.getValue(); 15 | if (v instanceof Map) { 16 | Map map = JsonUtil.toJsonMap(v); 17 | stringBuilder.append(AsyncAuditLog.mapToString(map)).append(", "); 18 | continue; 19 | } 20 | stringBuilder.append(AsyncAuditLog.toTextAndEscape(v, -1)).append(", "); 21 | } 22 | return stringBuilder.substring(0, stringBuilder.length() - 4); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/TraceContext.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | import java.util.Map; 4 | 5 | public interface TraceContext { 6 | Span startForServer(String type, String action); 7 | 8 | Span stopForServer(int stopCode); 9 | 10 | Span stopForServer(int retCode, String retMsg); 11 | 12 | Span rootSpan(); 13 | 14 | Span startAsync(String type, String action); 15 | 16 | Span appendSpan(String type, String action, long startMicros, String status, long timeUsedMicros); 17 | 18 | Span start(String type, String action); 19 | 20 | void tagForRpc(String key, String value); 21 | 22 | void tagForRpcIfAbsent(String key, String value); 23 | 24 | String getTagForRpc(String key); 25 | 26 | String getTagsForRpc(); 27 | 28 | TraceData getTrace(); 29 | 30 | long getThreadId(); 31 | 32 | String getThreadName(); 33 | 34 | String getThreadGroupName(); 35 | 36 | long getRequestTimeMicros(); 37 | 38 | long getStartMicros(); 39 | 40 | Map getTagsMapForRpc(); 41 | 42 | TraceContext detach(); 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/NamedThreadFactory.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger; 2 | 3 | import java.util.concurrent.ThreadFactory; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class NamedThreadFactory implements ThreadFactory { 7 | private final String namePrefix; 8 | private final AtomicInteger threadNumber = new AtomicInteger(1); 9 | private final SecurityManager s = System.getSecurityManager(); 10 | private final ThreadGroup group; 11 | 12 | public NamedThreadFactory(String namePrefix) { 13 | this.group = this.s != null 14 | ? this.s.getThreadGroup() 15 | : Thread.currentThread().getThreadGroup(); 16 | this.namePrefix = namePrefix; 17 | } 18 | 19 | public Thread newThread(Runnable r) { 20 | Thread t = new Thread(this.group, r, this.namePrefix + "-thread-" + this.threadNumber.getAndIncrement(), 0L); 21 | if (t.isDaemon()) { 22 | t.setDaemon(false); 23 | } 24 | 25 | if (t.getPriority() != 5) { 26 | t.setPriority(5); 27 | } 28 | 29 | return t; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/ElasticSearchConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.elasticsearch.ElasticSearchAuditLogAspect; 4 | import org.elasticsearch.client.RestClient; 5 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 6 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 8 | import org.springframework.boot.context.properties.ConfigurationProperties; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | @ConditionalOnClass({RestClient.class}) 14 | @ConditionalOnProperty(prefix = "elasticsearch", name = "hosts") 15 | @ConfigurationProperties(prefix = "elasticsearch") 16 | public class ElasticSearchConfiguration { 17 | 18 | @Bean 19 | @ConditionalOnMissingBean(name = "elasticSearchAuditLogAspect") 20 | public ElasticSearchAuditLogAspect elasticSearchAuditLogAspect() { 21 | return new ElasticSearchAuditLogAspect(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/JdbcAuditLogAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | import org.aspectj.lang.annotation.Around; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.aspectj.lang.annotation.Pointcut; 7 | 8 | @Aspect 9 | public class JdbcAuditLogAspect { 10 | public JdbcAuditLogAspect() {} 11 | 12 | @Pointcut( 13 | "execution(* *..JdbcTemplate.query(..))||execution(* *..JdbcTemplate.queryForList(..))||execution(* *..JdbcTemplate.queryForObject(..))||execution(* *..JdbcTemplate.queryForRowSet(..))||execution(* *..JdbcTemplate.queryForMap(..))||execution(* *..JdbcTemplate.execute(..))||execution(* *..JdbcTemplate.update(..))||execution(* *..JdbcTemplate.batchUpdate(..))") 14 | public void jdbcAuditLogPointCut() {} 15 | 16 | @Around("jdbcAuditLogPointCut()") 17 | public Object jdbcAuditLogAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 18 | String methodName = joinPoint.getSignature().getName().toLowerCase(); 19 | String msgName = "jdbc_" + methodName; 20 | DbMsgNameHolder.msgName.set(msgName); 21 | return joinPoint.proceed(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/MybatisAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.mybatis.SqlStatsInterceptor; 4 | import java.util.Map; 5 | import org.apache.ibatis.plugin.Interceptor; 6 | import org.apache.ibatis.session.SqlSessionFactory; 7 | import org.springframework.beans.BeansException; 8 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 9 | import org.springframework.context.ApplicationContext; 10 | import org.springframework.context.ApplicationContextAware; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.util.ClassUtils; 13 | 14 | @Configuration 15 | @ConditionalOnClass({Interceptor.class, SqlSessionFactory.class}) 16 | public class MybatisAutoConfiguration implements ApplicationContextAware { 17 | @Override 18 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 19 | Map sqlSessionFactoryMap = 20 | applicationContext.getBeansOfType(SqlSessionFactory.class); 21 | boolean useDruidDataSource = ClassUtils.isPresent("com.alibaba.druid.pool.DruidDataSource", null); 22 | sqlSessionFactoryMap.values().forEach(v -> v.getConfiguration() 23 | .addInterceptor(new SqlStatsInterceptor(useDruidDataSource))); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/BizAopConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.bizlog.BizAdvice; 4 | import fun.pxyc.onelogger.web.WebDynamicPointcut; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.aop.Pointcut; 8 | import org.springframework.aop.support.DefaultPointcutAdvisor; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | 12 | @Configuration 13 | public class BizAopConfiguration { 14 | 15 | private static final Logger log = LoggerFactory.getLogger(BizAopConfiguration.class); 16 | 17 | @Bean 18 | public Pointcut bizPointCut(EnvConfigProps envConfigProps) { 19 | WebDynamicPointcut webDynamicPointcut = new WebDynamicPointcut(); 20 | webDynamicPointcut.setExpression(envConfigProps.getBizCutPoint()); 21 | return webDynamicPointcut; 22 | } 23 | 24 | @Bean 25 | public BizAdvice getBizAdvice() { 26 | return new BizAdvice(); 27 | } 28 | 29 | @Bean 30 | public DefaultPointcutAdvisor bizPointcutAdvisor(EnvConfigProps envConfigProps) { 31 | DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(); 32 | defaultPointcutAdvisor.setPointcut(bizPointCut(envConfigProps)); 33 | defaultPointcutAdvisor.setAdvice(getBizAdvice()); 34 | log.info("biz log aop init..."); 35 | return defaultPointcutAdvisor; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/IgnoreLoggerFilter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import com.jayway.jsonpath.JsonPath; 4 | import fun.pxyc.onelogger.utils.JsonUtil; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Set; 9 | 10 | public class IgnoreLoggerFilter implements LoggerFilter { 11 | 12 | private static final String IGNORE_REPLACE_STR = "*****"; 13 | private List ignoreParams = new ArrayList<>(); 14 | 15 | @Override 16 | public Map beforeBuildLogStr(Map logParams) { 17 | // 处理忽略字段问题 18 | if (ignoreParams != null && !ignoreParams.isEmpty()) { 19 | String json = JsonUtil.toJson(logParams); 20 | Map indexValue = JsonUtil.findJsonFieldValue(json, ignoreParams); 21 | if (indexValue != null && !indexValue.isEmpty()) { 22 | Set keys = indexValue.keySet(); 23 | for (String key : keys) { 24 | json = JsonPath.parse(json).set(key, IGNORE_REPLACE_STR).jsonString(); 25 | } 26 | logParams = JsonUtil.toMap(json); 27 | } 28 | } 29 | return logParams; 30 | } 31 | 32 | @Override 33 | public String afterBuildLogStr(String logStr) { 34 | return logStr; 35 | } 36 | 37 | public void setIgnoreParams(List ignoreParams) { 38 | this.ignoreParams = ignoreParams; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/Event.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class Event { 4 | private final long startMicros = System.nanoTime() / 1000L; 5 | private String type; 6 | private String action; 7 | private String status; 8 | private String data; 9 | 10 | public Event(String type, String action, String status, String data) { 11 | this.type = type; 12 | this.action = action; 13 | this.status = status; 14 | this.data = data; 15 | } 16 | 17 | public String toAnnotationString() { 18 | String s = this.type + ":" + this.action + ":" + this.status; 19 | if (this.data != null) { 20 | s = s + ":" + this.data; 21 | } 22 | 23 | return s; 24 | } 25 | 26 | public String getType() { 27 | return this.type; 28 | } 29 | 30 | public void setType(String type) { 31 | this.type = type; 32 | } 33 | 34 | public String getData() { 35 | return this.data; 36 | } 37 | 38 | public void setData(String data) { 39 | this.data = data; 40 | } 41 | 42 | public String getAction() { 43 | return this.action; 44 | } 45 | 46 | public void setAction(String action) { 47 | this.action = action; 48 | } 49 | 50 | public String getStatus() { 51 | return this.status; 52 | } 53 | 54 | public void setStatus(String status) { 55 | this.status = status; 56 | } 57 | 58 | public long getStartMicros() { 59 | return this.startMicros; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /doc/benchmark.md: -------------------------------------------------------------------------------- 1 | ### 1. 压测环境搭建 2 | promethues+grafana+springboot-actuator环境搭建 3 | 4 | ### 2. 压测服务代码 5 | dubbo-provider 6 | dubbo-consumer 7 | 8 | ### 3. 使用postman测试服务响应 9 | 发送10000个请求 http://localhost:8081/hello?name=123 每个请求delay 1ms,模拟低并发请求环境下,请求耗时情况,使用grafana展示测试这段时间耗时P99 P90 P50情况和CPU使用率等指标。 10 | 11 | #### 3.1 使用统一审计日志配置和不使用对比 12 | 13 | | 列项 | 使用one-logger | 不使用 | 14 | |-----------------|-------------------------------------|-------------------------------------| 15 | | postman运行情况 | ![图片](assert/benchmark_image01.png) | ![图片](assert/benchmark_image02.png) | 16 | | P99 P90 P50耗时情况 | ![图片](assert/benchmark_image03.png) | ![图片](assert/benchmark_image04.png) | 17 | | CPU和内存占用情况 | ![图片](assert/benchmark_image05.png) | ![图片](assert/benchmark_image06.png) | 18 | | Arthas耗时分析 | ![图片](assert/benchmark_image07.png) | ![图片](assert/benchmark_image08.png) | 19 | 20 | #### 3.2 使用apache bench测试服务响应 21 | 使用apache bench分别在20、50、100、200并发下对请求耗时的影响,发送2000个请求 http://localhost:8081/hello?name=123 22 | 23 | | 列项 | 使用one-logger | 不使用 | 24 | |--------------------|-------------------------------------|-------------------------------------| 25 | | 2000requests 20并发 | ![图片](assert/benchmark_image09.png) | ![图片](assert/benchmark_image10.png) | 26 | | 2000requests 50并发 | ![图片](assert/benchmark_image11.png) | ![图片](assert/benchmark_image12.png) | 27 | | 2000requests 100并发 | ![图片](assert/benchmark_image13.png) | ![图片](assert/benchmark_image14.png) | 28 | | 2000requests 200并发 | ![图片](assert/benchmark_image15.png) | ![图片](assert/benchmark_image16.png) | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/ServerContextData.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger; 2 | 3 | import fun.pxyc.onelogger.trace.ContextData; 4 | import fun.pxyc.onelogger.trace.MetaData; 5 | import fun.pxyc.onelogger.trace.TraceContext; 6 | import java.util.concurrent.atomic.AtomicBoolean; 7 | 8 | public class ServerContextData extends ContextData { 9 | private final TraceContext traceContext; 10 | private final AtomicBoolean replied = new AtomicBoolean(false); 11 | private long decodeMicros = 0L; 12 | private long waitInQueueMicros = 0L; 13 | 14 | public ServerContextData(String connId, MetaData meta, TraceContext traceContext) { 15 | super(connId, meta); 16 | this.traceContext = traceContext; 17 | this.startMicros = traceContext.rootSpan().getStartMicros(); 18 | this.requestTimeMicros = 19 | traceContext.getRequestTimeMicros() + (this.startMicros - traceContext.getStartMicros()); 20 | } 21 | 22 | public TraceContext getTraceContext() { 23 | return this.traceContext; 24 | } 25 | 26 | public void afterQueue() { 27 | this.waitInQueueMicros = System.nanoTime() / 1000L - this.startMicros; 28 | } 29 | 30 | public long getWaitInQueueMicros() { 31 | return this.waitInQueueMicros; 32 | } 33 | 34 | public boolean isReplied() { 35 | return this.replied.get(); 36 | } 37 | 38 | public boolean setReplied() { 39 | return this.replied.compareAndSet(false, true); 40 | } 41 | 42 | public long getDecodeMicros() { 43 | return this.decodeMicros; 44 | } 45 | 46 | public void setDecodeMicros(long decodeMicros) { 47 | this.decodeMicros = decodeMicros; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/adpater/DefaultTraceAdapter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace.adpater; 2 | 3 | import fun.pxyc.onelogger.trace.*; 4 | import java.util.UUID; 5 | import java.util.concurrent.atomic.AtomicInteger; 6 | 7 | public class DefaultTraceAdapter implements TraceAdapter { 8 | 9 | public void send(TraceContext ctx, Span span) { 10 | // do nothing 11 | } 12 | 13 | @Override 14 | public boolean hasError(int retCode) { 15 | return retCode < 0; 16 | } 17 | 18 | public void inject(TraceContext ctx, Span span, TraceData traceData) { 19 | traceData.setTraceId(ctx.getTrace().getTraceId()); 20 | traceData.setParentSpanId(""); 21 | traceData.setSpanId(span.getSpanId()); 22 | traceData.setTags(ctx.getTagsForRpc()); 23 | } 24 | 25 | public SpanIds restore(String parentSpanId, String spanId) { 26 | return null; 27 | } 28 | 29 | // like ali eagle style 30 | public TraceIds newStartTraceIds(boolean isServer) { 31 | String traceId = newTraceId(); 32 | if (isServer) return new TraceIds(traceId, "", "0.1"); 33 | else return new TraceIds(traceId, "", ""); 34 | } 35 | 36 | public SpanIds newChildSpanIds(String spanId, AtomicInteger subCalls) { 37 | if (isEmpty(spanId)) spanId = "0"; 38 | String childId = spanId + "." + subCalls.incrementAndGet(); // 0.1.1.1 39 | return new SpanIds(spanId, childId); 40 | } 41 | 42 | String newTraceId() { 43 | String s = UUID.randomUUID().toString(); 44 | return s.replaceAll("-", ""); 45 | } 46 | 47 | boolean isEmpty(String s) { 48 | return s == null || s.isEmpty(); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/TraceData.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class TraceData { 4 | String peers; // ip:port for each hop, format: addr1,addr2,... 5 | String traceId; 6 | String parentSpanId; // may be empty 7 | String spanId; 8 | String tags; // key/value pairs passing in the chain, format: k1=v1&k2=v2&... 9 | int sampleFlag; // 0=default(yes) 1=force 2=no 10 | String dyeing; 11 | 12 | public TraceData() {} 13 | 14 | public String getPeers() { 15 | return peers; 16 | } 17 | 18 | public void setPeers(String peers) { 19 | this.peers = peers; 20 | } 21 | 22 | public String getTraceId() { 23 | return traceId; 24 | } 25 | 26 | public void setTraceId(String traceId) { 27 | this.traceId = traceId; 28 | } 29 | 30 | public String getParentSpanId() { 31 | return parentSpanId; 32 | } 33 | 34 | public void setParentSpanId(String parentSpanId) { 35 | this.parentSpanId = parentSpanId; 36 | } 37 | 38 | public String getSpanId() { 39 | return spanId; 40 | } 41 | 42 | public void setSpanId(String spanId) { 43 | this.spanId = spanId; 44 | } 45 | 46 | public String getTags() { 47 | return tags; 48 | } 49 | 50 | public void setTags(String tags) { 51 | this.tags = tags; 52 | } 53 | 54 | public int getSampleFlag() { 55 | return sampleFlag; 56 | } 57 | 58 | public void setSampleFlag(int sampleFlag) { 59 | this.sampleFlag = sampleFlag; 60 | } 61 | 62 | public String getDyeing() { 63 | return dyeing; 64 | } 65 | 66 | public void setDyeing(String dyeing) { 67 | this.dyeing = dyeing; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/DbInstrument.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | import java.sql.Connection; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class DbInstrument { 8 | private static final Logger log = LoggerFactory.getLogger(DbInstrument.class); 9 | static MySqlDriverInfo driverInfo = new MySqlDriver8(); 10 | 11 | public DbInstrument() {} 12 | 13 | public static String getDatabase(Connection conn) { 14 | return driverInfo == null ? "unknown" : driverInfo.getDatabase(conn); 15 | } 16 | 17 | public static String getHostPort(Connection conn) { 18 | return driverInfo == null ? "0.0.0.0:0:0" : driverInfo.getHostPort(conn); 19 | } 20 | 21 | public static String parseDatabase(String url) { 22 | int p1 = url.indexOf("?"); 23 | int p2; 24 | if (p1 >= 0) { 25 | p2 = url.lastIndexOf("/", p1); 26 | return url.substring(p2 + 1, p1); 27 | } else { 28 | p1 = url.indexOf("//"); 29 | if (p1 >= 0) { 30 | p2 = url.indexOf("/", p1 + 2); 31 | if (p2 >= 0) { 32 | return url.substring(p1 + 2, p2); 33 | } else { 34 | p2 = url.indexOf("?", p1 + 2); 35 | return p2 >= 0 ? url.substring(p1 + 2, p2) : url.substring(p1 + 2); 36 | } 37 | } else { 38 | return "unknown_db"; 39 | } 40 | } 41 | } 42 | 43 | public static boolean mybatisExisted() { 44 | try { 45 | Class.forName("org.apache.ibatis.annotations.Select"); 46 | return true; 47 | } catch (Exception e) { 48 | return false; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/Span.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | public interface Span { 7 | Span newChild(String type, String action); 8 | 9 | Span newChild(String type, String action, long startMicros); 10 | 11 | long stop(); 12 | 13 | long stop(boolean ok); 14 | 15 | long stop(String status); 16 | 17 | long stopForServer(String status); 18 | 19 | long stopWithTime(String status, long startMicros); 20 | 21 | void logEvent(String type, String action, String status, String data); 22 | 23 | void logException(Throwable t); 24 | 25 | void logException(String message, Throwable cause); 26 | 27 | void tag(String key, String value); 28 | 29 | void incCount(String key); 30 | 31 | void incQuantity(String key, long value); 32 | 33 | void incSum(String key, double value); 34 | 35 | void incQuantitySum(String key, long v1, double v2); 36 | 37 | Span getRootSpan(); 38 | 39 | String getRootSpanId(); 40 | 41 | SpanIds getSpanIds(); 42 | 43 | String getParentSpanId(); 44 | 45 | String getSpanId(); 46 | 47 | String getType(); 48 | 49 | String getAction(); 50 | 51 | long getStartMicros(); 52 | 53 | long getTimeUsedMicros(); 54 | 55 | String getTimeUsedMs(); 56 | 57 | String getStatus(); 58 | 59 | String getRemoteAddr(); 60 | 61 | void setRemoteAddr(String addr); 62 | 63 | List getEvents(); 64 | 65 | Map getTags(); 66 | 67 | List getMetrics(); 68 | 69 | void removeTag(String key); 70 | 71 | void removeTags(); 72 | 73 | List getChildren(); 74 | 75 | String statsTimeUsed(); 76 | 77 | void changeAction(String action); 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/MySqlDriver8.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | import com.mysql.cj.jdbc.ConnectionImpl; 4 | import java.lang.reflect.Field; 5 | import java.sql.Connection; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | public class MySqlDriver8 implements MySqlDriverInfo { 10 | private static final Logger log = LoggerFactory.getLogger(MySqlDriver8.class); 11 | static Field databaseField; 12 | 13 | static { 14 | initFields(); 15 | } 16 | 17 | public MySqlDriver8() {} 18 | 19 | static void initFields() { 20 | try { 21 | databaseField = ConnectionImpl.class.getDeclaredField("database"); 22 | databaseField.setAccessible(true); 23 | } catch (Throwable throwable) { 24 | log.error("cannot update JDBC4Connection database field"); 25 | databaseField = null; 26 | } 27 | } 28 | 29 | public String getDatabase(Connection conn) { 30 | if (!(conn instanceof ConnectionImpl)) { 31 | return "unknown"; 32 | } else if (databaseField == null) { 33 | return "unknown"; 34 | } else { 35 | try { 36 | String s = (String) databaseField.get(conn); 37 | return s != null && !s.isEmpty() ? s : "unknown"; 38 | } catch (Throwable throwable) { 39 | log.error("cannot get JDBC4Connection database field"); 40 | return "unknown"; 41 | } 42 | } 43 | } 44 | 45 | public String getHostPort(Connection conn) { 46 | if (conn instanceof ConnectionImpl) { 47 | ConnectionImpl mysqlConn = (ConnectionImpl) conn; 48 | return mysqlConn.getHostPortPair() + ":" + mysqlConn.getId(); 49 | } else { 50 | return "0.0.0.0:0:0"; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/WebControllerConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.web.TraceIdInterceptor; 4 | import fun.pxyc.onelogger.web.WebDynamicPointcut; 5 | import fun.pxyc.onelogger.web.WebLogInterceptor; 6 | import javax.servlet.http.HttpServletRequest; 7 | import org.springframework.aop.Pointcut; 8 | import org.springframework.aop.support.DefaultPointcutAdvisor; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.web.servlet.HandlerInterceptor; 13 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 14 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 15 | 16 | @Configuration 17 | @ConditionalOnClass({HttpServletRequest.class, HandlerInterceptor.class}) 18 | public class WebControllerConfiguration implements WebMvcConfigurer { 19 | 20 | @Bean 21 | public Pointcut customPointCut(EnvConfigProps envConfigProps) { 22 | WebDynamicPointcut webDynamicPointcut = new WebDynamicPointcut(); 23 | webDynamicPointcut.setExpression(envConfigProps.getWebCutPoint()); 24 | return webDynamicPointcut; 25 | } 26 | 27 | @Bean 28 | public WebLogInterceptor getAdvice() { 29 | return new WebLogInterceptor(); 30 | } 31 | 32 | @Bean 33 | public DefaultPointcutAdvisor defaultPointcutAdvisor(EnvConfigProps envConfigProps) { 34 | DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor(); 35 | defaultPointcutAdvisor.setPointcut(customPointCut(envConfigProps)); 36 | defaultPointcutAdvisor.setAdvice(getAdvice()); 37 | return defaultPointcutAdvisor; 38 | } 39 | 40 | @Bean 41 | public TraceIdInterceptor traceIdInterceptor() { 42 | return new TraceIdInterceptor(); 43 | } 44 | 45 | @Override 46 | public void addInterceptors(InterceptorRegistry registry) { 47 | registry.addInterceptor(traceIdInterceptor()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/Action.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import org.aspectj.lang.ProceedingJoinPoint; 4 | 5 | public class Action { 6 | String logType; 7 | 8 | String interfaceName; 9 | 10 | String method; 11 | 12 | String action; 13 | 14 | public Action(String logType, String interfaceName, String method, String action) { 15 | this.logType = logType; 16 | this.interfaceName = interfaceName; 17 | this.method = method; 18 | this.action = action; 19 | } 20 | 21 | public Action(String logType, String action) { 22 | this.logType = logType; 23 | this.interfaceName = ""; 24 | this.method = ""; 25 | this.action = action; 26 | } 27 | 28 | public Action(String logType, String interfaceName, String method) { 29 | this.logType = logType; 30 | this.interfaceName = interfaceName; 31 | this.method = method; 32 | this.action = logType + ":" + interfaceName + "." + method; 33 | } 34 | 35 | public Action(String logType, ProceedingJoinPoint joinPoint) { 36 | this.logType = logType; 37 | this.interfaceName = joinPoint.getTarget().getClass().getSimpleName(); 38 | this.method = joinPoint.getSignature().getName(); 39 | this.action = logType + ":" + interfaceName + "." + method; 40 | } 41 | 42 | public String getLogType() { 43 | return this.logType; 44 | } 45 | 46 | public void setLogType(String logType) { 47 | this.logType = logType; 48 | } 49 | 50 | public String getInterfaceName() { 51 | return this.interfaceName; 52 | } 53 | 54 | public void setInterfaceName(String interfaceName) { 55 | this.interfaceName = interfaceName; 56 | } 57 | 58 | public String getMethod() { 59 | return this.method; 60 | } 61 | 62 | public void setMethod(String method) { 63 | this.method = method; 64 | } 65 | 66 | public String getAction() { 67 | return this.action; 68 | } 69 | 70 | public void setAction(String action) { 71 | this.action = action; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/http/HttpClientProperties.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.http; 2 | 3 | public class HttpClientProperties { 4 | private int connectTimeout = 3000; 5 | private int readTimeout = 3000; 6 | private boolean usePool = false; 7 | private int maxTotalConnect = 128; 8 | private int maxConnectPerRoute = 32; 9 | private int errorCountToAlarm = 5; 10 | private boolean logAllHeaders = false; 11 | private String logHeaderNames = ""; 12 | 13 | public HttpClientProperties() {} 14 | 15 | public int getConnectTimeout() { 16 | return this.connectTimeout; 17 | } 18 | 19 | public void setConnectTimeout(int connectTimeout) { 20 | this.connectTimeout = connectTimeout; 21 | } 22 | 23 | public int getReadTimeout() { 24 | return this.readTimeout; 25 | } 26 | 27 | public void setReadTimeout(int readTimeout) { 28 | this.readTimeout = readTimeout; 29 | } 30 | 31 | public int getMaxTotalConnect() { 32 | return this.maxTotalConnect; 33 | } 34 | 35 | public void setMaxTotalConnect(int maxTotalConnect) { 36 | this.maxTotalConnect = maxTotalConnect; 37 | } 38 | 39 | public int getMaxConnectPerRoute() { 40 | return this.maxConnectPerRoute; 41 | } 42 | 43 | public void setMaxConnectPerRoute(int maxConnectPerRoute) { 44 | this.maxConnectPerRoute = maxConnectPerRoute; 45 | } 46 | 47 | public int getErrorCountToAlarm() { 48 | return this.errorCountToAlarm; 49 | } 50 | 51 | public void setErrorCountToAlarm(int errorCountToAlarm) { 52 | this.errorCountToAlarm = errorCountToAlarm; 53 | } 54 | 55 | public boolean isLogAllHeaders() { 56 | return this.logAllHeaders; 57 | } 58 | 59 | public void setLogAllHeaders(boolean logAllHeaders) { 60 | this.logAllHeaders = logAllHeaders; 61 | } 62 | 63 | public String getLogHeaderNames() { 64 | return this.logHeaderNames; 65 | } 66 | 67 | public void setLogHeaderNames(String logHeaderNames) { 68 | this.logHeaderNames = logHeaderNames; 69 | } 70 | 71 | public boolean isUsePool() { 72 | return this.usePool; 73 | } 74 | 75 | public void setUsePool(boolean usePool) { 76 | this.usePool = usePool; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/auditlog/AsyncAuditLogPool.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.auditlog; 2 | 3 | import fun.pxyc.onelogger.NamedThreadFactory; 4 | import java.util.concurrent.*; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | public class AsyncAuditLogPool { 9 | private static final Logger log = LoggerFactory.getLogger(AsyncAuditLogPool.class); 10 | private static AsyncAuditLogPool instance; 11 | private final int queueSize = 1000; 12 | private ThreadPoolExecutor asycAuditLogPool; 13 | 14 | public AsyncAuditLogPool() {} 15 | 16 | public static void asyncLog(Logger logger, String s) { 17 | if (instance != null) { 18 | instance.write(logger, s); 19 | } 20 | } 21 | 22 | public static void asyncLog(Logger logger, String s, Object... objects) { 23 | if (instance != null) { 24 | instance.write(logger, s, objects); 25 | } 26 | } 27 | 28 | public void init() { 29 | ThreadFactory asycAuditLogThreadFactory = new NamedThreadFactory("sq_envconfig_auditlog"); 30 | this.asycAuditLogPool = new ThreadPoolExecutor( 31 | 1, 1, 0L, TimeUnit.SECONDS, new ArrayBlockingQueue(this.queueSize), asycAuditLogThreadFactory); 32 | this.asycAuditLogPool.prestartAllCoreThreads(); 33 | log.info("asyncAuditLogPool inited"); 34 | instance = this; 35 | } 36 | 37 | public void close() { 38 | this.asycAuditLogPool.shutdown(); 39 | log.info("asyncAuditLogPool closed"); 40 | this.asycAuditLogPool = null; 41 | instance = null; 42 | } 43 | 44 | public void write(final Logger logger, final String s) { 45 | if (this.asycAuditLogPool != null) { 46 | try { 47 | this.asycAuditLogPool.execute(new Runnable() { 48 | public void run() { 49 | logger.info(s); 50 | } 51 | }); 52 | } catch (RejectedExecutionException e) { 53 | log.error("async audit asyncLog queue is full"); 54 | } 55 | } 56 | } 57 | 58 | public void write(final Logger logger, final String s, Object... objects) { 59 | if (this.asycAuditLogPool != null) { 60 | try { 61 | this.asycAuditLogPool.execute(new Runnable() { 62 | public void run() { 63 | logger.info(s, objects); 64 | } 65 | }); 66 | } catch (RejectedExecutionException e) { 67 | log.error("async audit asyncLog queue is full"); 68 | } 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/JdbcUrlUtil.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | public class JdbcUrlUtil { 6 | 7 | public static String findConnIdByUrl(String jdbcUrl) { 8 | int pos, pos1, pos2; 9 | String connUri; 10 | String host = "", port = "", database = ""; 11 | if (jdbcUrl == null || !jdbcUrl.startsWith("jdbc:") || (pos1 = jdbcUrl.indexOf(':', 5)) == -1) 12 | throw new IllegalArgumentException("Invalid JDBC url."); 13 | 14 | if ((pos2 = jdbcUrl.indexOf(';', pos1)) == -1) { 15 | connUri = jdbcUrl.substring(pos1 + 1); 16 | } else { 17 | connUri = jdbcUrl.substring(pos1 + 1, pos2); 18 | } 19 | 20 | if (connUri.startsWith("//")) { 21 | if ((pos = connUri.indexOf('/', 2)) != -1) { 22 | host = connUri.substring(2, pos); 23 | database = connUri.substring(pos + 1); 24 | 25 | if ((pos = host.indexOf(':')) != -1) { 26 | port = host.substring(pos + 1); 27 | host = host.substring(0, pos); 28 | } 29 | } 30 | } else { 31 | database = connUri; 32 | } 33 | return "hosts:" + host + "^_^port:" + port + "^_^database:" + database; 34 | } 35 | 36 | public static String findDataBaseNameByUrl(String jdbcUrl) { 37 | String database = null; 38 | int pos, pos1; 39 | String connUri; 40 | 41 | if (StringUtils.isBlank(jdbcUrl)) { 42 | return ""; 43 | } 44 | 45 | jdbcUrl = jdbcUrl.toLowerCase(); 46 | 47 | if (jdbcUrl.startsWith("jdbc:impala")) { 48 | jdbcUrl = jdbcUrl.replace(":impala", ""); 49 | } 50 | 51 | if (!jdbcUrl.startsWith("jdbc:") || (pos1 = jdbcUrl.indexOf(':', 5)) == -1) { 52 | throw new IllegalArgumentException("Invalid JDBC url."); 53 | } 54 | 55 | connUri = jdbcUrl.substring(pos1 + 1); 56 | 57 | if (connUri.startsWith("//")) { 58 | if ((pos = connUri.indexOf('/', 2)) != -1) { 59 | database = connUri.substring(pos + 1); 60 | } 61 | } else { 62 | database = connUri; 63 | } 64 | 65 | if (StringUtils.isBlank(database)) { 66 | return ""; 67 | } 68 | 69 | if (database.contains("?")) { 70 | database = database.substring(0, database.indexOf("?")); 71 | } 72 | 73 | if (database.contains(";")) { 74 | database = database.substring(0, database.indexOf(";")); 75 | } 76 | return database; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/Spis.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import fun.pxyc.onelogger.utils.ReflectionUtils; 4 | import java.util.*; 5 | 6 | public class Spis { 7 | 8 | public static HashMap> plugins = new HashMap<>(); 9 | 10 | public static List getPlugins(Class cls) { 11 | List pluginInfos = plugins.get(cls.getName()); 12 | List ps = new ArrayList<>(); 13 | for (PluginInfo pi : pluginInfos) { 14 | if (pi.bean != null) ps.add((T) pi.bean); 15 | pi.bean = ReflectionUtils.newObject(pi.impCls.getName()); 16 | ps.add((T) pi.bean); 17 | } 18 | return ps; 19 | } 20 | 21 | public static T getPlugin(Class cls, String params) { 22 | 23 | List list = plugins.get(cls.getName()); 24 | if (list == null) return null; 25 | String type = parseType(params); 26 | for (PluginInfo pi : list) { 27 | if (pi.matchNames.contains(type)) { 28 | if (pi.bean != null) return (T) pi.bean; 29 | pi.bean = ReflectionUtils.newObject(pi.impCls.getName()); 30 | String t = parseParams(params); 31 | return (T) pi.bean; 32 | } 33 | } 34 | return null; 35 | } 36 | 37 | public static String parseParams(String s) { 38 | int p = s.indexOf(":"); 39 | if (p >= 0) return s.substring(p + 1); 40 | return ""; 41 | } 42 | 43 | public static String parseType(String s) { 44 | s = s.toLowerCase(); 45 | int p = s.indexOf(":"); 46 | if (p >= 0) return s.substring(0, p); 47 | return s; 48 | } 49 | 50 | public static class PluginInfo { 51 | Class cls; 52 | Class impCls; 53 | Object bean; 54 | Set matchNames = new HashSet<>(); 55 | 56 | public PluginInfo(Class cls, Class impCls) { 57 | this(cls, impCls, null, null); 58 | } 59 | 60 | public PluginInfo(Class cls, Class impCls, Object bean, String beanName) { 61 | this.cls = cls; 62 | this.impCls = impCls; 63 | this.bean = bean; 64 | 65 | String suffix = cls.getSimpleName().toLowerCase(); 66 | 67 | if (beanName != null) matchNames.add(beanName); 68 | matchNames.add(impCls.getName().toLowerCase()); 69 | matchNames.add(impCls.getSimpleName().toLowerCase()); 70 | matchNames.add(removeSuffix(impCls.getSimpleName().toLowerCase(), suffix)); 71 | } 72 | 73 | String removeSuffix(String s, String suffix) { 74 | if (s.endsWith(suffix)) { 75 | return s.substring(0, s.length() - suffix.length()); 76 | } 77 | return s; 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/TypeSafeMap.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import java.util.Map; 4 | import java.util.Set; 5 | 6 | public class TypeSafeMap { 7 | private final Map body; 8 | 9 | public TypeSafeMap(Map body) { 10 | this.body = body; 11 | } 12 | 13 | public Map get() { 14 | return this.body; 15 | } 16 | 17 | public boolean contains(String name) { 18 | return this.body.containsKey(name); 19 | } 20 | 21 | public void remove(String name) { 22 | this.body.remove(name); 23 | } 24 | 25 | public Set keySet() { 26 | return this.body.keySet(); 27 | } 28 | 29 | public String stringValue(String name) { 30 | Object v = this.body.get(name); 31 | return TypeSafe.anyToString(v); 32 | } 33 | 34 | public String stringValue(String name, String defaultValue) { 35 | String v = this.stringValue(name); 36 | return v == null ? defaultValue : v; 37 | } 38 | 39 | public String notNullString(String name) { 40 | String v = this.stringValue(name); 41 | return v == null ? "" : v; 42 | } 43 | 44 | public String notNullString(String name, String defaultValue) { 45 | String v = this.stringValue(name); 46 | return v != null && !v.equals("") ? v : defaultValue; 47 | } 48 | 49 | public int intValue(String name) { 50 | Object v = this.body.get(name); 51 | return TypeSafe.anyToInt(v); 52 | } 53 | 54 | public int intValue(String name, int defaultValue) { 55 | Object v = this.body.get(name); 56 | return TypeSafe.anyToInt(v, defaultValue); 57 | } 58 | 59 | public long longValue(String name) { 60 | Object v = this.body.get(name); 61 | return TypeSafe.anyToLong(v); 62 | } 63 | 64 | public long longValue(String name, long defaultValue) { 65 | Object v = this.body.get(name); 66 | return TypeSafe.anyToLong(v, defaultValue); 67 | } 68 | 69 | public double doubleValue(String name) { 70 | Object v = this.body.get(name); 71 | return TypeSafe.anyToDouble(v); 72 | } 73 | 74 | public double doubleValue(String name, double defaultValue) { 75 | Object v = this.body.get(name); 76 | return TypeSafe.anyToDouble(v, defaultValue); 77 | } 78 | 79 | public boolean boolValue(String name) { 80 | Object v = this.body.get(name); 81 | return TypeSafe.anyToBool(v); 82 | } 83 | 84 | public boolean boolValue(String name, boolean defaultValue) { 85 | Object v = this.body.get(name); 86 | return TypeSafe.anyToBool(v, defaultValue); 87 | } 88 | 89 | public Map mapValue(String name) { 90 | Object v = this.body.get(name); 91 | return TypeSafe.anyToMap(v); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/RedisAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.redis.RedisAuditLogAspect; 4 | import io.lettuce.core.SocketOptions; 5 | import io.lettuce.core.cluster.ClusterClientOptions; 6 | import io.lettuce.core.cluster.ClusterTopologyRefreshOptions; 7 | import java.time.Duration; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 10 | import org.springframework.boot.autoconfigure.data.redis.LettuceClientConfigurationBuilderCustomizer; 11 | import org.springframework.context.annotation.Bean; 12 | import org.springframework.context.annotation.Configuration; 13 | import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration; 14 | import org.springframework.data.redis.core.RedisTemplate; 15 | 16 | @Configuration 17 | @ConditionalOnClass({RedisTemplate.class}) 18 | public class RedisAutoConfiguration { 19 | @Value("${spring.redis.enablePeriodicRefresh:true}") 20 | boolean enablePeriodicRefresh = true; 21 | 22 | @Value("${spring.redis.refreshPeriod:15}") 23 | int refreshPeriod = 15; 24 | 25 | @Value("${spring.redis.validateClusterNodeMembership:true}") 26 | boolean validateClusterNodeMembership = true; 27 | 28 | @Value("${spring.redis.cluster.maxRedirects:5}") 29 | int maxRedirects; 30 | 31 | public RedisAutoConfiguration() {} 32 | 33 | @Bean 34 | RedisAuditLogAspect redisAuditLogAspect() { 35 | return new RedisAuditLogAspect(); 36 | } 37 | 38 | @Bean 39 | LettuceClientConfigurationBuilderCustomizer lettuceCustomizer() { 40 | return new TopologyRefreshCustomizer(); 41 | } 42 | 43 | class TopologyRefreshCustomizer implements LettuceClientConfigurationBuilderCustomizer { 44 | TopologyRefreshCustomizer() {} 45 | 46 | public void customize(LettuceClientConfiguration.LettuceClientConfigurationBuilder clientConfigurationBuilder) { 47 | ClusterTopologyRefreshOptions o1 = ClusterTopologyRefreshOptions.builder() 48 | .enablePeriodicRefresh(RedisAutoConfiguration.this.enablePeriodicRefresh) 49 | .refreshPeriod(Duration.ofSeconds(RedisAutoConfiguration.this.refreshPeriod)) 50 | .build(); 51 | SocketOptions sockOptions = SocketOptions.builder().tcpNoDelay(true).build(); 52 | ClusterClientOptions c = ClusterClientOptions.builder() 53 | .socketOptions(sockOptions) 54 | .maxRedirects(RedisAutoConfiguration.this.maxRedirects) 55 | .topologyRefreshOptions(o1) 56 | .validateClusterNodeMembership(RedisAutoConfiguration.this.validateClusterNodeMembership) 57 | .build(); 58 | clientConfigurationBuilder.clientOptions(c); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/db/DbDyeing.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.db; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Iterator; 5 | import java.util.List; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | public class DbDyeing { 11 | static ConcurrentHashMap dyingedSqls = new ConcurrentHashMap(); 12 | private static List regexArray; 13 | private static String destValue; 14 | private static List loweredRegex; 15 | 16 | public DbDyeing() {} 17 | 18 | public static String undyeingTable(String tableName) { 19 | return tableName; 20 | } 21 | 22 | public static String dyeingSql(String sql) { 23 | String newSql = dyingedSqls.get(sql); 24 | if (newSql != null) { 25 | return newSql; 26 | } else { 27 | newSql = dyeingSqlInternal(sql); 28 | dyingedSqls.put(sql, newSql); 29 | return newSql; 30 | } 31 | } 32 | 33 | public static void init(String[] prefixArray) { 34 | regexArray = new ArrayList(); 35 | loweredRegex = new ArrayList(); 36 | 37 | for (int i = 0; i < prefixArray.length; ++i) { 38 | String p = prefixArray[i]; 39 | regexArray.add(genRegex(p.toLowerCase())); 40 | regexArray.add(genRegex(p.toUpperCase())); 41 | loweredRegex.add(Pattern.compile(genFindRegex(p.toLowerCase()), 32)); 42 | } 43 | 44 | destValue = "$1$2_$3"; 45 | } 46 | 47 | static String genRegex(String prefix) { 48 | return "([ \\.`])(" + prefix + "[a-zA-Z0-9_]+)([ \\.]*)"; 49 | } 50 | 51 | static String genFindRegex(String prefix) { 52 | return ".*([ \\.`])(" + prefix + "[a-zA-Z0-9_]+).*"; 53 | } 54 | 55 | public static String dyeingSqlInternal(String sql) { 56 | if (regexArray == null) { 57 | return sql; 58 | } else { 59 | String p; 60 | for (Iterator iterator = regexArray.iterator(); 61 | iterator.hasNext(); 62 | sql = sql.replaceAll(p, destValue)) { 63 | p = iterator.next(); 64 | } 65 | 66 | return sql; 67 | } 68 | } 69 | 70 | public static String findFirstTable(String sql0) { 71 | String sql = sql0.toLowerCase(); 72 | if (loweredRegex == null) { 73 | return "unknown_table"; 74 | } else { 75 | Iterator iterator = loweredRegex.iterator(); 76 | Matcher m; 77 | do { 78 | if (!iterator.hasNext()) { 79 | return "unknown_table"; 80 | } 81 | Pattern p = iterator.next(); 82 | m = p.matcher(sql); 83 | } while (!m.matches()); 84 | 85 | return m.group(2); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/MetaData.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | public class MetaData { 4 | TraceData trace; 5 | private Integer direction = 0; 6 | private Integer serviceId = 0; 7 | private Integer msgId = 0; 8 | private Integer sequence = 0; 9 | private Integer timeout = 0; 10 | private Integer retCode = 0; 11 | private String attachment = ""; 12 | private Integer compress = 0; 13 | private Integer encrypt = 0; 14 | 15 | public MetaData() {} 16 | 17 | public MetaData( 18 | Integer direction, 19 | Integer serviceId, 20 | Integer msgId, 21 | Integer sequence, 22 | Integer timeout, 23 | Integer retCode, 24 | String attachment, 25 | Integer compress, 26 | Integer encrypt) { 27 | this.direction = direction; 28 | this.serviceId = serviceId; 29 | this.msgId = msgId; 30 | this.sequence = sequence; 31 | this.timeout = timeout; 32 | this.retCode = retCode; 33 | this.attachment = attachment; 34 | this.compress = compress; 35 | this.encrypt = encrypt; 36 | } 37 | 38 | public Integer getDirection() { 39 | return direction; 40 | } 41 | 42 | public void setDirection(Integer direction) { 43 | this.direction = direction; 44 | } 45 | 46 | public Integer getServiceId() { 47 | return serviceId; 48 | } 49 | 50 | public void setServiceId(Integer serviceId) { 51 | this.serviceId = serviceId; 52 | } 53 | 54 | public Integer getMsgId() { 55 | return msgId; 56 | } 57 | 58 | public void setMsgId(Integer msgId) { 59 | this.msgId = msgId; 60 | } 61 | 62 | public Integer getSequence() { 63 | return sequence; 64 | } 65 | 66 | public void setSequence(Integer sequence) { 67 | this.sequence = sequence; 68 | } 69 | 70 | public Integer getTimeout() { 71 | return timeout; 72 | } 73 | 74 | public void setTimeout(Integer timeout) { 75 | this.timeout = timeout; 76 | } 77 | 78 | public Integer getRetCode() { 79 | return retCode; 80 | } 81 | 82 | public void setRetCode(Integer retCode) { 83 | this.retCode = retCode; 84 | } 85 | 86 | public String getAttachment() { 87 | return attachment; 88 | } 89 | 90 | public void setAttachment(String attachment) { 91 | this.attachment = attachment; 92 | } 93 | 94 | public Integer getCompress() { 95 | return compress; 96 | } 97 | 98 | public void setCompress(Integer compress) { 99 | this.compress = compress; 100 | } 101 | 102 | public Integer getEncrypt() { 103 | return encrypt; 104 | } 105 | 106 | public void setEncrypt(Integer encrypt) { 107 | this.encrypt = encrypt; 108 | } 109 | 110 | public TraceData getTrace() { 111 | return trace; 112 | } 113 | 114 | public void setTrace(TraceData trace) { 115 | this.trace = trace; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/ContextData.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | public abstract class ContextData { 7 | public long requestTimeMicros; 8 | public long startMicros; 9 | public long timeUsedMicros; 10 | protected String connId; 11 | MetaData meta; 12 | Map attributes; 13 | int retCode; 14 | 15 | public ContextData(String connId, MetaData meta) { 16 | this.connId = connId; 17 | this.meta = meta; 18 | } 19 | 20 | public long elapsedMillisByNow() { 21 | return (System.nanoTime() / 1000L - this.startMicros) / 1000L; 22 | } 23 | 24 | public void end() { 25 | this.timeUsedMicros = System.nanoTime() / 1000L - this.startMicros; 26 | } 27 | 28 | public long getResponseTimeMicros() { 29 | return this.requestTimeMicros + this.timeUsedMicros; 30 | } 31 | 32 | public long getTimeUsedMicros() { 33 | return this.timeUsedMicros; 34 | } 35 | 36 | public void endWithTime(long timeUsedMicros) { 37 | this.timeUsedMicros = timeUsedMicros; 38 | } 39 | 40 | public long getTimeUsedMillis() { 41 | return this.timeUsedMicros / 1000L; 42 | } 43 | 44 | public String getClientIp() { 45 | String peers = this.meta.getTrace().getPeers(); 46 | if (peers.isEmpty()) { 47 | return this.getRemoteIp(); 48 | } else { 49 | String s = peers.split(",")[0]; 50 | int p = s.indexOf(":"); 51 | return p >= 0 ? s.substring(0, p) : s; 52 | } 53 | } 54 | 55 | public String getRemoteIp() { 56 | String remoteAddr = this.getRemoteAddr(); 57 | int p = remoteAddr.lastIndexOf(":"); 58 | return remoteAddr.substring(0, p); 59 | } 60 | 61 | public String getRemoteAddr() { 62 | int p = this.connId.lastIndexOf(":"); 63 | return this.connId.substring(0, p); 64 | } 65 | 66 | public MetaData getMeta() { 67 | return this.meta; 68 | } 69 | 70 | public void setMeta(MetaData meta) { 71 | this.meta = meta; 72 | } 73 | 74 | public String getConnId() { 75 | return this.connId; 76 | } 77 | 78 | public void setConnId(String connId) { 79 | this.connId = connId; 80 | } 81 | 82 | public long getRequestTimeMicros() { 83 | return this.requestTimeMicros; 84 | } 85 | 86 | public long getStartMicros() { 87 | return this.startMicros; 88 | } 89 | 90 | public Map getAttributes() { 91 | return this.attributes; 92 | } 93 | 94 | public void setAttributes(Map attributes) { 95 | if (this.attributes == null) { 96 | this.attributes = new HashMap(); 97 | } 98 | 99 | this.attributes = attributes; 100 | } 101 | 102 | public void setAttribute(String key, Object obj) { 103 | if (this.attributes == null) { 104 | this.attributes = new HashMap(); 105 | } 106 | 107 | this.attributes.put(key, obj); 108 | } 109 | 110 | public Object getAttribute(String key) { 111 | return this.attributes == null ? null : this.attributes.get(key); 112 | } 113 | 114 | public void removeAttribute(String key) { 115 | if (this.attributes != null) { 116 | this.attributes.remove(key); 117 | } 118 | } 119 | 120 | public int getRetCode() { 121 | return this.retCode; 122 | } 123 | 124 | public void setRetCode(int retCode) { 125 | this.retCode = retCode; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/redis/RedisLogFormat.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.redis; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.TraceContext; 9 | import fun.pxyc.onelogger.trace.TraceData; 10 | import java.util.LinkedHashMap; 11 | import java.util.Map; 12 | import org.slf4j.Logger; 13 | import org.slf4j.LoggerFactory; 14 | 15 | public class RedisLogFormat extends LoggerFormatter { 16 | private static final Logger log = LoggerFactory.getLogger(RedisLogFormat.class); 17 | 18 | public RedisLogFormat() {} 19 | 20 | public static void log( 21 | Object res, Span span, String methodName, TraceContext ctx, Throwable throwable, Object... args) { 22 | try { 23 | logeInternal(res, span, methodName, ctx, throwable, args); 24 | } catch (Throwable t) { 25 | log.error("logeInternal exception", t); 26 | } 27 | } 28 | 29 | private static void logeInternal( 30 | Object res, Span span, String methodName, TraceContext ctx, Throwable throwable, Object... args) { 31 | TraceData trace = ctx.getTrace(); 32 | long duration = span.getTimeUsedMicros(); 33 | String timeUsedMs = span.getTimeUsedMs(); 34 | String traceId = trace.getTraceId(); 35 | if (duration >= EnvConfigProps.slowRedisMillis * 1000L) { 36 | log.warn("slow redis: ts=" + duration + ", traceId=" + traceId); 37 | } 38 | String timestamp = AsyncAuditLog.now(ctx, span); 39 | int sequence = RedisConfig.seq.incrementAndGet(); 40 | if (sequence >= 10000000) { 41 | RedisConfig.seq.compareAndSet(sequence, 0); 42 | } 43 | String serviceNameMsgName = RedisConfig.LOG_TYPE + methodName; 44 | Log( 45 | AsyncAuditLog.redisAuditLog, 46 | generateLogMap( 47 | timestamp, 48 | traceId, 49 | span.getSpanId(), 50 | span.getRootSpan().getAction(), 51 | genConnId(), 52 | new Action("REDIS", serviceNameMsgName), 53 | sequence, 54 | timeUsedMs, 55 | genJsonReq(args), 56 | genJsonRes(res), 57 | genExceptionStr(throwable), 58 | EnvConfigProps.redisLogMaxChars)); 59 | } 60 | 61 | static String genConnId() { 62 | return "0.0.0.0:0:0"; 63 | } 64 | 65 | static Map genJsonReq(Object... args) { 66 | Map req = new LinkedHashMap<>(); 67 | StringBuilder builder = new StringBuilder(); 68 | Object[] tmpArgs = args; 69 | 70 | for (int i = 0; i < args.length; ++i) { 71 | Object o = tmpArgs[i]; 72 | String s = AsyncAuditLog.toText(o); 73 | if (builder.length() > 0) { 74 | builder.append("|"); 75 | } 76 | builder.append(s); 77 | } 78 | String s = builder.toString(); 79 | if (s.length() > 10000) { 80 | s = s.substring(0, 10000) + "...."; 81 | } 82 | req.put("params", s); 83 | return req; 84 | } 85 | 86 | static Map genJsonRes(Object res) { 87 | Map result = new LinkedHashMap<>(); 88 | if (res != null) { 89 | result.put("result", res); 90 | } 91 | return result; 92 | } 93 | 94 | static String genExceptionStr(Throwable throwable) { 95 | if (throwable != null) { 96 | return AsyncAuditLog.escapeText(throwable.getMessage()); 97 | } 98 | return ""; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/bizlog/BizServiceInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.bizlog; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.Trace; 9 | import fun.pxyc.onelogger.trace.TraceContext; 10 | import fun.pxyc.onelogger.trace.TraceData; 11 | import java.util.concurrent.ConcurrentHashMap; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | import org.aopalliance.intercept.MethodInterceptor; 14 | import org.aopalliance.intercept.MethodInvocation; 15 | import org.slf4j.Logger; 16 | import org.slf4j.LoggerFactory; 17 | 18 | public class BizServiceInterceptor extends LoggerFormatter implements MethodInterceptor { 19 | 20 | private static final Logger log = LoggerFactory.getLogger(BizServiceInterceptor.class); 21 | 22 | private final ConcurrentHashMap seqMap = new ConcurrentHashMap<>(); 23 | 24 | @Override 25 | public Object invoke(MethodInvocation methodInvocation) throws Throwable { 26 | 27 | TraceContext ctx = Trace.currentContext(); 28 | if (ctx == null) { 29 | return methodInvocation.proceed(); 30 | } 31 | String methodName = methodInvocation.getMethod().getName(); 32 | String className = methodInvocation.getMethod().getDeclaringClass().getName(); 33 | Span span = Trace.startAsync("BIZ", className.toLowerCase() + "." + methodName.toLowerCase()); 34 | try { 35 | Object o = methodInvocation.proceed(); 36 | span.stop(true); 37 | this.log(span, ctx, methodInvocation, o, null); 38 | return o; 39 | } catch (Throwable t) { 40 | span.stop(false); 41 | this.log(span, ctx, methodInvocation, null, t); 42 | throw t; 43 | } 44 | } 45 | 46 | private void log(Span span, TraceContext ctx, MethodInvocation methodInvocation, Object o, Throwable throwable) { 47 | try { 48 | this.logInternal(span, ctx, methodInvocation, o, throwable); 49 | } catch (Throwable t) { 50 | log.error("logInternal exception", t); 51 | log.error(t.getMessage(), t); 52 | } 53 | } 54 | 55 | private void logInternal( 56 | Span span, TraceContext ctx, MethodInvocation joinPoint, Object retObj, Throwable throwable) { 57 | String methodName = joinPoint.getMethod().getName(); 58 | String className = joinPoint.getMethod().getDeclaringClass().getName(); 59 | TraceData trace = ctx.getTrace(); 60 | String costTime = span.getTimeUsedMs(); 61 | String traceId = trace.getTraceId(); 62 | String timestamp = AsyncAuditLog.now(ctx, span); 63 | 64 | String spanId = span.getSpanId(); 65 | String serviceNameMsgName = "BIZ." + className + "." + methodName; 66 | AtomicInteger atomicInteger = seqMap.get(serviceNameMsgName); 67 | if (atomicInteger == null) { 68 | atomicInteger = new AtomicInteger(0); 69 | seqMap.put(serviceNameMsgName, atomicInteger); 70 | } 71 | int sequence = atomicInteger.incrementAndGet(); 72 | if (sequence >= 10000000) { 73 | atomicInteger.compareAndSet(sequence, 0); 74 | } 75 | 76 | Log( 77 | AsyncAuditLog.bizProviderAuditLog, 78 | generateLogMap( 79 | timestamp, 80 | traceId, 81 | spanId, 82 | span.getRootSpan().getAction(), 83 | getConnId(), 84 | new Action("BIZ", className, methodName, serviceNameMsgName), 85 | sequence, 86 | costTime, 87 | getJsonReq(joinPoint), 88 | getJsonRes(retObj), 89 | genException(throwable), 90 | EnvConfigProps.bizLogMaxChars)); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/config/EnvConfig.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.config; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.Logger; 5 | import fun.pxyc.onelogger.autoconfig.GlobalConfig; 6 | import fun.pxyc.onelogger.utils.YamlParser; 7 | import java.io.InputStream; 8 | import java.util.Iterator; 9 | import java.util.List; 10 | import java.util.Properties; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.boot.SpringBootVersion; 13 | 14 | public class EnvConfig { 15 | 16 | public EnvConfig() {} 17 | 18 | public static void initEnv() { 19 | try { 20 | initEnvInternal(); 21 | } catch (Exception e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | public static void removeAllStdoutAppenders() { 27 | if (!GlobalConfig.globalProfile.equals("local") && !GlobalConfig.globalProfile.equals("dev")) { 28 | Logger rootLog = (Logger) LoggerFactory.getLogger("console"); 29 | if (GlobalConfig.globalProfile.equals("prd")) { 30 | if (rootLog != null 31 | && rootLog.getLevel() != null 32 | && rootLog.getLevel().equals(Level.DEBUG)) { 33 | rootLog.setLevel(Level.INFO); 34 | } 35 | 36 | Logger log3 = (Logger) LoggerFactory.getLogger("com.sqq"); 37 | if (log3 != null && log3.getLevel() != null && log3.getLevel().equals(Level.DEBUG)) { 38 | log3.setLevel(Level.INFO); 39 | } 40 | } 41 | 42 | if (rootLog != null) { 43 | List list = rootLog.getLoggerContext().getLoggerList(); 44 | if (list != null) { 45 | Iterator iterator = list.iterator(); 46 | 47 | while (iterator.hasNext()) { 48 | Logger l = (Logger) iterator.next(); 49 | l.detachAppender("STDOUT"); 50 | } 51 | } 52 | } 53 | } 54 | } 55 | 56 | private static void initEnvInternal() { 57 | org.slf4j.Logger log = LoggerFactory.getLogger(EnvConfig.class); 58 | log.info("spring boot version: " + SpringBootVersion.getVersion()); 59 | log.info("envconfig version 0.1.1 build @ 20240424"); 60 | } 61 | 62 | private static String convertEnv(String profile) { 63 | return profile.toLowerCase(); 64 | } 65 | 66 | private static Properties loadProfileProperties(String profile) throws Exception { 67 | String file = "application-" + profile + ".properties"; 68 | if (profile == null || profile.isEmpty()) { 69 | file = "application.properties"; 70 | } 71 | Properties p = new Properties(); 72 | InputStream in = null; 73 | try { 74 | in = EnvConfig.class.getClassLoader().getResourceAsStream(file); 75 | } catch (Exception e) { 76 | } 77 | if (in != null) { 78 | p.load(in); 79 | in.close(); 80 | } 81 | Properties p1 = loadYamlFile(profile); 82 | p.putAll(p1); 83 | return p; 84 | } 85 | 86 | private static Properties loadYamlFile(String profile) throws Exception { 87 | String file = "application-" + profile + ".yaml"; 88 | if (profile == null || profile.isEmpty()) { 89 | file = "application.yaml"; 90 | } 91 | InputStream in = null; 92 | try { 93 | in = EnvConfig.class.getClassLoader().getResourceAsStream(file); 94 | } catch (Exception exception) { 95 | 96 | } 97 | Properties p = new Properties(); 98 | if (in != null) { 99 | p.putAll(YamlParser.yamlToFlattenedMap(in)); 100 | in.close(); 101 | } 102 | file = file.replaceAll("yaml", "yml"); 103 | try { 104 | InputStream in1 = EnvConfig.class.getClassLoader().getResourceAsStream(file); 105 | if (in1 != null) { 106 | p.putAll(YamlParser.yamlToFlattenedMap(in1)); 107 | } 108 | } catch (Exception exception) { 109 | 110 | } 111 | return p; 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/trace/Trace.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.trace; 2 | 3 | import fun.pxyc.onelogger.trace.adpater.TraceAdapter; 4 | import java.util.Random; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.slf4j.MDC; 8 | 9 | public class Trace { 10 | private static final Logger log = LoggerFactory.getLogger(Trace.class); 11 | private static final ThreadLocal tlContext = new ThreadLocal(); 12 | private static final Random rand = new Random(); 13 | private static String appName = "unknown"; 14 | private static int sampleRate = 100; 15 | private static TraceAdapter adapter = null; 16 | 17 | public Trace() {} 18 | 19 | public static int getSampleFlag() { 20 | int sampleFlag = rand.nextInt(100) < getSampleRate() ? 0 : 2; 21 | return sampleFlag; 22 | } 23 | 24 | public static Span startForServer(TraceData trace, String type, String action) { 25 | TraceContext ctx = adapter.newTraceContext(trace, type.equals("RPCSERVER")); 26 | setCurrentContext(ctx); 27 | return ctx.startForServer(type, action); 28 | } 29 | 30 | public static Span start(String type, String action) { 31 | TraceContext ctx = getOrNew(); 32 | return ctx.start(type, action); 33 | } 34 | 35 | public static Span startAsync(String type, String action) { 36 | TraceContext ctx = getOrNew(); 37 | return ctx.startAsync(type, action); 38 | } 39 | 40 | public static Span appendSpan(String type, String action, long startMicros, String status, long timeUsedMicros) { 41 | TraceContext ctx = getOrNew(); 42 | return ctx.appendSpan(type, action, startMicros, status, timeUsedMicros); 43 | } 44 | 45 | private static TraceContext getOrNew() { 46 | TraceContext ctx = tlContext.get(); 47 | if (ctx != null) { 48 | return ctx; 49 | } else { 50 | ctx = adapter.newTraceContext(); 51 | setCurrentContext(ctx); 52 | MDC.put("TRACE_ID", ctx.getTrace().traceId); 53 | return ctx; 54 | } 55 | } 56 | 57 | public static void setCurrentContext(TraceContext traceContext) { 58 | tlContext.set(traceContext); 59 | } 60 | 61 | public static void tagForRpc(String key, String value) { 62 | TraceContext ctx = tlContext.get(); 63 | if (ctx != null) { 64 | ctx.tagForRpc(key, value); 65 | } 66 | } 67 | 68 | public static void tagForRpcIfAbsent(String key, String value) { 69 | if (getTagForRpc(key) == null) { 70 | tagForRpc(key, value); 71 | } 72 | } 73 | 74 | public static String getTagForRpc(String key) { 75 | TraceContext ctx = tlContext.get(); 76 | return ctx == null ? null : ctx.getTagForRpc(key); 77 | } 78 | 79 | public static TraceIds newStartTraceIds(boolean isServerSide) { 80 | return adapter.newStartTraceIds(isServerSide); 81 | } 82 | 83 | public static void inject(TraceContext ctx, Span span, TraceData traceData) { 84 | adapter.inject(ctx, span, traceData); 85 | } 86 | 87 | public static TraceContext currentContext() { 88 | return tlContext.get(); 89 | } 90 | 91 | public static void restoreContext(TraceContext traceContext) { 92 | tlContext.set(traceContext); 93 | } 94 | 95 | public static void restoreDetachedContext(TraceContext traceContext) { 96 | if (traceContext == null) { 97 | tlContext.set(null); 98 | } 99 | 100 | tlContext.set(traceContext.detach()); 101 | } 102 | 103 | public static void clearContext() { 104 | tlContext.set(null); 105 | } 106 | 107 | public static TraceAdapter getAdapter() { 108 | return adapter; 109 | } 110 | 111 | public static void setAdapter(TraceAdapter adapter) { 112 | Trace.adapter = adapter; 113 | } 114 | 115 | public static String getAppName() { 116 | return appName; 117 | } 118 | 119 | public static void setAppName(String appName) { 120 | Trace.appName = appName; 121 | } 122 | 123 | public static int getSampleRate() { 124 | return sampleRate; 125 | } 126 | 127 | public static void setSampleRate(int sampleRate) { 128 | Trace.sampleRate = sampleRate; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/RestTemplateWithPoolConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.http.HttpClientProperties; 4 | import fun.pxyc.onelogger.http.HttpsClientRequestFactory; 5 | import javax.net.ssl.SSLContext; 6 | import org.apache.http.client.HttpClient; 7 | import org.apache.http.conn.ssl.NoopHostnameVerifier; 8 | import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 9 | import org.apache.http.conn.ssl.TrustStrategy; 10 | import org.apache.http.impl.client.CloseableHttpClient; 11 | import org.apache.http.impl.client.HttpClientBuilder; 12 | import org.apache.http.impl.client.HttpClients; 13 | import org.apache.http.ssl.SSLContexts; 14 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 15 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 16 | import org.springframework.context.annotation.Bean; 17 | import org.springframework.http.client.ClientHttpRequestFactory; 18 | import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; 19 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 20 | 21 | @ConditionalOnClass({HttpClient.class}) 22 | public class RestTemplateWithPoolConfiguration { 23 | public RestTemplateWithPoolConfiguration() {} 24 | 25 | @Bean 26 | @ConditionalOnMissingBean(name = {"clientHttpRequestFactory"}) 27 | ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties httpClientProperties) { 28 | if (!httpClientProperties.isUsePool()) { 29 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 30 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 31 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 32 | return factory; 33 | } else { 34 | HttpClient httpClient = HttpClientBuilder.create() 35 | .setMaxConnTotal(httpClientProperties.getMaxTotalConnect()) 36 | .setMaxConnPerRoute(httpClientProperties.getMaxConnectPerRoute()) 37 | .build(); 38 | HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); 39 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 40 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 41 | factory.setConnectionRequestTimeout(httpClientProperties.getConnectTimeout()); 42 | return factory; 43 | } 44 | } 45 | 46 | @Bean 47 | @ConditionalOnMissingBean(name = {"httpsClientHttpRequestFactory"}) 48 | ClientHttpRequestFactory httpsClientHttpRequestFactory(HttpClientProperties httpClientProperties) { 49 | if (!httpClientProperties.isUsePool()) { 50 | HttpsClientRequestFactory factory = new HttpsClientRequestFactory(); 51 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 52 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 53 | return factory; 54 | } else { 55 | CloseableHttpClient httpClient; 56 | try { 57 | TrustStrategy acceptingTrustStrategy = (x509Certificates, authType) -> { 58 | return true; 59 | }; 60 | SSLContext sslContext = SSLContexts.custom() 61 | .loadTrustMaterial(null, acceptingTrustStrategy) 62 | .build(); 63 | SSLConnectionSocketFactory connectionSocketFactory = 64 | new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()); 65 | HttpClientBuilder httpClientBuilder = HttpClients.custom(); 66 | httpClientBuilder.setSSLSocketFactory(connectionSocketFactory); 67 | httpClient = httpClientBuilder.build(); 68 | } catch (Exception e) { 69 | throw new RuntimeException("create httpsClientHttpRequestFactory failed", e); 70 | } 71 | 72 | HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient); 73 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 74 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 75 | factory.setConnectionRequestTimeout(httpClientProperties.getConnectTimeout()); 76 | return factory; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/http/HttpsClientRequestFactory.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.http; 2 | 3 | import java.io.IOException; 4 | import java.net.HttpURLConnection; 5 | import java.net.InetAddress; 6 | import java.net.Socket; 7 | import java.security.SecureRandom; 8 | import java.security.cert.X509Certificate; 9 | import javax.net.ssl.*; 10 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 11 | 12 | public class HttpsClientRequestFactory extends SimpleClientHttpRequestFactory { 13 | public HttpsClientRequestFactory() {} 14 | 15 | protected void prepareConnection(HttpURLConnection connection, String httpMethod) { 16 | try { 17 | if (!(connection instanceof HttpsURLConnection)) { 18 | throw new RuntimeException("An instance of HttpsURLConnection is expected"); 19 | } 20 | 21 | HttpsURLConnection httpsConnection = (HttpsURLConnection) connection; 22 | TrustManager[] trustAllCerts = new TrustManager[] { 23 | new X509TrustManager() { 24 | public X509Certificate[] getAcceptedIssuers() { 25 | return null; 26 | } 27 | 28 | public void checkClientTrusted(X509Certificate[] certs, String authType) {} 29 | 30 | public void checkServerTrusted(X509Certificate[] certs, String authType) {} 31 | } 32 | }; 33 | SSLContext sslContext = SSLContext.getInstance("TLS"); 34 | sslContext.init(null, trustAllCerts, new SecureRandom()); 35 | httpsConnection.setSSLSocketFactory(new MyCustomSSLSocketFactory(sslContext.getSocketFactory())); 36 | httpsConnection.setHostnameVerifier(new HostnameVerifier() { 37 | public boolean verify(String s, SSLSession sslSession) { 38 | return true; 39 | } 40 | }); 41 | super.prepareConnection(httpsConnection, httpMethod); 42 | } catch (Exception exception) { 43 | exception.printStackTrace(); 44 | } 45 | } 46 | 47 | private static class MyCustomSSLSocketFactory extends SSLSocketFactory { 48 | private final SSLSocketFactory delegate; 49 | 50 | public MyCustomSSLSocketFactory(SSLSocketFactory delegate) { 51 | this.delegate = delegate; 52 | } 53 | 54 | public String[] getDefaultCipherSuites() { 55 | return this.delegate.getDefaultCipherSuites(); 56 | } 57 | 58 | public String[] getSupportedCipherSuites() { 59 | return this.delegate.getSupportedCipherSuites(); 60 | } 61 | 62 | public Socket createSocket(Socket socket, String host, int port, boolean autoClose) throws IOException { 63 | Socket underlyingSocket = this.delegate.createSocket(socket, host, port, autoClose); 64 | return this.overrideProtocol(underlyingSocket); 65 | } 66 | 67 | public Socket createSocket(String host, int port) throws IOException { 68 | Socket underlyingSocket = this.delegate.createSocket(host, port); 69 | return this.overrideProtocol(underlyingSocket); 70 | } 71 | 72 | public Socket createSocket(String host, int port, InetAddress localAddress, int localPort) throws IOException { 73 | Socket underlyingSocket = this.delegate.createSocket(host, port, localAddress, localPort); 74 | return this.overrideProtocol(underlyingSocket); 75 | } 76 | 77 | public Socket createSocket(InetAddress host, int port) throws IOException { 78 | Socket underlyingSocket = this.delegate.createSocket(host, port); 79 | return this.overrideProtocol(underlyingSocket); 80 | } 81 | 82 | public Socket createSocket(InetAddress host, int port, InetAddress localAddress, int localPort) 83 | throws IOException { 84 | Socket underlyingSocket = this.delegate.createSocket(host, port, localAddress, localPort); 85 | return this.overrideProtocol(underlyingSocket); 86 | } 87 | 88 | private Socket overrideProtocol(Socket socket) { 89 | if (!(socket instanceof SSLSocket)) { 90 | throw new RuntimeException("An instance of SSLSocket is expected"); 91 | } else { 92 | ((SSLSocket) socket).setEnabledProtocols(new String[] {"TLSv1"}); 93 | return socket; 94 | } 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/kafka/KafkaInstrument.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.kafka; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Map; 5 | import java.util.concurrent.ConcurrentHashMap; 6 | import javassist.ClassPool; 7 | import javassist.LoaderClassPath; 8 | import org.apache.kafka.clients.consumer.Consumer; 9 | import org.apache.kafka.clients.consumer.KafkaConsumer; 10 | import org.apache.kafka.clients.consumer.internals.AbstractCoordinator; 11 | import org.apache.kafka.clients.consumer.internals.ConsumerCoordinator; 12 | import org.apache.kafka.common.Node; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.kafka.core.DefaultKafkaProducerFactory; 16 | import org.springframework.kafka.core.KafkaTemplate; 17 | 18 | public class KafkaInstrument { 19 | private static final Logger log = LoggerFactory.getLogger(KafkaInstrument.class); 20 | static Field coordinatorField; 21 | static Field nodeField; 22 | private static Field producerFactoryField; 23 | private static final ConcurrentHashMap addrsMap = new ConcurrentHashMap(); 24 | private static ClassPool pool; 25 | 26 | static { 27 | initFields4Listener(); 28 | initFields4Producer(); 29 | initPool(); 30 | } 31 | 32 | public KafkaInstrument() {} 33 | 34 | private static void initPool() { 35 | pool = new ClassPool(true); 36 | pool.appendClassPath(new LoaderClassPath(Thread.currentThread().getContextClassLoader())); 37 | } 38 | 39 | private static void initFields4Listener() { 40 | try { 41 | coordinatorField = KafkaConsumer.class.getDeclaredField("coordinator"); 42 | coordinatorField.setAccessible(true); 43 | nodeField = AbstractCoordinator.class.getDeclaredField("coordinator"); 44 | nodeField.setAccessible(true); 45 | } catch (Throwable t) { 46 | log.error("cannot update consumer coordinator field"); 47 | coordinatorField = null; 48 | nodeField = null; 49 | } 50 | } 51 | 52 | public static String getIpPort(Consumer consumer) { 53 | if (coordinatorField != null && nodeField != null) { 54 | try { 55 | KafkaConsumer kc = (KafkaConsumer) consumer; 56 | ConsumerCoordinator cc = (ConsumerCoordinator) coordinatorField.get(kc); 57 | Node node = (Node) nodeField.get(cc); 58 | return node.host() + ":" + node.port(); 59 | } catch (Throwable t) { 60 | log.error("cannot get consumer coordinator field"); 61 | return "0.0.0.0:0"; 62 | } 63 | } else { 64 | return "0.0.0.0:0"; 65 | } 66 | } 67 | 68 | private static void initFields4Producer() { 69 | try { 70 | producerFactoryField = KafkaTemplate.class.getDeclaredField("producerFactory"); 71 | producerFactoryField.setAccessible(true); 72 | } catch (Throwable t) { 73 | log.error("cannot update KafkaTemplate producerFactory field"); 74 | producerFactoryField = null; 75 | } 76 | } 77 | 78 | private static DefaultKafkaProducerFactory getProducerFactory(KafkaTemplate cf) { 79 | if (producerFactoryField == null) { 80 | return null; 81 | } else { 82 | try { 83 | DefaultKafkaProducerFactory s = (DefaultKafkaProducerFactory) producerFactoryField.get(cf); 84 | return s; 85 | } catch (Throwable t) { 86 | log.error("cannot get KafkaTemplate producerFactory field"); 87 | return null; 88 | } 89 | } 90 | } 91 | 92 | private static String getTargetAddrsNoCache(KafkaTemplate template) { 93 | DefaultKafkaProducerFactory f = getProducerFactory(template); 94 | if (f == null) { 95 | return null; 96 | } else { 97 | Map map = f.getConfigurationProperties(); 98 | if (map == null || map.isEmpty()) { 99 | return null; 100 | } else { 101 | Object o = map.get("bootstrap.servers"); 102 | if (o == null) { 103 | return null; 104 | } 105 | return o.toString(); 106 | } 107 | } 108 | } 109 | 110 | public static String getTargetAddrs(KafkaTemplate template) { 111 | int hashCode = template.hashCode(); 112 | String addrs = addrsMap.get(hashCode); 113 | if (addrs != null) { 114 | return addrs; 115 | } else { 116 | addrs = getTargetAddrsNoCache(template); 117 | if (addrs == null) { 118 | addrs = "unknown"; 119 | } 120 | addrsMap.put(hashCode, addrs); 121 | return addrs; 122 | } 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/http/HttpAuditLogInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.http; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import java.io.ByteArrayInputStream; 5 | import java.io.ByteArrayOutputStream; 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import org.springframework.http.HttpHeaders; 9 | import org.springframework.http.HttpRequest; 10 | import org.springframework.http.HttpStatus; 11 | import org.springframework.http.client.ClientHttpRequestExecution; 12 | import org.springframework.http.client.ClientHttpRequestInterceptor; 13 | import org.springframework.http.client.ClientHttpResponse; 14 | import org.springframework.util.StreamUtils; 15 | 16 | public class HttpAuditLogInterceptor implements ClientHttpRequestInterceptor { 17 | public HttpAuditLogInterceptor() {} 18 | 19 | public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) 20 | throws IOException { 21 | HttpCallInfo callInfo = HttpCallInfo.instance.get(); 22 | if (callInfo == null) { 23 | return execution.execute(request, body); 24 | } else { 25 | callInfo.uri = request.getURI(); 26 | if (request.getMethod() != null) { 27 | callInfo.method = request.getMethod().toString(); 28 | } 29 | callInfo.reqHeaders = request.getHeaders().toSingleValueMap(); 30 | if (AsyncAuditLog.isHttpEnabled()) { 31 | callInfo.reqBodyStr = this.getReqBodyStr(request, body); 32 | } 33 | 34 | ClientHttpResponse res = execution.execute(request, body); 35 | callInfo.httpCode = res.getStatusCode().value(); 36 | callInfo.resHeaders = res.getHeaders().toSingleValueMap(); 37 | byte[] resBody = this.readResBody(res); 38 | callInfo.resBodyStr = this.getResBodyStr(res.getHeaders(), resBody); 39 | return new SimpleRestClientHttpResponse(res.getStatusCode(), res.getHeaders(), resBody); 40 | } 41 | } 42 | 43 | byte[] readResBody(ClientHttpResponse res) throws IOException { 44 | ByteArrayOutputStream os = new ByteArrayOutputStream(); 45 | StreamUtils.copy(res.getBody(), os); 46 | res.close(); 47 | return os.toByteArray(); 48 | } 49 | 50 | private String getResBodyStr(HttpHeaders headers, byte[] body) { 51 | if (body != null && body.length > 0 && headers.getContentType() != null) { 52 | String contentType = headers.getContentType().toString().toLowerCase(); 53 | String charset = !contentType.contains("gbk") && !contentType.contains("gb2312") ? "utf-8" : "gbk"; 54 | if (contentType.contains("application/x-www-form-urlencoded") 55 | || contentType.contains("application/json") 56 | || contentType.contains("text/xml") 57 | || contentType.contains("text/html") 58 | || contentType.contains("text/plain")) { 59 | return this.bodyToString(body, charset); 60 | } 61 | 62 | byte firstChar = body[0]; 63 | if (firstChar == 60 || firstChar == 123) { 64 | return this.bodyToString(body, charset); 65 | } 66 | } 67 | 68 | return ""; 69 | } 70 | 71 | private String getReqBodyStr(HttpRequest request, byte[] body) { 72 | if (body != null && request.getHeaders().getContentType() != null) { 73 | String contentType = 74 | request.getHeaders().getContentType().toString().toLowerCase(); 75 | if (contentType.contains("application/x-www-form-urlencoded") 76 | || contentType.contains("application/json") 77 | || contentType.contains("text/xml")) { 78 | String charset = !contentType.contains("gbk") && !contentType.contains("gb2312") ? "utf-8" : "gbk"; 79 | return this.bodyToString(body, charset); 80 | } 81 | } 82 | 83 | return ""; 84 | } 85 | 86 | private String bodyToString(byte[] body, String charset) { 87 | try { 88 | return new String(body, charset); 89 | } catch (Exception e) { 90 | return new String(body); 91 | } 92 | } 93 | 94 | static class SimpleRestClientHttpResponse implements ClientHttpResponse { 95 | HttpStatus statusCode; 96 | HttpHeaders httpHeaders; 97 | ByteArrayInputStream body; 98 | 99 | SimpleRestClientHttpResponse(HttpStatus statusCode, HttpHeaders httpHeaders, byte[] body) { 100 | this.statusCode = statusCode; 101 | this.httpHeaders = httpHeaders; 102 | this.body = new ByteArrayInputStream(body); 103 | } 104 | 105 | public HttpStatus getStatusCode() throws IOException { 106 | return this.statusCode; 107 | } 108 | 109 | public int getRawStatusCode() throws IOException { 110 | return this.statusCode.value(); 111 | } 112 | 113 | public String getStatusText() throws IOException { 114 | return this.statusCode.getReasonPhrase(); 115 | } 116 | 117 | public void close() { 118 | try { 119 | this.body.close(); 120 | } catch (Exception e) { 121 | } 122 | } 123 | 124 | public InputStream getBody() throws IOException { 125 | return this.body; 126 | } 127 | 128 | public HttpHeaders getHeaders() { 129 | return this.httpHeaders; 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/EnvAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLogPool; 4 | import fun.pxyc.onelogger.config.EnvConfig; 5 | import fun.pxyc.onelogger.log.*; 6 | import fun.pxyc.onelogger.trace.Trace; 7 | import fun.pxyc.onelogger.trace.adpater.DefaultTraceAdapter; 8 | import fun.pxyc.onelogger.trace.adpater.TraceAdapter; 9 | import java.io.BufferedReader; 10 | import java.io.IOException; 11 | import java.io.InputStreamReader; 12 | import java.net.URL; 13 | import java.util.ArrayList; 14 | import java.util.Arrays; 15 | import java.util.Enumeration; 16 | import java.util.List; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.beans.factory.annotation.Value; 20 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 21 | import org.springframework.context.ApplicationEvent; 22 | import org.springframework.context.ApplicationListener; 23 | import org.springframework.context.annotation.Bean; 24 | import org.springframework.context.annotation.Configuration; 25 | import org.springframework.context.event.ContextRefreshedEvent; 26 | 27 | @Configuration 28 | @EnableConfigurationProperties(EnvConfigProps.class) 29 | public class EnvAutoConfiguration implements ApplicationListener { 30 | private static final Logger log = LoggerFactory.getLogger(EnvAutoConfiguration.class); 31 | 32 | @Value("${spring.profiles.active:dev}") 33 | String globalProfile = ""; 34 | 35 | @Value("${server.port:8080}") 36 | int serverPort; 37 | 38 | @Value("${spring.application.name:unknown}") 39 | String springApplicationName; 40 | 41 | public EnvAutoConfiguration() {} 42 | 43 | public void onApplicationEvent(ApplicationEvent event) { 44 | if (event instanceof ContextRefreshedEvent) { 45 | GlobalConfig.globalServiceName = springApplicationName; 46 | // TODO serviceId:未配 采用端口号 47 | GlobalConfig.globalServiceId = serverPort; 48 | GlobalConfig.globalProfile = globalProfile; 49 | EnvConfig.initEnv(); 50 | if (EnvConfigProps.disableStdout) { 51 | EnvConfig.removeAllStdoutAppenders(); 52 | } 53 | // 拉取spi 54 | loadSpi(); 55 | TraceAdapter traceAdapter = Spis.getPlugin(TraceAdapter.class, EnvConfigProps.traceAdapterParam); 56 | if (traceAdapter == null) { 57 | Trace.setAdapter(newTraceAdapter()); 58 | } else { 59 | Trace.setAdapter(traceAdapter); 60 | } 61 | LoggerFormatterHandler loggerFormatterHandler = 62 | Spis.getPlugin(LoggerFormatterHandler.class, EnvConfigProps.logFormatter); 63 | if (loggerFormatterHandler == null) { 64 | loggerFormatterHandler = new JsonLoggerFormatterHandler(); 65 | } 66 | LoggerFormatter.setHandler(loggerFormatterHandler); 67 | // 获取filter spi 68 | List filters = Spis.getPlugins(LoggerFilter.class); 69 | IgnoreLoggerFilter ignoreLoggerFilter = new IgnoreLoggerFilter(); 70 | ignoreLoggerFilter.setIgnoreParams(convertToList(EnvConfigProps.ignoreParams)); 71 | filters.add(ignoreLoggerFilter); 72 | IndexLoggerFilter indexLoggerFilter = new IndexLoggerFilter(); 73 | indexLoggerFilter.setLogIndex(convertToList(EnvConfigProps.logIndex)); 74 | filters.add(indexLoggerFilter); 75 | LoggerFormatter.setFilters(filters); 76 | } 77 | } 78 | 79 | private List convertToList(String params) { 80 | String[] indexs = new String[] {params}; 81 | if (params.contains(";")) { 82 | indexs = params.split(";"); 83 | } 84 | return Arrays.asList(indexs); 85 | } 86 | 87 | private void loadSpi() { 88 | try { 89 | loadSpi(LoggerFormatterHandler.class); 90 | loadSpi(TraceAdapter.class); 91 | loadSpi(LoggerFilter.class); 92 | } catch (Exception e) { 93 | throw new RuntimeException(e); 94 | } 95 | } 96 | 97 | private void loadSpi(Class cls) throws Exception { 98 | Enumeration urls = getClass().getClassLoader().getResources("META-INF/services/" + cls.getName()); 99 | List list = new ArrayList<>(); 100 | while (urls.hasMoreElements()) { 101 | URL url = urls.nextElement(); 102 | List lines = readSpiLines(url); 103 | for (String s : lines) { 104 | if (s.trim().isEmpty()) continue; 105 | try { 106 | Class implCls = Class.forName(s); 107 | Spis.PluginInfo pi = new Spis.PluginInfo(cls, implCls); 108 | list.add(pi); 109 | } catch (Throwable e) { 110 | } 111 | } 112 | } 113 | Spis.plugins.put(cls.getName(), list); 114 | } 115 | 116 | List readSpiLines(URL url) { 117 | List lines = new ArrayList<>(); 118 | 119 | try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) { 120 | String s = in.readLine(); 121 | while (s != null) { 122 | if (!s.isEmpty()) { 123 | lines.add(s); 124 | } 125 | s = in.readLine(); 126 | } 127 | return lines; 128 | } catch (IOException e) { 129 | throw new RuntimeException(e); 130 | } 131 | } 132 | 133 | public TraceAdapter newTraceAdapter() { 134 | Trace.setAppName(GlobalConfig.globalServiceName); 135 | return new DefaultTraceAdapter(); 136 | } 137 | 138 | @Bean(initMethod = "init", destroyMethod = "close") 139 | AsyncAuditLogPool asyncLogPool() { 140 | return new AsyncAuditLogPool(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/autoconfig/RestTemplateConfiguration.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.autoconfig; 2 | 3 | import fun.pxyc.onelogger.http.HttpAuditLogInterceptor; 4 | import fun.pxyc.onelogger.http.HttpClientProperties; 5 | import fun.pxyc.onelogger.http.HttpsClientRequestFactory; 6 | import fun.pxyc.onelogger.http.RestTemplateAspect; 7 | import java.nio.charset.StandardCharsets; 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 13 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 14 | import org.springframework.boot.context.properties.ConfigurationProperties; 15 | import org.springframework.context.ApplicationContext; 16 | import org.springframework.context.ApplicationEvent; 17 | import org.springframework.context.ApplicationListener; 18 | import org.springframework.context.annotation.Bean; 19 | import org.springframework.context.annotation.Configuration; 20 | import org.springframework.context.annotation.Import; 21 | import org.springframework.context.event.ContextRefreshedEvent; 22 | import org.springframework.http.client.ClientHttpRequestFactory; 23 | import org.springframework.http.client.SimpleClientHttpRequestFactory; 24 | import org.springframework.http.converter.HttpMessageConverter; 25 | import org.springframework.http.converter.StringHttpMessageConverter; 26 | import org.springframework.web.client.RestTemplate; 27 | 28 | @Configuration 29 | @ConditionalOnClass({RestTemplate.class}) 30 | @Import({RestTemplateWithPoolConfiguration.class}) 31 | public class RestTemplateConfiguration implements ApplicationListener { 32 | public RestTemplateConfiguration() {} 33 | 34 | @Bean 35 | @ConfigurationProperties(prefix = "spring.resttemplate") 36 | @ConditionalOnMissingBean(name = {"httpClientProperties"}) 37 | HttpClientProperties httpClientProperties() { 38 | return new HttpClientProperties(); 39 | } 40 | 41 | @Bean 42 | @ConditionalOnMissingBean(name = {"clientHttpRequestFactory"}) 43 | ClientHttpRequestFactory clientHttpRequestFactory(HttpClientProperties httpClientProperties) { 44 | if (httpClientProperties.isUsePool()) { 45 | throw new RuntimeException( 46 | "spring.resttemplate.usePool = true depends on org.apache.httpcomponents:httpclient:4.5.2 "); 47 | } else { 48 | httpClientProperties.setMaxConnectPerRoute(0); 49 | SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); 50 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 51 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 52 | return factory; 53 | } 54 | } 55 | 56 | @Bean 57 | @ConditionalOnMissingBean(name = {"httpsClientHttpRequestFactory"}) 58 | ClientHttpRequestFactory httpsClientHttpRequestFactory(HttpClientProperties httpClientProperties) { 59 | if (httpClientProperties.isUsePool()) { 60 | throw new RuntimeException( 61 | "spring.resttemplate.usePool = true depends on org.apache.httpcomponents:httpclient:4.5.2 "); 62 | } else { 63 | httpClientProperties.setMaxConnectPerRoute(0); 64 | HttpsClientRequestFactory factory = new HttpsClientRequestFactory(); 65 | factory.setConnectTimeout(httpClientProperties.getConnectTimeout()); 66 | factory.setReadTimeout(httpClientProperties.getReadTimeout()); 67 | return factory; 68 | } 69 | } 70 | 71 | @Bean 72 | @ConditionalOnMissingBean(name = {"restTemplate"}) 73 | RestTemplate restTemplate(ClientHttpRequestFactory clientHttpRequestFactory) { 74 | return getRestTemplate(clientHttpRequestFactory); 75 | } 76 | 77 | private RestTemplate getRestTemplate(ClientHttpRequestFactory clientHttpRequestFactory) { 78 | RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory); 79 | List> cnvts = restTemplate.getMessageConverters(); 80 | Iterator> iterator = cnvts.iterator(); 81 | 82 | while (iterator.hasNext()) { 83 | HttpMessageConverter c = iterator.next(); 84 | if (c instanceof StringHttpMessageConverter) { 85 | ((StringHttpMessageConverter) c).setDefaultCharset(StandardCharsets.UTF_8); 86 | } 87 | } 88 | 89 | return restTemplate; 90 | } 91 | 92 | @Bean 93 | @ConditionalOnMissingBean(name = {"httpsRestTemplate"}) 94 | RestTemplate httpsRestTemplate(ClientHttpRequestFactory httpsClientHttpRequestFactory) { 95 | return getRestTemplate(httpsClientHttpRequestFactory); 96 | } 97 | 98 | @Bean 99 | RestTemplateAspect restTemplateAspect(HttpClientProperties httpClientProperties) { 100 | RestTemplateAspect.logAllHeaders = httpClientProperties.isLogAllHeaders(); 101 | RestTemplateAspect.initLogHeaderNames(httpClientProperties.getLogHeaderNames()); 102 | return new RestTemplateAspect(); 103 | } 104 | 105 | public void onApplicationEvent(ApplicationEvent event) { 106 | if (event instanceof ContextRefreshedEvent) { 107 | this.addInterceptor(((ContextRefreshedEvent) event).getApplicationContext()); 108 | } 109 | } 110 | 111 | private void addInterceptor(ApplicationContext context) { 112 | Collection templates = 113 | context.getBeansOfType(RestTemplate.class).values(); 114 | if (!templates.isEmpty()) { 115 | templates.forEach(this::setInterceptor); 116 | } 117 | } 118 | 119 | private void setInterceptor(RestTemplate r) { 120 | if (r.getInterceptors() == null) { 121 | r.setInterceptors(new ArrayList()); 122 | } 123 | 124 | r.getInterceptors().add(new HttpAuditLogInterceptor()); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/dubbo/ConsumerTraceFilter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.dubbo; 2 | 3 | import static org.apache.dubbo.common.constants.CommonConstants.CONSUMER; 4 | 5 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 6 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 7 | import fun.pxyc.onelogger.log.Action; 8 | import fun.pxyc.onelogger.log.LoggerFormatter; 9 | import fun.pxyc.onelogger.trace.Span; 10 | import fun.pxyc.onelogger.trace.Trace; 11 | import fun.pxyc.onelogger.trace.TraceContext; 12 | import fun.pxyc.onelogger.trace.TraceData; 13 | import fun.pxyc.onelogger.utils.JsonUtil; 14 | import java.net.InetSocketAddress; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map; 17 | import java.util.concurrent.ConcurrentHashMap; 18 | import java.util.concurrent.atomic.AtomicInteger; 19 | import org.apache.dubbo.common.extension.Activate; 20 | import org.apache.dubbo.rpc.*; 21 | import org.slf4j.Logger; 22 | import org.slf4j.LoggerFactory; 23 | import org.slf4j.MDC; 24 | 25 | @Activate( 26 | group = {CONSUMER}, 27 | order = -9999) 28 | public class ConsumerTraceFilter extends LoggerFormatter implements Filter { 29 | 30 | public static final String TRACE_ID = "traceId"; 31 | public static final String CLIENT_IP = "clientIp"; 32 | 33 | private static final Logger log = LoggerFactory.getLogger(ConsumerTraceFilter.class); 34 | 35 | private final ConcurrentHashMap seqMap = new ConcurrentHashMap<>(); 36 | 37 | public static String getIP() { 38 | return ProviderTraceFilter.getAddr(); 39 | } 40 | 41 | @Override 42 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 43 | 44 | Span span = Trace.startAsync("DUBBO", "CONSUMER"); 45 | TraceContext ctx = Trace.currentContext(); 46 | 47 | String traceId = ctx.getTrace().getTraceId(); 48 | RpcContext.getContext().setAttachment(TRACE_ID, traceId); 49 | MDC.put("TRACE_ID", traceId); 50 | RpcContext.getContext().setAttachment(CLIENT_IP, getConnectId()); 51 | 52 | try { 53 | Result o = invoker.invoke(invocation); 54 | span.stop(true); 55 | this.log(span, ctx, invocation, o, null); 56 | return o; 57 | } catch (Throwable throwable) { 58 | span.stop(false); 59 | this.log(span, ctx, invocation, null, throwable); 60 | throw throwable; 61 | } 62 | } 63 | 64 | private void log(Span span, TraceContext context, Invocation invocation, Result result, Throwable throwable) { 65 | try { 66 | this.logInternal(span, context, invocation, result, throwable); 67 | } catch (Throwable t) { 68 | log.error("logInternal exception", t); 69 | log.error(t.getMessage(), t); 70 | } 71 | } 72 | 73 | private void logInternal(Span span, TraceContext ctx, Invocation invocation, Result result, Throwable throwable) { 74 | TraceData trace = ctx.getTrace(); 75 | long duration = span.getTimeUsedMicros(); 76 | String costTime = span.getTimeUsedMs(); 77 | String traceId = trace.getTraceId(); 78 | String remoteInterface = invocation.getInvoker().getInterface().getName(); 79 | String remoteMethod = invocation.getMethodName(); 80 | String dubboUrl = remoteInterface + "_" + remoteMethod; 81 | span.changeAction("DUBBO." + dubboUrl); 82 | if (duration >= EnvConfigProps.slowHttpMillis * 1000L) { 83 | log.warn("slow dubbo request:" + dubboUrl + ", ts=" + duration + ", traceId=" + traceId); 84 | } 85 | 86 | if (AsyncAuditLog.isDubboConsumerAuditLogEnabled()) { 87 | String timestamp = AsyncAuditLog.now(ctx, span); 88 | AtomicInteger atomicInteger = seqMap.get(dubboUrl); 89 | if (atomicInteger == null) { 90 | atomicInteger = new AtomicInteger(0); 91 | seqMap.put(dubboUrl, atomicInteger); 92 | } 93 | int sequence = atomicInteger.incrementAndGet(); 94 | if (sequence >= 10000000) { 95 | atomicInteger.compareAndSet(sequence, 0); 96 | } 97 | String spanId = span.getSpanId(); 98 | String serviceNameMsgName = "DUBBO." + dubboUrl; 99 | InetSocketAddress remoteAddress = RpcContext.getContext().getRemoteAddress(); 100 | String remoteAddressIp = "0.0.0.0:0:0"; 101 | if (remoteAddress != null) { 102 | remoteAddressIp = remoteAddress.toString(); 103 | } 104 | Log( 105 | AsyncAuditLog.dubboConsumerAuditLog, 106 | generateLogMap( 107 | timestamp, 108 | traceId, 109 | spanId, 110 | span.getRootSpan().getAction(), 111 | remoteAddressIp, 112 | new Action("DUBBO", remoteInterface, remoteMethod, serviceNameMsgName), 113 | sequence, 114 | costTime, 115 | getJsonReq(invocation), 116 | getJsonRes(result), 117 | genException(throwable), 118 | EnvConfigProps.dubboLogMaxChars)); 119 | } 120 | } 121 | 122 | private String getConnectId() { 123 | String ip = getIP(); 124 | if (!ip.isEmpty()) { 125 | return ip; 126 | } 127 | return RpcContext.getContext().getLocalAddressString(); 128 | } 129 | 130 | public Map getJsonReq(Invocation invocation) { 131 | Object[] arguments = invocation.getArguments(); 132 | Map argMap = new LinkedHashMap<>(); 133 | Class[] parameterTypes = invocation.getParameterTypes(); 134 | if (arguments == null || arguments.length == 0) { 135 | return argMap; 136 | } 137 | for (int i = 0; i < arguments.length; i++) { 138 | String simpleName = parameterTypes.getClass().getSimpleName(); 139 | argMap.put(simpleName.toLowerCase(), JsonUtil.toJsonObject(arguments[i])); 140 | } 141 | return argMap; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/dubbo/ProviderTraceFilter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.dubbo; 2 | 3 | import static org.apache.dubbo.common.constants.CommonConstants.PROVIDER; 4 | 5 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 6 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 7 | import fun.pxyc.onelogger.log.Action; 8 | import fun.pxyc.onelogger.log.LoggerFormatter; 9 | import fun.pxyc.onelogger.trace.Span; 10 | import fun.pxyc.onelogger.trace.Trace; 11 | import fun.pxyc.onelogger.trace.TraceContext; 12 | import fun.pxyc.onelogger.trace.TraceData; 13 | import fun.pxyc.onelogger.utils.JsonUtil; 14 | import java.util.LinkedHashMap; 15 | import java.util.Map; 16 | import java.util.concurrent.ConcurrentHashMap; 17 | import java.util.concurrent.atomic.AtomicInteger; 18 | import org.apache.dubbo.common.extension.Activate; 19 | import org.apache.dubbo.rpc.*; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | import org.slf4j.MDC; 23 | 24 | @Activate( 25 | group = {PROVIDER}, 26 | order = -9999) 27 | public class ProviderTraceFilter extends LoggerFormatter implements Filter { 28 | public static final String TRACE_ID = "traceId"; 29 | public static final String CLIENT_IP = "clientIp"; 30 | private static final Logger log = LoggerFactory.getLogger(ProviderTraceFilter.class); 31 | private final ConcurrentHashMap seqMap = new ConcurrentHashMap<>(); 32 | 33 | public static String getAddr() { 34 | String addr = RpcContext.getContext().getAttachment(CLIENT_IP); 35 | if (addr == null || addr.isEmpty()) { 36 | addr = RpcContext.getContext().getRemoteHost() + ":" 37 | + RpcContext.getContext().getRemotePort(); 38 | } 39 | return addr; 40 | } 41 | 42 | public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { 43 | 44 | String addr = getAddr(); 45 | TraceData t = AsyncAuditLog.newTrace(addr); 46 | String traceId = RpcContext.getContext().getAttachment(TRACE_ID); 47 | if (traceId != null && !traceId.isEmpty()) { 48 | t.setTraceId(traceId); 49 | MDC.put("TRACE_ID", traceId); 50 | } 51 | String remoteInterface = invocation.getInvoker().getInterface().getName(); 52 | String remoteMethod = invocation.getMethodName(); 53 | String action = remoteInterface + "_" + remoteMethod; 54 | Span span = Trace.startForServer(t, "DUBBOPROVIDER", action); 55 | span.setRemoteAddr(addr); 56 | TraceContext ctx = Trace.currentContext(); 57 | Result o = null; 58 | try { 59 | o = invoker.invoke(invocation); 60 | span.stop(true); 61 | this.log(span, ctx, invocation, o, null); 62 | return o; 63 | } catch (Throwable throwable) { 64 | span.stop(false); 65 | this.log(span, ctx, invocation, null, throwable); 66 | throw throwable; 67 | } 68 | } 69 | 70 | private void log(Span span, TraceContext context, Invocation invocation, Result result, Throwable throwable) { 71 | try { 72 | this.logInternal(span, context, invocation, result, throwable); 73 | } catch (Throwable t) { 74 | log.error("logInternal exception", t); 75 | log.error(t.getMessage(), t); 76 | } 77 | } 78 | 79 | private void logInternal(Span span, TraceContext ctx, Invocation invocation, Result result, Throwable throwable) { 80 | TraceData trace = ctx.getTrace(); 81 | long duration = span.getTimeUsedMicros(); 82 | String costTime = span.getTimeUsedMs(); 83 | 84 | String traceId = trace.getTraceId(); 85 | String remoteInterface = invocation.getInvoker().getInterface().getName(); 86 | String remoteMethod = invocation.getMethodName(); 87 | String dubboUrl = remoteInterface + "_" + remoteMethod; 88 | // span.changeAction("DUBBO." + dubboUrl); 89 | if (duration >= EnvConfigProps.slowDubboMillis * 1000L) { 90 | log.warn("slow dubbo request:" + dubboUrl + ", ts=" + duration + ", traceId=" + traceId); 91 | } 92 | 93 | if (AsyncAuditLog.isDubboProviderAuditLogEnabled()) { 94 | String timestamp = AsyncAuditLog.now(ctx, span); 95 | AtomicInteger atomicInteger = seqMap.get(dubboUrl); 96 | if (atomicInteger == null) { 97 | atomicInteger = new AtomicInteger(0); 98 | seqMap.put(dubboUrl, atomicInteger); 99 | } 100 | int sequence = atomicInteger.incrementAndGet(); 101 | if (sequence >= 10000000) { 102 | atomicInteger.compareAndSet(sequence, 0); 103 | } 104 | String spanId = span.getSpanId(); 105 | String serviceNameMsgName = "DUBBO." + dubboUrl; 106 | Log( 107 | AsyncAuditLog.dubboProviderAuditLog, 108 | generateLogMap( 109 | timestamp, 110 | traceId, 111 | spanId, 112 | span.getRootSpan().getAction(), 113 | getAddr(), 114 | new Action("DUBBO", remoteInterface, remoteMethod, serviceNameMsgName), 115 | sequence, 116 | costTime, 117 | getJsonReq(invocation), 118 | getJsonRes(result), 119 | genException(throwable), 120 | EnvConfigProps.dubboLogMaxChars)); 121 | } 122 | } 123 | 124 | public Map getJsonReq(Invocation invocation) { 125 | Object[] arguments = invocation.getArguments(); 126 | Map argMap = new LinkedHashMap<>(); 127 | Class[] parameterTypes = invocation.getParameterTypes(); 128 | if (arguments == null || arguments.length == 0) { 129 | return argMap; 130 | } 131 | for (int i = 0; i < arguments.length; i++) { 132 | String simpleName = parameterTypes.getClass().getSimpleName(); 133 | argMap.put(simpleName.toLowerCase(), JsonUtil.toJsonObject(arguments[i])); 134 | } 135 | return argMap; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/rabbitmq/RabbitMqAuditSendAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.rabbitmq; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.Trace; 9 | import fun.pxyc.onelogger.trace.TraceContext; 10 | import fun.pxyc.onelogger.trace.TraceData; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | import org.aspectj.lang.ProceedingJoinPoint; 15 | import org.aspectj.lang.annotation.Around; 16 | import org.aspectj.lang.annotation.Aspect; 17 | import org.aspectj.lang.annotation.Pointcut; 18 | import org.aspectj.lang.reflect.CodeSignature; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.amqp.core.Message; 22 | import org.springframework.amqp.rabbit.core.RabbitTemplate; 23 | 24 | @Aspect 25 | public class RabbitMqAuditSendAspect extends LoggerFormatter { 26 | private static final Logger log = LoggerFactory.getLogger(RabbitMqAuditSendAspect.class); 27 | private final AtomicInteger seq = new AtomicInteger(0); 28 | 29 | public RabbitMqAuditSendAspect() {} 30 | 31 | @Pointcut("execution(* org.springframework.amqp.rabbit.core.RabbitTemplate.convertAndSend(..))") 32 | public void rabbitMqAuditSendPointCut() {} 33 | 34 | @Around("rabbitMqAuditSendPointCut()") 35 | public void rabbitMqAuditSendAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 36 | 37 | Span span = Trace.startAsync("RABBITMQ", "send"); 38 | TraceContext ctx = Trace.currentContext(); 39 | try { 40 | joinPoint.proceed(); 41 | span.stop(true); 42 | this.log(span, ctx, joinPoint, null); 43 | } catch (Throwable throwable) { 44 | span.stop(false); 45 | this.log(span, ctx, joinPoint, throwable); 46 | throw throwable; 47 | } 48 | } 49 | 50 | private void log(Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable throwable) { 51 | try { 52 | this.logInternal(span, ctx, joinPoint, throwable); 53 | } catch (Throwable t) { 54 | log.error("logInternal exception", t); 55 | } 56 | } 57 | 58 | private void logInternal(Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable throwable) { 59 | Object[] args = joinPoint.getArgs(); 60 | String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); 61 | String exchange = ((RabbitTemplate) joinPoint.getTarget()).getExchange(); 62 | TraceData trace = ctx.getTrace(); 63 | long duration = span.getTimeUsedMicros(); 64 | String traceId = trace.getTraceId(); 65 | if (duration >= (EnvConfigProps.slowMqMillis * 1000L)) { 66 | log.warn("slow mq: ts=" + duration + ", traceId=" + traceId); 67 | } 68 | if (AsyncAuditLog.isMqEnabled()) { 69 | String timestamp = AsyncAuditLog.now(ctx, span); 70 | String connId = this.genConnId(); 71 | int sequence = this.seq.incrementAndGet(); 72 | if (sequence >= 10000000) { 73 | this.seq.compareAndSet(sequence, 0); 74 | } 75 | String spanId = span.getSpanId(); 76 | Action action = new Action("RABBITMQ.send", joinPoint); 77 | Map reqParams = this.genReqParams(args, paramNames); 78 | Map resParams = this.genResParams(throwable); 79 | Log( 80 | AsyncAuditLog.mqAuditLog, 81 | generateLogMapWithExtraInfo( 82 | timestamp, 83 | traceId, 84 | spanId, 85 | span.getRootSpan().getAction(), 86 | connId, 87 | action, 88 | sequence, 89 | span.getTimeUsedMs(), 90 | reqParams, 91 | resParams, 92 | genException(throwable), 93 | this.genExchangeName(args, paramNames, exchange), 94 | EnvConfigProps.mqLogMaxChars)); 95 | } 96 | } 97 | 98 | String genConnId() { 99 | return "0.0.0.0:0:0"; 100 | } 101 | 102 | String genExchangeName(Object[] args, String[] paramNames, String defaultExchange) { 103 | StringBuilder builder = new StringBuilder(); 104 | String exchange = null; 105 | String routingKey = "default"; 106 | 107 | for (int i = 0; i < paramNames.length; ++i) { 108 | if (paramNames[i].equalsIgnoreCase("exchange")) { 109 | exchange = args[i].toString(); 110 | } else if (paramNames[i].equalsIgnoreCase("routingKey")) { 111 | routingKey = args[i].toString(); 112 | } 113 | } 114 | 115 | if (exchange == null || exchange.isEmpty()) { 116 | exchange = defaultExchange; 117 | } 118 | 119 | if (exchange == null || exchange.isEmpty()) { 120 | exchange = "default"; 121 | } 122 | 123 | builder.append("exchange:").append(exchange).append("^routingKey:").append(routingKey); 124 | return builder.toString(); 125 | } 126 | 127 | Map genReqParams(Object[] args, String[] paramNames) { 128 | StringBuilder builder = new StringBuilder(); 129 | Map reqParams = new HashMap<>(); 130 | for (int i = 0; i < args.length; ++i) { 131 | if (builder.length() > 0) { 132 | builder.append("^"); 133 | } 134 | 135 | if (paramNames[i].equals("object")) { 136 | String s = ""; 137 | Object obj = args[i]; 138 | if (obj instanceof Message) { 139 | Message m = (Message) obj; 140 | s = AsyncAuditLog.bytesToTextAndEscape(m.getBody()); 141 | } else { 142 | s = AsyncAuditLog.toTextAndEscape(args[i]); 143 | } 144 | reqParams.put(paramNames[i], s); 145 | } 146 | } 147 | 148 | return reqParams; 149 | } 150 | 151 | Map genResParams(Throwable throwable) { 152 | Map map = new HashMap(); 153 | if (throwable != null) { 154 | String s = AsyncAuditLog.escapeText(throwable.getMessage()); 155 | map.put("exception", s); 156 | return map; 157 | } else { 158 | return map; 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/TypeSafe.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import java.math.BigInteger; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Date; 6 | import java.util.List; 7 | import java.util.Map; 8 | 9 | public class TypeSafe { 10 | public TypeSafe() {} 11 | 12 | public static String anyToString(Object v) { 13 | if (v == null) { 14 | return null; 15 | } else { 16 | return v instanceof String ? (String) v : v.toString(); 17 | } 18 | } 19 | 20 | public static String anyToString(Object v, String defaultValue) { 21 | if (v == null) { 22 | return defaultValue; 23 | } else { 24 | return v instanceof String ? (String) v : v.toString(); 25 | } 26 | } 27 | 28 | public static int anyToInt(Object v) { 29 | return anyToInt(v, 0); 30 | } 31 | 32 | public static int anyToInt(Object v, int defaultValue) { 33 | if (v == null) { 34 | return defaultValue; 35 | } else if (v instanceof Integer) { 36 | return (Integer) v; 37 | } else if (v instanceof Number) { 38 | return ((Number) v).intValue(); 39 | } else { 40 | try { 41 | return Integer.parseInt(v.toString()); 42 | } catch (Exception e) { 43 | return defaultValue; 44 | } 45 | } 46 | } 47 | 48 | public static long anyToLong(Object v) { 49 | return anyToLong(v, 0L); 50 | } 51 | 52 | public static long anyToLong(Object v, long defaultValue) { 53 | if (v == null) { 54 | return defaultValue; 55 | } else if (v instanceof Integer) { 56 | return (long) (Integer) v; 57 | } else if (v instanceof Long) { 58 | return (Long) v; 59 | } else if (v instanceof Number) { 60 | return ((Number) v).longValue(); 61 | } else { 62 | try { 63 | return Long.parseLong(v.toString()); 64 | } catch (Exception e) { 65 | return defaultValue; 66 | } 67 | } 68 | } 69 | 70 | public static float anyToFloat(Object v) { 71 | return anyToFloat(v, 0.0F); 72 | } 73 | 74 | public static float anyToFloat(Object v, float defaultValue) { 75 | if (v == null) { 76 | return defaultValue; 77 | } else if (v instanceof Float) { 78 | return (Float) v; 79 | } else if (v instanceof Number) { 80 | return ((Number) v).floatValue(); 81 | } else { 82 | try { 83 | return Float.parseFloat(v.toString()); 84 | } catch (Exception e) { 85 | return defaultValue; 86 | } 87 | } 88 | } 89 | 90 | public static boolean anyToBool(Object v) { 91 | return anyToBool(v, false); 92 | } 93 | 94 | public static boolean anyToBool(Object v, boolean defaultValue) { 95 | if (v != null && !v.equals("")) { 96 | if (v instanceof Boolean) { 97 | return (Boolean) v; 98 | } else if (v instanceof Number) { 99 | return ((Number) v).intValue() == 1; 100 | } else { 101 | try { 102 | String s = v.toString().toLowerCase(); 103 | if (!s.equals("true") && !s.equals("yes") && !s.equals("t") && !s.equals("y") && !s.equals("1")) { 104 | return !s.equals("false") 105 | && !s.equals("no") 106 | && !s.equals("f") 107 | && !s.equals("n") 108 | && !s.equals("0") 109 | && defaultValue; 110 | } else { 111 | return true; 112 | } 113 | } catch (Exception e) { 114 | return defaultValue; 115 | } 116 | } 117 | } else { 118 | return defaultValue; 119 | } 120 | } 121 | 122 | public static double anyToDouble(Object v) { 123 | return anyToDouble(v, 0.0); 124 | } 125 | 126 | public static double anyToDouble(Object v, double defaultValue) { 127 | if (v == null) { 128 | return defaultValue; 129 | } else if (v instanceof Double) { 130 | return (Double) v; 131 | } else if (v instanceof Number) { 132 | return ((Number) v).doubleValue(); 133 | } else { 134 | try { 135 | return Double.parseDouble(v.toString()); 136 | } catch (Exception e) { 137 | return 0.0; 138 | } 139 | } 140 | } 141 | 142 | public static BigInteger anyToBigInteger(Object v) { 143 | if (v == null) { 144 | return BigInteger.ZERO; 145 | } else { 146 | try { 147 | return new BigInteger(v.toString()); 148 | } catch (Exception e) { 149 | return BigInteger.ZERO; 150 | } 151 | } 152 | } 153 | 154 | public static Map anyToMap(Object v) { 155 | if (v == null) { 156 | return null; 157 | } else { 158 | return v instanceof Map ? (Map) v : null; 159 | } 160 | } 161 | 162 | public static List anyToList(Object v) { 163 | if (v == null) { 164 | return null; 165 | } else { 166 | return v instanceof List ? (List) v : null; 167 | } 168 | } 169 | 170 | public static Date anyToDate(Object v) { 171 | if (v == null) { 172 | return null; 173 | } else if (v instanceof Date) { 174 | return (Date) v; 175 | } else if (v instanceof Long) { 176 | return new Date((Long) v); 177 | } else if (v instanceof String) { 178 | String s = (String) v; 179 | if (s.isEmpty()) { 180 | return null; 181 | } else if (s.contains("-")) { 182 | return parseDate(s); 183 | } else { 184 | long l = anyToLong(s); 185 | return l == 0L ? null : new Date(l); 186 | } 187 | } else { 188 | return null; 189 | } 190 | } 191 | 192 | static Date parseDate(String s) { 193 | try { 194 | SimpleDateFormat f; 195 | if (s.length() == 19) { 196 | f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 197 | } else { 198 | if (s.length() != 10) { 199 | return null; 200 | } 201 | 202 | f = new SimpleDateFormat("yyyy-MM-dd"); 203 | } 204 | 205 | return f.parse(s); 206 | } catch (Exception e) { 207 | return null; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/rabbitmq/RabbitMqAuditRecvAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.rabbitmq; 2 | 3 | import com.rabbitmq.client.Channel; 4 | import fun.pxyc.onelogger.ServerContextData; 5 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 6 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 7 | import fun.pxyc.onelogger.autoconfig.GlobalConfig; 8 | import fun.pxyc.onelogger.log.Action; 9 | import fun.pxyc.onelogger.log.LoggerFormatter; 10 | import fun.pxyc.onelogger.trace.*; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.aspectj.lang.ProceedingJoinPoint; 16 | import org.aspectj.lang.annotation.Around; 17 | import org.aspectj.lang.annotation.Aspect; 18 | import org.aspectj.lang.annotation.Pointcut; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.slf4j.MDC; 22 | import org.springframework.amqp.core.Message; 23 | 24 | @Aspect 25 | public class RabbitMqAuditRecvAspect extends LoggerFormatter { 26 | private static final Logger log = LoggerFactory.getLogger(RabbitMqAuditRecvAspect.class); 27 | private final AtomicInteger seq = new AtomicInteger(0); 28 | 29 | public RabbitMqAuditRecvAspect() {} 30 | 31 | @Pointcut("execution(* *..ChannelAwareMessageListener.onMessage(..))") 32 | public void rabbitMqAuditRecvPointCut1() {} 33 | 34 | @Around("rabbitMqAuditRecvPointCut1()") 35 | public void rabbitMqAuditRecvAdvice1(ProceedingJoinPoint joinPoint) throws Throwable { 36 | this.doAdvice(joinPoint); 37 | } 38 | 39 | @Pointcut("@annotation(org.springframework.amqp.rabbit.annotation.RabbitListener)") 40 | public void rabbitMqAuditRecvPointCut2() {} 41 | 42 | @Around("rabbitMqAuditRecvPointCut2()") 43 | public void rabbitMqAuditRecvAdvice2(ProceedingJoinPoint joinPoint) throws Throwable { 44 | this.doAdvice(joinPoint); 45 | } 46 | 47 | public void doAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 48 | Object[] args = joinPoint.getArgs(); 49 | Message message = null; 50 | Channel channel = null; 51 | for (int i = 0; i < args.length; ++i) { 52 | Object arg = args[i]; 53 | if (arg instanceof Channel) { 54 | channel = (Channel) arg; 55 | } 56 | if (arg instanceof Message) { 57 | message = (Message) arg; 58 | } 59 | } 60 | if (message != null) { 61 | String addr = null; 62 | if (channel != null) { 63 | addr = channel.getConnection().getAddress().getHostAddress() + ":" 64 | + channel.getConnection().getPort(); 65 | } else { 66 | addr = GlobalConfig.sourceIp; 67 | } 68 | TraceData t = AsyncAuditLog.newTrace(addr); 69 | String traceId = MDC.get("TRACE_ID"); 70 | if (StringUtils.isNotEmpty(traceId)) { 71 | t.setTraceId(traceId); 72 | } 73 | Action action = new Action( 74 | "RABBITMQ.RECEIVE", 75 | joinPoint.getTarget().getClass().getSimpleName(), 76 | joinPoint.getSignature().getName()); 77 | Span span = Trace.startForServer(t, "RABBITMQ.RECEIVE", action.getAction()); 78 | span.setRemoteAddr(addr); 79 | TraceContext ctx = Trace.currentContext(); 80 | int sequence = this.seq.incrementAndGet(); 81 | if (sequence >= 10000000) { 82 | this.seq.compareAndSet(sequence, 0); 83 | } 84 | String connId = addr + ":0"; 85 | MetaData meta = new MetaData(); 86 | meta.setSequence(sequence); 87 | meta.setDirection(0); 88 | meta.setTrace(t); 89 | ServerContextData sctx = new ServerContextData(connId, meta, ctx); 90 | ServerLogContextData.set(sctx); 91 | try { 92 | joinPoint.proceed(); 93 | ctx.stopForServer(0); 94 | this.log(span, sctx, action, message, null); 95 | } catch (Throwable throwable) { 96 | ctx.stopForServer(-620); 97 | this.log(span, sctx, action, message, throwable); 98 | throw throwable; 99 | } 100 | } else { 101 | joinPoint.proceed(); 102 | } 103 | } 104 | 105 | private void log(Span span, ServerContextData sctx, Action action, Message m, Throwable throwable) { 106 | try { 107 | this.logInternal(span, sctx, action, m, throwable); 108 | } catch (Throwable t) { 109 | log.error("logInternal exception", t); 110 | } 111 | } 112 | 113 | private void logInternal(Span span, ServerContextData sctx, Action action, Message m, Throwable throwable) { 114 | if (span == null) { 115 | log.error("span is null in RabbitMqAuditRecvAspect"); 116 | } else { 117 | TraceContext ctx = sctx.getTraceContext(); 118 | if (AsyncAuditLog.isMqRecvEnabled()) { 119 | TraceData trace = ctx.getTrace(); 120 | String traceId = trace.getTraceId(); 121 | String timestamp = AsyncAuditLog.now(ctx, span); 122 | String connId = sctx.getConnId(); 123 | int sequence = sctx.getMeta().getSequence(); 124 | String spanId = trace.getSpanId(); 125 | Map reqParams = this.genReqParams(m); 126 | Map resParams = this.genResParams(throwable); 127 | Log( 128 | AsyncAuditLog.mqRecvAuditLog, 129 | generateLogMap( 130 | timestamp, 131 | traceId, 132 | spanId, 133 | span.getRootSpan().getAction(), 134 | connId, 135 | action, 136 | sequence, 137 | span.getTimeUsedMs(), 138 | reqParams, 139 | resParams, 140 | genException(throwable), 141 | EnvConfigProps.mqLogMaxChars)); 142 | } 143 | } 144 | } 145 | 146 | public Map genReqParams(Message m) { 147 | Map reqParams = new HashMap<>(); 148 | reqParams.put("queue", m.getMessageProperties().getConsumerQueue()); 149 | reqParams.put("deliverTag", m.getMessageProperties().getDeliveryTag()); 150 | reqParams.put("body", AsyncAuditLog.bytesToTextAndEscape(m.getBody())); 151 | return reqParams; 152 | } 153 | 154 | public Map genResParams(Throwable throwable) { 155 | Map map = new HashMap<>(); 156 | if (throwable != null) { 157 | String message = AsyncAuditLog.escapeText(throwable.getMessage()); 158 | map.put("exception", message); 159 | return map; 160 | } else { 161 | return map; 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/log/LoggerFormatter.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.log; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.auditlog.AsyncAuditLogPool; 5 | import fun.pxyc.onelogger.autoconfig.GlobalConfig; 6 | import fun.pxyc.onelogger.utils.JsonUtil; 7 | import java.lang.reflect.Method; 8 | import java.lang.reflect.Parameter; 9 | import java.util.ArrayList; 10 | import java.util.LinkedHashMap; 11 | import java.util.List; 12 | import java.util.Map; 13 | import org.aopalliance.intercept.MethodInvocation; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.slf4j.MDC; 17 | 18 | public class LoggerFormatter { 19 | 20 | private static LoggerFormatterHandler handler; 21 | private static List loggerFilters = new ArrayList<>(); 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(LoggerFormatter.class); 24 | 25 | protected static void Log(Logger logger, Map params) { 26 | boolean infoEnabled = logger.isInfoEnabled(); 27 | if (!infoEnabled || handler == null) { 28 | return; 29 | } 30 | params.forEach((k, v) -> { 31 | if (k != null && v != null) { 32 | if ("throwable".equals(k)) { 33 | MDC.put(k, ""); 34 | } 35 | MDC.put(k, v.toString()); 36 | } 37 | }); 38 | for (LoggerFilter loggerFilter : loggerFilters) { 39 | params = loggerFilter.beforeBuildLogStr(params); 40 | } 41 | String logStr = handler.logStr(params); 42 | 43 | for (LoggerFilter loggerFilter : loggerFilters) { 44 | logStr = loggerFilter.afterBuildLogStr(logStr); 45 | } 46 | AsyncAuditLogPool.asyncLog(logger, logStr); 47 | } 48 | 49 | public static void setHandler(LoggerFormatterHandler handler) { 50 | LoggerFormatter.handler = handler; 51 | } 52 | 53 | public static void setFilters(List filters) { 54 | if (filters != null) { 55 | LoggerFormatter.loggerFilters = filters; 56 | } 57 | } 58 | 59 | public static void addFilter(LoggerFilter filter) { 60 | if (LoggerFormatter.loggerFilters == null) { 61 | LoggerFormatter.loggerFilters = new ArrayList<>(); 62 | } 63 | LoggerFormatter.loggerFilters.add(filter); 64 | } 65 | 66 | public static Map generateLogMap( 67 | String timestamp, 68 | String traceId, 69 | String spanId, 70 | String rootSpan, 71 | String connId, 72 | Action action, 73 | Integer sequence, 74 | String timeUsedMs, 75 | Map reqParams, 76 | Map resData, 77 | String exception, 78 | int logSize) { 79 | Map logMap = new LinkedHashMap<>(); 80 | logMap.put("timestamp", timestamp); 81 | logMap.put("traceId", traceId); 82 | logMap.put("applicationName", GlobalConfig.globalServiceName); 83 | logMap.put("logType", action.getLogType()); 84 | logMap.put("action", action.getAction()); 85 | logMap.put("rootSpan", rootSpan); 86 | logMap.put("interface", action.getInterfaceName()); 87 | logMap.put("method", action.getMethod()); 88 | logMap.put("connId", connId); 89 | logMap.put("sourceIp", GlobalConfig.sourceIp); 90 | logMap.put("spanId", spanId); 91 | logMap.put("sequence", sequence); 92 | logMap.put("cost", getCost(timeUsedMs)); 93 | String reqStr = reqParams != null ? JsonUtil.toJson(reqParams) : ""; 94 | if (reqStr == null) { 95 | logger.error("generate reqJsonStr error,reqParams is {}", JsonUtil.toJson(reqParams)); 96 | reqStr = ""; 97 | } 98 | if (logSize != -1 && logSize != 0) { 99 | if (reqStr.length() > logSize - 3) { 100 | reqStr = reqStr.substring(0, logSize - 3); 101 | reqStr = reqStr + "..."; 102 | } 103 | } 104 | String resStr = resData != null ? JsonUtil.toJson(resData) : ""; 105 | if (resStr == null) { 106 | logger.error("generate reqJsonStr error,reqParams is {}", JsonUtil.toJson(reqParams)); 107 | resStr = ""; 108 | } 109 | if (logSize != -1 && logSize != 0) { 110 | if (resStr.length() > logSize - 3) { 111 | resStr = resStr.substring(0, logSize - 3); 112 | resStr = resStr + "..."; 113 | } 114 | } 115 | logMap.put("reqStr", reqStr); 116 | logMap.put("resStr", resStr); 117 | if (exception != null && !exception.isEmpty()) { 118 | logMap.put("throwable", exception); 119 | } 120 | return logMap; 121 | } 122 | 123 | public static Long getCost(String timeUsedMs) { 124 | if (timeUsedMs.contains("ms")) { 125 | return Long.valueOf(timeUsedMs.split("ms")[0]); 126 | } else if (timeUsedMs.contains("ns")) { 127 | Long cost = Long.valueOf(timeUsedMs.split("ns")[0]); 128 | return cost / 1000L; 129 | } 130 | return 0L; 131 | } 132 | 133 | public String genException(Throwable throwable) { 134 | if (throwable != null) { 135 | return AsyncAuditLog.toTextAndEscape(throwable.getMessage(), -1); 136 | } 137 | return ""; 138 | } 139 | 140 | public Map generateLogMapWithExtraInfo( 141 | String timestamp, 142 | String traceId, 143 | String spanId, 144 | String rootSpan, 145 | String connId, 146 | Action action, 147 | Integer sequence, 148 | String timeUsedMs, 149 | Map reqParams, 150 | Map resData, 151 | String exception, 152 | String extraInfo, 153 | int logSize) { 154 | 155 | Map logMap = generateLogMap( 156 | timestamp, 157 | traceId, 158 | spanId, 159 | rootSpan, 160 | connId, 161 | action, 162 | sequence, 163 | timeUsedMs, 164 | reqParams, 165 | resData, 166 | exception, 167 | logSize); 168 | logMap.put("extraInfo", extraInfo); 169 | return logMap; 170 | } 171 | 172 | public Map getJsonReq(MethodInvocation joinPoint) { 173 | Map req = new LinkedHashMap<>(); 174 | Object[] arguments = joinPoint.getArguments(); 175 | Method method = joinPoint.getMethod(); 176 | Parameter[] parameters = method.getParameters(); 177 | for (int i = 0; i < arguments.length; i++) { 178 | Parameter parameter = parameters[i]; 179 | req.put(parameter.getName(), JsonUtil.toJsonObject(arguments[i])); 180 | } 181 | return req; 182 | } 183 | 184 | public Map getJsonRes(Object retObj) { 185 | Map res = new LinkedHashMap<>(); 186 | if (retObj != null) { 187 | Map o = JsonUtil.toJsonMap(retObj); 188 | res.putAll(o); 189 | } 190 | return res; 191 | } 192 | 193 | public String getConnId() { 194 | return "0.0.0.0:0:0"; 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/utils/JsonUtil.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.utils; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude.Include; 4 | import com.fasterxml.jackson.core.type.TypeReference; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.google.gson.Gson; 7 | import com.jayway.jsonpath.JsonPath; 8 | import java.util.*; 9 | import java.util.concurrent.TimeUnit; 10 | import org.apache.commons.lang3.StringUtils; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | 14 | public class JsonUtil { 15 | 16 | private static final Logger log = LoggerFactory.getLogger(JsonUtil.class); 17 | private static final ObjectMapper mapper = new ObjectMapper(); 18 | private static final Gson gson = new Gson(); 19 | 20 | static { 21 | mapper.setSerializationInclusion(Include.NON_NULL); 22 | } 23 | 24 | public JsonUtil() {} 25 | 26 | public static String toJson(Object o) { 27 | try { 28 | return mapper.writeValueAsString(o); 29 | } catch (Exception e) { 30 | try { 31 | return gsonParser(o); 32 | } catch (Exception e1) { 33 | log.error("json convert exception", e1); 34 | } 35 | } 36 | return null; 37 | } 38 | 39 | public static String gsonParser(Object o) { 40 | try { 41 | return gson.toJson(o); 42 | } catch (Exception e) { 43 | log.error("json convert exception", e); 44 | return null; 45 | } 46 | } 47 | 48 | public static Object toJsonObject(Object o) { 49 | if (o == null) { 50 | return ""; 51 | } else if (o instanceof String) { 52 | return o; 53 | } else if (o instanceof Number) { 54 | return o.toString(); 55 | } else if (o instanceof TimeUnit) { 56 | return o.toString(); 57 | } else if (o instanceof Map) { 58 | return toMap(toJson(o)); 59 | } else if (o instanceof List) { 60 | return toList(toJson(o)); 61 | } else { 62 | if (o instanceof Date) { 63 | String s = o.toString(); 64 | int p = s.lastIndexOf("."); 65 | return p >= 0 ? s.substring(0, p) : s; 66 | } else { 67 | String s = JsonUtil.toJson(o); 68 | return s == null ? o.toString() : s; 69 | } 70 | } 71 | } 72 | 73 | public static Map toJsonMap(Object o) { 74 | HashMap map = new HashMap<>(); 75 | if (o == null) { 76 | return map; 77 | } else if (o instanceof String) { 78 | map.put("data", o); 79 | return map; 80 | } else if (o instanceof Number) { 81 | map.put("number", o); 82 | return map; 83 | } else if (o instanceof TimeUnit) { 84 | map.put("timeunit", o); 85 | return map; 86 | } else if (o instanceof Map) { 87 | return toMap(toJson(o)); 88 | } else if (o instanceof List) { 89 | List objects = toList(toJson(o)); 90 | map.put("list", objects); 91 | return map; 92 | } else { 93 | if (o instanceof Date) { 94 | String s = o.toString(); 95 | int p = s.lastIndexOf("."); 96 | map.put("date", p >= 0 ? s.substring(0, p) : s); 97 | return map; 98 | } else { 99 | return toMap(toJson(o)); 100 | } 101 | } 102 | } 103 | 104 | public static Map toMap(String s) { 105 | if (s == null || s.isEmpty()) { 106 | return null; 107 | } 108 | try { 109 | Map results = mapper.readValue(s, new TypeReference>() {}); 110 | return results; 111 | } catch (Exception e) { 112 | log.error("json convert exception", e); 113 | return null; 114 | } 115 | } 116 | 117 | public static Map findJsonFieldValue(String json, List jsonField) { 118 | Map indexValue = new LinkedHashMap<>(); 119 | for (String index : jsonField) { 120 | if (StringUtils.isEmpty(index)) { 121 | continue; 122 | } 123 | try { 124 | Object read = JsonPath.parse(json).read(index, String.class); 125 | if (read != null) { 126 | indexValue.put(index, read); 127 | } 128 | } catch (Exception e) { 129 | } 130 | } 131 | return indexValue; 132 | } 133 | 134 | public static HashMap toHashMap(String s) { 135 | try { 136 | HashMap results = mapper.readValue(s, new TypeReference>() {}); 137 | return results; 138 | } catch (Exception e) { 139 | log.error("json convert exception", e); 140 | return null; 141 | } 142 | } 143 | 144 | public static LinkedHashMap toLinkedHashMap(String s) { 145 | try { 146 | return mapper.readValue(s, new TypeReference>() {}); 147 | } catch (Exception e) { 148 | log.error("json convert exception", e); 149 | return null; 150 | } 151 | } 152 | 153 | public static TreeMap toTreeMap(String s) { 154 | try { 155 | return mapper.readValue(s, new TypeReference>() {}); 156 | } catch (Exception e) { 157 | log.error("json convert exception", e); 158 | return null; 159 | } 160 | } 161 | 162 | public static List toList(String s) { 163 | try { 164 | return mapper.readValue(s, new TypeReference>() {}); 165 | } catch (Exception e) { 166 | log.error("json convert exception", e); 167 | return null; 168 | } 169 | } 170 | 171 | public static ArrayList toArrayList(String s) { 172 | try { 173 | return mapper.readValue(s, new TypeReference>() {}); 174 | } catch (Exception e) { 175 | log.error("json convert exception", e); 176 | return null; 177 | } 178 | } 179 | 180 | public static LinkedList toLinkedList(String s) { 181 | try { 182 | return mapper.readValue(s, new TypeReference>() {}); 183 | } catch (Exception e) { 184 | log.error("json convert exception", e); 185 | return null; 186 | } 187 | } 188 | 189 | public static Object toRawObject(String s, Class cls) { 190 | try { 191 | Object results = mapper.readValue(s, cls); 192 | return results; 193 | } catch (Exception e) { 194 | log.error("json convert exception", e); 195 | return null; 196 | } 197 | } 198 | 199 | public static T toObject(String s, Class cls) { 200 | try { 201 | T results = mapper.readValue(s, cls); 202 | return results; 203 | } catch (Exception e) { 204 | log.error("json convert exception", e); 205 | return null; 206 | } 207 | } 208 | 209 | public static T toObject(String s, TypeReference tr) { 210 | try { 211 | T results = mapper.readValue(s, tr); 212 | return results; 213 | } catch (Exception e) { 214 | log.error("json convert exception", e); 215 | return null; 216 | } 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/kafka/KafkaAuditLogListenerAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.kafka; 2 | 3 | import fun.pxyc.onelogger.ServerContextData; 4 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 5 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 6 | import fun.pxyc.onelogger.log.Action; 7 | import fun.pxyc.onelogger.log.LoggerFormatter; 8 | import fun.pxyc.onelogger.trace.*; 9 | import java.util.HashMap; 10 | import java.util.Iterator; 11 | import java.util.List; 12 | import java.util.Map; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | import org.apache.kafka.clients.consumer.Consumer; 15 | import org.apache.kafka.clients.consumer.ConsumerRecord; 16 | import org.aspectj.lang.ProceedingJoinPoint; 17 | import org.aspectj.lang.annotation.Around; 18 | import org.aspectj.lang.annotation.Aspect; 19 | import org.aspectj.lang.annotation.Pointcut; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | @Aspect 24 | public class KafkaAuditLogListenerAspect extends LoggerFormatter { 25 | private static final Logger log = LoggerFactory.getLogger(KafkaAuditLogListenerAspect.class); 26 | private final AtomicInteger seq = new AtomicInteger(0); 27 | 28 | public KafkaAuditLogListenerAspect() {} 29 | 30 | @Pointcut("@annotation(org.springframework.kafka.annotation.KafkaListener)") 31 | public void kafkaAuditLogListenerPointCut() {} 32 | 33 | @Around("kafkaAuditLogListenerPointCut()") 34 | public void kafkaAuditLogListenerAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 35 | this.doAdvice(joinPoint); 36 | } 37 | 38 | public void doAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 39 | Object[] args = joinPoint.getArgs(); 40 | Consumer consumer = null; 41 | ConsumerRecord m = null; 42 | List mlist = null; 43 | int length = args.length; 44 | for (int i = 0; i < length; ++i) { 45 | Object arg = args[i]; 46 | if (arg instanceof Consumer) { 47 | consumer = (Consumer) arg; 48 | } 49 | 50 | if (arg instanceof ConsumerRecord) { 51 | m = (ConsumerRecord) arg; 52 | } 53 | 54 | if (arg instanceof List) { 55 | List list = (List) arg; 56 | if (list.size() > 0 && list.get(0) instanceof ConsumerRecord) { 57 | mlist = list; 58 | } 59 | } 60 | } 61 | 62 | if ((m != null || mlist != null) && consumer != null) { 63 | String address = this.getAddress(consumer); 64 | TraceData t = AsyncAuditLog.newTrace(address); 65 | String interfaceName = 66 | joinPoint.getTarget().getClass().getSimpleName().toLowerCase() + "." 67 | + joinPoint.getSignature().getName().toLowerCase(); 68 | String serviceNameMsgName = "KAFKA." + "RECEIVE"; 69 | Action action = new Action( 70 | "KAFKA.RECEIVE", 71 | joinPoint.getTarget().getClass().getName(), 72 | joinPoint.getSignature().getName(), 73 | serviceNameMsgName); 74 | Span span = Trace.startForServer(t, "KAFKASERVER", interfaceName); 75 | span.setRemoteAddr(address); 76 | TraceContext ctx = Trace.currentContext(); 77 | int sequence = this.seq.incrementAndGet(); 78 | if (sequence >= 10000000) { 79 | this.seq.compareAndSet(sequence, 0); 80 | } 81 | 82 | String connId = address + ":0"; 83 | MetaData meta = new MetaData(); 84 | meta.setSequence(sequence); 85 | meta.setDirection(1); 86 | meta.setTrace(t); 87 | ServerContextData sctx = new ServerContextData(connId, meta, ctx); 88 | ServerLogContextData.set(sctx); 89 | try { 90 | joinPoint.proceed(); 91 | ctx.stopForServer(0); 92 | if (m != null) { 93 | this.log(span, sctx, action, m, null); 94 | } else { 95 | this.log(span, sctx, action, mlist, null); 96 | } 97 | 98 | } catch (Throwable throwable) { 99 | ctx.stopForServer(-1); 100 | if (m != null) { 101 | this.log(span, sctx, action, m, throwable); 102 | } else { 103 | this.log(span, sctx, action, mlist, throwable); 104 | } 105 | throw throwable; 106 | } 107 | } else { 108 | joinPoint.proceed(); 109 | } 110 | } 111 | 112 | String getAddress(Consumer consumer) { 113 | return KafkaInstrument.getIpPort(consumer); 114 | } 115 | 116 | private void log(Span span, ServerContextData sctx, Action action, ConsumerRecord m, Throwable throwable) { 117 | try { 118 | this.logInternal(span, sctx, action, m, throwable); 119 | } catch (Throwable t) { 120 | log.error("logInternal exception", t); 121 | } 122 | } 123 | 124 | private void log(Span span, ServerContextData sctx, Action action, List mlist, Throwable throwable) { 125 | Iterator m = mlist.iterator(); 126 | while (m.hasNext()) { 127 | ConsumerRecord mr = (ConsumerRecord) m.next(); 128 | try { 129 | this.logInternal(span, sctx, action, mr, throwable); 130 | } catch (Throwable t) { 131 | log.error("logInternal exception", t); 132 | } 133 | } 134 | } 135 | 136 | private void logInternal( 137 | Span span, ServerContextData sctx, Action action, ConsumerRecord m, Throwable throwable) { 138 | if (span == null) { 139 | log.error("span is null in KafkaAuditLogListenerAspect"); 140 | } else { 141 | TraceContext ctx = sctx.getTraceContext(); 142 | MetaData meta = sctx.getMeta(); 143 | TraceData trace = ctx.getTrace(); 144 | String timestamp = AsyncAuditLog.now(ctx, span); 145 | if (AsyncAuditLog.isKafkaRecvEnabled()) { 146 | LoggerFormatter.Log( 147 | AsyncAuditLog.kafkaRecvAuditLog, 148 | generateLogMap( 149 | timestamp, 150 | trace.getTraceId(), 151 | trace.getSpanId(), 152 | span.getRootSpan().getAction(), 153 | sctx.getConnId(), 154 | action, 155 | meta.getSequence(), 156 | span.getTimeUsedMs(), 157 | genReqParams(m), 158 | genResParams(throwable), 159 | genException(throwable), 160 | EnvConfigProps.kafkaLogMaxChars)); 161 | } 162 | } 163 | } 164 | 165 | Map genReqParams(ConsumerRecord m) { 166 | Map reqMap = new HashMap<>(); 167 | reqMap.put("partition", m.partition()); 168 | reqMap.put("topic", m.topic()); 169 | reqMap.put("offset", m.offset()); 170 | reqMap.put("body", m.value()); 171 | return reqMap; 172 | } 173 | 174 | private Map genResParams(Throwable throwable) { 175 | Map resMap = new HashMap<>(); 176 | if (throwable != null) { 177 | String exceptionStr = AsyncAuditLog.escapeText(throwable.getMessage()); 178 | resMap.put("exception", exceptionStr); 179 | } 180 | return resMap; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/kafka/KafkaAuditLogSendAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.kafka; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.Trace; 9 | import fun.pxyc.onelogger.trace.TraceContext; 10 | import fun.pxyc.onelogger.trace.TraceData; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | import java.util.concurrent.atomic.AtomicInteger; 14 | import org.aspectj.lang.ProceedingJoinPoint; 15 | import org.aspectj.lang.annotation.Around; 16 | import org.aspectj.lang.annotation.Aspect; 17 | import org.aspectj.lang.annotation.Pointcut; 18 | import org.aspectj.lang.reflect.CodeSignature; 19 | import org.slf4j.Logger; 20 | import org.slf4j.LoggerFactory; 21 | import org.springframework.kafka.core.KafkaTemplate; 22 | import org.springframework.kafka.support.SendResult; 23 | import org.springframework.messaging.support.GenericMessage; 24 | import org.springframework.util.concurrent.ListenableFuture; 25 | import org.springframework.util.concurrent.ListenableFutureCallback; 26 | 27 | @Aspect 28 | public class KafkaAuditLogSendAspect extends LoggerFormatter { 29 | private static final Logger log = LoggerFactory.getLogger(KafkaAuditLogSendAspect.class); 30 | private final AtomicInteger seq = new AtomicInteger(0); 31 | 32 | public KafkaAuditLogSendAspect() {} 33 | 34 | @Pointcut( 35 | "execution(* org.springframework.kafka.core.KafkaTemplate.send(..))||execution(* org.springframework.kafka.core.KafkaTemplate.sendDefault(..))") 36 | public void kafkaAuditLogSendPointCut() {} 37 | 38 | @Around("kafkaAuditLogSendPointCut()") 39 | public Object kafkaAuditLogSendAdvice(final ProceedingJoinPoint joinPoint) throws Throwable { 40 | final Span span = Trace.startAsync( 41 | "KAFKA.send", 42 | joinPoint.getTarget().getClass().getSimpleName().toLowerCase() + "." 43 | + joinPoint.getSignature().getName()); 44 | final TraceContext ctx = Trace.currentContext(); 45 | Object result = null; 46 | try { 47 | result = joinPoint.proceed(); 48 | span.stop(true); 49 | ListenableFuture> future = (ListenableFuture) result; 50 | future.addCallback(new ListenableFutureCallback>() { 51 | public void onFailure(Throwable throwable) { 52 | long now = System.nanoTime() / 1000L; 53 | KafkaAuditLogSendAspect.this.log(span, now, ctx, joinPoint, throwable); 54 | String targetAddrs = KafkaInstrument.getTargetAddrs((KafkaTemplate) joinPoint.getTarget()); 55 | // TODO 对接告警 ??? 56 | KafkaErrorCounter.addErrorCount(targetAddrs); 57 | } 58 | 59 | public void onSuccess(SendResult stringStringSendResult) { 60 | long now = System.nanoTime() / 1000L; 61 | KafkaAuditLogSendAspect.this.log(span, now, ctx, joinPoint, null); 62 | } 63 | }); 64 | return result; 65 | } catch (Throwable t) { 66 | span.stop(false); 67 | long now = System.nanoTime() / 1000L; 68 | this.log(span, now, ctx, joinPoint, t); 69 | String targetAddrs = KafkaInstrument.getTargetAddrs((KafkaTemplate) joinPoint.getTarget()); 70 | KafkaErrorCounter.addErrorCount(targetAddrs); 71 | throw t; 72 | } 73 | } 74 | 75 | private void log(Span span, long now, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable e) { 76 | try { 77 | this.logInternal(span, now, ctx, joinPoint, e); 78 | } catch (Exception ex) { 79 | log.error("logInternal exception", ex); 80 | } 81 | } 82 | 83 | private void logInternal(Span span, long now, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable e) { 84 | Action action = new Action( 85 | "KAFKA.send", 86 | joinPoint.getTarget().getClass().getName(), 87 | joinPoint.getSignature().getName(), 88 | "KAFKA.send"); 89 | Object[] args = joinPoint.getArgs(); 90 | String[] paramNames = ((CodeSignature) joinPoint.getSignature()).getParameterNames(); 91 | String defaultTopic = ((KafkaTemplate) joinPoint.getTarget()).getDefaultTopic(); 92 | TraceData trace = ctx.getTrace(); 93 | long duration = span.getTimeUsedMicros(); 94 | long fullDuration = now - span.getStartMicros(); 95 | String traceId = trace.getTraceId(); 96 | if (duration >= EnvConfigProps.slowKafkaMillis * 1000L) { 97 | log.warn("slow kafka: ts=" + duration + ", traceId=" + traceId); 98 | } 99 | if (AsyncAuditLog.isKafkaEnabled()) { 100 | String timestamp = AsyncAuditLog.now(ctx, now); 101 | String connId = this.genConnId(joinPoint); 102 | int sequence = this.seq.incrementAndGet(); 103 | if (sequence >= 10000000) { 104 | this.seq.compareAndSet(sequence, 0); 105 | } 106 | 107 | String spanId = span.getSpanId(); 108 | String rootSpan = span.getRootSpan().getAction(); 109 | String extraInfo = ""; 110 | extraInfo = extraInfo + this.genTopicName(args, paramNames, defaultTopic); 111 | extraInfo = extraInfo + "^aftercallback:" + fullDuration; 112 | Log( 113 | AsyncAuditLog.kafkaAuditLog, 114 | generateLogMapWithExtraInfo( 115 | timestamp, 116 | traceId, 117 | spanId, 118 | rootSpan, 119 | connId, 120 | action, 121 | sequence, 122 | span.getTimeUsedMs(), 123 | this.genReqParams(args, paramNames), 124 | this.genResParams(), 125 | genException(e), 126 | extraInfo, 127 | EnvConfigProps.kafkaLogMaxChars)); 128 | } 129 | } 130 | 131 | private String genConnId(ProceedingJoinPoint joinPoint) { 132 | String targetAddrs = KafkaInstrument.getTargetAddrs((KafkaTemplate) joinPoint.getTarget()); 133 | if (targetAddrs == null || targetAddrs.isEmpty()) { 134 | return "0.0.0.0:0:0"; 135 | } 136 | return targetAddrs; 137 | } 138 | 139 | private String genTopicName(Object[] args, String[] paramNames, String defaultTopic) { 140 | StringBuilder builder = new StringBuilder(); 141 | builder.append("topic:"); 142 | String topic = null; 143 | 144 | for (int i = 0; i < paramNames.length; ++i) { 145 | if (args[i] instanceof GenericMessage) { 146 | GenericMessage gm = (GenericMessage) args[i]; 147 | topic = (String) gm.getHeaders().get("kafka_topic"); 148 | break; 149 | } 150 | 151 | if ("topic".equalsIgnoreCase(paramNames[i])) { 152 | topic = args[i].toString(); 153 | break; 154 | } 155 | } 156 | 157 | if (topic == null || topic.isEmpty()) { 158 | topic = defaultTopic; 159 | } 160 | 161 | if (topic == null || topic.isEmpty()) { 162 | topic = "default"; 163 | } 164 | 165 | builder.append(topic); 166 | return builder.toString(); 167 | } 168 | 169 | private Map genReqParams(Object[] args, String[] paramNames) { 170 | Map req = new HashMap<>(); 171 | for (int i = 0; i < args.length; ++i) { 172 | if (!"topic".equals(paramNames[i])) { 173 | req.put(paramNames[i], AsyncAuditLog.toTextAndEscape(args[i])); 174 | } 175 | } 176 | return req; 177 | } 178 | 179 | private Map genResParams() { 180 | return new HashMap<>(); 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/redis/RedisAuditLogAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.redis; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.redis.proxy.*; 6 | import fun.pxyc.onelogger.trace.Span; 7 | import fun.pxyc.onelogger.trace.Trace; 8 | import fun.pxyc.onelogger.trace.TraceContext; 9 | import fun.pxyc.onelogger.trace.TraceData; 10 | import org.aspectj.lang.ProceedingJoinPoint; 11 | import org.aspectj.lang.annotation.Around; 12 | import org.aspectj.lang.annotation.Aspect; 13 | import org.aspectj.lang.annotation.Pointcut; 14 | import org.slf4j.Logger; 15 | import org.slf4j.LoggerFactory; 16 | import org.springframework.data.redis.core.*; 17 | 18 | @Aspect 19 | public class RedisAuditLogAspect { 20 | private static final Logger log = LoggerFactory.getLogger(RedisAuditLogAspect.class); 21 | 22 | public RedisAuditLogAspect() {} 23 | 24 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForValue(..))") 25 | public void opsForValuePointCut() {} 26 | 27 | @Around("opsForValuePointCut()") 28 | public Object opsForValueAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 29 | Object object = joinPoint.proceed(); 30 | return new ValueOperationsProxy((ValueOperations) object); 31 | } 32 | 33 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForList(..))") 34 | public void opsForListPointCut() {} 35 | 36 | @Around("opsForListPointCut()") 37 | public Object opsForListAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 38 | 39 | Object object = joinPoint.proceed(); 40 | return new ListOperationsProxy((ListOperations) object); 41 | } 42 | 43 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForHash(..))") 44 | public void opsForHashPointCut() {} 45 | 46 | @Around("opsForHashPointCut()") 47 | public Object opsForHashAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 48 | 49 | Object object = joinPoint.proceed(); 50 | return new HashOperationsProxy((HashOperations) object); 51 | } 52 | 53 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForSet(..))") 54 | public void opsForSetPointCut() {} 55 | 56 | @Around("opsForSetPointCut()") 57 | public Object opsForSetAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 58 | 59 | Object object = joinPoint.proceed(); 60 | return new SetOperationsProxy((SetOperations) object); 61 | } 62 | 63 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForZSet(..))") 64 | public void opsForZSetPointCut() {} 65 | 66 | @Around("opsForZSetPointCut()") 67 | public Object opsForZSetAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 68 | 69 | Object object = joinPoint.proceed(); 70 | return new ZSetOperationsProxy((ZSetOperations) object); 71 | } 72 | 73 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.opsForGeo(..))") 74 | public void opsForGeoPointCut() {} 75 | 76 | @Around("opsForGeoPointCut()") 77 | public Object opsForGeoAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 78 | 79 | Object object = joinPoint.proceed(); 80 | return new GeoOperationsProxy((GeoOperations) object); 81 | } 82 | 83 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.boundValueOps(..))") 84 | public void boundValueOpsPointCut() {} 85 | 86 | @Around("boundValueOpsPointCut()") 87 | public Object boundValueOpsAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 88 | RedisTemplate t = (RedisTemplate) joinPoint.getTarget(); 89 | 90 | Object[] args = joinPoint.getArgs(); 91 | return new BoundValueOperationsProxy(t.boundValueOps(args[0])); 92 | } 93 | 94 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.boundListOps(..))") 95 | public void boundListOpsPointCut() {} 96 | 97 | @Around("boundListOpsPointCut()") 98 | public Object boundListOpsAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 99 | RedisTemplate t = (RedisTemplate) joinPoint.getTarget(); 100 | 101 | Object[] args = joinPoint.getArgs(); 102 | return new BoundListOperationsProxy(t.boundListOps(args[0])); 103 | } 104 | 105 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.boundHashOps(..))") 106 | public void boundHashOpsPointCut() {} 107 | 108 | @Around("boundHashOpsPointCut()") 109 | public Object boundHashOpsAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 110 | RedisTemplate t = (RedisTemplate) joinPoint.getTarget(); 111 | 112 | Object[] args = joinPoint.getArgs(); 113 | 114 | return new BoundHashOperationsProxy(t.boundHashOps(args[0])); 115 | } 116 | 117 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.boundSetOps(..))") 118 | public void boundSetOpsPointCut() {} 119 | 120 | @Around("boundSetOpsPointCut()") 121 | public Object boundSetOpsAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 122 | RedisTemplate t = (RedisTemplate) joinPoint.getTarget(); 123 | 124 | Object[] args = joinPoint.getArgs(); 125 | return new BoundSetOperationsProxy(t.boundSetOps(args[0])); 126 | } 127 | 128 | @Pointcut("execution(* org.springframework.data.redis.core.RedisTemplate.boundZSetOps(..))") 129 | public void boundZSetOpsPointCut() {} 130 | 131 | @Around("boundZSetOpsPointCut()") 132 | public Object boundZSetOpsAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 133 | RedisTemplate t = (RedisTemplate) joinPoint.getTarget(); 134 | 135 | Object[] args = joinPoint.getArgs(); 136 | 137 | return new BoundZSetOperationsProxy(t.boundZSetOps(args[0])); 138 | } 139 | 140 | @Pointcut( 141 | "execution(* org.springframework.data.redis.core.RedisTemplate.delete(..))||execution(* org.springframework.data.redis.core.RedisTemplate.rename(..))||execution(* org.springframework.data.redis.core.RedisTemplate.expire(..)) ||execution(* org.springframework.data.redis.core.RedisTemplate.expireAt(..)) ||execution(* org.springframework.data.redis.core.RedisTemplate.hasKey(..)) ||execution(* org.springframework.data.redis.core.RedisTemplate.getExpire(..)) ||execution(* org.springframework.data.redis.core.RedisTemplate.keys(..))") 142 | public void appointPointCut() {} 143 | 144 | @Around("appointPointCut()") 145 | public Object appointAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 146 | 147 | String methodName = joinPoint.getSignature().getName(); 148 | Span span = Trace.startAsync("REDIS", methodName.toLowerCase()); 149 | TraceContext ctx = Trace.currentContext(); 150 | Object o = null; 151 | 152 | try { 153 | o = joinPoint.proceed(); 154 | span.stop(true); 155 | this.log(o, span, ctx, joinPoint, null); 156 | return o; 157 | } catch (Throwable throwable) { 158 | span.stop(false); 159 | this.log(null, span, ctx, joinPoint, throwable); 160 | throw throwable; 161 | } 162 | } 163 | 164 | private void log(Object res, Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable throwable) { 165 | try { 166 | this.logInternal(res, span, ctx, joinPoint, throwable); 167 | } catch (Throwable t) { 168 | log.error("logInternal exception", t); 169 | } 170 | } 171 | 172 | private void logInternal( 173 | Object res, Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Throwable throwable) { 174 | Object[] args = joinPoint.getArgs(); 175 | TraceData trace = ctx.getTrace(); 176 | String traceId = trace.getTraceId(); 177 | String methodName = joinPoint.getSignature().getName().toLowerCase(); 178 | long duration = span.getTimeUsedMicros(); 179 | if (duration >= EnvConfigProps.slowRedisMillis * 1000L) { 180 | log.warn("slow redis: ts=" + duration + ", traceId=" + traceId); 181 | } 182 | 183 | if (AsyncAuditLog.isRedisEnabled()) { 184 | RedisLogFormat.log(res, span, methodName, ctx, throwable, args); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/elasticsearch/ElasticSearchAuditLogAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.elasticsearch; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.Trace; 9 | import fun.pxyc.onelogger.trace.TraceContext; 10 | import fun.pxyc.onelogger.trace.TraceData; 11 | import java.io.BufferedReader; 12 | import java.io.IOException; 13 | import java.io.InputStreamReader; 14 | import java.lang.reflect.Field; 15 | import java.nio.charset.Charset; 16 | import java.nio.charset.StandardCharsets; 17 | import java.util.HashMap; 18 | import java.util.LinkedHashMap; 19 | import java.util.Map; 20 | import java.util.concurrent.atomic.AtomicInteger; 21 | import org.apache.http.HttpEntity; 22 | import org.apache.http.HttpResponse; 23 | import org.apache.http.entity.BufferedHttpEntity; 24 | import org.apache.http.entity.ContentType; 25 | import org.apache.http.util.EntityUtils; 26 | import org.aspectj.lang.ProceedingJoinPoint; 27 | import org.aspectj.lang.annotation.Around; 28 | import org.aspectj.lang.annotation.Aspect; 29 | import org.aspectj.lang.annotation.Pointcut; 30 | import org.elasticsearch.client.Request; 31 | import org.elasticsearch.client.Response; 32 | import org.slf4j.Logger; 33 | import org.slf4j.LoggerFactory; 34 | import org.springframework.util.CollectionUtils; 35 | 36 | @Aspect 37 | public class ElasticSearchAuditLogAspect extends LoggerFormatter { 38 | 39 | private static final Logger log = LoggerFactory.getLogger(ElasticSearchAuditLogAspect.class); 40 | 41 | private final AtomicInteger seq = new AtomicInteger(0); 42 | 43 | public ElasticSearchAuditLogAspect() {} 44 | 45 | @Pointcut("execution(* org.elasticsearch.client.RestClient.performRequest(org.elasticsearch.client.Request))") 46 | public void elasticSearchPointCut() {} 47 | 48 | @Around("elasticSearchPointCut()") 49 | public Object elasticSearchAuditAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 50 | return this.doAdvice(joinPoint); 51 | } 52 | 53 | public Object doAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 54 | Object[] args = joinPoint.getArgs(); 55 | String addr = ""; 56 | String action = joinPoint.getTarget().getClass().getSimpleName().toLowerCase() + "." 57 | + joinPoint.getSignature().getName().toLowerCase(); 58 | Span span = Trace.startAsync("ElasticSearch", action); 59 | span.setRemoteAddr(addr); 60 | try { 61 | if (args[0] instanceof Request) { 62 | Request req = (Request) args[0]; 63 | action = req.getEndpoint(); 64 | span.changeAction(action); 65 | Object result = joinPoint.proceed(args); 66 | if (result instanceof Response) { 67 | Response resp = (Response) result; 68 | Map propertyMap = reflectObjToMap(resp); 69 | if (!CollectionUtils.isEmpty(propertyMap) && propertyMap.containsKey("response")) { 70 | Object response = propertyMap.get("response"); 71 | span.setRemoteAddr(propertyMap.getOrDefault("host", "").toString()); 72 | span.stop(true); 73 | this.log(span, action, req, response, null); 74 | } 75 | } 76 | return result; 77 | } else { 78 | return joinPoint.proceed(args); 79 | } 80 | } catch (Throwable t) { 81 | span.stop(true); 82 | this.log(span, action, (Request) args[0], null, t); 83 | throw t; 84 | } 85 | } 86 | 87 | private void log(Span span, String action, Request req, Object result, Throwable throwable) { 88 | try { 89 | this.logInternal(span, action, req, result, throwable); 90 | } catch (Throwable t) { 91 | log.error("logInternal exception", t); 92 | } 93 | } 94 | 95 | private void logInternal(Span span, String action, Request req, Object result, Throwable throwable) { 96 | if (span == null) { 97 | log.error("span is null in ElasticSearchAuditLogAspect"); 98 | } else { 99 | if (AsyncAuditLog.isElasticSearchEnabled()) { 100 | TraceContext ctx = Trace.currentContext(); 101 | TraceData trace = ctx.getTrace(); 102 | String traceId = trace.getTraceId(); 103 | String timestamp = AsyncAuditLog.now(ctx, span); 104 | String connId = getConnId(); 105 | int sequence = this.seq.incrementAndGet(); 106 | if (sequence >= 10000000) { 107 | this.seq.compareAndSet(sequence, 0); 108 | } 109 | long duration = span.getTimeUsedMicros(); 110 | String spanId = trace.getSpanId(); 111 | String serviceNameMsgName = "ElasticSearch." + action; 112 | Map reqParams = new LinkedHashMap<>(); 113 | reqParams.put("req", getRequestParams(req)); 114 | Log( 115 | AsyncAuditLog.elasticSearchAuditLog, 116 | generateLogMap( 117 | timestamp, 118 | traceId, 119 | spanId, 120 | span.getRootSpan().getAction(), 121 | connId, 122 | new Action("ElasticSearch", action, action, serviceNameMsgName), 123 | sequence, 124 | duration / 1000 + "ms", 125 | reqParams, 126 | getResponseRes(result), 127 | genException(throwable), 128 | EnvConfigProps.esLogMaxChars)); 129 | } 130 | } 131 | } 132 | 133 | public String getRequestParams(Request request) { 134 | String requestLine = ""; 135 | try { 136 | if (request.getEntity() != null) { 137 | HttpEntity entity = request.getEntity(); 138 | if (!entity.isRepeatable()) { 139 | entity = new BufferedHttpEntity(entity); 140 | request.setEntity(entity); 141 | } 142 | requestLine += EntityUtils.toString(entity, StandardCharsets.UTF_8); 143 | } 144 | return requestLine; 145 | } catch (IOException e) { 146 | throw new RuntimeException(e); 147 | } 148 | } 149 | 150 | public Map getResponseRes(Object response) { 151 | StringBuilder responseLine = new StringBuilder(); 152 | try { 153 | if (response instanceof HttpResponse) { 154 | HttpResponse httpResponse = (HttpResponse) response; 155 | HttpEntity entity = httpResponse.getEntity(); 156 | if (entity != null) { 157 | if (!entity.isRepeatable()) { 158 | entity = new BufferedHttpEntity(entity); 159 | } 160 | httpResponse.setEntity(entity); 161 | ContentType contentType = ContentType.get(entity); 162 | Charset charset = StandardCharsets.UTF_8; 163 | if (contentType != null && contentType.getCharset() != null) { 164 | charset = contentType.getCharset(); 165 | } 166 | try (BufferedReader reader = 167 | new BufferedReader(new InputStreamReader(entity.getContent(), charset))) { 168 | String line; 169 | while ((line = reader.readLine()) != null) { 170 | responseLine.append(line); 171 | } 172 | } 173 | } 174 | } 175 | return getJsonRes(responseLine.toString()); 176 | } catch (IOException e) { 177 | throw new RuntimeException(e); 178 | } 179 | } 180 | 181 | private Map reflectObjToMap(Object obj) throws IllegalAccessException { 182 | Class traversalClass = obj.getClass(); 183 | Field[] declaredFields = traversalClass.getDeclaredFields(); 184 | Map propertyMap = new HashMap<>(); 185 | for (Field field : declaredFields) { 186 | field.setAccessible(true); 187 | propertyMap.put(field.getName(), field.get(obj)); 188 | } 189 | return propertyMap; 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/main/java/fun/pxyc/onelogger/http/RestTemplateAspect.java: -------------------------------------------------------------------------------- 1 | package fun.pxyc.onelogger.http; 2 | 3 | import fun.pxyc.onelogger.auditlog.AsyncAuditLog; 4 | import fun.pxyc.onelogger.autoconfig.EnvConfigProps; 5 | import fun.pxyc.onelogger.log.Action; 6 | import fun.pxyc.onelogger.log.LoggerFormatter; 7 | import fun.pxyc.onelogger.trace.Span; 8 | import fun.pxyc.onelogger.trace.Trace; 9 | import fun.pxyc.onelogger.trace.TraceContext; 10 | import fun.pxyc.onelogger.trace.TraceData; 11 | import java.util.*; 12 | import java.util.concurrent.atomic.AtomicInteger; 13 | import org.aspectj.lang.ProceedingJoinPoint; 14 | import org.aspectj.lang.annotation.Around; 15 | import org.aspectj.lang.annotation.Aspect; 16 | import org.aspectj.lang.annotation.Pointcut; 17 | import org.slf4j.Logger; 18 | import org.slf4j.LoggerFactory; 19 | import org.springframework.http.ResponseEntity; 20 | 21 | @Aspect 22 | public class RestTemplateAspect extends LoggerFormatter { 23 | private static final Logger log = LoggerFactory.getLogger(RestTemplateAspect.class); 24 | private static final Set logHeaderNames = new HashSet(); 25 | public static boolean logAllHeaders = false; 26 | 27 | static { 28 | logHeaderNames.add("content-type"); 29 | logHeaderNames.add("content-length"); 30 | } 31 | 32 | private final AtomicInteger seq = new AtomicInteger(0); 33 | 34 | public RestTemplateAspect() {} 35 | 36 | public static void initLogHeaderNames(String s) { 37 | if (s != null && !s.isEmpty()) { 38 | String[] ss = s.split(","); 39 | String[] strings = ss; 40 | int length = ss.length; 41 | 42 | for (int i = 0; i < length; ++i) { 43 | String t = strings[i]; 44 | logHeaderNames.add(t.toLowerCase()); 45 | } 46 | } 47 | } 48 | 49 | @Pointcut( 50 | "execution(* org.springframework.web.client.RestTemplate.exchange(..))||execution(* org.springframework.web.client.RestTemplate.getFor*(..))||execution(* org.springframework.web.client.RestTemplate.headFor*(..))||execution(* org.springframework.web.client.RestTemplate.postFor*(..))||execution(* org.springframework.web.client.RestTemplate.put*(..))||execution(* org.springframework.web.client.RestTemplate.delete*(..))") 51 | public void restTemplatePointCut() {} 52 | 53 | @Around("restTemplatePointCut()") 54 | public Object restTemplateAdvice(ProceedingJoinPoint joinPoint) throws Throwable { 55 | Span span = Trace.startAsync("HTTP", "SEND"); 56 | TraceContext ctx = Trace.currentContext(); 57 | HttpCallInfo callInfo = new HttpCallInfo(); 58 | HttpCallInfo.instance.set(callInfo); 59 | 60 | try { 61 | Object o = joinPoint.proceed(); 62 | span.stop(true); 63 | this.log(span, ctx, joinPoint, o, null); 64 | return o; 65 | } catch (Throwable t) { 66 | span.stop(false); 67 | callInfo.httpCode = 999; 68 | this.log(span, ctx, joinPoint, null, t); 69 | throw t; 70 | } 71 | } 72 | 73 | private void log(Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Object o, Throwable throwable) { 74 | try { 75 | this.logInternal(span, ctx, joinPoint, o, throwable); 76 | } catch (Throwable t) { 77 | log.error("logInternal exception", t); 78 | } 79 | } 80 | 81 | private void logInternal( 82 | Span span, TraceContext ctx, ProceedingJoinPoint joinPoint, Object retObj, Throwable throwable) { 83 | HttpCallInfo callInfo = HttpCallInfo.instance.get(); 84 | if (callInfo != null) { 85 | TraceData trace = ctx.getTrace(); 86 | long duration = span.getTimeUsedMicros(); 87 | String timeUsedMs = span.getTimeUsedMs(); 88 | String traceId = trace.getTraceId(); 89 | String path = callInfo.uri.getRawPath().toLowerCase(); 90 | span.changeAction(path); 91 | if (duration >= EnvConfigProps.slowHttpMillis * 1000L) { 92 | log.warn("slow http request:" + callInfo.uri.toString() + ", ts=" + duration + ", traceId=" + traceId); 93 | } 94 | 95 | if (AsyncAuditLog.isHttpEnabled()) { 96 | String timestamp = AsyncAuditLog.now(ctx, span); 97 | int port = callInfo.uri.getPort(); 98 | if (port == -1) { 99 | if (callInfo.uri.getScheme() != null 100 | && callInfo.uri.getScheme().compareToIgnoreCase("https") == 0) { 101 | port = 443; 102 | } else { 103 | port = 80; 104 | } 105 | } 106 | 107 | String connId = callInfo.uri.getHost() + ":" + port; 108 | int sequence = this.seq.incrementAndGet(); 109 | if (sequence >= 10000000) { 110 | this.seq.compareAndSet(sequence, 0); 111 | } 112 | 113 | String spanId = span.getSpanId(); 114 | String serviceNameMsgName = "HTTP." + callInfo.uri; 115 | Log( 116 | AsyncAuditLog.httpAuditLog, 117 | generateLogMap( 118 | timestamp, 119 | traceId, 120 | spanId, 121 | span.getRootSpan().getAction(), 122 | connId, 123 | new Action("HTTP", serviceNameMsgName), 124 | sequence, 125 | timeUsedMs, 126 | getJsonReq(callInfo), 127 | getJsonRes(callInfo, retObj), 128 | genException(throwable), 129 | EnvConfigProps.restTemplateLogMaxChars)); 130 | } 131 | } 132 | } 133 | 134 | Map getJsonReq(HttpCallInfo callInfo) { 135 | Map req = new LinkedHashMap<>(); 136 | req.put("method", callInfo.method); 137 | String queryString = callInfo.uri.getQuery(); 138 | if (queryString != null && !queryString.isEmpty()) { 139 | req.put("queryString", AsyncAuditLog.escapeText(queryString, -1)); 140 | } 141 | 142 | if (callInfo.reqHeaders != null) { 143 | Iterator iterator = callInfo.reqHeaders.entrySet().iterator(); 144 | label44: 145 | while (true) { 146 | Map.Entry entry; 147 | String lkey; 148 | do { 149 | do { 150 | if (!iterator.hasNext()) { 151 | break label44; 152 | } 153 | entry = (Map.Entry) iterator.next(); 154 | lkey = ((String) entry.getKey()).toLowerCase(); 155 | } while (!logAllHeaders && !logHeaderNames.contains(lkey)); 156 | } while (lkey.equals("content-length") 157 | && callInfo.method.equals("GET") 158 | && entry.getValue().equals("0")); 159 | req.put((String) entry.getKey(), AsyncAuditLog.escapeText((String) entry.getValue())); 160 | } 161 | } 162 | 163 | if (callInfo.reqBodyStr != null && !callInfo.reqBodyStr.isEmpty()) { 164 | req.put("body", AsyncAuditLog.escapeText(callInfo.reqBodyStr, -1)); 165 | } 166 | return req; 167 | } 168 | 169 | Map getJsonRes(HttpCallInfo callInfo, Object retObj) { 170 | Map res = new LinkedHashMap<>(); 171 | Map result = new LinkedHashMap<>(); 172 | result.put("httpCode", callInfo.httpCode); 173 | if (callInfo.resHeaders != null) { 174 | Iterator iterator = callInfo.resHeaders.entrySet().iterator(); 175 | label32: 176 | while (true) { 177 | Map.Entry entry; 178 | do { 179 | if (!iterator.hasNext()) { 180 | break label32; 181 | } 182 | 183 | entry = (Map.Entry) iterator.next(); 184 | } while (!logAllHeaders && !logHeaderNames.contains(((String) entry.getKey()).toLowerCase())); 185 | result.put((String) entry.getKey(), AsyncAuditLog.escapeText((String) entry.getValue())); 186 | } 187 | } 188 | 189 | if (retObj != null) { 190 | if (retObj instanceof ResponseEntity) { 191 | ResponseEntity re = (ResponseEntity) retObj; 192 | retObj = re.getBody(); 193 | } 194 | result.put("body", AsyncAuditLog.toTextAndEscape(retObj, -1)); 195 | } 196 | res.put("result", result); 197 | return res; 198 | } 199 | } 200 | --------------------------------------------------------------------------------