diff --git a/README.md b/README.md new file mode 100644 index 0000000..ad2db93 --- /dev/null +++ b/README.md @@ -0,0 +1,104 @@ +# biz-boot-starter-logger + +### 项目介绍 +该模块用于将日志上传到elk中(支持RabbitMQ或Kafka两种上传方式),将异常堆栈信息上传到sentry中。 + +1. 将logback日志通过Appender发送到指定RabbitMQ消息队列/Kafka消息队列中,然后通过配置logstash的input为 +RabbitMQ、output为elasticsearch即可将日志收集到ES中并在Kibana中展示 +2. 将error级别日志通过SentryAppender发送到指定的Sentry DSN地址,便于错误日志汇总、Bug排查定位,还能及时收到应用的错误报警 +3. 添加调用链路信息到请求头和MDC上下文中,实现微服务的全链路追踪 +4. 定制化banner样式 +5. 添加默认的 `logback-spring.xml` 配置,实际项目中无需再添加日志配置文件 +6. elk中记录的信息除当前服务的基础信息之外,还包含调用方信息、微服务调用链路追踪信息 + ``` + ---------- 基础信息 ---------- + parentProjectVersion - 父项目版本号。该值为 `biz.logger.version.parent-project` 的值,用于定位基础服务框架的版本问题 + appId - 项目唯一标识。该值为 `spring.application.name` 的值 + appName - 项目名称。该值为 `spring.application.name` 的值 + hostName - 服务器名 + hostAddress - 服务器IP + logger - logger名称 + threadName - 线程名称 + level - 日志级别 + time - 日志时间。格式为 yyyy-MM-dd HH:mm:ss.SSS + message - 日志内容 + ``` + 示例:`requestId`追踪微服务调用日志 + + ![调用链路追踪](./docs/elk-1.png "调用链路追踪") + + ![ELK记录信息](./docs/elk-2.png "ELK记录信息") + +### 调用链路追踪 +1. MDCFilter过滤器 + 该过滤器用于请求头中的调用方信息添加到 MDC上下文中,并且完善调用链路追踪信息 + ``` + ---------- 调用方信息 ---------- + api-version - 请求的API版本号 + channel - 渠道,调用方标识 + device-id - 设备id + ---------- 链路追踪信息 ---------- + request-id - 请求跟踪号,全链路唯一标识。格式为UUID(32个字符),来自header或者由该过滤器初始化 + sc-client-ip - 请求来源客户端ip + origin-url - 请求来源地址 + trace-app-ids - appId调用链路追踪记录。来自header并由该过滤器追加,以`,`分割 + trace-app-names - appName调用链路追踪记录。来自header并由该过滤器追加,以`,`分割 + trace-host-names - hostName调用链路追踪记录。来自header并由该过滤器追加,以`,`分割 + trace-host-addresses - hostAddress调用链路追踪记录。来自header并由该过滤器追加,以`,`分割 + ``` + +### 使用方式 +1. 添加依赖 + ``` + + cn.waynechu + biz-boot-starter-logger + + ``` +2. 添加配置 + ``` + ## sentry + sentry.enable=true + sentry.dsn=http://a1c395c85d244742ae2a50b90f1535b8@sentry.waynechu.cn:9000/2 + sentry.stacktrace-app-packages= + + ## elk-rabbit (两种方式二选一即可) + elk.rabbitmq.enable=true + elk.rabbitmq.host=mq.waynechu.cn + elk.rabbitmq.port=5672 + elk.rabbitmq.username=waynechu + elk.rabbitmq.password=123456 + elk.rabbitmq.application-id=${spring.application.name} + elk.rabbitmq.virtual-host=/logback + elk.rabbitmq.exchange=topic.loggingExchange + elk.rabbitmq.routing-key=logback.# + elk.rabbitmq.connection-name=biz|${spring.application.name} + + ## elk-kafka (两种方式二选一即可) + elk.kafka.enable=false + elk.kafka.host=kafka.waynechu.cn + elk.kafka.port=9092 + elk.kafka.topic=logback + ``` + +### 其他说明 +1. 如果抛出 `org.springframework.amqp.AmqpConnectException` Rabbit health check failed,这是因为`org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration`生效。 + 需添加Rabbitmq配置: + ``` + spring.rabbitmq.host=mq.waynechu.cn + spring.rabbitmq.port=5672 + spring.rabbitmq.username=waynechu + spring.rabbitmq.password=123456 + spring.rabbitmq.virtual-host=/logback + spring.rabbitmq.publisher-confirm-type=correlated + spring.rabbitmq.publisher-returns=true + spring.rabbitmq.template.mandatory=true + ``` + +2. (可选)如需要自定义日志相关配置,可新建`src/main/resources/logback-custom-spring.xml`来增加相关配置 + ``` + + + 自定义配置 + + ``` \ No newline at end of file diff --git a/biz-boot-starter-logger.iml b/biz-boot-starter-logger.iml new file mode 100644 index 0000000..ca5973e --- /dev/null +++ b/biz-boot-starter-logger.iml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/docs/elk-1.png b/docs/elk-1.png new file mode 100644 index 0000000..9eb645b Binary files /dev/null and b/docs/elk-1.png differ diff --git a/docs/elk-2.png b/docs/elk-2.png new file mode 100644 index 0000000..14fca9b Binary files /dev/null and b/docs/elk-2.png differ diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bd498d6 --- /dev/null +++ b/pom.xml @@ -0,0 +1,91 @@ + + + 4.0.0 + + + cn.waynechu + biz-parent + 1.0.0-RELEASE + + + biz-boot-starter-logger + 1.0.0-RELEASE + biz-boot-starter-logger + biz-boot-starter-logger module + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.springframework + spring-web + + + + javax.servlet + javax.servlet-api + + + + io.github.openfeign + feign-core + + + + + io.sentry + sentry-logback + + + + + org.springframework.boot + spring-boot-starter-amqp + + + + com.github.danielwegener + logback-kafka-appender + 0.2.0-RC2 + + + + + org.codehaus.janino + janino + + + + cn.waynechu + biz-spring-cloud-common + + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + diff --git a/src/main/java/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.java b/src/main/java/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.java new file mode 100644 index 0000000..d0a0448 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.java @@ -0,0 +1,40 @@ +package cn.waynechu.bootstarter.logger; + +import cn.waynechu.bootstarter.logger.aware.SentryContextAware; +import cn.waynechu.bootstarter.logger.filter.MDCFilter; +import cn.waynechu.bootstarter.logger.properties.ElkProperty; +import cn.waynechu.bootstarter.logger.properties.LoggerProperty; +import cn.waynechu.bootstarter.logger.properties.SentryProperties; +import cn.waynechu.bootstarter.logger.provider.ApplicationProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; + +/** + * @author zhuwei + * @since 2019/1/8 13:50 + */ +@Configuration +@EnableConfigurationProperties({SentryProperties.class, ElkProperty.class, LoggerProperty.class}) +@Import({ApplicationProvider.class}) +public class LoggerAutoConfiguration { + + @Bean + @ConditionalOnProperty(value = SentryContextAware.SENTRY_ENABLE, havingValue = "true") + public SentryContextAware sentryInitializer() { + return new SentryContextAware(); + } + + @Bean + public FilterRegistrationBean mdcFilterRegistrationBean() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + MDCFilter mdcFilter = new MDCFilter(); + + registrationBean.setFilter(mdcFilter); + registrationBean.setOrder(1); + return registrationBean; + } +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/appender/AmqpAppender.java b/src/main/java/cn/waynechu/bootstarter/logger/appender/AmqpAppender.java new file mode 100644 index 0000000..77a2162 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/appender/AmqpAppender.java @@ -0,0 +1,1026 @@ +/* + * Copyright 2014-2020 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package cn.waynechu.bootstarter.logger.appender; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.pattern.TargetLengthBasedClassNameAbbreviator; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.LoggingEvent; +import ch.qos.logback.core.AppenderBase; +import ch.qos.logback.core.Layout; +import ch.qos.logback.core.encoder.Encoder; +import com.rabbitmq.client.ConnectionFactory; +import org.springframework.amqp.AmqpApplicationContextClosedException; +import org.springframework.amqp.AmqpException; +import org.springframework.amqp.core.*; +import org.springframework.amqp.rabbit.connection.*; +import org.springframework.amqp.rabbit.core.DeclareExchangeConnectionListener; +import org.springframework.amqp.rabbit.core.RabbitAdmin; +import org.springframework.amqp.rabbit.core.RabbitTemplate; +import org.springframework.amqp.rabbit.support.RabbitExceptionTranslator; +import org.springframework.amqp.utils.JavaUtils; +import org.springframework.context.ApplicationContext; +import org.springframework.context.event.ContextClosedEvent; +import org.springframework.context.support.GenericApplicationContext; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.util.StringUtils; + +import java.io.UnsupportedEncodingException; +import java.net.URI; +import java.util.*; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * A Logback appender that publishes logging events to an AMQP Exchange. + *

