解决 spring boot devtool 热部署后出现访问404问题
DevTools的检测时间和idea的编译所需时间存在差异。在idea还没完成编译工作前,DevTools就开始进行重启和加载,导致@RequestMapping没有被全部正常处理。其他方法没试,就直接用了看起来最简单的方法:牺牲一点时间,去加长devtools的轮询时间,增大等待时间。 解决方案如下: spring.devtools.restart.poll-interval=3000ms spring.devtools.restart.quiet-period=2999ms from:https://www.cnblogs.com/yxfcnbg/p/11510426.html
View Details把object转成JSONObject JSON.toJSON
把object转成JSONObject JSON.toJSON public void onNext(Object o) { LogUtil.i("getFavorites", "json=" + o.toString()); JSONObject json = JSON.parseObject(o.toString()); // JSONObject json = JSON.parseObject("{\"code\":1001,\"timestamp\":\"2018-11-05 03:40:54\"}"); LogUtil 的可以打印o.toString() 出来,直接传字符串解析json可以获得属性值,为什么JSONObject json = JSON.parseObject(o.toString()); 就报错了?强制转换也报错,把object转成JSONObject 这种要怎么用的? 或者怎么把Object o 这个对象里面的属性值读出来的? 怎么变成字符串的? public interface ObserverResponseListener<T> { void onNext(T t); 传进来的Object是个泛型的 JSONObject json = (JSONObject) JSON.toJSON(o); 用这样可以了 from:https://www.cnblogs.com/zdz8207/p/java-object-JSONObject.html
View Detailsjava 遍历数组常见的3种方式
1.for循环,最常见 2.利用foreach 3.利用jdk自带的方法 --> java.util.Arrays.toString() from:https://www.cnblogs.com/baiaixing/p/11369514.html
View DetailsX-Frame-Options响应头配置详解
X-Frame-Options HTTP 响应头是用来给浏览器指示允许一个页面可否在 <frame>, </iframe> 或者 <object> 中展现的标记。网站可以使用此功能,来确保自己网站的内容没有被嵌套到别人的网站中去,也从而避免了点击劫持 (clickjacking) 的攻击。 X-Frame-Options三个参数: 1、DENY 表示该页面不允许在frame中展示,即便是在相同域名的页面中嵌套也不允许。 2、SAMEORIGIN 表示该页面可以在相同域名页面的frame中展示。 3、ALLOW-FROM uri 表示该页面可以在指定来源的frame中展示。 换一句话说,如果设置为DENY,不光在别人的网站frame嵌入时会无法加载,在同域名页面中同样会无法加载。另一方面,如果设置为SAMEORIGIN,那么页面就可以在同域名页面的frame中嵌套。正常情况下我们通常使用SAMEORIGIN参数。 Apache配置 需要把下面这行添加到 'site' 的配置中
1 |
Header always append X-Frame-Options SAMEORIGIN |
nginx配置 需要添加到 ‘http’, ‘server’ 或者 ‘location’ 的配置项中,个人来讲喜欢配置在‘server’ 中 正常情况下都是使用SAMEORIGIN参数,允许同域嵌套
1 |
add_header X-Frame-Options SAMEORIGIN; |
允许单个域名iframe嵌套
1 |
add_header X-Frame-Options ALLOW-FROM http://whsir.com/; |
允许多个域名iframe嵌套,注意这里是用逗号分隔
1 |
add_header X-Frame-Options "ALLOW-FROM http://whsir.com/,https://cacti.org.cn/"; |
IIS配置 添加下面的配置到 ‘Web.config’文件中
1 2 3 4 5 6 7 8 9 |
<system.webServer> ... <httpProtocol> <customHeaders> <add name="X-Frame-Options" value="SAMEORIGIN" /> </customHeaders> </httpProtocol> ... </system.webServer> |
HAProxy配置 添加下面这行到 ‘front-end, listen, or backend’配置中
1 |
rspadd X-Frame-Options:\ SAMEORIGIN |
Tomcat配置 在 ‘conf/web.xml’填加以下配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
<filter> <filter-name>httpHeaderSecurity</filter-name> <filter-class>org.apache.catalina.filters.HttpHeaderSecurityFilter</filter-class> <init-param> <param-name>antiClickJackingOption</param-name> <param-value>SAMEORIGIN</param-value> </init-param> <async-supported>true</async-supported> </filter> <filter-mapping> <filter-name>httpHeaderSecurity</filter-name> <url-pattern>/*</url-pattern> <dispatcher>REQUEST</dispatcher> <dispatcher>FORWARD</dispatcher> </filter-mapping> |
配置后如何确定X-Frame-Options是否已生效呢?我这里以Google浏览器为例,打开网站按F12键,选择Network,找到对应的Headers,如下图所示 from:https://blog.whsir.com/post-3919.html
View Detailsidea中配置Lombok + Logback + Slf4j打印日志(附所有踩坑记录)
本文介绍非spring项目中利用Lombok + Logback + Slf4j记录日志,并附上所有的踩坑记录 本文环境 macos Idea2019.2 配置过程 首先在idea中下载lombok插件 在pom.xml文件中引入Lombok,Logback,Slf4j依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.25</version> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> <scope>provided</scope> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> |
在项目中的resources文件夹中添加logback.xml配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="UTF-8"?> <configuration debug="false"> <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径--> <property name="LOG_HOME" value="/logs" /> <!-- 控制台输出 --> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <!-- <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %boldMagenta(%-5level %logger{50}) : %msg%n</pattern>--> <pattern>%d{yyyy-MM-dd HH:mm:ss:SS} %green([%thread]) %highlight(%-5level) %boldMagenta(%logger{50}) - %cyan(%msg%n)</pattern> </encoder> </appender> <!-- 按照每天生成日志文件 --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/provider.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!--日志文件输出的文件名--> <FileNamePattern>${LOG_HOME}/provider.log.%d{yyyy-MM-dd}.log</FileNamePattern> <!--日志文件保留天数--> <MaxHistory>30</MaxHistory> </rollingPolicy> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符--> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern> </encoder> <!--日志文件最大的大小--> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- 日志输出级别 --> <root level="INFO"> <appender-ref ref="STDOUT" /> </root> </configuration> |
编写代码测试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
import lombok.extern.slf4j.Slf4j; @Slf4j public class ProviderStart { public static void main(String[] args) { log.debug("输出DEBUG级别日志"); log.info("输出INFO级别日志"); log.warn("输出WARN级别日志"); log.error("输出ERROR级别日志"); } } |
输出为 踩过的坑 看上去简简单单几步就完成了,然后并不是,接下来附上踩过的坑以及解决方法 idea中plugins的marketplace直接打不开 在idea中使用lombok需要在plugins中的marketplace中下载插件,然而我下载时marketplace迟迟无法连接上 我们需要在设置中的Appearance & Behavior --> System Settings --> Http Proxy中选择Auto-detect proxy settings勾选Automatic proxy configuration URL选项并填写url为http://127.0.0.1:1080,重启idea 代码运行成功,但是不显示日志信息 也是很邪门的一种情况,代码没错,但是不显示log打印的信息。 解决方案为,在Build,Execution,Deployment–>compiler–>Annotation Processors中勾选Enable annotation processing,重启idea 多个Slf4j冲突 报错信息如下
1 2 |
SLF4J Warning: Class Path Contains Multiple SLF4J Bindings ..... |
说明是jar包冲突了,maven仓库中可能同时包含了log4j,logback,然后slf4j在程序运行时,不知道binding哪一个jar包。 解决方案为:在maven仓库中删除其中一个jar包,只保留一个即可。 from:https://blog.csdn.net/jerseywwwwei/article/details/105871320
View Details基于 spring boot 下logback 的 logback-spring.xml 配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?xml version="1.0" encoding="UTF-8"?> <configuration scan="true" debug="false"> <springProperty scop="context" name="spring.application.name" source="spring.application.name" defaultValue=""/> <!--日志存放路径--> <property name="PATH" value="logs"/> <property name="FILE_NAME" value="${spring.application.name}"/> <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> <withJansi>true</withJansi> <encoder> <charset>UTF-8</charset> <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) %magenta([PID:${PID:-}]) %green([%15.15thread]) %cyan(%40.40logger:%-5.5L):%msg%n</pattern> </encoder> </appender> <!--trace--> <appender name="TRACE_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${PATH}/${FILE_NAME}_trace.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${PATH}/${FILE_NAME}_trace.%d{yyyy-MM-dd}.log</FileNamePattern> <maxHistory>60</maxHistory> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %highlight([%-5level]) %green([%15.15thread]) %cyan([%logger:%line])--%mdc{client} %msg%n</pattern> </encoder> </appender> <!--error--> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${PATH}/${FILE_NAME}_error.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <FileNamePattern>${PATH}/${FILE_NAME}_error.%d{yyyy-MM-dd}.log</FileNamePattern> <maxHistory>60</maxHistory> <totalSizeCap>1GB</totalSizeCap> </rollingPolicy> <encoder> <pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] %highlight([%-5level]) %green([%15.15thread]) %cyan([%logger:%line])--%mdc{client} %msg%n</pattern> </encoder> <filter class="ch.qos.logback.classic.filter.ThresholdFilter"> <level>ERROR</level> </filter> </appender> <root level="INFO"> <appender-ref ref="STDOUT" /> <appender-ref ref="TRACE_FILE" /> <appender-ref ref="ERROR_FILE" /> </root> </configuration> |
from:https://my.oschina.net/haopeng/blog/3065616
View Details日志配置logback-spring.xml
参考官方网址: https://logging.apache.org/log4j/2.x 一、log4j日志级别配置说明
1 2 3 4 5 6 7 8 9 10 11 12 13 |
log4j提供了4种日志级别和2个日志开关。 DEBUG:输出调试信息;指出细粒度信息事件对调试应用程序是非常有帮助的。 INFO: 输出提示信息;消息在粗粒度级别上突出强调应用程序的运行过程。 WARN: 输出警告信息;表明会出现潜在错误的情形。 ERROR:输出错误信息;指出虽然发生错误事件,但仍然不影响系统的继续运行。 FATAL: 输出致命错误;指出每个严重的错误事件将会导致应用程序的退出。 ALL level:打开所有日志记录开关;是最低等级的,用于打开所有日志记录。 OFF level:关闭所有日志记录开关;是最高等级的,用于关闭所有日志记录。 按照范围从小到大排序:OFF level > FATAL > ERROR > WARN > INFO > DEBUG > ALL level;范围大的会包含范围小的,例如日志设置为INFO级别的话则FATAL、ERROR、WARN、INFO的日志开关都是打开的,而DEBUG的日志开关将是关闭的。 Log4j建议只使用四个级别,优先级从高到低分别是 ERROR、WARN、INFO、DEBUG。 |
二、logback-spring.xml配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 |
<?xml version="1.0" encoding="UTF-8"?> <!-- 说明: 1、日志级别及文件 日志记录采用分级记录,级别与日志文件名相对应,不同级别的日志信息记录到不同的日志文件中。 2、日志级别可以根据开发环境进行配置,为方便统一管理查看日志,日志文件路径统一由LOG_PATH:-.配置在/home/项目名称/logs --> <configuration> <!-- 引入默认设置 --> <include resource="org/springframework/boot/logging/logback/defaults.xml"/> <!-- 编码格式设置 --> <property name="ENCODING" value="UTF-8" /> <!-- 日志文件的存储地址,由application.yml中的logging.path配置,根路径默认同项目路径 --> <property name="LOG_HOME" value="${LOG_PATH:-.}" /> <!-- 常规输出格式:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符 --> <property name="NORMAL_LOG_PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}.%method@%line - %msg%n"/> <!-- 彩色输出格式:magenta:洋红,boldMagenta:粗红,yan:青色,·#══> --> <property name="CONSOLE_LOG_PATTERN" value="%boldMagenta([%d{yyyy-MM-dd HH:mm:ss.SSS}]) %red([%thread]) %boldMagenta(%-5level) %blue(%logger{20}.%method@%line) %magenta(·#═>) %cyan(%msg%n)"/> <!-- ==========================控制台输出设置========================== --> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>${CONSOLE_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> </encoder> </appender> <!-- ==========================按天输出日志设置========================== --> <appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/system-info.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天回滚 daily --> <FileNamePattern>${LOG_HOME}/system-info.%d{yyyy-MM-dd}.log</FileNamePattern> <!-- 日志文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>INFO</level> <!-- 设置拦截的对象为INFO级别日志 --> <onMatch>ACCEPT</onMatch> <!-- 当遇到了INFO级别时,启用该段配置 --> <onMismatch>DENY</onMismatch> <!-- 没有遇到INFO级别日志时,屏蔽该段配置 --> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${NORMAL_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> </encoder> <!-- 日志文件最大的大小 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- ==========================按天输出ERROR级别日志设置========================== --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/system-error.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天回滚 daily --> <FileNamePattern>${LOG_HOME}/system-error.%d{yyyy-MM-dd}.log</FileNamePattern> <!-- 日志文件保留天数 --> <MaxHistory>30</MaxHistory> </rollingPolicy> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <!-- 设置拦截的对象为ERROR级别日志 --> <onMatch>ACCEPT</onMatch> <!-- 当遇到了ERROR级别时,启用该段配置 --> <onMismatch>DENY</onMismatch> <!-- 没有遇到ERROR级别日志时,屏蔽该段配置 --> </filter> <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"> <pattern>${NORMAL_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> </encoder> <!-- 日志文件最大的大小 --> <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy"> <MaxFileSize>10MB</MaxFileSize> </triggeringPolicy> </appender> <!-- ==========================用户登录日志设置========================== --> <appender name="USER_LOGIN" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>${LOG_HOME}/user-login.log</file> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <!-- 按天回滚 daily --> <fileNamePattern>${LOG_HOME}/auth-user.%d{yyyy-MM-dd}.log</fileNamePattern> <!-- 日志最大的历史 30天 --> <maxHistory>30</maxHistory> </rollingPolicy> <encoder> <pattern>${NORMAL_LOG_PATTERN}</pattern> <charset>${ENCODING}</charset> </encoder> </appender> <!-- ===日志输出级别,OFF level > FATAL > ERROR > WARN > INFO > DEBUG > ALL level=== --> <logger name="com.sand" level="INFO"/> <logger name="com.apache.ibatis" level="INFO"/> <logger name="java.sql.Statement" level="INFO"/> <logger name="java.sql.Connection" level="INFO"/> <logger name="java.sql.PreparedStatement" level="INFO"/> <logger name="org.springframework" level="WARN"/> <logger name="com.baomidou.mybatisplus" level="WARN"/> <!-- 用户登录日志 --> <logger name="user-login" level="INFO"> <appender-ref ref="USER_LOGIN"/> </logger> <!-- ======开发环境:打印控制台和输出到文件====== --> <springProfile name="dev"><!-- 由application.yml中的spring.profiles.active配置 --> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="INFO_FILE"/> <appender-ref ref="ERROR_FILE"/> </root> </springProfile> <!-- ======测试环境:打印控制台和输出到文件====== --> <springProfile name="test"><!-- 由application.yml中的spring.profiles.active配置 --> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="INFO_FILE"/> <appender-ref ref="ERROR_FILE"/> </root> </springProfile> <!-- ======生产环境:打印控制台和输出到文件====== --> <springProfile name="prod"><!-- 由application.yml中的spring.profiles.active配置 --> <root level="INFO"> <appender-ref ref="CONSOLE"/> <appender-ref ref="INFO_FILE"/> <appender-ref ref="ERROR_FILE"/> </root> </springProfile> </configuration> |
注意:登录日志的记录还需进行以下配置,用lombok的话直接使用注解即可 @Slf4j(topic = "user-login") 配置效果如下: from:https://www.cnblogs.com/54hsh/p/12684241.html
View DetailsAndroid Studio首次使用问题解决
今天下了个Android Studio,准备换个平台,但是创建新的项目遇到点问题,网上也没有找到完整的解决方法,研究了一下发出来供大家参考 问题:找不到R文件 报错Error:Execution failed for task ':app:processDebugResources'. > com.android.ide.common.process.ProcessException: Failed to execute aapt 问题原因:compileSdkVersion高于buildToolsVersion,可以用高版本的build-tool去构建一个低版本的sdk工程,但是反过来就不行了 解决方法:1.更新Android-SDK Build-Tools 2.修改build.gradle中相应版本号 之后我的问题就解决了 可能对于很多人来说根本不是问题,但是既然我遇上了,也就有可能有朋友还会遇到相同问题,希望能提供参考 from:https://www.cnblogs.com/madzxr/p/6322504.html
View Detailsjs调app原生方法
// 定义 const ApiBridge = { msgQueue: [], callbackCache: [], callbackId: 0, processingMsg: false, isReady: false, isNotifyReady: false }; ApiBridge.callNative = function(clz, method, args, callback) { var msgJson = {}; msgJson.clz = clz; msgJson.method = method; if (args != undefined) msgJson.args = args; return prompt(JSON.stringify(msgJson)); }; // 调用 setTimeout(() => { this.$ApiBridge.callNative("KCApiTSLModule", "notifyF", { action: 'closePage', param: {} }); }); 以上代码经测试不能很好的兼容ios,下面的代码可以:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 |
; (function(window) { if (window.WebViewJSBridge) return; window.WebViewJSBridge = {}; console.log("--- kerkee init begin---"); var browser = { versions: function() { var u = navigator.userAgent, app = navigator.appVersion; return { trident: u.indexOf('Trident') > -1, //IE presto: u.indexOf('Presto') > -1, //opera webKit: u.indexOf('AppleWebKit') > -1, //apple&google kernel gecko: u.indexOf('Gecko') > -1 && u.indexOf('KHTML') == -1, //firfox mobile: !!u.match(/AppleWebKit.*Mobile.*/), //is Mobile ios: !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/), //is ios android: u.indexOf('Android') > -1 || u.indexOf('Adr') > -1, //android iPhone: u.indexOf('iPhone') > -1, //iPhone or QQHD iPad: u.indexOf('iPad') > -1, //iPad iPod: u.indexOf('iPod') > -1, //iPod webApp: u.indexOf('Safari') == -1, //is webapp,no header and footer weixin: u.indexOf('MicroMessenger') > -1, //is wechat qq: u.match(/\sQQ/i) == " qq", //is qq, wxwork: u.indexOf('wxwork') > -1 //is wechat }; }(), language: (navigator.browserLanguage || navigator.language).toLowerCase() } var global = this || window; var ApiBridge = { msgQueue: [], callbackCache: [], callbackId: 0, processingMsg: false, isReady: false, isNotifyReady: false }; ApiBridge.create = function() { ApiBridge.bridgeIframe = document.createElement("iframe"); ApiBridge.bridgeIframe.style.display = 'none'; document.documentElement.appendChild(ApiBridge.bridgeIframe); }; ApiBridge.prepareProcessingMessages = function() { ApiBridge.processingMsg = true; }; ApiBridge.fetchMessages = function() { if (ApiBridge.msgQueue.length > 0) { var messages = JSON.stringify(ApiBridge.msgQueue); ApiBridge.msgQueue.length = 0; return messages; } ApiBridge.processingMsg = false; return ''; }; ApiBridge.callNative = function(clz, method, args, callback) { var msgJson = {}; msgJson.clz = clz; msgJson.method = method; if (args != undefined) msgJson.args = args; if (callback) { var callbackId = ApiBridge.getCallbackId(); ApiBridge.callbackCache[callbackId] = callback; if (msgJson.args) { msgJson.args.callbackId = callbackId.toString(); } else { msgJson.args = { "callbackId": callbackId.toString() }; } } if (browser.versions.wxwork || browser.versions.weixin) { return; } if (browser.versions.ios) { if (ApiBridge.bridgeIframe == undefined) { ApiBridge.create(); } // var msgJson = {"clz": clz, "method": method, "args": args}; ApiBridge.msgQueue.push(msgJson); if (!ApiBridge.processingMsg) ApiBridge.bridgeIframe.src = "kcnative://go"; } else if (browser.versions.android) { // android return prompt(JSON.stringify(msgJson)); } }; ApiBridge.showVersion = function() { return browser.versions; } ApiBridge.log = function(msg) { ApiBridge.callNative("ApiBridge", "JSLog", { "msg": msg }); } ApiBridge.getCallbackId = function() { return ApiBridge.callbackId++; } ApiBridge.onCallback = function(callbackId, obj) { if (ApiBridge.callbackCache[callbackId]) { ApiBridge.callbackCache[callbackId](obj); } } ApiBridge.onBridgeInitComplete = function(callback) { ApiBridge.callNative("ApiBridge", "onBridgeInitComplete", {}, callback); } ApiBridge.onNativeInitComplete = function(callback) { ApiBridge.isReady = true; if (callback) { callback(); ApiBridge.isNotifyReady = true; } } ApiBridge.compile = function(aIdentity, aJS) { var value; var error; try { value = eval(aJS); } catch (e) { var err = {}; err.name = e.name; err.message = e.message; err.number = e.number & 0xFFFF; error = err; } ApiBridge.callNative("ApiBridge", "compile", { "identity": aIdentity, "returnValue": value, "error": error }); } var _Configs = { isOpenJSLog: false, isOpenNativeXHR: false }; var kerkee = {}; /***************************************** * 事件监听 *****************************************/ kerkee.Event = {}; // kerkee.Event.LOADED = "loaded"; // kerkee.Event.LOAD_ERROR = "load_error"; // kerkee.Event.LOAD_PROGRESS = "load_progress"; kerkee.addEventListener = function(event, callback) { ApiBridge.callNative("event", "addEventListener", { "event": event }, callback); } /* Scroll to the bottom of the page when the callback function and the threshold setting */ //callback:Return to the page in webview upper vertex Y value kerkee.registerHitPageBottomListener = function(callback, threshold) { ApiBridge.callNative("ApiBridge", "setHitPageBottomThreshold", { "threshold": threshold }); kerkee.onHitPageBottom = callback; }; kerkee.registerScrollListener = function(callback) { ApiBridge.callNative("ApiBridge", "setPageScroll", { "isScrollOn": true }); kerkee.onPageScroll = callback; }; kerkee.notifyF = function(aString) { ApiBridge.callNative("KCApiTSLModule", "notifyF", aString); }; global.KCApiTSLModule = kerkee; /***************************************** * 接口 *****************************************/ kerkee.testJSBrige = function(aString) { ApiBridge.callNative("jsBridgeClient", "testJSBrige", { "info": aString }); }; kerkee.openJSLog = function() { _Configs.isOpenJSLog = true; } kerkee.closeJSLog = function() { _Configs.isOpenJSLog = false; } kerkee.commonApi = function(aString, callback) { ApiBridge.callNative("jsBridgeClient", "commonApi", { "info": aString }, callback); } kerkee.onDeviceReady = function(handler) { ApiBridge.onDeviceReady = handler; if (ApiBridge.isReady && !ApiBridge.isNotifyReady && handler) { handler(); ApiBridge.isNotifyReady = true; } }; kerkee.invoke = function(clz, method, args, callback) { if (callback) { ApiBridge.callNative(clz, method, args, callback); } else { ApiBridge.callNative(clz, method, args); } }; kerkee.onSetImage = function(srcSuffix, desUri) { var obj = document.querySelectorAll('img[src$="' + srcSuffix + '"]'); for (var i = 0; i < obj.length; ++i) { obj[i].src = desUri; } }; /* * var windowOpen = function (url) { ApiBridge.callNative("JavascriptAPIInterceptor", "windowOpen", { "url" : url }); }; */ global.ApiBridge = ApiBridge; global.kerkee = kerkee; global.jsBridgeClient = kerkee; kerkee.register = function(_window) { _window.ApiBridge = window.ApiBridge; _window.kerkee = window.kerkee; _window.console.log = window.console.log; _window.open = window.open; }; ApiBridge.onBridgeInitComplete(function(aConfigs) { if (aConfigs) { if (aConfigs.hasOwnProperty('isOpenJSLog')) { _Configs.isOpenJSLog = aConfigs.isOpenJSLog; } if (aConfigs.hasOwnProperty('isOpenNativeXHR')) { _Configs.isOpenNativeXHR = aConfigs.isOpenNativeXHR; } } if (_Configs.isOpenJSLog) { //global.console.log = ApiBridge.log; } ApiBridge.onNativeInitComplete(ApiBridge.onDeviceReady); }); })(window); |
View Details
Springboot整合log4j2日志全解
在项目推进中,如果说第一件事是搭Spring框架的话,那么第二件事情就是在Sring基础上搭建日志框架,我想很多人都知道日志对于一个项目的重要性,尤其是线上Web项目,因为日志可能是我们了解应用如何执行的唯一方式。在18年大环境下,更多的企业使用Springboot和Springcloud来搭建他们的企业微服务项目,此篇文章是博主在实践中用Springboot整合log4j2日志的总结。 目录 Springboot整合log4j2日志全解 常用日志框架 日志门面slf4j 为什么选用log4j2 整合步骤 引入Jar包 配置文件 配置文件模版 配置参数简介 Log4j2配置详解 简单使用 使用lombok工具简化创建Logger类 参考文章 1|0Springboot整合log4j2日志全解 1|1常用日志框架 java.util.logging:是JDK在1.4版本中引入的Java原生日志框架 Log4j:Apache的一个开源项目,可以控制日志信息输送的目的地是控制台、文件、GUI组件等,可以控制每一条日志的输出格式,这些可以通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。虽然已经停止维护了,但目前绝大部分企业都是用的log4j。 LogBack:是Log4j的一个改良版本 Log4j2:Log4j2已经不仅仅是Log4j的一个升级版本了,它从头到尾都被重写了 1|2日志门面slf4j 上述介绍的是一些日志框架的实现,这里我们需要用日志门面来解决系统与日志实现框架的耦合性。SLF4J,即简单日志门面(Simple Logging Facade for Java),它不是一个真正的日志实现,而是一个抽象层( abstraction layer),它允许你在后台使用任意一个日志实现。 前面介绍的几种日志框架一样,每一种日志框架都有自己单独的API,要使用对应的框架就要使用其对应的API,这就大大的增加应用程序代码对于日志框架的耦合性。 使用了slf4j后,对于应用程序来说,无论底层的日志框架如何变,应用程序不需要修改任意一行代码,就可以直接上线了。 1|3为什么选用log4j2 相比与其他的日志系统,log4j2丢数据这种情况少;disruptor技术,在多线程环境下,性能高于logback等10倍以上;利用jdk1.5并发的特性,减少了死锁的发生; 在这列举一下一些网上其他博文中对它们的性能评测: 可以看到在同步日志模式下, Logback的性能是最糟糕的. log4j2的性能无论在同步日志模式还是异步日志模式下都是最佳的. log4j2优越的性能其原因在于log4j2使用了LMAX,一个无锁的线程间通信库代替了,logback和log4j之前的队列. 并发性能大大提升。 1|4整合步骤 引入Jar包 springboot默认是用logback的日志框架的,所以需要排除logback,不然会出现jar依赖冲突的报错。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions><!-- 去掉springboot默认配置 --> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <!-- 引入log4j2依赖 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-log4j2</artifactId> </dependency> 配置文件 如果自定义了文件名,需要在application.yml中配置 logging: config: xxxx.xml level: cn.jay.repository: trace 默认名log4j2-spring.xml,就省下了在application.yml中配置 配置文件模版 log4j是通过一个.properties的文件作为主配置文件的,而现在的log4j2则已经弃用了这种方式,采用的是.xml,.json或者.jsn这种方式来做,可能这也是技术发展的一个必然性,因为properties文件的可阅读性真的是有点差。这里给出博主自配的一个模版,供大家参考。 <?xml version="1.0" encoding="UTF-8"?> <!--Configuration后面的status,这个用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,你会看到log4j2内部各种详细输出--> <!--monitorInterval:Log4j能够自动检测修改配置 文件和重新配置本身,设置间隔秒数--> <configuration monitorInterval="5"> <!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN […]
View Details