diff --git a/example/integration-log/integration-log-main/pom.xml b/example/integration-log/integration-log-main/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..a77618ac2b14b21033ce3f75476f5b70da458357 --- /dev/null +++ b/example/integration-log/integration-log-main/pom.xml @@ -0,0 +1,80 @@ + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.0.3.RELEASE + + + + com.gitee.starblues + 2.4.2-RELEASE + integration-log-main + jar + 集成插件日志 案例--主程序 + + + 1.8 + UTF-8 + UTF-8 + 3.7.0 + 2.4.2-RELEASE + 2.4.2-RELEASE + + + + + com.gitee.starblues + springboot-plugin-framework + ${springboot-plugin-framework.version} + + + com.gitee.starblues + springboot-plugin-framework-extension-log + ${extension-log.version} + + + org.springframework.boot + spring-boot-starter-web + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + exec + + + + + + + + + + \ No newline at end of file diff --git a/example/integration-log/integration-log-main/src/main/java/com/log/main/LogMain.java b/example/integration-log/integration-log-main/src/main/java/com/log/main/LogMain.java new file mode 100644 index 0000000000000000000000000000000000000000..69b8d7e213c55ca46b9d53ea000b5d8f97e9c60c --- /dev/null +++ b/example/integration-log/integration-log-main/src/main/java/com/log/main/LogMain.java @@ -0,0 +1,19 @@ +package com.log.main; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +/** + * 主启动程序 + * + * @author starBlues + * @version 1.0 + */ +@SpringBootApplication() +public class LogMain { + + public static void main(String[] args) { + SpringApplication.run(LogMain.class, args); + } + +} diff --git a/example/integration-log/integration-log-main/src/main/java/com/log/main/config/PluginBeanConfig.java b/example/integration-log/integration-log-main/src/main/java/com/log/main/config/PluginBeanConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..cf5c0a012a70a84f7eb0651d6d6f225e65acb15c --- /dev/null +++ b/example/integration-log/integration-log-main/src/main/java/com/log/main/config/PluginBeanConfig.java @@ -0,0 +1,78 @@ +package com.log.main.config; + +import com.gitee.starblues.extension.log.SpringBootLogExtension; +import com.gitee.starblues.integration.ConfigurationBuilder; +import com.gitee.starblues.integration.IntegrationConfiguration; +import com.gitee.starblues.integration.application.AutoPluginApplication; +import com.gitee.starblues.integration.application.PluginApplication; +import org.pf4j.RuntimeMode; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.stereotype.Component; + +@Component +@ConfigurationProperties(prefix = "plugin") +public class PluginBeanConfig { + + /** + * 运行模式 + * 开发环境: development、dev + * 生产/部署 环境: deployment、prod + */ + @Value("${runMode:dev}") + private String runMode; + + /** + * 插件的路径 + */ + @Value("${pluginPath:plugins}") + private String pluginPath; + + /** + * 插件文件的路径 + */ + @Value("${pluginConfigFilePath:pluginConfigs}") + private String pluginConfigFilePath; + + + + @Bean + public IntegrationConfiguration configuration(){ + return ConfigurationBuilder.toBuilder() + .runtimeMode(RuntimeMode.byName(runMode)) + .pluginPath(pluginPath) + .pluginConfigFilePath(pluginConfigFilePath) + .uploadTempPath("temp") + .backupPath("backupPlugin") + .pluginRestPathPrefix("/api/plugin") + .enablePluginIdRestPathPrefix(true) + .enableSwaggerRefresh(true) + .build(); + } + + + /** + * 定义插件应用。使用可以注入它操作插件。 + * @return PluginApplication + */ + @Bean + public PluginApplication pluginApplication(){ + // 实例化自动初始化插件的PluginApplication + PluginApplication pluginApplication = new AutoPluginApplication(); + pluginApplication.addExtension(new SpringBootLogExtension()); + return pluginApplication; + } + + public void setRunMode(String runMode) { + this.runMode = runMode; + } + + public void setPluginPath(String pluginPath) { + this.pluginPath = pluginPath; + } + + public void setPluginConfigFilePath(String pluginConfigFilePath) { + this.pluginConfigFilePath = pluginConfigFilePath; + } +} diff --git a/example/integration-log/integration-log-main/src/main/java/com/log/main/rest/MainRest.java b/example/integration-log/integration-log-main/src/main/java/com/log/main/rest/MainRest.java new file mode 100644 index 0000000000000000000000000000000000000000..e48f48e6f822a1bb52aa362e168eefc589b57e9e --- /dev/null +++ b/example/integration-log/integration-log-main/src/main/java/com/log/main/rest/MainRest.java @@ -0,0 +1,24 @@ +package com.log.main.rest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; + +/** + * 插件jar 包测试功能 + * @author sousouki + * @version 1.0 + */ +@RestController +@RequestMapping("/log/main") +public class MainRest { + + private static final Logger log = LoggerFactory.getLogger(MainRest.class); + + @GetMapping("/print") + public String print(@RequestParam("value") String value){ + log.info("Request value: {}", value); + return value; + } + +} diff --git a/example/integration-log/plugins/integration-log-plugin/plugin.properties b/example/integration-log/plugins/integration-log-plugin/plugin.properties new file mode 100644 index 0000000000000000000000000000000000000000..c541bf21843c28502d1fc0499507f3faf26dfec7 --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/plugin.properties @@ -0,0 +1,4 @@ +plugin.id=integration-log-plugin +plugin.class=com.log.plugin.LogPlugin +plugin.version=2.4.2-RELEASE +plugin.provider=sousouki \ No newline at end of file diff --git a/example/integration-log/plugins/integration-log-plugin/pom.xml b/example/integration-log/plugins/integration-log-plugin/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..cfefb45a63e3fec8bb06b7c500800ff334ae91c5 --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/pom.xml @@ -0,0 +1,87 @@ + + + 4.0.0 + + com.gitee.starblues + integration-log-plugin + 2.4.2-RELEASE + jar + + + integration-log-plugin + com.log.plugin.TkMybatisPlugin + ${project.version} + sousouki + + + 1.8 + UTF-8 + UTF-8 + + 3.7.0 + 3.1.1 + + + + + + + com.gitee.starblues + integration-log-main + ${project.version} + provided + + + + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${java.version} + ${java.version} + ${project.build.sourceEncoding} + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + jar-with-dependencies + + + + true + true + + + ${plugin.id} + ${plugin.version} + ${plugin.provider} + ${plugin.class} + + + + + + make-assembly + package + + single + + + + + + + \ No newline at end of file diff --git a/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/LogMain.java b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/LogMain.java new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/LogPlugin.java b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/LogPlugin.java new file mode 100644 index 0000000000000000000000000000000000000000..346cae5c2c6827bb4dd14b1460dcbee828e71cca --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/LogPlugin.java @@ -0,0 +1,45 @@ +package com.log.plugin; + +import com.gitee.starblues.extension.log.config.SpringBootLogConfig; +import com.gitee.starblues.realize.BasePlugin; +import org.pf4j.PluginWrapper; + +import java.util.HashSet; +import java.util.Set; + +/** + * description + * + * @author sousouki + * @version 1.0 + */ +public class LogPlugin extends BasePlugin implements SpringBootLogConfig { + + private Set locations = new HashSet<>(); + + public LogPlugin(PluginWrapper wrapper) { + super(wrapper); + locations.add("classpath:log.xml"); + } + + @Override + protected void startEvent() { + + } + + @Override + protected void deleteEvent() { + + } + + @Override + protected void stopEvent() { + + } + + @Override + public Set logConfigLocations() { + return locations; + } +} + diff --git a/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/MainRest.java b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/MainRest.java new file mode 100644 index 0000000000000000000000000000000000000000..e48f48e6f822a1bb52aa362e168eefc589b57e9e --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/MainRest.java @@ -0,0 +1,24 @@ +package com.log.main.rest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.*; + +/** + * 插件jar 包测试功能 + * @author sousouki + * @version 1.0 + */ +@RestController +@RequestMapping("/log/main") +public class MainRest { + + private static final Logger log = LoggerFactory.getLogger(MainRest.class); + + @GetMapping("/print") + public String print(@RequestParam("value") String value){ + log.info("Request value: {}", value); + return value; + } + +} diff --git a/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/PluginRest.java b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/PluginRest.java new file mode 100644 index 0000000000000000000000000000000000000000..26bcc02fc33be241dd94ea046409502dbe807825 --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/src/main/java/com/log/main/rest/PluginRest.java @@ -0,0 +1,28 @@ +package com.log.plugin.rest; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +/** + * description + * + * @author starBlues + * @version 1.0 + */ +@RestController +@RequestMapping("/log/plugin") +public class PluginRest { + + private static final Logger log = LoggerFactory.getLogger(PluginRest.class); + + @GetMapping("/print") + public String print(@RequestParam("value") String value){ + log.info("Request value: {}", value); + return value; + } + +} diff --git a/example/integration-log/plugins/integration-log-plugin/src/main/resources/log.xml b/example/integration-log/plugins/integration-log-plugin/src/main/resources/log.xml new file mode 100644 index 0000000000000000000000000000000000000000..b0fabf290e7aab4e8b8055b6b0026e63f08f1571 --- /dev/null +++ b/example/integration-log/plugins/integration-log-plugin/src/main/resources/log.xml @@ -0,0 +1,9 @@ + + + integration-log-plugin + INFO + 10MB + 10GB + 30 + %d{yyyy-MM-dd HH:mm:ss.SSS} -%5p --- [%t] %-40.40logger{39} : %m%n + \ No newline at end of file diff --git a/example/integration-log/pom.xml b/example/integration-log/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..876a306d3fe77f2498db8db94535e1e5590d0825 --- /dev/null +++ b/example/integration-log/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + com.gitee.starblues + integration-log + 2.4.2-RELEASE + pom + 集成插件日志案例 + + + integration-log-main + plugins/integration-log-plugin + + + \ No newline at end of file diff --git a/example/pom.xml b/example/pom.xml index f192a2684053ca4445252be2002751be8564c083..a8acf4bf2b7b269ded68f6f82f1ece8411b3e948 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -14,6 +14,7 @@ integration-mybatis integration-mybatisplus integration-tkmybatis + integration-log diff --git a/springboot-plugin-framework-extension/pom.xml b/springboot-plugin-framework-extension/pom.xml index 55ce429ed22c68dc62915575b71577ff09d65270..0c360834d2253595776ae0722ffc282680d86273 100644 --- a/springboot-plugin-framework-extension/pom.xml +++ b/springboot-plugin-framework-extension/pom.xml @@ -15,6 +15,7 @@ springboot-plugin-framework-extension-mybatis springboot-plugin-framework-extension-resources + springboot-plugin-framework-extension-log \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml new file mode 100644 index 0000000000000000000000000000000000000000..74ff998e7be4614dcdea39b5c82a1a75e0e40d06 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/pom.xml @@ -0,0 +1,45 @@ + + + + org.sonatype.oss + oss-parent + 7 + + + 4.0.0 + + com.gitee.starblues + springboot-plugin-framework-extension-log + 2.4.2-RELEASE + + + 2.4.2-RELEASE + 5.0.7.RELEASE + 1.2.3 + + + + + ch.qos.logback + logback-classic + ${logback.version} + provided + + + org.springframework + spring-context + ${spring-version} + compile + + + com.gitee.starblues + springboot-plugin-framework + ${springboot-plugin-framework.version} + compile + + + + + \ No newline at end of file diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigLoader.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..cffdc0e334530c260c3cd9017905ed1166ca6eb8 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/PluginLogConfigLoader.java @@ -0,0 +1,171 @@ +package com.gitee.starblues.extension.log; + +import com.gitee.starblues.extension.log.config.SpringBootLogConfig; +import com.gitee.starblues.factory.PluginRegistryInfo; +import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; +import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; +import com.gitee.starblues.realize.BasePlugin; +import com.gitee.starblues.utils.OrderPriority; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.util.CollectionUtils; + +import java.io.BufferedInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Enumeration; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; +import java.util.stream.Collectors; + +public class PluginLogConfigLoader implements PluginResourceLoader { + + private Logger log = LoggerFactory.getLogger(PluginLogConfigLoader.class); + + public static final String KEY = "SpringBootLogConfigLoader"; + + private final static String TYPE_FILE = "file"; + private final static String TYPE_CLASSPATH = "classpath"; + private final static String TYPE_PACKAGE = "package"; + + @Override + public String key() { + return KEY; + } + + @Override + public ResourceWrapper load(PluginRegistryInfo pluginRegistryInfo) throws Exception { + BasePlugin basePlugin = pluginRegistryInfo.getBasePlugin(); + if (!(basePlugin instanceof SpringBootLogConfig)) { + log.warn("Plugin '{}' not implements SpringBootLogConfig, If you need to use log in the plugin," + "Please implements SpringBootLogConfig interface", basePlugin.getWrapper().getPluginId()); + return null; + } + SpringBootLogConfig springBootLogConfig = (SpringBootLogConfig) basePlugin; + Set logConfigLocations = springBootLogConfig.logConfigLocations(); + if (logConfigLocations == null || logConfigLocations.isEmpty()) { + log.warn("SpringBootLogConfig -> logConfigLocations return is empty, " + "Please check configuration"); + return new ResourceWrapper(); + } + List resources = new ArrayList<>(); + for (String logConfigLocation : logConfigLocations) { + if (logConfigLocation == null || "".equals(logConfigLocation)) { + continue; + } + List loadResources = load(logConfigLocation, basePlugin); + if (loadResources != null && !loadResources.isEmpty()) { + resources.addAll(loadResources); + } + } + ResourceWrapper resourceWrapper = new ResourceWrapper(); + resourceWrapper.addResources(resources); + return resourceWrapper; + } + + @Override + public void unload(PluginRegistryInfo pluginRegistryInfo, ResourceWrapper resourceWrapper) throws Exception { + + } + + @Override + public OrderPriority order() { + return OrderPriority.getHighPriority(); + } + + private List load(String logConfigLocation, BasePlugin basePlugin) throws Exception { + String[] split = logConfigLocation.split(":"); + if (split.length != 2) { + return null; + } + String type = split[0]; + String location = split[1]; + String matchLocation; + if (Objects.equals(type, TYPE_CLASSPATH)) { + matchLocation = location; + } else if (Objects.equals(type, TYPE_FILE)) { + + return loadFileSystemResources(basePlugin, location); + } else if (Objects.equals(type, TYPE_PACKAGE)) { + matchLocation = location.replaceAll("\\.", "/"); + } else { + log.warn("logConfigLocation {} illegal", logConfigLocation); + return null; + } + return loadClassPathResources(basePlugin, matchLocation); + } + + private List loadClassPathResources(BasePlugin basePlugin, String matchLocation) throws Exception { + Logger log = LoggerFactory.getLogger(getClass()); + String pluginPath = basePlugin.getWrapper().getPluginPath().toString(); + try (JarFile jarFile = new JarFile(pluginPath)) { + Enumeration entryEnumeration = jarFile.entries(); + List entries = Collections.list(entryEnumeration); + return entries.stream().filter(entry -> { + String entryName = entry.getName(); + // 将资源规则转换为正则表达式,例如“mapper/*Mapper.xml”转换后为“mapper/.*Mapper\\.xml” + String regex = matchLocation.replaceAll("\\\\", "/").replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*"); + return entryName.matches(regex); + }).map(entry -> { + try (InputStream inputStream = jarFile.getInputStream(entry); + ByteArrayOutputStream bos = new ByteArrayOutputStream(); + BufferedInputStream in = new BufferedInputStream(inputStream)) { + int buf_size = 1024; + byte[] buffer = new byte[buf_size]; + int len; + while (-1 != (len = in.read(buffer, 0, buf_size))) { + bos.write(buffer, 0, len); + } + // 这里只能用ByteArrayResource,使用InputStreamResource后续读取时会报流已关闭 + ByteArrayResource resource = new ByteArrayResource(bos.toByteArray()); + log.debug("Loaded plugin resource '{}'.", entry.getName()); + return resource; + } catch (IOException e) { + log.warn("Load resource failed, caused by: {}", e.toString()); + return null; + } + }).filter(Objects::nonNull).collect(Collectors.toList()); + } + } + + private List loadFileSystemResources(BasePlugin basePlugin, String matchLocation) { + String basePath = basePlugin.getWrapper().getPluginPath().toString().concat(File.separator).concat(basePlugin.getWrapper().getPluginId()).concat("/config/"); + String tmpPath = basePath.concat(matchLocation); + String regex = tmpPath.replaceAll("\\\\", "/").replaceAll("\\.", "\\\\.").replaceAll("\\*", ".*"); + List matchedFilePaths = new ArrayList<>(); + getMatchedFilePaths(regex, new File(basePath), matchedFilePaths); + if (CollectionUtils.isEmpty(matchedFilePaths)) { + return new ArrayList<>(); + } + return matchedFilePaths.stream().map(FileSystemResource::new).collect(Collectors.toList()); + } + + private void getMatchedFilePaths(String regex, File file, List matchedFilePath) { + File[] files = file.listFiles(); + if (files == null) { + return; + } + for (File subFile : files) { + if (subFile.isDirectory()) { + getMatchedFilePaths(regex, subFile, matchedFilePath); + } else { + try { + String filePath = subFile.getCanonicalPath(); + filePath = filePath.replaceAll("\\\\", "/"); + if (filePath.matches(regex)) { + matchedFilePath.add(subFile.getCanonicalPath()); + } + } catch (IOException ignored) { + } + } + } + } +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java new file mode 100644 index 0000000000000000000000000000000000000000..8cc5baed53ec90c75189f947689d77b94c4a4c98 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/SpringBootLogExtension.java @@ -0,0 +1,34 @@ +package com.gitee.starblues.extension.log; + + +import com.gitee.starblues.extension.AbstractExtension; +import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; +import com.gitee.starblues.factory.process.pipe.loader.PluginResourceLoader; +import org.springframework.context.ApplicationContext; + +import java.util.ArrayList; +import java.util.List; + +public class SpringBootLogExtension extends AbstractExtension { + + public static final String KEY = "SpringBootLogExtension"; + + @Override + public String key() { + return KEY; + } + + @Override + public List getPluginResourceLoader() { + List resourceLoaders = new ArrayList<>(); + resourceLoaders.add(new PluginLogConfigLoader()); + return resourceLoaders; + } + + @Override + public List getPluginPipeProcessor(ApplicationContext applicationContext) { + List pipeProcessorExtends = new ArrayList<>(); + pipeProcessorExtends.add(new PluginLogConfigProcessor()); + return pipeProcessorExtends; + } +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java new file mode 100644 index 0000000000000000000000000000000000000000..c868a46430ae2a0a30a7532065f3269f138cbfea --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/annotation/ConfigItem.java @@ -0,0 +1,14 @@ +package com.gitee.starblues.extension.log.annotation; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface ConfigItem { + String defaultValue() default ""; +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..64ef3d840c78c64cce73acad9f56dd45b1fc83ba --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/LogConfig.java @@ -0,0 +1,91 @@ +package com.gitee.starblues.extension.log.config; + +import com.gitee.starblues.extension.log.annotation.ConfigItem; + +import javax.xml.bind.annotation.XmlElement; +import javax.xml.bind.annotation.XmlTransient; + +public class LogConfig { + + @XmlElement(name = "fileName") + private String fileName; + + @ConfigItem(defaultValue = "INFO") + @XmlElement(name = "level") + private String level; + + @ConfigItem(defaultValue = "10MB") + @XmlElement(name = "maxFileSize") + private String maxFileSize; + + @ConfigItem(defaultValue = "10GB") + @XmlElement(name = "totalFileSize") + private String totalFileSize; + + @ConfigItem(defaultValue = "30") + @XmlElement(name = "maxHistory") + private Integer maxHistory; + + @ConfigItem(defaultValue = "%d{yyyy-MM-dd HH:mm:ss.SSS} -%5p --- [%t] %-40.40logger{39} : %m%n") + @XmlElement(name = "pattern") + private String pattern; + + @XmlTransient + private String packageName; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getLevel() { + return level; + } + + public void setLevel(String level) { + this.level = level; + } + + public String getMaxFileSize() { + return maxFileSize; + } + + public void setMaxFileSize(String maxFileSize) { + this.maxFileSize = maxFileSize; + } + + public String getTotalFileSize() { + return totalFileSize; + } + + public void setTotalFileSize(String totalFileSize) { + this.totalFileSize = totalFileSize; + } + + public Integer getMaxHistory() { + return maxHistory; + } + + public void setMaxHistory(Integer maxHistory) { + this.maxHistory = maxHistory; + } + + public String getPattern() { + return pattern; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/SpringBootLogConfig.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/SpringBootLogConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..3b1688188bcbc32d855e229898adb7e30482bd8c --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/config/SpringBootLogConfig.java @@ -0,0 +1,7 @@ +package com.gitee.starblues.extension.log.config; + +import java.util.Set; + +public interface SpringBootLogConfig { + Set logConfigLocations(); +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logConfigProcess.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logConfigProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..0671450fa3f3d47944f7c91e854e31599adfd778 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/logConfigProcess.java @@ -0,0 +1,54 @@ +package com.gitee.starblues.extension.log; + +import com.gitee.starblues.extension.log.util.LogConfigProcess; +import com.gitee.starblues.factory.PluginRegistryInfo; +import com.gitee.starblues.factory.process.pipe.PluginPipeProcessorExtend; +import com.gitee.starblues.factory.process.pipe.loader.ResourceWrapper; +import com.gitee.starblues.utils.OrderPriority; +import org.pf4j.PluginWrapper; +import org.springframework.core.io.Resource; + +import java.util.List; + +public class PluginLogConfigProcessor implements PluginPipeProcessorExtend { + + private final LogConfigProcess logConfigProcess = LogConfigProcess.getInstance(); + + @Override + public String key() { + return null; + } + + @Override + public OrderPriority order() { + return OrderPriority.getLowPriority(); + } + + @Override + public void initialize() throws Exception { + + } + + @Override + public void registry(PluginRegistryInfo pluginRegistryInfo) throws Exception { + if (logConfigProcess == null) { + return; + } + PluginWrapper pluginWrapper = pluginRegistryInfo.getPluginWrapper(); + ResourceWrapper resourceWrapper = pluginRegistryInfo.getPluginLoadResource(PluginLogConfigLoader.KEY); + if (resourceWrapper == null) { + return; + } + + List pluginResources = resourceWrapper.getResources(); + if (pluginResources == null || pluginResources.isEmpty()) { + return; + } + logConfigProcess.loadLogConfig(pluginResources, pluginWrapper); + } + + @Override + public void unRegistry(PluginRegistryInfo pluginRegistryInfo) throws Exception { + logConfigProcess.unloadLogConfig(pluginRegistryInfo.getPluginWrapper()); + } +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ ObjectUtil.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ ObjectUtil.java new file mode 100644 index 0000000000000000000000000000000000000000..ac2909251d94117a615c2ac43e9123cb6b04d602 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/ ObjectUtil.java @@ -0,0 +1,49 @@ +package com.gitee.starblues.extension.log.util; + +import java.io.IOException; +import java.io.InputStream; + +public class ObjectUtil { + + public static Object parseBasicTypeValue(Class fieldType, String defaultValue) { + Object fixedValue; + if (Byte.class == fieldType || byte.class == fieldType) { + fixedValue = Byte.parseByte(defaultValue); + } else if (Integer.class == fieldType || int.class == fieldType) { + fixedValue = Integer.parseInt(defaultValue); + } else if (Double.class == fieldType || double.class == fieldType) { + fixedValue = Double.parseDouble(defaultValue); + } else if (Short.class == fieldType || short.class == fieldType) { + fixedValue = Short.parseShort(defaultValue); + } else if (Long.class == fieldType || long.class == fieldType) { + fixedValue = Long.parseLong(defaultValue); + } else if (Float.class == fieldType || float.class == fieldType) { + fixedValue = Float.parseFloat(defaultValue); + } else if (Boolean.class == fieldType || boolean.class == fieldType) { + fixedValue = Boolean.parseBoolean(defaultValue); + } else if (Character.class == fieldType || char.class == fieldType) { + fixedValue = defaultValue.charAt(0); + } else { + fixedValue = defaultValue; + } + return fixedValue; + } + + public static boolean isEmptyObject(Class fieldType, Object fieldValue) { + if (Byte.class == fieldType || byte.class == fieldType) { + return (byte) fieldValue == 0; + } else if (Integer.class == fieldType || int.class == fieldType) { + return (int) fieldValue == 0; + } else if (Double.class == fieldType || double.class == fieldType) { + return (double) fieldValue == 0; + } else if (Short.class == fieldType || short.class == fieldType) { + return (short) fieldValue == 0; + } else if (Long.class == fieldType || long.class == fieldType) { + return (long) fieldValue == 0; + } else if (Float.class == fieldType || float.class == fieldType) { + return (float) fieldValue == 0; + } + return false; + } + +} diff --git a/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigProcess.java b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigProcess.java new file mode 100644 index 0000000000000000000000000000000000000000..53aa7bcd0f578f3a69ce94e2017838075536abe6 --- /dev/null +++ b/springboot-plugin-framework-extension/springboot-plugin-framework-extension-log/src/main/java/com/gitee/starblues/extension/log/util/LogConfigProcess.java @@ -0,0 +1,244 @@ +package com.gitee.starblues.extension.log.util; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.ConsoleAppender; +import ch.qos.logback.core.filter.Filter; +import ch.qos.logback.core.rolling.RollingFileAppender; +import ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy; +import ch.qos.logback.core.spi.FilterReply; +import ch.qos.logback.core.util.FileSize; +import ch.qos.logback.core.util.OptionHelper; +import com.gitee.starblues.extension.log.annotation.ConfigItem; +import com.gitee.starblues.extension.log.config.LogConfig; +import org.pf4j.PluginWrapper; +import org.slf4j.LoggerFactory; +import org.springframework.core.io.Resource; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Unmarshaller; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.lang.reflect.Field; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class LogConfigProcess { + + private org.slf4j.Logger log = LoggerFactory.getLogger(LogConfigProcess.class); + + private final Map pluginLogMap = new HashMap<>(); + + private static LogConfigProcess instance = null; + + private LogConfigProcess() { + } + + public static LogConfigProcess getInstance() { + if (instance == null) { + synchronized (LogConfigProcess.class) { + if (instance == null) { + instance = new LogConfigProcess(); + } + } + } + return instance; + } + + public void loadLogConfig(List resources, PluginWrapper pluginWrapper) { + resources.forEach(resource -> { + String configText; + try { + configText = readConfigText(resource); + } catch (IOException e) { + log.error("Failed to read log configuration.", e); + return; + } + LogConfig logConfig; + try { + logConfig = (LogConfig) xml2object(configText); + } catch (Exception e) { + log.error("", e); + return; + } + checkLogConfig(logConfig, pluginWrapper.getPluginId()); + String pluginClass = pluginWrapper.getDescriptor().getPluginClass(); + String packageName = pluginClass.substring(0, pluginClass.lastIndexOf(".")); + splitPluginLog(pluginWrapper, logConfig, packageName); + String pluginId = pluginWrapper.getPluginId(); + logConfig.setPattern(packageName); + pluginLogMap.put(pluginId, logConfig); + }); + } + + public void unloadLogConfig(PluginWrapper pluginWrapper) { + pluginLogMap.remove(pluginWrapper.getPluginId()); + } + + private void splitPluginLog(PluginWrapper pluginWrapper, LogConfig logConfig, String packageName) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + Logger logger = context.getLogger(packageName); + logger.detachAndStopAllAppenders(); + + ConsoleAppender consoleAppender = createConsoleAppender(pluginWrapper, logConfig, packageName); + RollingFileAppender fileAppender = createFileAppender(pluginWrapper, logConfig, packageName); + + logger.setAdditive(false); + logger.setLevel(Level.toLevel(logConfig.getLevel())); + logger.addAppender(consoleAppender); + logger.addAppender(fileAppender); + } + + private ConsoleAppender createConsoleAppender(PluginWrapper pluginWrapper, LogConfig logConfig, String packageName) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + ConsoleAppender appender = new ConsoleAppender<>(); + Filter filter = new LogFilter(packageName); + filter.start(); + appender.addFilter(filter); + appender.setContext(context); + appender.setName(pluginWrapper.getPluginId().concat("-console")); + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(context); + encoder.setPattern(logConfig.getPattern()); + encoder.start(); + + appender.setEncoder(encoder); + appender.start(); + return appender; + } + + private RollingFileAppender createFileAppender(PluginWrapper pluginWrapper, LogConfig logConfig, String packageName) { + LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory(); + RollingFileAppender appender = new RollingFileAppender<>(); + Filter filter = new LogFilter(packageName); + filter.start(); + + appender.addFilter(filter); + + appender.setContext(context); + appender.setName(pluginWrapper.getPluginId().concat("-file")); + + String fileName = logConfig.getFileName(); + if (fileName == null || "".equals(fileName)) { + fileName = pluginWrapper.getPluginId(); + } + + String home = pluginWrapper.getPluginPath().toString(); + String logPrefix = home.concat("/logs/").concat(pluginWrapper.getPluginId()).concat("/").concat(fileName); + appender.setFile(OptionHelper.substVars(logPrefix.concat(".log"), context)); + + appender.setAppend(true); + appender.setPrudent(false); + + SizeAndTimeBasedRollingPolicy policy = new SizeAndTimeBasedRollingPolicy<>(); + + String fp = OptionHelper.substVars(logPrefix.concat(".%d{yyyy-MM-dd}%i.log"), context); + policy.setMaxFileSize(FileSize.valueOf(logConfig.getMaxFileSize())); + policy.setFileNamePattern(fp); + policy.setMaxHistory(logConfig.getMaxHistory()); + policy.setTotalSizeCap(FileSize.valueOf(logConfig.getTotalFileSize())); + policy.setParent(appender); + policy.setContext(context); + policy.start(); + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(context); + encoder.setPattern(logConfig.getPattern()); + encoder.start(); + + appender.setRollingPolicy(policy); + appender.setEncoder(encoder); + appender.start(); + return appender; + } + + private static class LogFilter extends Filter { + + private final String packageName; + private final LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory(); + + private LogFilter(String packageName) { + this.packageName = packageName; + } + + @Override + public FilterReply decide(ILoggingEvent event) { + Logger logger = loggerContext.getLogger(packageName); + if (event.getLoggerName().startsWith(packageName) && event.getLevel().isGreaterOrEqual(logger.getLevel())) { + return FilterReply.ACCEPT; + } + return FilterReply.DENY; + } + } + + private void checkLogConfig(LogConfig logConfig, String pluginId) { + String fileName = logConfig.getFileName(); + if (fileName == null || "".equals(fileName)) { + logConfig.setFileName(pluginId.concat("Log")); + } + Field[] fields = LogConfig.class.getDeclaredFields(); + Arrays.stream(fields).forEach(field -> { + if (!field.isAccessible()) { + field.setAccessible(true); + } + ConfigItem configItem = field.getDeclaredAnnotation(ConfigItem.class); + if (configItem == null) { + return; + } + try { + Object fieldValue = field.get(logConfig); + Class fieldType = field.getType(); + if ("".equals(fieldValue.toString()) || ObjectUtil.isEmptyObject(fieldType, fieldValue)) { + String defaultValue = configItem.defaultValue(); + log.debug("Field {} is not config or invalid in log config of plugin {}, set it to default value {}.", field.getName(), defaultValue, pluginId); + Object fixedValue = ObjectUtil.parseBasicTypeValue(fieldType, defaultValue); + field.set(logConfig, fixedValue); + } + } catch (IllegalAccessException e) { + log.error("Failed to check config item {} in log config.", field.getName()); + } + }); + } + + public Map getPluginLogMap() { + return pluginLogMap; + } + + private String readConfigText(Resource resource) throws IOException { + String fileContent; + try (InputStream inputStream = resource.getInputStream(); + ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + byte[] buff = new byte[1024]; + int len; + while ((len = inputStream.read(buff)) != -1) { + baos.write(buff, 0, len); + } + byte[] data = baos.toByteArray(); + fileContent = new String(data); + } + return fileContent; + } + + private Object xml2object(String xml) throws Exception { + Object object; + try { + JAXBContext context = JAXBContext.newInstance(LogConfig.class); + Unmarshaller unmarshaller = context.createUnmarshaller(); + StringReader stringReader = new StringReader(xml); + object = unmarshaller.unmarshal(stringReader); + } catch (JAXBException e) { + throw new Exception("Invalid xml definition"); + } + return object; + } + +}