+ * A fully-configured AmqpAppender, with every option set to their defaults, would look like this: + * + *

+ * {@code
+ * 
+ *     
+ *        %n ]]>
+ *     
+ *      
+ *     AmqpAppenderTest
+ *     %property{applicationId}.%c.%p
+ *     true
+ *     UTF-8
+ *     false
+ *     NON_PERSISTENT
+ * 
+ * }
+ * 
+ * + * @author Artem Bilan + * @author Gary Russell + * @author Stephen Oakey + * @author Dominique Villard + * @author Nicolas Ristock + * @author Eugene Gusev + * @since 1.4 + */ +public class AmqpAppender extends AppenderBase { + + private static final int DEFAULT_MAX_SENDER_RETRIES = 30; + + /** + * Key name for the application id (if there is one set via the appender config) in the message properties. + */ + public static final String APPLICATION_ID = "applicationId"; + + /** + * Key name for the logger category name in the message properties + */ + public static final String CATEGORY_NAME = "categoryName"; + + /** + * Key name for the logger level name in the message properties + */ + public static final String CATEGORY_LEVEL = "level"; + + /** + * Key name for the thread name in the message properties. + */ + public static final String THREAD_NAME = "thread"; + + private final ApplicationContext context = new GenericApplicationContext(); + + /** + * Name of the exchange to publish log events to. + */ + private String exchangeName = "logs"; + + /** + * Type of the exchange to publish log events to. + */ + private String exchangeType = "topic"; + + private final PatternLayout locationLayout = new PatternLayout(); + + { + this.locationLayout.setPattern("%nopex%class|%method|%line"); + } + + /** + * Logback Layout to use to generate routing key. + */ + private final PatternLayout routingKeyLayout = new PatternLayout(); + + { + this.routingKeyLayout.setPattern("%nopex%c.%p"); + } + + private final AtomicBoolean headerWritten = new AtomicBoolean(); + + /** + * Configuration arbitrary application ID. + */ + private String applicationId = null; + + /** + * Where LoggingEvents are queued to send. + */ + private BlockingQueue events; + + /** + * The pool of senders. + */ + private ExecutorService senderPool = null; + + /** + * How many senders to use at once. Use more senders if you have lots of log output going through this appender. + */ + private int senderPoolSize = 200; + + /** + * How many times to retry sending a message if the broker is unavailable or there is some other error. + */ + private int maxSenderRetries = DEFAULT_MAX_SENDER_RETRIES; + + /** + * Retries are delayed like: N ^ log(N), where N is the retry number. + */ + private final Timer retryTimer = new Timer("log-event-retry-delay", true); + + /** + * RabbitMQ ConnectionFactory. + */ + private AbstractConnectionFactory connectionFactory; + + /** + * A name for the connection (appears on the RabbitMQ Admin UI). + */ + private String connectionName; + + /** + * Additional client connection properties added to the rabbit connection, with the form + * {@code key:value[,key:value]...}. + */ + private String clientConnectionProperties; + + /** + * A comma-delimited list of broker addresses: host:port[,host:port]* + * + * @since 1.5.6 + */ + private String addresses; + + /** + * RabbitMQ host to connect to. + */ + private URI uri; + + /** + * RabbitMQ host to connect to. + */ + private String host; + + /** + * RabbitMQ virtual host to connect to. + */ + private String virtualHost; + + /** + * RabbitMQ port to connect to. + */ + private Integer port; + + /** + * RabbitMQ user to connect as. + */ + private String username; + + /** + * RabbitMQ password for this user. + */ + private String password; + + /** + * Use an SSL connection. + */ + private boolean useSsl; + + /** + * The SSL algorithm to use. + */ + private String sslAlgorithm; + + /** + * Location of resource containing keystore and truststore information. + */ + private String sslPropertiesLocation; + + /** + * Keystore location. + */ + private String keyStore; + + /** + * Keystore passphrase. + */ + private String keyStorePassphrase; + + /** + * Keystore type. + */ + private String keyStoreType = "JKS"; + + /** + * Truststore location. + */ + private String trustStore; + + /** + * Truststore passphrase. + */ + private String trustStorePassphrase; + + /** + * Truststore type. + */ + private String trustStoreType = "JKS"; + + /** + * SaslConfig. + * + * @see RabbitUtils#stringToSaslConfig(String, ConnectionFactory) + */ + private String saslConfig; + + private boolean verifyHostname = true; + + /** + * Default content-type of log messages. + */ + private String contentType = "text/plain"; + + /** + * Default content-encoding of log messages. + */ + private String contentEncoding = null; + + /** + * Whether or not to try and declare the configured exchange when this appender starts. + */ + private boolean declareExchange = false; + + /** + * charset to use when converting String to byte[], default null (system default charset used). + * If the charset is unsupported on the current platform, we fall back to using + * the system charset. + */ + private String charset; + + /** + * Whether or not add MDC properties into message headers. true by default for backward compatibility + */ + private boolean addMdcAsHeaders = true; + + private boolean durable = true; + + private MessageDeliveryMode deliveryMode = MessageDeliveryMode.PERSISTENT; + + private boolean autoDelete = false; + + /** + * Used to determine whether {@link MessageProperties#setMessageId(String)} is set. + */ + private boolean generateId = false; + + private Layout layout; + + private Encoder encoder; + + private TargetLengthBasedClassNameAbbreviator abbreviator; + + private boolean includeCallerData; + + public void setRoutingKeyPattern(String routingKeyPattern) { + this.routingKeyLayout.setPattern("%nopex{}" + routingKeyPattern); + } + + public URI getUri() { + return this.uri; + } + + public void setUri(URI uri) { + this.uri = uri; + } + + public String getHost() { + return this.host; + } + + public void setHost(String host) { + this.host = host; + } + + public Integer getPort() { + return this.port; + } + + public void setPort(Integer port) { + this.port = port; + } + + public void setAddresses(String addresses) { + this.addresses = addresses; + } + + public String getAddresses() { + return this.addresses; + } + + public String getVirtualHost() { + return this.virtualHost; + } + + public void setVirtualHost(String virtualHost) { + this.virtualHost = virtualHost; + } + + public String getUsername() { + return this.username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return this.password; + } + + public void setPassword(String password) { + this.password = password; + } + + public boolean isUseSsl() { + return this.useSsl; + } + + public void setUseSsl(boolean ssl) { + this.useSsl = ssl; + } + + /** + * Enable server hostname verification for TLS connections. + * + * @param enable false to disable. + * @see RabbitConnectionFactoryBean#setEnableHostnameVerification(boolean) + * @since 2.1.6 + */ + public void setVerifyHostname(boolean enable) { + this.verifyHostname = enable; + } + + /** + * Return true (default) if TLS hostname verification is enabled. + * + * @return true (default) if TLS hostname verification is enabled. + * @since 2.1.6 + */ + public boolean isVerifyHostname() { + return this.verifyHostname; + } + + public String getSslAlgorithm() { + return this.sslAlgorithm; + } + + public void setSslAlgorithm(String sslAlgorithm) { + this.sslAlgorithm = sslAlgorithm; + } + + public String getSslPropertiesLocation() { + return this.sslPropertiesLocation; + } + + public void setSslPropertiesLocation(String sslPropertiesLocation) { + this.sslPropertiesLocation = sslPropertiesLocation; + } + + public String getKeyStore() { + return this.keyStore; + } + + public void setKeyStore(String keyStore) { + this.keyStore = keyStore; + } + + public String getKeyStorePassphrase() { + return this.keyStorePassphrase; + } + + public void setKeyStorePassphrase(String keyStorePassphrase) { + this.keyStorePassphrase = keyStorePassphrase; + } + + public String getKeyStoreType() { + return this.keyStoreType; + } + + public void setKeyStoreType(String keyStoreType) { + this.keyStoreType = keyStoreType; + } + + public String getTrustStore() { + return this.trustStore; + } + + public void setTrustStore(String trustStore) { + this.trustStore = trustStore; + } + + public String getTrustStorePassphrase() { + return this.trustStorePassphrase; + } + + public void setTrustStorePassphrase(String trustStorePassphrase) { + this.trustStorePassphrase = trustStorePassphrase; + } + + public String getTrustStoreType() { + return this.trustStoreType; + } + + public void setTrustStoreType(String trustStoreType) { + this.trustStoreType = trustStoreType; + } + + public String getSaslConfig() { + return this.saslConfig; + } + + /** + * Set the {@link com.rabbitmq.client.SaslConfig}. + * + * @param saslConfig the saslConfig to set + * @see RabbitUtils#stringToSaslConfig(String, ConnectionFactory) + * @since 1.7.14 + */ + public void setSaslConfig(String saslConfig) { + this.saslConfig = saslConfig; + } + + public String getExchangeName() { + return this.exchangeName; + } + + public void setExchangeName(String exchangeName) { + this.exchangeName = exchangeName; + } + + public String getExchangeType() { + return this.exchangeType; + } + + public void setExchangeType(String exchangeType) { + this.exchangeType = exchangeType; + } + + public String getRoutingKeyPattern() { + return this.routingKeyLayout.getPattern(); + } + + public boolean isDeclareExchange() { + return this.declareExchange; + } + + public void setDeclareExchange(boolean declareExchange) { + this.declareExchange = declareExchange; + } + + public String getContentType() { + return this.contentType; + } + + public void setContentType(String contentType) { + this.contentType = contentType; + } + + public String getContentEncoding() { + return this.contentEncoding; + } + + public void setContentEncoding(String contentEncoding) { + this.contentEncoding = contentEncoding; + } + + public String getApplicationId() { + return this.applicationId; + } + + public void setApplicationId(String applicationId) { + this.applicationId = applicationId; + } + + public int getSenderPoolSize() { + return this.senderPoolSize; + } + + public void setSenderPoolSize(int senderPoolSize) { + this.senderPoolSize = senderPoolSize; + } + + public int getMaxSenderRetries() { + return this.maxSenderRetries; + } + + public void setMaxSenderRetries(int maxSenderRetries) { + this.maxSenderRetries = maxSenderRetries; + } + + public boolean isAddMdcAsHeaders() { + return this.addMdcAsHeaders; + } + + public void setAddMdcAsHeaders(boolean addMdcAsHeaders) { + this.addMdcAsHeaders = addMdcAsHeaders; + } + + public boolean isDurable() { + return this.durable; + } + + public void setDurable(boolean durable) { + this.durable = durable; + } + + public String getDeliveryMode() { + return this.deliveryMode.toString(); + } + + public void setDeliveryMode(String deliveryMode) { + this.deliveryMode = MessageDeliveryMode.valueOf(deliveryMode); + } + + public boolean isAutoDelete() { + return this.autoDelete; + } + + public void setAutoDelete(boolean autoDelete) { + this.autoDelete = autoDelete; + } + + public boolean isGenerateId() { + return this.generateId; + } + + public void setGenerateId(boolean generateId) { + this.generateId = generateId; + } + + public String getCharset() { + return this.charset; + } + + public void setCharset(String charset) { + this.charset = charset; + } + + public void setLayout(Layout layout) { + this.layout = layout; + } + + public Encoder getEncoder() { + return this.encoder; + } + + public void setEncoder(final Encoder encoder) { + this.encoder = encoder; + } + + public void setAbbreviation(int len) { + this.abbreviator = new TargetLengthBasedClassNameAbbreviator(len); + } + + /** + * Return the number of events waiting to be sent. + * + * @return the number of events waiting to be sent. + */ + public int getQueuedEventCount() { + return this.events.size(); + } + + /** + * Set a name for the connection which will appear on the RabbitMQ Admin UI. + * + * @param connectionName the connection name. + * @since 2.1.1 + */ + public void setConnectionName(String connectionName) { + this.connectionName = connectionName; + } + + /** + * Set additional client connection properties to be added to the rabbit connection, + * with the form {@code key:value[,key:value]...}. + * + * @param clientConnectionProperties the properties. + * @since 1.5.6 + */ + public void setClientConnectionProperties(String clientConnectionProperties) { + this.clientConnectionProperties = clientConnectionProperties; + } + + public boolean isIncludeCallerData() { + return this.includeCallerData; + } + + /** + * If true, the caller data will be available in the target AMQP message. + * By default no caller data is sent to the RabbitMQ. + * + * @param includeCallerData include or on caller data + * @see ILoggingEvent#getCallerData() + * @since 1.7.1 + */ + public void setIncludeCallerData(boolean includeCallerData) { + this.includeCallerData = includeCallerData; + } + + @Override + public void start() { + this.events = createEventQueue(); + + ConnectionFactory rabbitConnectionFactory = createRabbitConnectionFactory(); + if (rabbitConnectionFactory != null) { + super.start(); + this.routingKeyLayout.setPattern(this.routingKeyLayout.getPattern() + .replaceAll("%property\\{applicationId}", this.applicationId)); + this.routingKeyLayout.setContext(getContext()); + this.routingKeyLayout.start(); + this.locationLayout.setContext(getContext()); + this.locationLayout.start(); + this.connectionFactory = new CachingConnectionFactory(rabbitConnectionFactory); + this.connectionFactory.setApplicationContext(this.context); + if (StringUtils.hasText(this.connectionName)) { + this.connectionFactory.setConnectionNameStrategy(cf -> this.connectionName); + } + if (this.addresses != null) { + this.connectionFactory.setAddresses(this.addresses); + } + ConnectionFactoryConfigurationUtils.updateClientConnectionProperties(this.connectionFactory, + this.clientConnectionProperties); + updateConnectionClientProperties(this.connectionFactory.getRabbitConnectionFactory().getClientProperties()); + setUpExchangeDeclaration(); + this.senderPool = Executors.newCachedThreadPool(); + for (int i = 0; i < this.senderPoolSize; i++) { + this.senderPool.submit(new EventSender()); + } + } + } + + /** + * Create the {@link ConnectionFactory}. + * + * @return a {@link ConnectionFactory}. + */ + protected ConnectionFactory createRabbitConnectionFactory() { + RabbitConnectionFactoryBean factoryBean = new RabbitConnectionFactoryBean(); + configureRabbitConnectionFactory(factoryBean); + try { + factoryBean.afterPropertiesSet(); + return factoryBean.getObject(); + } catch (Exception e) { + addError("Failed to create customized Rabbit ConnectionFactory.", e); + return null; + } + } + + /** + * Configure the {@link RabbitConnectionFactoryBean}. Sub-classes may override to + * customize the configuration of the bean. + * + * @param factoryBean the {@link RabbitConnectionFactoryBean}. + */ + protected void configureRabbitConnectionFactory(RabbitConnectionFactoryBean factoryBean) { + JavaUtils.INSTANCE + .acceptIfNotNull(this.host, factoryBean::setHost) + .acceptIfNotNull(this.port, factoryBean::setPort) + .acceptIfNotNull(this.username, factoryBean::setUsername) + .acceptIfNotNull(this.password, factoryBean::setPassword) + .acceptIfNotNull(this.virtualHost, factoryBean::setVirtualHost) + // overrides all preceding items when set + .acceptIfNotNull(this.uri, factoryBean::setUri); + + if (this.useSsl) { + factoryBean.setUseSSL(true); + factoryBean.setEnableHostnameVerification(this.verifyHostname); + if (this.sslAlgorithm != null) { + factoryBean.setSslAlgorithm(this.sslAlgorithm); + } + if (this.sslPropertiesLocation != null) { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource sslPropertiesResource = resolver.getResource(this.sslPropertiesLocation); + factoryBean.setSslPropertiesLocation(sslPropertiesResource); + } else { + factoryBean.setKeyStore(this.keyStore); + factoryBean.setKeyStorePassphrase(this.keyStorePassphrase); + factoryBean.setKeyStoreType(this.keyStoreType); + factoryBean.setTrustStore(this.trustStore); + factoryBean.setTrustStorePassphrase(this.trustStorePassphrase); + factoryBean.setTrustStoreType(this.trustStoreType); + JavaUtils.INSTANCE + .acceptIfNotNull(this.saslConfig, config -> { + try { + factoryBean.setSaslConfig(RabbitUtils.stringToSaslConfig(config, + factoryBean.getRabbitConnectionFactory())); + } catch (Exception e) { + throw RabbitExceptionTranslator.convertRabbitAccessException(e); + } + }); + } + } + if (this.layout == null && this.encoder == null) { + addError("Either a layout or encoder is required"); + } + + if (this.layout != null && this.encoder != null) { + addError("Only one of layout or encoder is possible"); + } + } + + /** + * Subclasses can override this method to add properties to the connection client + * properties. + * + * @param clientProperties the client properties. + * @since 1.5.6 + */ + protected void updateConnectionClientProperties(Map clientProperties) { + } + + /** + * Subclasses can override this method to inject a custom queue implementation. + * + * @return the queue to use for queueing logging events before processing them. + * @since 2.0.1 + */ + protected BlockingQueue createEventQueue() { + return new LinkedBlockingQueue<>(); + } + + @Override + public void stop() { + super.stop(); + if (null != this.senderPool) { + this.senderPool.shutdownNow(); + this.senderPool = null; + } + if (null != this.connectionFactory) { + this.connectionFactory.destroy(); + this.connectionFactory.onApplicationEvent(new ContextClosedEvent(this.context)); + } + this.retryTimer.cancel(); + this.routingKeyLayout.stop(); + } + + @Override + protected void append(ILoggingEvent event) { + if (isIncludeCallerData()) { + event.getCallerData(); + } + event.getThreadName(); + this.events.add(new Event(event)); + } + + protected void setUpExchangeDeclaration() { + RabbitAdmin admin = new RabbitAdmin(this.connectionFactory); + if (this.declareExchange) { + Exchange x; + if ("topic".equals(this.exchangeType)) { + x = new TopicExchange(this.exchangeName, this.durable, this.autoDelete); + } else if ("direct".equals(this.exchangeType)) { + x = new DirectExchange(this.exchangeName, this.durable, this.autoDelete); + } else if ("fanout".equals(this.exchangeType)) { + x = new FanoutExchange(this.exchangeName, this.durable, this.autoDelete); + } else if ("headers".equals(this.exchangeType)) { + x = new HeadersExchange(this.exchangeType, this.durable, this.autoDelete); + } else { + x = new TopicExchange(this.exchangeName, this.durable, this.autoDelete); + } + this.connectionFactory.addConnectionListener(new DeclareExchangeConnectionListener(x, admin)); + } + } + + protected MessageProperties prepareMessageProperties(Event event) { + ILoggingEvent logEvent = event.getEvent(); + + String name = logEvent.getLoggerName(); + Level level = logEvent.getLevel(); + + MessageProperties amqpProps = new MessageProperties(); + amqpProps.setDeliveryMode(this.deliveryMode); + amqpProps.setContentType(this.contentType); + if (null != this.contentEncoding) { + amqpProps.setContentEncoding(this.contentEncoding); + } + amqpProps.setHeader(CATEGORY_NAME, name); + amqpProps.setHeader(THREAD_NAME, logEvent.getThreadName()); + amqpProps.setHeader(CATEGORY_LEVEL, level.toString()); + if (this.generateId) { + amqpProps.setMessageId(UUID.randomUUID().toString()); + } + + // Set timestamp + Calendar tstamp = Calendar.getInstance(); + tstamp.setTimeInMillis(logEvent.getTimeStamp()); + amqpProps.setTimestamp(tstamp.getTime()); + + // Copy properties in from MDC + if (this.addMdcAsHeaders) { + Map props = event.getProperties(); + Set> entrySet = props.entrySet(); + for (Entry entry : entrySet) { + amqpProps.setHeader(entry.getKey(), entry.getValue()); + } + } + + String[] location = this.locationLayout.doLayout(logEvent).split("\\|"); + if (!"?".equals(location[0])) { + amqpProps.setHeader( + "location", + String.format("%s.%s()[%s]", location[0], location[1], location[2])); + } + + // Set applicationId, if we're using one + if (this.applicationId != null) { + amqpProps.setAppId(this.applicationId); + } + + return amqpProps; + } + + /** + * Subclasses may modify the final message before sending. + * + * @param message The message. + * @param event The event. + * @return The modified message. + * @since 1.4 + */ + protected Message postProcessMessageBeforeSend(Message message, Event event) { + return message; + } + + /** + * Helper class to actually send LoggingEvents asynchronously. + */ + protected class EventSender implements Runnable { + + @Override + public void run() { + try { + RabbitTemplate rabbitTemplate = new RabbitTemplate(AmqpAppender.this.connectionFactory); + while (true) { + final Event event = AmqpAppender.this.events.take(); + + MessageProperties amqpProps = prepareMessageProperties(event); + + String routingKey = AmqpAppender.this.routingKeyLayout.doLayout(event.getEvent()); + + sendOneEncoderPatternMessage(rabbitTemplate, routingKey); + + doSend(rabbitTemplate, event, event.getEvent(), name, amqpProps, routingKey); + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + // https://github.com/spring-projects/spring-amqp/pull/1248 + catch (Exception e) { + addError("Could not send log message, appender is stopped", e); + } + } + + private void sendOneEncoderPatternMessage(RabbitTemplate rabbitTemplate, String routingKey) { + /* + * If the encoder provides its pattern, send it as an additional one-time message. + */ + if (AmqpAppender.this.encoder != null + && AmqpAppender.this.headerWritten.compareAndSet(false, true)) { + byte[] header = AmqpAppender.this.encoder.headerBytes(); + if (header != null && header.length > 0) { + rabbitTemplate.convertAndSend(AmqpAppender.this.exchangeName, routingKey, header, m -> { + if (AmqpAppender.this.applicationId != null) { + m.getMessageProperties().setAppId(AmqpAppender.this.applicationId); + } + return m; + }); + } + } + } + + private void doSend(RabbitTemplate rabbitTemplate, final Event event, ILoggingEvent logEvent, String name, + MessageProperties amqpProps, String routingKey) { + byte[] msgBody; + if (AmqpAppender.this.abbreviator != null && logEvent instanceof LoggingEvent) { + ((LoggingEvent) logEvent).setLoggerName(AmqpAppender.this.abbreviator.abbreviate(name)); + msgBody = encodeMessage(logEvent); + ((LoggingEvent) logEvent).setLoggerName(name); + } else { + msgBody = encodeMessage(logEvent); + } + + // Send a message + try { + Message message = new Message(msgBody, amqpProps); + + message = postProcessMessageBeforeSend(message, event); + rabbitTemplate.send(AmqpAppender.this.exchangeName, routingKey, message); + } catch (AmqpApplicationContextClosedException e) { + addError("Could not send log message " + logEvent.getMessage() + " appender is stopped"); + } catch (AmqpException e) { + int retries = event.incrementRetries(); + if (retries < AmqpAppender.this.maxSenderRetries) { + // Schedule a retry based on the number of times I've tried to re-send this + AmqpAppender.this.retryTimer.schedule(new TimerTask() { + + @Override + public void run() { + AmqpAppender.this.events.add(event); + } + + }, (long) (Math.pow(retries, Math.log(retries)) * 1000)); // NOSONAR magic # + } else { + addError("Could not send log message " + logEvent.getMessage() + + " after " + AmqpAppender.this.maxSenderRetries + " retries", e); + } + } + } + + private byte[] encodeMessage(ILoggingEvent logEvent) { + if (AmqpAppender.this.encoder != null) { + return AmqpAppender.this.encoder.encode(logEvent); + } + + String msgBody = AmqpAppender.this.layout.doLayout(logEvent); + if (AmqpAppender.this.charset != null) { + try { + return msgBody.getBytes(AmqpAppender.this.charset); + } catch (UnsupportedEncodingException e) { + return msgBody.getBytes(); //NOSONAR (default charset) + } + } else { + return msgBody.getBytes(); //NOSONAR (default charset) + } + } + + } + + /** + * Small helper class to encapsulate a LoggingEvent, its MDC properties, and the number of retries. + */ + protected static class Event { + + private final ILoggingEvent event; + + private final Map properties; + + private final AtomicInteger retries = new AtomicInteger(0); + + public Event(ILoggingEvent event) { + this.event = event; + this.properties = this.event.getMDCPropertyMap(); + } + + public ILoggingEvent getEvent() { + return this.event; + } + + public Map getProperties() { + return this.properties; + } + + public int incrementRetries() { + return this.retries.incrementAndGet(); + } + + } + +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/aware/SentryContextAware.java b/src/main/java/cn/waynechu/bootstarter/logger/aware/SentryContextAware.java new file mode 100644 index 0000000..e194800 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/aware/SentryContextAware.java @@ -0,0 +1,34 @@ +package cn.waynechu.bootstarter.logger.aware; + +import cn.waynechu.springcloud.common.util.StringUtil; +import org.springframework.beans.BeansException; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.core.env.Environment; + +/** + * @author zhuwei + * @since 2019/1/8 13:26 + */ +public class SentryContextAware implements ApplicationContextAware, PriorityOrdered { + public static final String SENTRY_ENABLE = "sentry.enable"; + private static final String SENTRY_DSN = "sentry.dsn"; + private static final String TRUE = "true"; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + Environment environment = applicationContext.getEnvironment(); + String sentryEnable = environment.getProperty(SENTRY_ENABLE); + String sentryDsn = environment.getProperty(SENTRY_DSN); + if (TRUE.equalsIgnoreCase(sentryEnable) && StringUtil.isBlank(sentryDsn)) { + throw new RuntimeException("sentry.dsn未配置!"); + } + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.java b/src/main/java/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.java new file mode 100644 index 0000000..738623e --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.java @@ -0,0 +1,39 @@ +package cn.waynechu.bootstarter.logger.constant; + +import lombok.experimental.UtilityClass; + +/** + * @author zhuwei + * @since 2020/3/26 11:39 + */ +@UtilityClass +public class TraceKeyConstant { + + /** + * 请求头追踪key + */ + public static final String HEADER_KEY_API_VERSION = "api-version"; + public static final String HEADER_KEY_CHANNEL = "channel"; + public static final String HEADER_KEY_DEVICE_ID = "device-id"; + public static final String HEADER_KEY_REQUEST_ID = "request-id"; + public static final String HEADER_KEY_SC_CLIENT_IP = "sc-client-ip"; + public static final String HEADER_KEY_ORIGIN_URL = "origin-url"; + public static final String HEADER_KEY_TRACE_APP_IDS = "trace-app-ids"; + public static final String HEADER_KEY_TRACE_APP_NAMES = "trace-app-names"; + public static final String HEADER_KEY_TRACE_HOST_NAMES = "trace-host-names"; + public static final String HEADER_KEY_TRACE_HOST_ADDRESSES = "trace-host-addresses"; + + /** + * MDC追踪key + */ + public static final String MDC_KEY_API_VERSION = "apiVersion"; + public static final String MDC_KEY_CHANNEL = "channel"; + public static final String MDC_KEY_DEVICE_ID = "deviceId"; + public static final String MDC_KEY_REQUEST_ID = "requestId"; + public static final String MDC_KEY_SC_CLIENT_IP = "scClientIp"; + public static final String MDC_KEY_ORIGIN_URL = "originUrl"; + public static final String MDC_KEY_TRACE_APP_IDS = "traceAppIds"; + public static final String MDC_KEY_TRACE_APP_NAMES = "traceAppNames"; + public static final String MDC_KEY_TRACE_HOST_NAMES = "traceHostNames"; + public static final String MDC_KEY_TRACE_HOST_ADDRESSES = "traceHostAddresses"; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/filter/MDCFilter.java b/src/main/java/cn/waynechu/bootstarter/logger/filter/MDCFilter.java new file mode 100644 index 0000000..1887fbb --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/filter/MDCFilter.java @@ -0,0 +1,101 @@ +package cn.waynechu.bootstarter.logger.filter; + +import cn.waynechu.bootstarter.logger.provider.ApplicationProvider; +import cn.waynechu.springcloud.common.util.MDCUtil; +import cn.waynechu.springcloud.common.util.StringUtil; +import cn.waynechu.springcloud.common.util.UUIDUtil; +import cn.waynechu.springcloud.common.util.WebUtil; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.MDC; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +import static cn.waynechu.bootstarter.logger.constant.TraceKeyConstant.*; + +/** + * MDC过滤器 + *

+ * 该过滤器用于添加请求信息到MDC上下文中,并且添加header追踪信息 + * + * @author zhuwei + * @since 2019/1/4 15:10 + */ +@Slf4j +@Data +public class MDCFilter implements Filter { + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + try { + // 添加请求头及MDC信息 + ModifyHttpServletRequestWrapper modifyHttpServletRequestWrapper = this.modifyRequestAndAddMdc((HttpServletRequest) servletRequest); + + // ResponseHeader返回requestId + HttpServletResponse response = (HttpServletResponse) servletResponse; + response.setHeader(HEADER_KEY_REQUEST_ID, modifyHttpServletRequestWrapper.getHeader(HEADER_KEY_REQUEST_ID)); + filterChain.doFilter(modifyHttpServletRequestWrapper, servletResponse); + } finally { + MDC.clear(); + } + } + + private ModifyHttpServletRequestWrapper modifyRequestAndAddMdc(HttpServletRequest request) { + ModifyHttpServletRequestWrapper httpServletRequestWrapper = new ModifyHttpServletRequestWrapper(request); + + MDCUtil.copyReqHeaderToMDC(httpServletRequestWrapper, HEADER_KEY_API_VERSION, HEADER_KEY_CHANNEL, HEADER_KEY_DEVICE_ID); + + String requestId = WebUtil.getReqHeader(HEADER_KEY_REQUEST_ID, request); + if (StringUtil.isBlank(requestId)) { + // 除了网关传递过来requestId外,应用本身也能产生requestId + requestId = UUIDUtil.getShortUUID(); + httpServletRequestWrapper.putHeader(HEADER_KEY_REQUEST_ID, requestId); + } + MDC.put(MDC_KEY_REQUEST_ID, requestId); + + String scClientIp = WebUtil.getReqHeader(HEADER_KEY_SC_CLIENT_IP, request); + MDC.put(MDC_KEY_SC_CLIENT_IP, scClientIp); + httpServletRequestWrapper.putHeader(HEADER_KEY_SC_CLIENT_IP, scClientIp); + + String originUrl = WebUtil.getReqHeader(HEADER_KEY_ORIGIN_URL, request); + MDC.put(MDC_KEY_ORIGIN_URL, originUrl); + httpServletRequestWrapper.putHeader(HEADER_KEY_ORIGIN_URL, originUrl); + + String appId = ApplicationProvider.getAppId(); + String traceAppIds = WebUtil.getReqHeader(HEADER_KEY_TRACE_APP_IDS, request); + if (StringUtil.isNotBlank(traceAppIds)) { + appId = traceAppIds + "," + appId; + } + MDC.put(MDC_KEY_TRACE_APP_IDS, appId); + httpServletRequestWrapper.putHeader(HEADER_KEY_TRACE_APP_IDS, appId); + + String appName = ApplicationProvider.getAppName(); + String traceAppNames = WebUtil.getReqHeader(HEADER_KEY_TRACE_APP_NAMES, request); + if (StringUtil.isNotBlank(traceAppNames)) { + appName = traceAppNames + "," + appName; + } + MDC.put(MDC_KEY_TRACE_APP_NAMES, appName); + httpServletRequestWrapper.putHeader(HEADER_KEY_TRACE_APP_NAMES, appName); + + String hostName = ApplicationProvider.getHostName(); + String traceHostNames = WebUtil.getReqHeader(HEADER_KEY_TRACE_HOST_NAMES, request); + if (StringUtil.isNotBlank(traceHostNames)) { + hostName = traceHostNames + "," + hostName; + } + MDC.put(MDC_KEY_TRACE_HOST_NAMES, hostName); + httpServletRequestWrapper.putHeader(HEADER_KEY_TRACE_HOST_NAMES, hostName); + + String hostAddress = ApplicationProvider.getHostAddress(); + String traceHostAddress = WebUtil.getReqHeader(HEADER_KEY_TRACE_HOST_ADDRESSES, request); + if (StringUtil.isNotBlank(traceHostAddress)) { + hostAddress = traceHostAddress + "," + hostAddress; + } + MDC.put(MDC_KEY_TRACE_HOST_ADDRESSES, hostAddress); + httpServletRequestWrapper.putHeader(HEADER_KEY_TRACE_HOST_ADDRESSES, hostAddress); + + return httpServletRequestWrapper; + } +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.java b/src/main/java/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.java new file mode 100644 index 0000000..7d9af7c --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.java @@ -0,0 +1,43 @@ +package cn.waynechu.bootstarter.logger.filter; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import java.util.*; + +/** + * @author zhuwei + * @since 2019/9/29 19:31 + */ +public class ModifyHttpServletRequestWrapper extends HttpServletRequestWrapper { + + private Map customHeaders; + + public ModifyHttpServletRequestWrapper(HttpServletRequest request) { + super(request); + this.customHeaders = new HashMap<>(); + } + + public void putHeader(String name, String value) { + this.customHeaders.put(name, value); + } + + @Override + public String getHeader(String name) { + String value = customHeaders.get(name); + if (value != null) { + return value; + } + return ((HttpServletRequest) getRequest()).getHeader(name); + } + + @Override + public Enumeration getHeaderNames() { + Set set = new HashSet<>(customHeaders.keySet()); + Enumeration enumeration = ((HttpServletRequest) getRequest()).getHeaderNames(); + while (enumeration.hasMoreElements()) { + String name = enumeration.nextElement(); + set.add(name); + } + return Collections.enumeration(set); + } +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.java b/src/main/java/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.java new file mode 100644 index 0000000..89b3e3a --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.java @@ -0,0 +1,112 @@ +package cn.waynechu.bootstarter.logger.layout; + +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.classic.spi.IThrowableProxy; +import ch.qos.logback.classic.spi.ThrowableProxy; +import ch.qos.logback.core.LayoutBase; +import cn.waynechu.bootstarter.logger.provider.ApplicationProvider; +import cn.waynechu.springcloud.common.aspect.AbstractControllerLogAspect; +import cn.waynechu.springcloud.common.util.DesensitizeUtil; +import cn.waynechu.springcloud.common.util.StringUtil; +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.List; + +/** + * @author zhuwei + * @since 2018/12/27 18:52 + */ +@Slf4j +public class RabbitmqLayout extends LayoutBase { + + @Override + public String doLayout(ILoggingEvent iLoggingEvent) { + return buildElkFormat(iLoggingEvent); + } + + private String buildElkFormat(ILoggingEvent iLoggingEvent) { + JSONObject root = new JSONObject(); + + // write mdc fields + writeMdc(root, iLoggingEvent); + // write basic fields + writeBasic(root, iLoggingEvent); + // write throwable fields + writeThrowable(root, iLoggingEvent); + + return root.toString(); + } + + private void writeMdc(JSONObject json, ILoggingEvent event) { + if (event.getMDCPropertyMap() != null) { + json.putAll(event.getMDCPropertyMap()); + + // 转化timeTaken为Integer类型 + String timeTaken = event.getMDCPropertyMap().get(AbstractControllerLogAspect.MDC_KEY_TIME_TAKEN); + if (StringUtil.isNotBlank(timeTaken)) { + try { + json.put(AbstractControllerLogAspect.MDC_KEY_TIME_TAKEN, Integer.valueOf(timeTaken)); + } catch (Exception e) { + this.addError("timeTaken转换成Integer失败", e); + } + } + } + } + + private void writeBasic(JSONObject json, ILoggingEvent event) { + json.put("parentProjectVersion", ApplicationProvider.getParentProjectVersion()); + json.put("appId", ApplicationProvider.getAppId()); + json.put("appName", ApplicationProvider.getAppName()); + json.put("hostName", ApplicationProvider.getHostName()); + json.put("hostAddress", ApplicationProvider.getHostAddress()); + + json.put("logger", event.getLoggerName()); + json.put("threadName", event.getThreadName()); + json.put("level", event.getLevel().toString()); + LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(event.getTimeStamp()), + ZoneId.systemDefault()); + json.put("time", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").format(localDateTime)); + // 日志脱敏 & 超长截取 + String[] keyArray = {DesensitizeUtil.PASSWORD, DesensitizeUtil.PWD}; + String message = event.getFormattedMessage(); + if (StringUtil.isNotBlank(message)) { + message = message.length() <= 4096 ? message : message.substring(0, 4096) + "...总长度为: " + message.length(); + } + json.put("message", DesensitizeUtil.desensitize(message, keyArray)); + } + + private void writeThrowable(JSONObject json, ILoggingEvent event) { + IThrowableProxy iThrowableProxy = event.getThrowableProxy(); + if (iThrowableProxy instanceof ThrowableProxy) { + ThrowableProxy throwableProxy = (ThrowableProxy) iThrowableProxy; + Throwable t = throwableProxy.getThrowable(); + Throwable ec = t.getCause(); + JSONObject throwable = new JSONObject(); + + throwable.put("message", t.getMessage()); + throwable.put("className", t.getClass().getCanonicalName()); + + if (ec == null) { + List traceObjects = new ArrayList<>(); + for (StackTraceElement ste : t.getStackTrace()) { + JSONObject element = new JSONObject(); + element.put("class", ste.getClassName()); + element.put("method", ste.getMethodName()); + element.put("line", ste.getLineNumber()); + element.put("file", ste.getFileName()); + traceObjects.add(element); + } + json.put("stackTrace", traceObjects); + } else { + throwable.put("cause", ec); + } + json.put("throwable", throwable); + } + } +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/properties/ElkProperty.java b/src/main/java/cn/waynechu/bootstarter/logger/properties/ElkProperty.java new file mode 100644 index 0000000..da4f0bb --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/properties/ElkProperty.java @@ -0,0 +1,29 @@ +package cn.waynechu.bootstarter.logger.properties; + +import cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty; +import cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty; +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.NestedConfigurationProperty; + +/** + * @author zhuwei + * @since 2019/4/29 17:40 + */ +@Data +@ConfigurationProperties(prefix = ElkProperty.ELK_CONFIG_PREFIX) +public class ElkProperty { + public static final String ELK_CONFIG_PREFIX = "elk"; + + /** + * rabbitmq配置 + */ + @NestedConfigurationProperty + private RabbitmqProperty rabbitmq; + + /** + * kafka配置 + */ + @NestedConfigurationProperty + private KafkaProperty kafka; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/properties/LoggerProperty.java b/src/main/java/cn/waynechu/bootstarter/logger/properties/LoggerProperty.java new file mode 100644 index 0000000..d6122d0 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/properties/LoggerProperty.java @@ -0,0 +1,25 @@ +package cn.waynechu.bootstarter.logger.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author zhuwei + * @since 2019/7/10 20:34 + */ +@Data +@ConfigurationProperties(prefix = LoggerProperty.LOGGER_PREFIX) +public class LoggerProperty { + + public static final String LOGGER_PREFIX = "biz.logger.version"; + + /** + * 指定banner展示的 SpringCloud 版本 + */ + private String springCloudDependencies; + + /** + * 指定banner展示的父项目版本 + */ + private String parentProject; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/properties/SentryProperties.java b/src/main/java/cn/waynechu/bootstarter/logger/properties/SentryProperties.java new file mode 100644 index 0000000..3949466 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/properties/SentryProperties.java @@ -0,0 +1,28 @@ +package cn.waynechu.bootstarter.logger.properties; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * @author zhuwei + * @since 2019/1/8 12:52 + */ +@Data +@ConfigurationProperties(prefix = "sentry") +public class SentryProperties { + + /** + * 是否开启sentry日志上传 + */ + private boolean enable = false; + + /** + * sentry dsn 地址 + */ + private String dsn; + + /** + * 要追踪的包名,多个请用 “,” 分割。默认上传所有包下的日志 + */ + private String stacktraceAppPackages = ""; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.java b/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.java new file mode 100644 index 0000000..bea92d0 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.java @@ -0,0 +1,36 @@ +package cn.waynechu.bootstarter.logger.properties.nested; + +import lombok.Data; + +/** + * @author zhuwei + * @since 2019/10/31 15:46 + */ +@Data +public class KafkaProperty { + + /** + * 是否开启日志上传 + */ + private Boolean enable = false; + + private String host; + + private Integer port; + + private String topic; + + private Long bufferMemory; + + private String defaultHost; + + private Integer acks; + + private Integer lingerMs; + + private Integer maxBlockMs; + + private String compressionType; + + private Integer retries; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.java b/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.java new file mode 100644 index 0000000..b31c009 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.java @@ -0,0 +1,61 @@ +package cn.waynechu.bootstarter.logger.properties.nested; + +import lombok.Data; + +/** + * @author zhuwei + * @since 2019/10/31 15:45 + */ +@Data +public class RabbitmqProperty { + + /** + * 是否开启日志上传 + */ + private Boolean enable = false; + + /** + * RabbitMQ host + */ + private String host; + + /** + * RabbitMQ port + */ + private Integer port; + + /** + * RabbitMQ username + */ + private String username; + + /** + * RabbitMQ password + */ + private String password; + + /** + * RabbitMQ virtualHost + */ + private String virtualHost; + + /** + * RabbitMQ exchange + */ + private String exchange; + + /** + * RabbitMQ routingKey + */ + private String routingKey; + + /** + * 应用ID + */ + private String applicationId; + + /** + * 客户端标识(在Rabbit Admin界面展示) + */ + private String connectionName; +} diff --git a/src/main/java/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.java b/src/main/java/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.java new file mode 100644 index 0000000..679e4b3 --- /dev/null +++ b/src/main/java/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.java @@ -0,0 +1,124 @@ +package cn.waynechu.bootstarter.logger.provider; + + +import cn.waynechu.springcloud.common.util.StringUtil; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.stereotype.Component; + +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +/** + * @author zhuwei + * @since 2019/9/26 15:43 + */ +@Slf4j +@Component +@Order(Ordered.HIGHEST_PRECEDENCE) +public class ApplicationProvider implements InitializingBean { + + private static String hostName = "-"; + private static String hostAddress = "-"; + + private static String appId = "-"; + private static String appName = "-"; + private static String parentProjectVersion = "-"; + + private Properties appProperties = new Properties(); + public static final String APP_PROPERTIES_CLASSPATH = "/META-INF/app.properties"; + + public ApplicationProvider(@Value("${spring.application.name:-}") String applicationName, + @Value("${biz.logger.version.parent-project:-}") String parentProjectVersion) { + try { + ApplicationProvider.appName = applicationName; + InetAddress address = InetAddress.getLocalHost(); + ApplicationProvider.hostName = address.getHostName(); + ApplicationProvider.hostAddress = address.getHostAddress(); + ApplicationProvider.parentProjectVersion = parentProjectVersion; + } catch (UnknownHostException e) { + // do nothing here. + } + } + + @Override + public void afterPropertiesSet() { + try { + InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream(APP_PROPERTIES_CLASSPATH); + if (in == null) { + in = ApplicationProvider.class.getResourceAsStream(APP_PROPERTIES_CLASSPATH); + } + + appProperties.load(new InputStreamReader(in, StandardCharsets.UTF_8)); + this.initAppId(); + } catch (Exception ex) { + log.error("Initialize DefaultApplicationProvider failed.", ex); + } + } + + private void initAppId() { + // 1. Get app.id from System Property + appId = System.getProperty("app.id"); + if (StringUtil.isNotBlank(appId)) { + appId = appId.trim(); + log.info("App ID is set to {} by app.id property from System Property", appId); + return; + } + + // 2. Try to get app id from OS environment variable + appId = System.getenv("APP_ID"); + if (StringUtil.isNotBlank(appId)) { + appId = appId.trim(); + log.info("App ID is set to {} by APP_ID property from OS environment variable", appId); + return; + } + + // 3. Try to get app id from app.properties. + appId = appProperties.getProperty("app.id"); + if (StringUtil.isNotBlank(appId)) { + appId = appId.trim(); + log.info("App ID is set to {} by app.id property from {}", appId, APP_PROPERTIES_CLASSPATH); + return; + } + + appId = "-"; + log.warn("app.id is not available from System Property and {}. It is set to -", APP_PROPERTIES_CLASSPATH); + } + + public static String getHostAddress() { + return hostAddress; + } + + public static String getHostName() { + return hostName; + } + + public static String getAppName() { + return appName; + } + + public static String getParentProjectVersion() { + return parentProjectVersion; + } + + public static String getAppId() { + return appId; + } + + public String getProperty(String name, String defaultValue) { + if ("app.id".equals(name)) { + String val = getAppId(); + return val == null ? defaultValue : val; + } else { + String val = appProperties.getProperty(name, defaultValue); + return val == null ? defaultValue : val; + } + } +} diff --git a/src/main/resources/META-INF/spring.factories b/src/main/resources/META-INF/spring.factories new file mode 100644 index 0000000..b5f10d0 --- /dev/null +++ b/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.waynechu.bootstarter.logger.LoggerAutoConfiguration \ No newline at end of file diff --git a/src/main/resources/banner.txt b/src/main/resources/banner.txt new file mode 100644 index 0000000..3fee5b0 --- /dev/null +++ b/src/main/resources/banner.txt @@ -0,0 +1,10 @@ + +___ __ ______________ +__ | / /_____ _____ _____________ __ ____/__ /_____ __ +__ | /| / /_ __ `/_ / / /_ __ \ _ \ _ / __ __ \ / / / +__ |/ |/ / / /_/ /_ /_/ /_ / / / __/ / /___ _ / / / /_/ / +____/|__/ \__,_/ _\__, / /_/ /_/\___/ \____/ /_/ /_/\__,_/ + /____/ +${AnsiColor.YELLOW}:: Spring Boot :: ${AnsiColor.WHITE}${spring-boot.formatted-version} +${AnsiColor.YELLOW}:: Spring Cloud :: ${AnsiColor.WHITE}(${biz.logger.version.spring-cloud-dependencies}) +${AnsiColor.YELLOW}:: Parent Project :: ${AnsiColor.WHITE}(${biz.logger.version.parent-project}) \ No newline at end of file diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml new file mode 100644 index 0000000..c00147e --- /dev/null +++ b/src/main/resources/logback-spring.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + + + + + ERROR + + + + + + + + + + + + ${elkRabbitmqHost} + ${elkRabbitmqPort} + ${elkRabbitmqUsername} + ${elkRabbitmqPassword} + ${elkRabbitmqApplicationId} + ${elkRabbitmqVirtualHost} + ${elkRabbitmqExchange} + topic + true + ${elkRabbitmqRoutingKey} + false + true + true + PERSISTENT + UTF-8 + text/json + ${elkRabbitmqConnectionName} + + + + + + + + + + + + + INFO + + + ${elkKafkaTopic} + + + + + + + + + + bootstrap.servers=${elkKafkaHost}:${elkKafkaPort} + buffer.memory=${elkKafkaBufferMemory} + acks=${elkKafkaAcks} + linger.ms=${elkKafkaLingerMs} + max.block.ms=${elkKafkaMaxBlockMs} + compression.type=${elkKafkaCompressionType} + retries=${elkKafkaRetries} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/target/biz-boot-starter-logger-1.0.0-RELEASE-sources.jar b/target/biz-boot-starter-logger-1.0.0-RELEASE-sources.jar new file mode 100644 index 0000000..c915d1d Binary files /dev/null and b/target/biz-boot-starter-logger-1.0.0-RELEASE-sources.jar differ diff --git a/target/biz-boot-starter-logger-1.0.0-RELEASE.jar b/target/biz-boot-starter-logger-1.0.0-RELEASE.jar new file mode 100644 index 0000000..e313cf9 Binary files /dev/null and b/target/biz-boot-starter-logger-1.0.0-RELEASE.jar differ diff --git a/target/classes/META-INF/spring-configuration-metadata.json b/target/classes/META-INF/spring-configuration-metadata.json new file mode 100644 index 0000000..d36933c --- /dev/null +++ b/target/classes/META-INF/spring-configuration-metadata.json @@ -0,0 +1,182 @@ +{ + "groups": [ + { + "name": "biz.logger.version", + "type": "cn.waynechu.bootstarter.logger.properties.LoggerProperty", + "sourceType": "cn.waynechu.bootstarter.logger.properties.LoggerProperty" + }, + { + "name": "elk", + "type": "cn.waynechu.bootstarter.logger.properties.ElkProperty", + "sourceType": "cn.waynechu.bootstarter.logger.properties.ElkProperty" + }, + { + "name": "elk.kafka", + "type": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty", + "sourceType": "cn.waynechu.bootstarter.logger.properties.ElkProperty" + }, + { + "name": "elk.rabbitmq", + "type": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty", + "sourceType": "cn.waynechu.bootstarter.logger.properties.ElkProperty" + }, + { + "name": "sentry", + "type": "cn.waynechu.bootstarter.logger.properties.SentryProperties", + "sourceType": "cn.waynechu.bootstarter.logger.properties.SentryProperties" + } + ], + "properties": [ + { + "name": "biz.logger.version.parent-project", + "type": "java.lang.String", + "description": "指定banner展示的父项目版本", + "sourceType": "cn.waynechu.bootstarter.logger.properties.LoggerProperty" + }, + { + "name": "biz.logger.version.spring-cloud-dependencies", + "type": "java.lang.String", + "description": "指定banner展示的 SpringCloud 版本", + "sourceType": "cn.waynechu.bootstarter.logger.properties.LoggerProperty" + }, + { + "name": "elk.kafka.acks", + "type": "java.lang.Integer", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.buffer-memory", + "type": "java.lang.Long", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.compression-type", + "type": "java.lang.String", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.default-host", + "type": "java.lang.String", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.enable", + "type": "java.lang.Boolean", + "description": "是否开启日志上传", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty", + "defaultValue": false + }, + { + "name": "elk.kafka.host", + "type": "java.lang.String", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.linger-ms", + "type": "java.lang.Integer", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.max-block-ms", + "type": "java.lang.Integer", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.port", + "type": "java.lang.Integer", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.retries", + "type": "java.lang.Integer", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.kafka.topic", + "type": "java.lang.String", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.KafkaProperty" + }, + { + "name": "elk.rabbitmq.application-id", + "type": "java.lang.String", + "description": "应用ID", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.connection-name", + "type": "java.lang.String", + "description": "客户端标识(在Rabbit Admin界面展示)", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.enable", + "type": "java.lang.Boolean", + "description": "是否开启日志上传", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty", + "defaultValue": false + }, + { + "name": "elk.rabbitmq.exchange", + "type": "java.lang.String", + "description": "RabbitMQ exchange", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.host", + "type": "java.lang.String", + "description": "RabbitMQ host", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.password", + "type": "java.lang.String", + "description": "RabbitMQ password", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.port", + "type": "java.lang.Integer", + "description": "RabbitMQ port", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.routing-key", + "type": "java.lang.String", + "description": "RabbitMQ routingKey", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.username", + "type": "java.lang.String", + "description": "RabbitMQ username", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "elk.rabbitmq.virtual-host", + "type": "java.lang.String", + "description": "RabbitMQ virtualHost", + "sourceType": "cn.waynechu.bootstarter.logger.properties.nested.RabbitmqProperty" + }, + { + "name": "sentry.dsn", + "type": "java.lang.String", + "description": "sentry dsn 地址", + "sourceType": "cn.waynechu.bootstarter.logger.properties.SentryProperties" + }, + { + "name": "sentry.enable", + "type": "java.lang.Boolean", + "description": "是否开启sentry日志上传", + "sourceType": "cn.waynechu.bootstarter.logger.properties.SentryProperties", + "defaultValue": false + }, + { + "name": "sentry.stacktrace-app-packages", + "type": "java.lang.String", + "description": "要追踪的包名,多个请用 “,” 分割。默认上传所有包下的日志", + "sourceType": "cn.waynechu.bootstarter.logger.properties.SentryProperties", + "defaultValue": "" + } + ], + "hints": [] +} \ No newline at end of file diff --git a/target/classes/META-INF/spring.factories b/target/classes/META-INF/spring.factories new file mode 100644 index 0000000..b5f10d0 --- /dev/null +++ b/target/classes/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.waynechu.bootstarter.logger.LoggerAutoConfiguration \ No newline at end of file diff --git a/target/classes/banner.txt b/target/classes/banner.txt new file mode 100644 index 0000000..3fee5b0 --- /dev/null +++ b/target/classes/banner.txt @@ -0,0 +1,10 @@ + +___ __ ______________ +__ | / /_____ _____ _____________ __ ____/__ /_____ __ +__ | /| / /_ __ `/_ / / /_ __ \ _ \ _ / __ __ \ / / / +__ |/ |/ / / /_/ /_ /_/ /_ / / / __/ / /___ _ / / / /_/ / +____/|__/ \__,_/ _\__, / /_/ /_/\___/ \____/ /_/ /_/\__,_/ + /____/ +${AnsiColor.YELLOW}:: Spring Boot :: ${AnsiColor.WHITE}${spring-boot.formatted-version} +${AnsiColor.YELLOW}:: Spring Cloud :: ${AnsiColor.WHITE}(${biz.logger.version.spring-cloud-dependencies}) +${AnsiColor.YELLOW}:: Parent Project :: ${AnsiColor.WHITE}(${biz.logger.version.parent-project}) \ No newline at end of file diff --git a/target/classes/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.class b/target/classes/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.class new file mode 100644 index 0000000..0fd32f2 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/LoggerAutoConfiguration.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$Event.class b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$Event.class new file mode 100644 index 0000000..2a1aca2 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$Event.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender$1.class b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender$1.class new file mode 100644 index 0000000..288ac65 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender$1.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender.class b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender.class new file mode 100644 index 0000000..fd798d7 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender$EventSender.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender.class b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender.class new file mode 100644 index 0000000..cc7146f Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/appender/AmqpAppender.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/aware/SentryContextAware.class b/target/classes/cn/waynechu/bootstarter/logger/aware/SentryContextAware.class new file mode 100644 index 0000000..8622c4f Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/aware/SentryContextAware.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.class b/target/classes/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.class new file mode 100644 index 0000000..d64e386 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/constant/TraceKeyConstant.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/filter/MDCFilter.class b/target/classes/cn/waynechu/bootstarter/logger/filter/MDCFilter.class new file mode 100644 index 0000000..9eb483a Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/filter/MDCFilter.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.class b/target/classes/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.class new file mode 100644 index 0000000..afe9ef1 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/filter/ModifyHttpServletRequestWrapper.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.class b/target/classes/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.class new file mode 100644 index 0000000..cce308f Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/layout/RabbitmqLayout.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/properties/ElkProperty.class b/target/classes/cn/waynechu/bootstarter/logger/properties/ElkProperty.class new file mode 100644 index 0000000..87fa21a Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/properties/ElkProperty.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/properties/LoggerProperty.class b/target/classes/cn/waynechu/bootstarter/logger/properties/LoggerProperty.class new file mode 100644 index 0000000..f873232 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/properties/LoggerProperty.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/properties/SentryProperties.class b/target/classes/cn/waynechu/bootstarter/logger/properties/SentryProperties.class new file mode 100644 index 0000000..c30284a Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/properties/SentryProperties.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.class b/target/classes/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.class new file mode 100644 index 0000000..7a18c0e Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/properties/nested/KafkaProperty.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.class b/target/classes/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.class new file mode 100644 index 0000000..2d150ad Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/properties/nested/RabbitmqProperty.class differ diff --git a/target/classes/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.class b/target/classes/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.class new file mode 100644 index 0000000..15ee291 Binary files /dev/null and b/target/classes/cn/waynechu/bootstarter/logger/provider/ApplicationProvider.class differ diff --git a/target/classes/logback-spring.xml b/target/classes/logback-spring.xml new file mode 100644 index 0000000..c00147e --- /dev/null +++ b/target/classes/logback-spring.xml @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ${CONSOLE_LOG_PATTERN} + + + + + + + + + + ERROR + + + + + + + + + + + + ${elkRabbitmqHost} + ${elkRabbitmqPort} + ${elkRabbitmqUsername} + ${elkRabbitmqPassword} + ${elkRabbitmqApplicationId} + ${elkRabbitmqVirtualHost} + ${elkRabbitmqExchange} + topic + true + ${elkRabbitmqRoutingKey} + false + true + true + PERSISTENT + UTF-8 + text/json + ${elkRabbitmqConnectionName} + + + + + + + + + + + + + INFO + + + ${elkKafkaTopic} + + + + + + + + + + bootstrap.servers=${elkKafkaHost}:${elkKafkaPort} + buffer.memory=${elkKafkaBufferMemory} + acks=${elkKafkaAcks} + linger.ms=${elkKafkaLingerMs} + max.block.ms=${elkKafkaMaxBlockMs} + compression.type=${elkKafkaCompressionType} + retries=${elkKafkaRetries} + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/target/maven-archiver/pom.properties b/target/maven-archiver/pom.properties new file mode 100644 index 0000000..b9b5eaf --- /dev/null +++ b/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=biz-boot-starter-logger +groupId=cn.waynechu +version=1.0.0-RELEASE diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..3065248 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,17 @@ +cn\waynechu\bootstarter\logger\constant\TraceKeyConstant.class +cn\waynechu\bootstarter\logger\layout\RabbitmqLayout.class +cn\waynechu\bootstarter\logger\properties\SentryProperties.class +cn\waynechu\bootstarter\logger\appender\AmqpAppender$EventSender.class +cn\waynechu\bootstarter\logger\properties\nested\KafkaProperty.class +META-INF\spring-configuration-metadata.json +cn\waynechu\bootstarter\logger\filter\ModifyHttpServletRequestWrapper.class +cn\waynechu\bootstarter\logger\appender\AmqpAppender$Event.class +cn\waynechu\bootstarter\logger\provider\ApplicationProvider.class +cn\waynechu\bootstarter\logger\filter\MDCFilter.class +cn\waynechu\bootstarter\logger\appender\AmqpAppender.class +cn\waynechu\bootstarter\logger\properties\LoggerProperty.class +cn\waynechu\bootstarter\logger\properties\nested\RabbitmqProperty.class +cn\waynechu\bootstarter\logger\appender\AmqpAppender$EventSender$1.class +cn\waynechu\bootstarter\logger\LoggerAutoConfiguration.class +cn\waynechu\bootstarter\logger\properties\ElkProperty.class +cn\waynechu\bootstarter\logger\aware\SentryContextAware.class diff --git a/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..5b159d4 --- /dev/null +++ b/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,13 @@ +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\LoggerAutoConfiguration.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\properties\ElkProperty.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\aware\SentryContextAware.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\filter\MDCFilter.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\filter\ModifyHttpServletRequestWrapper.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\provider\ApplicationProvider.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\appender\AmqpAppender.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\constant\TraceKeyConstant.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\layout\RabbitmqLayout.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\properties\SentryProperties.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\properties\nested\KafkaProperty.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\properties\nested\RabbitmqProperty.java +D:\workspace\biz-parent\biz-boot-starter-logger\src\main\java\cn\waynechu\bootstarter\logger\properties\LoggerProperty.java