一、logback填坑,log4j2填坑:
1.logback填坑
bootstrap.yml(bootstrap.properties)先加载,用于应用程序上下文的引导阶段。
application.yml(application.properties)后加载,由父Spring ApplicationContext加载。
bootstrap.yml 是系统级别的一些参数配置,这些参数一般是不变的。
application.yml 一般用来定义单个应用级别的,如果搭配 spring-cloud-config 使用 application.yml 里面定义的文件可以实现动态替换。
由于将指定logback配置文件地址的配置项(logging.config=classpath:logback-custom.xml)写在了bootstrap.yml中,log_base_dir配置项位于配置中心,bootstrap.yml会先加载,将读取不到log_base_dir从而logback会自动建一个名为log_base_dir_IS_UNDEFINED的文件夹
logback-custom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback-custom</contextName>
<!--该配置项位于配置中心,若该配置文件先加载,将读取不到log_base_dir从而logback会自动建一个名为log_base_dir_IS_UNDEFINED的文件夹-->
<springProperty scope="context" name="log_base_dir" source="my.log_base_dir"/>
<springProperty scope="context" name="log_app_dir" source="spring.application.name"/>
<!--输出到控制台-->
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%p][%c][%M][%L]-> %m%n</pattern>
<charset class="java.nio.charset.Charset">UTF-8</charset>
</encoder>
</appender>
<appender name="rollingfile_other" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log_base_dir:-/my_log}/%d{yyyy-MM-dd}/${log_app_dir}/other_%i.log
</fileNamePattern>
<maxHistory>30</maxHistory>
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>DENY</onMatch>
<onMismatch>ACCEPT</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%p][%c][%M][%L]-> %m%n</pattern>
</layout>
</appender>
<appender name="rollingfile_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log_base_dir:-/my_log}/%d{yyyy-MM-dd}/${log_app_dir}/error_%i.log
</fileNamePattern>
<maxHistory>30</maxHistory>
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%p][%c][%M][%L]-> %m%n</pattern>
</layout>
</appender>
<appender name="rollingfile_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${log_base_dir:-/my_log}/%d{yyyy-MM-dd}/${log_app_dir}/operation_%i.log
</fileNamePattern>
<maxHistory>30</maxHistory>
<maxFileSize>100MB</maxFileSize>
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<layout class="ch.qos.logback.classic.PatternLayout">
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%p][%c]-> %m%n</pattern>
</layout>
</appender>
<root level="Info">
<appender-ref ref="console" />
<appender-ref ref="rollingfile_error" />
<appender-ref ref="rollingfile_other" />
<appender-ref ref="rollingfile_info" />
</root>
</configuration>
解决方案:
将logging.config=classpath:logback-custom.xml放于配置中心的该应用的配置下即可。
附上:
1️⃣外部配置加载
Spring Boot 支持多种外部配置方式,如下所示,从上往下加载优先级由高到低,内容相同时覆盖,不相同时累加。
命令行参数
来自java:comp/env的JNDI属性
使用“spring.config.location”改变默认的配置文件位置
Java系统属性(System.getProperties())
操作系统环境变量
RandomValuePropertySource配置的random.*属性值
jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件
jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件
jar包外部的application.properties或application.yml(不带spring.profile)配置文件
jar包内部的application.properties或application.yml(不带spring.profile)配置文件
@Configuration注解类上的@PropertySource
通过SpringApplication.setDefaultProperties指定的默认属性
官方文档:文档
2️⃣logback.xml加载早于application.yml,用这种方式:logging.config=classpath:logback-custom.xml 会延迟加载logback的配置文件
2.log4j2填坑
log4j2想实现的也是能够读取配置中心的配置项-日志输出路径
但是,log4j2.xml不像logback似乎没有办法获取配置中心的配置;
解决方案:参考RollingFileAppender,写一个自定义的日志输出类
因为该类加载时,spring容器尚未注入bean,因此通过rest请求直接向配置中心获取配置。
@Plugin(
name = "CustomRollingFile",//自定义标签名<CustomRollingFile></CustomRollingFile>
category = "Core",
elementType = "appender",
printObject = true
)
public final class CustomRollingFileAppender extends AbstractOutputStreamAppender<RollingFileManager> {
public static final String PLUGIN_NAME = "CustomRollingFile";
private static final int DEFAULT_BUFFER_SIZE = 8192;
//配置中心请求路径
private static final String CONFIG_REQUEST_URL = "http://localhost:7010/application-pro.yml";
//日志输出地址基地址
private static String log_base_dir;
private static RestTemplate restTemplate = new RestTemplate ();
private final String fileName;
private final String filePattern;
private Object advertisement;
private final Advertiser advertiser;
private CustomRollingFileAppender(String name, Layout<? extends Serializable> layout, Filter filter, RollingFileManager manager, String fileName, String filePattern, boolean ignoreExceptions, boolean immediateFlush, Advertiser advertiser) {
super(name, layout, filter, ignoreExceptions, immediateFlush, manager);
if (advertiser != null) {
Map<String, String> configuration = new HashMap(layout.getContentFormat());
configuration.put("contentType", layout.getContentType());
configuration.put("name", name);
this.advertisement = advertiser.advertise(configuration);
}
this.fileName = fileName;
this.filePattern = filePattern;
this.advertiser = advertiser;
}
public boolean stop(long timeout, TimeUnit timeUnit) {
this.setStopping();
boolean stopped = super.stop(timeout, timeUnit, false);
if (this.advertiser != null) {
this.advertiser.unadvertise(this.advertisement);
}
this.setStopped();
return stopped;
}
public void append(LogEvent event) {
((RollingFileManager)this.getManager()).checkRollover(event);
super.append(event);
}
public String getFileName() {
return this.fileName;
}
public String getFilePattern() {
return this.filePattern;
}
public <T extends TriggeringPolicy> T getTriggeringPolicy() {
return ((RollingFileManager)this.getManager()).getTriggeringPolicy();
}
@PluginBuilderFactory
public static <B extends CustomRollingFileAppender.Builder<B>> B newBuilder() {
return (B) (new Builder()).asBuilder();
}
public static class Builder<B extends CustomRollingFileAppender.Builder<B>> extends org.apache.logging.log4j.core.appender.AbstractOutputStreamAppender.Builder<B> implements org.apache.logging.log4j.core.util.Builder<CustomRollingFileAppender> {
@PluginBuilderAttribute
private String fileName;
@PluginBuilderAttribute
@Required
private String filePattern;
@PluginBuilderAttribute
private boolean append = true;
@PluginBuilderAttribute
private boolean locking;
@PluginElement("Policy")
@Required
private TriggeringPolicy policy;
@PluginElement("Strategy")
private RolloverStrategy strategy;
@PluginBuilderAttribute
private boolean advertise;
@PluginBuilderAttribute
private String advertiseUri;
@PluginBuilderAttribute
private boolean createOnDemand;
@PluginBuilderAttribute
private String filePermissions;
@PluginBuilderAttribute
private String fileOwner;
@PluginBuilderAttribute
private String fileGroup;
public Builder() {
}
public CustomRollingFileAppender build() {
try{
Yaml yaml = new Yaml ();
String urlStr = restTemplate.getForEntity (CONFIG_REQUEST_URL, String.class, new HashMap<> ()).getBody ();
log_base_dir = ((Map<String,String>)((Map)yaml.load (urlStr)).get ("idm")).get ("log_base_dir");
}catch (Exception e){
log_base_dir = "/idm_log";
}
SimpleDateFormat format = new SimpleDateFormat ("yyyy-MM-dd");
//修改的生成的文件名
this.fileName = log_base_dir + "/" + format.format (new Date ()) + fileName;
//修改追加生成的文件名
this.filePattern = log_base_dir + filePattern;
boolean isBufferedIo = this.isBufferedIo();
int bufferSize = this.getBufferSize();
if (this.getName() == null) {
CustomRollingFileAppender.LOGGER.error("CustomRollingFileAppender '{}': No name provided.", this.getName());
return null;
} else {
if (!isBufferedIo && bufferSize > 0) {
CustomRollingFileAppender.LOGGER.warn("CustomRollingFileAppender '{}': The bufferSize is set to {} but bufferedIO is not true", this.getName(), bufferSize);
}
if (this.filePattern == null) {
CustomRollingFileAppender.LOGGER.error("CustomRollingFileAppender '{}': No file name pattern provided.", this.getName());
return null;
} else if (this.policy == null) {
CustomRollingFileAppender.LOGGER.error("CustomRollingFileAppender '{}': No TriggeringPolicy provided.", this.getName());
return null;
} else {
if (this.strategy == null) {
if (this.fileName != null) {
this.strategy = DefaultRolloverStrategy.newBuilder().withCompressionLevelStr(String.valueOf(-1)).withConfig(this.getConfiguration()).build();
} else {
this.strategy = DirectWriteRolloverStrategy.newBuilder().withCompressionLevelStr(String.valueOf(-1)).withConfig(this.getConfiguration()).build();
}
} else if (this.fileName == null && !(this.strategy instanceof DirectFileRolloverStrategy)) {
CustomRollingFileAppender.LOGGER.error("CustomRollingFileAppender '{}': When no file name is provided a DirectFilenameRolloverStrategy must be configured");
return null;
}
Layout<? extends Serializable> layout = this.getOrCreateLayout();
RollingFileManager manager = RollingFileManager.getFileManager(this.fileName, this.filePattern, this.append, isBufferedIo, this.policy, this.strategy, this.advertiseUri, layout, bufferSize, this.isImmediateFlush(), this.createOnDemand, this.filePermissions, this.fileOwner, this.fileGroup, this.getConfiguration());
if (manager == null) {
return null;
} else {
manager.initialize();
return new CustomRollingFileAppender(this.getName(), layout, this.getFilter(), manager, this.fileName, this.filePattern, this.isIgnoreExceptions(), this.isImmediateFlush(), this.advertise ? this.getConfiguration().getAdvertiser() : null);
}
}
}
}
public String getAdvertiseUri() {
return this.advertiseUri;
}
public String getFileName() {
return this.fileName;
}
public boolean isAdvertise() {
return this.advertise;
}
public boolean isAppend() {
return this.append;
}
public boolean isCreateOnDemand() {
return this.createOnDemand;
}
public boolean isLocking() {
return this.locking;
}
public String getFilePermissions() {
return this.filePermissions;
}
public String getFileOwner() {
return this.fileOwner;
}
public String getFileGroup() {
return this.fileGroup;
}
public B withAdvertise(boolean advertise) {
this.advertise = advertise;
return this.asBuilder();
}
public B withAdvertiseUri(String advertiseUri) {
this.advertiseUri = advertiseUri;
return this.asBuilder();
}
public B withAppend(boolean append) {
this.append = append;
return this.asBuilder();
}
public B withFileName(String fileName) {
this.fileName = fileName;
return this.asBuilder();
}
public B withCreateOnDemand(boolean createOnDemand) {
this.createOnDemand = createOnDemand;
return this.asBuilder();
}
public B withLocking(boolean locking) {
this.locking = locking;
return this.asBuilder();
}
public String getFilePattern() {
return this.filePattern;
}
public TriggeringPolicy getPolicy() {
return this.policy;
}
public RolloverStrategy getStrategy() {
return this.strategy;
}
public B withFilePattern(String filePattern) {
this.filePattern = filePattern;
return this.asBuilder();
}
public B withPolicy(TriggeringPolicy policy) {
this.policy = policy;
return this.asBuilder();
}
public B withStrategy(RolloverStrategy strategy) {
this.strategy = strategy;
return this.asBuilder();
}
public B withFilePermissions(String filePermissions) {
this.filePermissions = filePermissions;
return this.asBuilder();
}
public B withFileOwner(String fileOwner) {
this.fileOwner = fileOwner;
return this.asBuilder();
}
public B withFileGroup(String fileGroup) {
this.fileGroup = fileGroup;
return this.asBuilder();
}
}
}
二、设置jpa.hibernate.ddl-auto: update启动服务器自动建表报错
More than one table found in namespace (, ) - SchemaExtractionException
因为在同一个数据源下存在多个相同表结构的schema。
解决办法:指定schema
jpa:
properties:
hibernate:
default_schema: IDM
jsonView填坑:
当一个对象有子对象时,该怎么控制其子对象的输出?
解决方法
public class UserInfoDto {
public interface SimpleView{}
@JsonView(SimpleView.class)
private String id;
/**
* 姓名
*/
@JsonView(SimpleView.class)
private String name;
/**
* 账号信息
*/
@JsonView(SimpleView.class)
private AccountDto account;
/**getter and setter**/
}
public class AccountDto {
@JsonView(UserInfoDto.SimpleView.class)
private String id;
/**
* 账号名
*/
@JsonView(UserInfoDto.SimpleView.class)
private String name;
/**
* 密码
*/
private String password;
/**getter and setter**/
}
三、因为Date,DateTime类型没有时区数据,所以决定用LocalDate
springboot jdk8 LocalDate LocalDateTime jpa 映射字段是tinyblob 解决办法
hibernate版本低于5.1.2会这样,加依赖compile ‘org.hibernate:hibernate-java8:5.1.2.Final’
或者用转换器
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.sql.Timestamp;
import java.time.LocalDateTime;
@Converter(autoApply = true)
public class LocalDateTimeAttributeConverter implements AttributeConverter<LocalDateTime, Timestamp> {
@Override
public Timestamp convertToDatabaseColumn(LocalDateTime locDateTime) {
return (locDateTime == null ? null : Timestamp.valueOf(locDateTime));
}
@Override
public LocalDateTime convertToEntityAttribute(Timestamp sqlTimestamp) {
return (sqlTimestamp == null ? null : sqlTimestamp.toLocalDateTime());
}
}
@Convert(converter = LocalDateTimeAttributeConverter.class)
private LocalDateTime verificationDate;
错误记录:有多个LocalDateTime字段,我只讲数据库中的一个字段重置了类型,因此一直报PGSQL转换日期类型错误。。。
LocalDateTime反序列化失败
解决办法:加注解
@JsonDeserialize(using = LocalDateTimeDeserializer.class)
@JsonSerialize(using = LocalDateTimeSerializer.class)
private LocalDateTime verificationDate;
Swagger自动生成代码工具会把实体的LocalDateTime类型换成org.threeten.bp.OffsetDateTime,若不对LocalDatetime类型做格式化处理,则会报日期类型转换的相关错误;
解决方法:在属性上加上日期格式化注解
@JsonFormat(pattern=“yyyy-MM-dd’T’HH:mm:ss’Z’”)
四、jpa 分页查询 踩坑
0,1页码 index是从0开始的