添加 axzo-logger-spring-boot-starter

This commit is contained in:
zhourui 周锐 2022-09-09 14:24:52 +08:00
parent 2110f70532
commit f6cf98297f
9 changed files with 386 additions and 11 deletions

View File

@ -0,0 +1,33 @@
# axzo-logger-spring-boot-starter
日志支持库.
更多细节用法请参考:
- [Spring Boot Logging](https://docs.spring.io/spring-boot/docs/2.4.13/reference/html/spring-boot-features.html#boot-features-logging)
## Quickstart
### 1、引入依赖
``` xml
<!-- Logger依赖 -->
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-logger-spring-boot-starter</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
```
### 2、日志配置logback-spring.xml
``` xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration>
<!-- 导入安心筑全局日志配置 -->
<include resource="logback/logback-axzo.xml" />
<!-- 覆盖开发环境日志配置 -->
<springProfile name="local,dev">
<logger name="cn.axzo" level="DEBUG" />
</springProfile>
</configuration>
```

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-framework</artifactId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>axzo-logger-spring-boot-starter</artifactId>
<version>2.0.0-SNAPSHOT</version>
<name>axzo-logger-spring-boot-starter</name>
<description>安心筑日志支持库</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
<dependency>
<groupId>org.apache.skywalking</groupId>
<artifactId>apm-toolkit-logback-1.x</artifactId>
</dependency>
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,77 @@
package cn.axzo.framework.logger;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.beans.BeansException;
import org.springframework.boot.context.logging.LoggingApplicationListener;
import org.springframework.boot.logging.LogLevel;
import org.springframework.boot.logging.LoggerConfiguration;
import org.springframework.boot.logging.LoggerGroup;
import org.springframework.boot.logging.LoggerGroups;
import org.springframework.boot.logging.LoggingSystem;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* 动态日志Level调整管理器
*
* @see org.springframework.boot.actuate.autoconfigure.logging.LoggersEndpointAutoConfiguration
* @see org.springframework.boot.actuate.logging.LoggersEndpoint
*/
public class DynamicLoggerManager implements ApplicationContextAware {
private ApplicationContext context;
private static final Map<String, LogLevel> LEVELS = new HashMap<>();
static {
LEVELS.put("trace", LogLevel.TRACE);
LEVELS.put("debug", LogLevel.DEBUG);
LEVELS.put("info", LogLevel.INFO);
LEVELS.put("warn", LogLevel.WARN);
LEVELS.put("error", LogLevel.ERROR);
LEVELS.put("off", LogLevel.OFF);
}
/**
* 动态设置日志级别支持按group设置也支持按名称设置
* @param name group名称或者包名称Spring内置group有websql
* @param level 只支持tracedebuginfowarnerroroff
* @return 返回变更后的日志配置文本列表
*/
public List<String> setLevel(String name, String level) {
LogLevel logLevel = LEVELS.getOrDefault(level, LogLevel.INFO);
LoggingSystem loggingSystem = context.getBean(LoggingApplicationListener.LOGGING_SYSTEM_BEAN_NAME, LoggingSystem.class);
// 按分组设置日志级别
LoggerGroups loggerGroups = context.getBean(LoggingApplicationListener.LOGGER_GROUPS_BEAN_NAME, LoggerGroups.class);
LoggerGroup group = loggerGroups.get(name);
if (group != null && group.hasMembers()) {
group.configureLogLevel(logLevel, loggingSystem::setLogLevel);
List<String> loggerConfigs = group.getMembers().stream().map(loggingSystem::getLoggerConfiguration)
.sorted(Comparator.comparing(LoggerConfiguration::getName)).map(LoggerConfiguration::toString).collect(Collectors.toList());
return loggerConfigs;
}
// 单个loggerName设置日志级别
LoggerConfiguration loggerConfiguration = loggingSystem.getLoggerConfiguration(name);
if (loggerConfiguration == null) {
return Collections.singletonList("Logger not exist : " + name);
}
loggingSystem.setLogLevel(name, logLevel);
return Collections.singletonList(loggingSystem.getLoggerConfiguration(name).toString());
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}

View File

@ -0,0 +1,28 @@
package cn.axzo.framework.logger;
import java.net.InetAddress;
import java.net.UnknownHostException;
import ch.qos.logback.core.PropertyDefinerBase;
/**
* 获取主机IP地址
* //TODO 业务代码中的CanonicalHostNamePropertyDefiner类需要删除
*/
public class HostNamePropertyDefiner extends PropertyDefinerBase {
@Override
public String getPropertyValue() {
InetAddress ia;
try {
ia = InetAddress.getLocalHost();
String host = ia.getHostName();
return host;
} catch (UnknownHostException e) {
e.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,19 @@
package cn.axzo.framework.logger;
import ch.qos.logback.core.PropertyDefinerBase;
/**
* 日志路径定义
* 非容器环境下日志目录为target/logs/容器环境下日志目录为/mnt/app-logdata/ + podNamespace
* //TODO connom包中的冗余代码可删除
*/
public class LogPrefixhPropertyDefiner extends PropertyDefinerBase {
private static final String POD_NAMESPACE_KEY = "MY_POD_NAMESPACE";
@Override
public String getPropertyValue() {
String podNamespace = SystemPropertyResolver.getPropertyValue(POD_NAMESPACE_KEY);
return podNamespace == null ? "target/logs/" : "/mnt/app-logdata/" + podNamespace;
}
}

View File

@ -0,0 +1,16 @@
package cn.axzo.framework.logger;
import ch.qos.logback.core.PropertyDefinerBase;
/**
* 获取 Pod Namespaces变量值
*/
public class PodNamespacePropertyDefiner extends PropertyDefinerBase {
private static final String POD_NAMESPACE_KEY = "MY_POD_NAMESPACE";
@Override
public String getPropertyValue() {
return SystemPropertyResolver.getPropertyValue(POD_NAMESPACE_KEY);
}
}

View File

@ -0,0 +1,48 @@
package cn.axzo.framework.logger;
import java.security.AccessControlException;
import java.util.Collections;
import java.util.Map;
import java.util.Properties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* 从系统变量中解析属性
*/
public class SystemPropertyResolver {
private static Logger logger = LoggerFactory.getLogger(SystemPropertyResolver.class);
public static String getPropertyValue(String key) {
String envValue = getSystemEnvironment().get(key);
return envValue == null ? getSystemProperties().getProperty(key) : envValue;
}
private static Map<String, String> getSystemEnvironment() {
try {
return System.getenv();
} catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system environment, its value will be returned [null]. Reason: "
+ ex.getMessage());
}
return Collections.emptyMap();
}
}
private static Properties getSystemProperties() {
try {
return System.getProperties();
} catch (AccessControlException ex) {
if (logger.isInfoEnabled()) {
logger.info("Caught AccessControlException when accessing system property, its value will be returned [null]. Reason: "
+ ex.getMessage());
}
return new Properties();
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<included>
<!-- 引用 Spring Boot 的 logback 基础配置 -->
<include resource="org/springframework/boot/logging/logback/defaults.xml" />
<!-- spring变量 -->
<springProperty scope="context" name="appName" source="spring.application.name"/>
<define name="hostname" class="cn.axzo.framework.logger.HostNamePropertyDefiner"/>
<define name="LOG_PREFIX" class="cn.axzo.framework.logger.LogPrefixhPropertyDefiner"/>
<property name="LOG_FILE" value="${appName}.log"/>
<!--日志路径-->
<property name="LOG_PATH" value="${LOG_PREFIX}/${appName}/"/>
<!-- 日志最大的历史 7天 -->
<property name="MAX_HISTORY" value="7"/>
<!--默认日志输出模式-->
<property name="PATTERN_CONSOLE" value="%date{HH:mm:ss} %highlight(%-5level) [%15thread] %cyan([%class{36}#%M:%L]) %msg%n"/>
<property name="PATTERN_FILE" value="%date{yyyy-MM-dd HH:mm:ss} %-5level --- [%15thread] [%X{ctxLogId}] %logger: %msg%n"/>
<property name="PATTERN_SKWALKING" value="%d{yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%thread] [%tid] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<contextName>${appName}</contextName>
<!-- 控制台 Appender -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${PATTERN_CONSOLE}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!-- 文件 Appender -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.TraceIdPatternLogbackLayout">
<pattern>${PATTERN_FILE}</pattern>
</layout>
</encoder>
<!-- 日志文件名 -->
<file>${LOG_PATH}/${LOG_FILE}</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- 滚动后的日志文件名 -->
<fileNamePattern>${LOGBACK_ROLLINGPOLICY_FILE_NAME_PATTERN:-${LOG_FILE}.%d{yyyy-MM-dd}.%i.gz}</fileNamePattern>
<!-- 启动服务时,是否清理历史日志,一般不建议清理 -->
<cleanHistoryOnStart>${LOGBACK_ROLLINGPOLICY_CLEAN_HISTORY_ON_START:-false}</cleanHistoryOnStart>
<!-- 日志文件,到达多少容量,进行滚动 -->
<maxFileSize>${LOGBACK_ROLLINGPOLICY_MAX_FILE_SIZE:-10MB}</maxFileSize>
<!-- 日志文件的总大小0 表示不限制 -->
<totalSizeCap>${LOGBACK_ROLLINGPOLICY_TOTAL_SIZE_CAP:-1GB}</totalSizeCap>
<!-- 日志文件的保留天数 -->
<maxHistory>${LOGBACK_ROLLINGPOLICY_MAX_HISTORY:-30}</maxHistory>
</rollingPolicy>
</appender>
<!-- 异步写入日志,提升性能 -->
<appender name="ASYNC" class="ch.qos.logback.classic.AsyncAppender">
<!-- 不丢失日志。默认的,如果队列的 80% 已满,则会丢弃 TRACT、DEBUG、INFO 级别的日志 -->
<discardingThreshold>0</discardingThreshold>
<!-- 更改默认的队列的深度,该值会影响性能。默认值为 256 -->
<queueSize>256</queueSize>
<appender-ref ref="FILE"/>
</appender>
<!-- 接入了skywalking才配置该项 -->
<appender name="SKYWALKING" class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.log.GRPCLogClientAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="org.apache.skywalking.apm.toolkit.log.logback.v1.x.mdc.TraceIdMDCPatternLogbackLayout">
<Pattern>${PATTERN_SKWALKING}</Pattern>
</layout>
</encoder>
</appender>
<!-- 控制台输出LOGSTASH JSON日志 -->
<appender name="LOGSTASH" class="ch.qos.logback.core.ConsoleAppender" addtivity="false">
<encoder charset="UTF-8" class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<timestamp>
<fieldName>@timestamp</fieldName>
<pattern>yyyy-MM-dd'T'HH:mm:ss.SSSx</pattern>
<timeZone>GMT+8</timeZone>
</timestamp>
<pattern>
<pattern>
{
"app":"${appName}",
"level":"%level",
"traceId":"%X{ctxLogId}",
"thread":"%thread",
"class":"%logger{40}",
"message":"%message",
"m":"#asJson{%message}",
"error_level":"%X{errorLevel}",
"error_type":"%X{errorType}",
"stack_trace":"%exception{20}"
}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<!-- 本地环境 -->
<springProfile name="local,dev">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</springProfile>
<!-- 其它环境 -->
<springProfile name="test,test1,uat,pre,master,default,pre-new">
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="ASYNC"/>
<appender-ref ref="LOGSTASH"/>
<appender-ref ref="SKYWALKING"/>
</root>
</springProfile>
</included>

20
pom.xml
View File

@ -19,7 +19,6 @@
<properties>
<revision>2.0.0-SNAPSHOT</revision>
<axzo-bom.version>2.0.0-SNAPSHOT</axzo-bom.version>
</properties>
<modules>
@ -32,18 +31,17 @@
<module>canal-alarm</module>
<module>axzo-auth-spring-boot-starter</module>
<module>axzo-test-spring-boot-starter</module>
<module>axzo-logger-spring-boot-starter</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>cn.axzo.infra</groupId>
<artifactId>axzo-bom</artifactId>
<version>${axzo-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
<dependencies>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-common</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>