logback配置全解析
作者:muggle Logback是由log4j创始人设计的另一个开源日志组件,分为三个模块:
logback-core:其它两个模块的基础模块
logback-classic:它是log4j的一个改良版本,同时它完整实现了slf4j API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging
logback-access:访问模块与Servlet容器集成提供通过Http来访问日志的功能 在springboot中我们通过xml配置来操作logback
springboot中logback的默认配置文件名称为logback-spring.xml,若需要指定xml名称,需在application.properties(application.yml)中配置logging.config=xxxx.xml 现在贴出一份logback的xml配置,可直接使用,懒得看的小伙伴复制粘贴到你的项目中去体验吧
%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight([%-5level]) %logger - %msg%n
${log_dir}/%d{yyyy-MM-dd}-poseidon.log
${maxHistory}
%d{yyyy-MM-dd HH:mm:ss.SSS} %highlight([%-5level]) %logger - %msg%n
${log_dir}/runningTime/%d{yyyy-MM-dd}-poseidon.log
${maxHistory}
%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n
%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %logger - %msg%n
com.mysql.cj.jdbc.Driver
jdbc:mysql://xxx/xxxx?characterEncoding=UTF-8
xx
xxxx
我们可以看到xml中有四种节点 appender,logger,root,configuration
节点解读
configuration包含三个属性:
scan: 当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod: 设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug: 当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
Logger作为日志的记录器,把它关联到应用的对应的context上后,主要用于存放日志对象,也可以定义日志类型、级别。
Appender主要用于指定日志输出的目的地,目的地可以是控制台、文件、远程套接字服务器、 MySQL、PostreSQL、 Oracle和其他数据库、 JMS和远程UNIX Syslog守护进程等。
root 就是最高级别logger,所有不被指定logger的日志都归root管理。
在slf4j框架下我们使用log是这样的:
private static final Logger logger= LoggerFactory.getLogger(xxx.class);
或者
private static final Logger logger= LoggerFactory.getLogger("xxxx");
可以理解为代码中的getLogger() 方法就是获取xml配置中的logger,如果没有配置相应的logger则为root 比如我配置了:
那我在获得一个logger时可以这样获得它:
private static final Logger logger= LoggerFactory.getLogger("hhh");
我所输出的日志将被这个logger所管理 logger 上有三个配置 name level additivity name就是这个logger的名称,level就是这个日志过滤的级别,低于这个级别的日志不输入到对应的appender中;additivity是否向上级logger传递打印信息,默认是true。logger中可以配置多个appender-ref,也就是可以指定多个输出地点。 而root只是特殊的logger,用法上无差别
appender节点: appender节点是logback配置的关键,其name属性指定其名称,class属性指定实现类,对应得实现类有
ch.qos.logback.core.ConsoleAppender // 以控制台作为输出
ch.qos.logback.core.rolling.RollingFileAppender//以日志文件作为输出
ch.qos.logback.classic.db.DBAppender//以数据库作为输出
net.logstash.logback.appender.LogstashTcpSocketAppender//以logstash作为输出需要引入如下依赖:
ch.qos.logback.classic.AsyncAppender//异步输出 需要定义appender-ref
// logstash依赖
net.logstash.logback
logstash-logback-encoder
4.11
所有的appender 实现ch.qos.logback.core.Appender接口或者 ch.qos.logback.core.UnsynchronizedAppenderBase接口(异步),我们也可以自定义appender来指定日志输出;
在Appender中可以定义哪些节点我们一个个来看:
第一种: ConsoleAppender 如同它的名字一样,这个Appender将日志输出到console,更准确的说是System.out 或者System.err。 它包含的参数如下:
Property
Name Type
Description
encoder
Encoder
通常在其pattern里指定日志格式 如: %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight([%-5level]) %logger - %msg%n表示 日期格式 日志级别(高亮)logger的名称 logger的message
target
String
指定输出目标。可选值:System.out 或 System.err。默认值:System.out
withJansi
boolean
是否支持ANSI color codes(类似linux中的shell脚本的输出字符串颜色控制代码)。默认为false。如果设置为true。例如:[31m 代表将前景色设置成红色。在windows中,需要提供"org.fusesource.jansi:jansi:1.9",而在linux,mac os x中默认支持。
第二种: FileAppender 将日志输出到文件当中,目标文件取决于file属性。是否追加输出,取决于append属性。
Property
Name Type
Description
append
boolean
是否以追加方式输出。默认为true。
encoder
Encoder
See OutputStreamAppender properties.
file
String
指定文件名。注意在windows当中,反斜杠 \ 需要转义,或直接使用 / 也可以。例如 c:/temp/test.logor 或 c:\temp\test.log 都可以。没有默认值,如果上层目录不存在,FileAppender会自动创建。
prudent
boolean
是否工作在谨慎模式下。在谨慎模式下,FileAppender将会安全写入日志到指定文件,即时在不同的虚拟机jvm中有另一个相同的FileAppender实例。默认值:fales;设置为true,意味着append会被自动设置成true。prudent依赖于文件排它锁。实验表明,使用文件锁,会增加3倍的日志写入消耗。比如说,当prudent模式为off,写入一条日志到文件只要10毫秒,但是prudent为真,则会接近30毫秒。prudent 模式实际上是将I/O请求序列化,因此在I/O数量较大,比如说100次/s或更多的时候,带来的延迟也会显而易见,所以应该避免。在networked file system(远程文件系统)中,这种消耗将会更大,可能导致死锁。
第三个: RollingFileAppender
RollingFileAppender继承自FileAppender,提供日志目标文件自动切换的功能。例如可以用日期作为日志分割的条件。 RollingFileAppender有两个重要属性,RollingPolicy负责怎么切换日志,TriggeringPolicy负责何时切换。为了使RollingFileAppender起作用,这两个属性必须设置,但是如果RollingPolicy的实现类同样实现了TriggeringPolicy接口,则也可以只设置RollingPolicy这个属性。 下面是它的参数:
Property
Name Type
Description
file
String
指定文件名。注意在windows当中,反斜杠 \ 需要转义,或直接使用 / 也可以。例如 c:/temp/test.logor 或 c:\temp\test.log 都可以。没有默认值,如果上层目录不存在,FileAppender会自动创建。
append
boolean
是否以追加方式输出。默认为true。
encoder
Encoder
See OutputStreamAppender properties.
rollingPolicy
RollingPolicy
当发生日志切换时,RollingFileAppender的切换行为。例如日志文件名的修改
triggeringPolicy
TriggeringPolicy
决定什么时候发生日志切换,例如日期,日志文件大小到达一定值
prudent
boolean
FixedWindowRollingPolicy 不支持prudent模式。TimeBasedRollingPolicy 支持prudent模式,但是需要满足一下两条约束:在prudent模式中,日志文件的压缩是不被允许,不被支持的。不能设置file属性。
第四个:SocketAppender及SSLSocketAppender(未尝试过)
到目前为止我们讲的appender都只能将日志输出到本地资源。与之相对的,SocketAppender就是被设计用来输出日志到远程实例中的。SocketAppender输出日志采用明文方式,SSLSocketAppender则采用加密方式传输日志。 被序列化的日志事件的类型是 LoggingEventVO 继承ILoggingEvent接口。远程日志记录并非是侵入式的。在反序列化接收后,日志事件就可以好像在本地生成的日志一样处理了。多个SockerAppender可以向同一台日志服务器发送日志。SocketAppender并不需要关联一个Layout,因为它只是发送序列化的日志事件给远程日志服务器。SocketAppender的发送操作是基于TCP协议的。因此如果远程服务器是可到达的,则日志会被其处理,如果远程服务器宕机或不可到达,那么日志将会被丢弃。等到远程服务器复活,日志发送将会透明的重新开始。这种透明式的重连,是通过一个“连接“线程周期性的尝试连接远程服务器实现的。 Logging events会由TCP协议实现自动缓冲。这意味着,如果网络速度比日志请求产生速度快,则网络速度并不会影响应用。但如果网络速度过慢,则网络速度则会变成限制,在极端情况下,如果远程日志服务器不可到达,则会导致应用最终阻塞。不过,如果服务器可到达,但是服务器宕机了,这种情况,应用不会阻塞,而只是丢失一些日志事件而已。 需要注意的是,即使SocketAppender没有被logger链接,它也不会被gc回收,因为他在connector thread中任然存在引用。一个connector thread 只有在网络不可达的情况下,才会退出。为了防止这个垃圾回收的问题,我们应该显示声明关闭SocketAppender。长久存活并创建/销毁大量的SocketAppender实例的应用,更应该注意这个问题。不过大多数应用可以忽略这个问题。如果JVM在SocketAppender关闭之前将其退出,又或者是被垃圾回收,这样子可能导致丢失一些还未被传输,在管道中等待的日志数据。为了防止避免日志丢失,经常可靠的办法就是调用SocketAppender的close方法,或者调用LoggerContext的stop方法,在退出应用之前。
下面我们来看看SocketAppender的属性:
Property
Name Type
Description
includeCallerData
boolean
是否包含调用者的信息如果为true,则以下日志输出的 ?:? 会替换成调用者的文件名跟行号,为false,则为问号。2019-01-06 17:37:30,968 DEBUG [Thread-0] [?:?] chapters.appenders.socket.SocketClient2 - Hi
port
int
端口号
reconnectionDelay
Duration
重连延时,如果设置成“10 seconds”,就会在连接u武器失败后,等待10秒,再连接。默认值:“30 seconds”。如果设置成0,则关闭重连功能。
queueSize
int
设置缓冲日志数,如果设置成0,日志发送是同步的,如果设置成大于0的值,会将日志放入队列,队列长度到达指定值,在统一发送。可以加大服务吞吐量。
eventDelayLimit
Duration
设置日志超时丢弃时间。当设置“10 seconds”类似的值,如果日志队列已满,而服务器长时间来不及接收,当滞留时间超过10 seconds,日志就会被丢弃。默认值: 100 milliseconds
remoteHost
String
远程日志服务器的IP
ssl
SSLConfiguration
只在SSLSocketAppender包含该属性节点。提供SSL配置,详情见 Using SSL.
标准的Logback Classic包含四个可供使用的Receiver用来接收来自SocketAppender的logging evnets。
第五个: SMTPAppender
SMTPAppender 可以将logging event存放在一个或多个固定大小的缓冲区中,然后在用户指定的event到来之时,将适当的大小的logging event以邮件方式发送给运维人员。 详细属性如下:
Property
Name Type
Description
smtpHost
String
SMTP server的地址,必需指定。如网易的SMTP服务器地址是: smtp.163.com
smtpPort
int
SMTP server的端口地址。默认值:25
to
String
指定发送到那个邮箱,可设置多个属性,指定多个目的邮箱
from
String
指定发件人名称。如果设置成“muggle ”,则邮件发件人将会是“muggle hh@moral.org ”
subject
String
指定emial的标题,它需要满足PatternLayout中的格式要求。如果设置成“Log: %logger - %msg”,就案例来讲,则发送邮件时,标题为“Log: com.foo.Bar - Hello World ”。 默认值:"%logger{20} - %m".
discriminator
Discriminator
通过Discriminator, SMTPAppender可以根据Discriminator的返回值,将到来的logging event分发到不同的缓冲区中。默认情况下,总是返回相同的值来达到使用一个缓冲区的目的。
evaluator
IEvaluator
指定触发日志发送的条件。通过指定EventEvaluator接口的实现类。默认情况下SMTPAppeender使用的是OnErrorEvaluator,表示当发送ERROR或更高级别的日志请求时,发送邮件。Logback提供了几个evaluators:OnErrorEvaluator、OnMarkerEvaluator、JaninoEventEvaluator、GEventEvaluator(功能强大)
cyclicBufferTracker
CyclicBufferTracker
指定一个cyclicBufferTracker跟踪cyclic buffer。它是基于discriminator的实现的。如果你不指定,默认会创建一个CyclicBufferTracker ,默认设置cyclic buffer大小为256。你也可以手动指定使用默认的CyclicBufferTracker,并且通过属性修改默认的缓冲区接收多少条logging event。
username
String
发送邮件账号,默认为null
password
String
发送邮件密码,默认为null
STARTTLS
boolean
如果设置为true,appender会尝试使用STARTTLS命令,如果服务端支持,则会将明文连接转换成加密连接。需要注意的是,与日志服务器连接一开始是未加密的。默认值:false
SSL
boolean
如果设置为true,appender将会使用SSL连接到日志服务器。 默认值:false
charsetEncoding
String
指定邮件信息的编码格式 默认值:UTF-8
localhost
String
如果smtpHost没有正确配置,比如说不是完整的地址。这时候就需要localhost这个属性提供服务器的完整路径(如同java中的完全限定名 ),详情参考com.sun.mail.smtp 中的mail.smtp.localhost属性
asynchronousSending
boolean
这个属性决定email的发送是否是异步。默认:true,异步发送但是在某些情况下,需要以同步方式发送错误日志的邮件给管理人员,防止不能及时维护应用。
includeCallerData
boolean
默认:false 指定是否包含callerData在日志中
sessionViaJNDI
boolean
SMTPAppender依赖javax.mail.Session来发送邮件。默认情况下,sessionViaJNDI为false。javax.mail.Session实例的创建依赖于SMTPAppender本身的配置信息。如果设置为true,则Session的创建时通过JNDI获取引用。这样做的好处可以让你的代码复用更好,让配置更简洁。需要注意的是,如果使用JNDI获取Session对象,需要保证移除mail.jar以及activation.jar这两个jar包
jndiLocation
String
如果sessionViaJNDI设置为true,则jndiLocation指定JNDI的资源名,默认值为:"java:comp/env/mail/Session"
SMTPAppender只保留最近的256条logging events 在循环缓冲区中,当缓冲区慢,就会开始丢弃最老的logging event。因此不管什么时候,SMTPAppender一封邮件最多传递256条日志事件。SMTPAppender依赖于JavaMail API。而JavaMail API又依赖于IOC框架(依赖注入)。
第六个:DBAppender
DBAppender 可以将日志事件插入到3张数据表中。它们分别是logging_event,logging_event_property,logging_event_exception。这三张数据表必须在DBAppender工作之前存在。它们的sql脚本可以在 logback-classic/src/main/java/ch/qos/logback/classic/db/script folder 这个目录下找到。这个脚本对大部分SQL数据库都是有效的,除了少部分,少数语法有差异需要调整。 下面是logback与常见数据库的支持信息:
RDBMS
tested version(s)
tested JDBC driver version(s)
supports getGeneratedKeys() method
is a dialect provided by logback
DB2
untested
untested
unknown
NO
H2
--
-
unknown
YES
HSQL
--
-
NO
YES
Microsoft SQL Server
--
--
YES
YES
MySQL
5.7
YES
YES
PostgreSQL
--
--
NO
YES
Oracle
--
--
YES
YES
SQLLite
--
-
unknown
YES
Sybase
--
-
unknown
YES
下面给出三张表的sql语句:
BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;
BEGIN;
CREATE TABLE logging_event
(
timestmp BIGINT NOT NULL,
formatted_message TEXT NOT NULL,
logger_name VARCHAR(254) NOT NULL,
level_string VARCHAR(254) NOT NULL,
thread_name VARCHAR(254),
reference_flag SMALLINT,
arg0 VARCHAR(254),
arg1 VARCHAR(254),
arg2 VARCHAR(254),
arg3 VARCHAR(254),
caller_filename VARCHAR(254) NOT NULL,
caller_class VARCHAR(254) NOT NULL,
caller_method VARCHAR(254) NOT NULL,
caller_line CHAR(4) NOT NULL,
event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_property
(
event_id BIGINT NOT NULL,
mapped_key VARCHAR(254) NOT NULL,
mapped_value TEXT,
PRIMARY KEY(event_id, mapped_key),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_exception
(
event_id BIGINT NOT NULL,
i SMALLINT NOT NULL,
trace_line VARCHAR(254) NOT NULL,
PRIMARY KEY(event_id, i),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
第七个: AsyncAppender
AsyncAppender记录ILoggingEvents的方式是异步的。它仅仅相当于一个event分配器,因此需要配合其他appender才能有所作为。
需要注意的是:AsyncAppender将event缓存在 BlockingQueue ,一个由AsyncAppender创建的工作线程,会一直从这个队列的头部获取events,然后将它们分配给与AsyncAppender唯一关联的Appender中。默认情况下,如果这个队列80%已经被占满,则AsyncAppender会丢弃等级为 TRACE,DEBUG,INFO这三个等级的日志事件。 在应用关闭或重新部署的时候,AsyncAppender一定要被关闭,目的是为了停止,回收再利用worker thread,和刷新缓冲队列中logging events。那如果关闭AsyncAppender呢?可以通过关闭LoggerContext来关闭所有appender,当然也包括AsyncAppender了。AsyncAppender会在maxFlushTime属性设置的时间内等待Worker thread刷新全部日志event。如果你发现缓冲的event在关闭LoggerContext的时候被丢弃,这时候你就也许需要增加等待的时间。将maxFlushTime设置成0,就是AsyncAppender一直等待直到工作线程将所有被缓冲的events全部刷新出去才执行才结束。 根据JVM退出的模式,工作线程worker thread处理被缓冲的events的工作是可以被中断的,这样就导致了剩余未处理的events被搁浅。这种现象通常的原因是当LoggerContext没有完全关闭,或者当JVM终止那些非典型的控制流(不明觉厉)。为了避免工作线程的因为这些情况而发生中断,一个shutdown hook(关闭钩子)可以被插入到JVM运行的时候,这个钩子的作用是在JVM开始shutdown刚开始的时候执行关闭 LoggerContext的任务。
下面是AsyncAppender的属性表
Property
Name Type
Description
queueSize
int
设置blocking queue的最大容量,默认是256条events
discardingThreshold
int
默认,当blocking queue被占用80%以上,AsyncAppender就会丢弃level为 TRACE,DEBUG,INFO的日志事件,如果要保留所有等级的日志,需要设置成0
includeCallerData
boolean
提取CallerData代价比较昂贵,为了提高性能,caller data默认不提供。只有一些获取代价较低的数据,如线程名称,MDC值才会被保留。如果设置为true,就会包含caller data
maxFlushTime
int
设置最大等待刷新事件,单位为miliseconds(毫秒)。当LoggerContext关闭的时候,AsyncAppender会在这个时间内等待工作线程完成events的flush工作,超时未处理的events将会被抛弃。
neverBlock
boolean
默认为false,如果队列被填满,为了处理所有日志,就会阻塞的应用。如果为true,为了不阻塞你的应用,也会选择抛弃一些message。
默认情况下,event queue最大的容量是256。如果队列被填充满那么就会阻塞你的应用,直到队列能够容纳新的logging event。所以当AsyncAppender工作在队列满的情况下,可以称作伪同步。 在以下四种情况下容易导致AsyncAppender伪同步状态的出现:
应用中存在大量线程
每秒产生大量的logging events
每一个logging event都存在大量的数据
子appender中存在很高的延迟
为了避免伪同步的出现,提高queueSizes普遍有效,但是就消耗了应用的可用内存。
下面列出一些 appender配置示例:
myapp.log
%logger{35} - %msg%n
com.mysql.jdbc.Driver
jdbc:mysql://${serverName}:${port}/${dbName}
${user}
${password}
smtp.gmail.com
465
true
YOUR_USERNAME@gmail.com
YOUR_GMAIL_PASSWORD
EMAIL-DESTINATION
ANOTHER_EMAIL_DESTINATION
YOUR_USERNAME@gmail.com
TESTING: %logger{20} - %m
%date %-5level %logger{35} - %message%n
smtp.gmail.com
587
true
YOUR_USERNAME@gmail.com
YOUR_GMAIL_xPASSWORD
EMAIL-DESTINATION
ANOTHER_EMAIL_DESTINATION
YOUR_USERNAME@gmail.com
TESTING: %logger{20} - %m
%date %-5level %logger - %message%n
SimpleSocketServer需要两个命令行参数,port 和 configFile路径。(该方法待验证)
java ch.qos.logback.classic.net.SimpleSocketServer 6000 \ src/main/java/chapters/appenders/socket/server1.xml
客户端的SocketAppender的简单配置例子:
192.168.0.101
8888
10000
true
在服务端使用SimpleSSLSocketServer
java -Djavax.net.ssl.keyStore=src/main/java/chapters/appenders/socket/ssl/keystore.jks \ -Djavax.net.ssl.keyStorePassword=changeit \ ch.qos.logback.classic.net.SimpleSSLSocketServer 6000 \ src/main/java/chapters/appenders/socket/ssl/server.xml
SSLSocketAppender配置
${host}
${port}
10000
${truststore}
${password}
testFile.log
true
%-4relative [%thread] %-5level %logger{35} - %msg%n
参考:https://blog.csdn.net/tianyaleixiaowu/article/details/73327752
下面基于logback配置做一个请求日志的的封装
功能:记录每次请求的参数和用户ID存入数据库或者elk 问题:javaee规范中request输入输出流都只能被读取一次,所以如果用过滤器或者拦截器读取request中的流都会导致后面的controller无法接受到数据。 所以我们要用原生的aop获得请求参数,切点为controller,这就很好的避开了以上问题。
package com.muggle.poseidon.core.aspect;
import com.muggle.poseidon.manager.UserInfoManager;
import com.muggle.poseidon.utils.RequestUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
/**
* @program: hiram_erp
* @description: 日志信息切面
* @author: muggle
* @create: 2019-02-21
**/
@Aspect
@Component
public class LogMessageAspect {
private final static Logger logger = LoggerFactory.getLogger("requestLog");
// private final static Logger timeLog = LoggerFactory.getLogger(LogMessageAspect.class);
private static final ThreadLocal threadLocal = new ThreadLocal<>();
@Pointcut("execution(public * com.hiram.erp.controller.*.*(..))")
public void webLog() {}
/**
* 在切点之前织入
* @param joinPoint
* @throws Throwable
*/
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// System.out.println("sssssssssssssssssssssssssssssssssssssssssssssssssssss");
/* // 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
// 打印请求 url
// 请求id
Long userId=null;
if (user!=null){
userId=user.getUserInfo().getUserId();
}
logger.info("URL : {}, 登录id: {} ,HTTP Method: {},ip :{},Request Args : {}", request.getRequestURL().toString(),userId, request.getMethod(),request.getRemoteAddr());
*/ }
/**
* 在切点之后织入
* @throws Throwable
*/
@After("webLog()")
public void doAfter(JoinPoint joinPoint) throws Throwable {
}
/**
* 环绕
* @param joinPoint
* @return
* @throws Throwable
*/
@Around("webLog()")
public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = joinPoint.proceed();
// 打印出参
// logger.info("Response Args : {},", JSONObject.toJSONString(result),new Date());
// 执行耗时
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
HttpServletResponse response = attributes.getResponse();
String requestURL = request.getRequestURL().toString();
if (requestURL.contains("/sys/log_info/")){
return result;
}
// 打印请求相关参数
// 打印请求 url
// 请求id
String userId = UserInfoManager.getUserId();
String url = request.getRequestURL().toString();
String method = request.getMethod();
String remoteAddr = RequestUtils.getIpAddr(request);
Object[] args = joinPoint.getArgs();
// List objects=new ArrayList<>();
StringBuilder stringBuilder = new StringBuilder();
for (int i=0;i
对于数据库存储,如果我们希望log存在另外一个数据库中不存在项目里的数据库中,并且可以通过持久化框架查询数据库内信息。我们则可以配置多数据源,如果将日志放在同一个数据库中则直接配置appender就行了,很方便。 多数据源配置mybatis版:
其原理是配置多个sessionfactory,然后根据不同的mapperscan来区分不同mapper对应的数据库
以druid连接池为例
application.yml
log:
datasource:
druid:
url: ${mysql_url}/log?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
initialSize: 5
maxActive: 20
maxPoolPreparedStatementPerConnectionSize: 20
maxWait: 60000
minIdle: 5
poolPreparedStatements: true
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
timeBetweenEvictionRunsMillis: 60000
validationQuery: SELECT 1
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
druid:
url: ${mysql_url}/hiram_erp?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username:
password:
driver-class-name: com.mysql.cj.jdbc.Driver
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
filters: stat,wall
initialSize: 5
maxActive: 20
maxPoolPreparedStatementPerConnectionSize: 20
maxWait: 60000
minIdle: 5
poolPreparedStatements: true
testOnBorrow: false
testOnReturn: false
testWhileIdle: true
timeBetweenEvictionRunsMillis: 60000
validationQuery: SELECT 1
@Configuration
// 主数据库配置 指定mapper位置
@MapperScan(basePackages = {"com.muggle.poseidon.mapper"}, sqlSessionTemplateRef = "sqlSessionTemplate")
public class ManySourceDBConfig {
@Bean(name = "dataSource")
// 读取application的配置信息
@ConfigurationProperties(prefix = "spring.datasource.druid")
// 最高优先级,表示系统默认使用该配置
@Primary
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
List filterList = new ArrayList<>();
filterList.add(wallFilter());
druidDataSource.setProxyFilters(filterList);
return druidDataSource;
}
@Bean(name = "sqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory() throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(this.dataSource());
Properties props = new Properties();
props.setProperty("localCacheScope", "true");
props.setProperty("lazyLoadingEnabled", "true");
props.setProperty("aggressiveLazyLoading", "false");
props.setProperty("jdbcTypeForNull", "NULL");
sqlSessionFactoryBean.setConfigurationProperties(props);
sqlSessionFactoryBean.setVfs(SpringBootVFS.class);
//pageHelper
Properties properties = new Properties();
properties.setProperty("reasonable", "true");
properties.setProperty("supportMethodsArguments", "true");
properties.setProperty("params", "count=countSql");
properties.setProperty("pageSizeZero", "true");
PageInterceptor interceptor = new PageInterceptor();
interceptor.setProperties(properties);
sqlSessionFactoryBean.setPlugins(new Interceptor[]{interceptor});
sqlSessionFactoryBean.setTypeAliasesPackage("com.muggle.poseidon.model");
PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
sqlSessionFactoryBean.setMapperLocations(resolver.getResources("classpath*:/mapper/*.xml"));
return sqlSessionFactoryBean.getObject();
}
@Bean(name = "transactionManager")
@Primary
public PlatformTransactionManager transactionManager() {
return new DataSourceTransactionManager(this.dataSource());
}
@Bean(name = "sqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean druid = new ServletRegistrationBean();
druid.setServlet(new StatViewServlet());
druid.setUrlMappings(Collections.singletonList("/druid/*"));
Map params = new HashMap<>();
params.put("loginUsername", "");
params.put("loginPassword", "");
druid.setInitParameters(params);
return druid;
}
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean fitler = new FilterRegistrationBean();
fitler.setFilter(new WebStatFilter());
fitler.setUrlPatterns(Collections.singletonList("/*"));
fitler.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return fitler;
}
@Bean
public WallFilter wallFilter() {
WallFilter wallFilter = new WallFilter();
wallFilter.setConfig(wallConfig());
return wallFilter;
}
@Bean
public WallConfig wallConfig() {
WallConfig config = new WallConfig();
config.setMultiStatementAllow(true);//允许一次执行多条语句
config.setNoneBaseStatementAllow(true);//允许非基本语句的其他语句
return config;
}
@Bean
public ProcessEngineConfiguration processEngineConfiguration() {
ProcessEngineConfiguration pec = StandaloneProcessEngineConfiguration.createStandaloneProcessEngineConfiguration();
pec.setDataSource(dataSource());
//如果表不存在,自动创建表
pec.setDatabaseSchemaUpdate(ProcessEngineConfiguration.DB_SCHEMA_UPDATE_TRUE);
//属性asyncExecutorActivate定义为true,工作流引擎在启动时就建立启动async executor线程池
pec.setAsyncExecutorActivate(false);
return pec;
}
@Bean
public ProcessEngine processEngine() {
return processEngineConfiguration().buildProcessEngine();
}
}
log数据库配置
/**
* @program:
* @description:
* @author: muggle
* @create: 2019-02-23
**/
@Configuration
// 注意确保主配置无法扫描到这个包
@MapperScan(basePackages = "com.muggle.poseidon.logmapper", sqlSessionTemplateRef = "test1SqlSessionTemplate")
public class LogDBConfig {
@Bean(name = "test1DataSource")
@ConfigurationProperties(prefix = "log.datasource.druid")
public DataSource dataSource() {
DruidDataSource druidDataSource = new DruidDataSource();
List filterList = new ArrayList<>();
filterList.add(wallFilter());
druidDataSource.setProxyFilters(filterList);
return druidDataSource;
}
@Bean(name = "test1SqlSessionFactory")
public SqlSessionFactory testSqlSessionFactory(@Qualifier("test1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new
// mapper位置,不要和主配置的mapper放到一起
PathMatchingResourcePatternResolver().getResources("classpath*:/mapper/log/*.xml"));
return bean.getObject();
}
@Bean(name = "test1TransactionManager")
public DataSourceTransactionManager testTransactionManager(@Qualifier("test1DataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean(name = "test1SqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(@Qualifier("test1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public WallFilter wallFilter() {
WallFilter wallFilter = new WallFilter();
wallFilter.setConfig(wallConfig());
return wallFilter;
}
@Bean
public WallConfig wallConfig() {
WallConfig config = new WallConfig();
config.setMultiStatementAllow(true);//允许一次执行多条语句
config.setNoneBaseStatementAllow(true);//允许非基本语句的其他语句
return config;
}
}
多数据源jpa版
package com.muggle.poseidon.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class DataSourceConfig {
/**
* 扫描spring.datasource.primary开头的配置信息
*
* @return 数据源配置信息
*/
@Primary
@Bean(name = "primaryDataSourceProperties")
@ConfigurationProperties(prefix = "spring.datasource")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* 获取主库数据源对象
*
* @param properties 注入名为primaryDataSourceProperties的bean
* @return 数据源对象
*/
@Primary
@Bean(name = "primaryDataSource")
public DataSource dataSource(@Qualifier("primaryDataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
/**
* 该方法仅在需要使用JdbcTemplate对象时选用
*
* @param dataSource 注入名为primaryDataSource的bean
* @return 数据源JdbcTemplate对象
*/
@Primary
@Bean(name = "primaryJdbcTemplate")
public JdbcTemplate jdbcTemplate(@Qualifier("primaryDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
package com.muggle.poseidon.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Map;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
// repository包名
basePackages = "com.muggle.poseidon.repos",
// 实体管理bean名称
entityManagerFactoryRef = "primaryEntityManagerFactory",
// 事务管理bean名称
transactionManagerRef = "primaryTransactionManager"
)
public class MainDataBaseConfig {
/**
* 扫描spring.jpa.primary开头的配置信息
*
* @return jpa配置信息
*/
@Primary
@Bean(name = "primaryJpaProperties")
@ConfigurationProperties(prefix = "spring.jpa")
public JpaProperties jpaProperties() {
return new JpaProperties();
}
/**
* 获取主库实体管理工厂对象
*
* @param primaryDataSource 注入名为primaryDataSource的数据源
* @param jpaProperties 注入名为primaryJpaProperties的jpa配置信息
* @param builder 注入EntityManagerFactoryBuilder
* @return 实体管理工厂对象
*/
@Primary
@Bean(name = "primaryEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("primaryDataSource") DataSource primaryDataSource
, @Qualifier("primaryJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) {
return builder
// 设置数据源
.dataSource(primaryDataSource)
// 设置jpa配置
.properties(jpaProperties.getProperties())
// 设置hibernate配置
.properties(jpaProperties.getHibernateProperties(new HibernateSettings()))
// 设置实体包名
.packages("com.muggle.poseidon.model")
// 设置持久化单元名,用于@PersistenceContext注解获取EntityManager时指定数据源
.persistenceUnit("primaryPersistenceUnit")
.build();
}
/**
* 获取实体管理对象
*
* @param factory 注入名为primaryEntityManagerFactory的bean
* @return 实体管理对象
*/
@Primary
@Bean(name = "primaryEntityManager")
public EntityManager entityManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory factory) {
return factory.createEntityManager();
}
/**
* 获取主库事务管理对象
*
* @param factory 注入名为primaryEntityManagerFactory的bean
* @return 事务管理对象
*/
@Primary
@Bean(name = "primaryTransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("primaryEntityManagerFactory") EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
}
```java
package com.muggle.poseidon.core.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.orm.jpa.EntityManagerFactoryBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
// repository包名
basePackages = "com.muggle.poseidon.logrep",
// 实体管理bean名称
entityManagerFactoryRef = "secondEntityManagerFactory",
// 事务管理bean名称
transactionManagerRef = "secondTransactionManager"
)
public class LogDataBaseConfig {
/**
* 扫描spring.jpa.second开头的配置信息
*
* @return jpa配置信息
*/
@Bean(name = "secondJpaProperties")
@ConfigurationProperties(prefix = "spring.aa")
public JpaProperties jpaProperties() {
return new JpaProperties();
}
/**
* 获取从库实体管理工厂对象
*
* @param secondDataSource 注入名为secondDataSource的数据源
* @param jpaProperties 注入名为secondJpaProperties的jpa配置信息
* @param builder 注入EntityManagerFactoryBuilder
* @return 实体管理工厂对象
*/
@Bean(name = "secondEntityManagerFactory")
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("secondDataSource") DataSource secondDataSource
, @Qualifier("secondJpaProperties") JpaProperties jpaProperties, EntityManagerFactoryBuilder builder) {
return builder
// 设置数据源
.dataSource(secondDataSource)
// 设置jpa配置
.properties(jpaProperties.getProperties())
// 设置hibernate配置
.properties(jpaProperties.getHibernateProperties(new HibernateSettings()))
// 设置实体包名
.packages("com.muggle.poseidon.entity")
// 设置持久化单元名,用于@PersistenceContext注解获取EntityManager时指定数据源
.persistenceUnit("secondPersistenceUnit")
.build();
}
/**
* 获取实体管理对象
*
* @param factory 注入名为secondEntityManagerFactory的bean
* @return 实体管理对象
*/
@Bean(name = "secondEntityManager")
public EntityManager entityManager(@Qualifier("secondEntityManagerFactory") EntityManagerFactory factory) {
return factory.createEntityManager();
}
/**
* 获取从库事务管理对象
*
* @param factory 注入名为secondEntityManagerFactory的bean
* @return 事务管理对象
*/
@Bean(name = "secondTransactionManager")
public PlatformTransactionManager transactionManager(@Qualifier("secondEntityManagerFactory") EntityManagerFactory factory) {
return new JpaTransactionManager(factory);
}
}
package com.muggle.poseidon.core.config;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
public class LogDataConfig {
/**
* 扫描spring.datasource.second开头的配置信息
*
* @return 数据源配置信息
*/
@Bean(name = "secondDataSourceProperties")
@ConfigurationProperties(prefix = "spring.ss")
public DataSourceProperties dataSourceProperties() {
return new DataSourceProperties();
}
/**
* 获取从库数据源对象
*
* @param properties 注入名为secondDataSourceProperties的beanf
* @return 数据源对象
*/
@Bean(name = "secondDataSource")
public DataSource dataSource(@Qualifier("secondDataSourceProperties") DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
/**
* 该方法仅在需要使用JdbcTemplate对象时选用
*
* @param dataSource 注入名为secondDataSource的bean
* @return 数据源JdbcTemplate对象
*/
@Bean(name = "secondJdbcTemplate")
public JdbcTemplate jdbcTemplate(@Qualifier("secondDataSource") DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
application.properties
server.port=8080
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#spring.datasource.url = jdbc:mysql://localhost:3306/test
spring.datasource.driverClassName = com.mysql.cj.jdbc.Driver
spring.datasource.url = jdbc:mysql://119.23.75.58:3306/poseidon?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true
spring.datasource.username =
spring.datasource.password =
spring.datasource.max-active=20
spring.datasource.max-idle=8
spring.datasource.min-idle=8
spring.datasource.initial-size=10
spring.jpa.database=mysql
spring.jpa.show-sql = true
#配置方言
spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect
spring.ss.type=com.alibaba.druid.pool.DruidDataSource
#spring.datasource.url = jdbc:mysql://localhost:3306/test
spring.ss.driverClassName = com.mysql.cj.jdbc.Driver
spring.ss.url = jdbc:mysql://zzzzz/log?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowMultiQueries=true
spring.ss.username =
spring.ss.password =
spring.ss.max-active=20
spring.ss.max-idle=8
spring.ss.min-idle=8
spring.ss.initial-size=10
spring.aa.database=mysql
spring.aa.show-sql = true
#配置方言
spring.aa.database-platform=org.hibernate.dialect.MySQL5Dialect
以数据库作为输出配置就算完成了,接下来整合elk系统到我们日志系统中:
先整合logstash
logstash安装和配置: https://www.elastic.co/cn/downloads/logstash 选择zip包下载
解压,进入bin目录 创建logstash.conf 并配置:
input {
tcp {
##host:port就是上面appender中的 destination,这里其实把logstash作为服务,开启9250端口接收logback发出的消息
host => "127.0.0.1"
port => 9100
mode => "server"
tags => ["tags"]
codec => json_lines
}
}
output {
stdout { codec => rubydebug }
#输出到es
#elasticsearch { hosts => "127.0.0.1:9200" }
#输出到一个文件中
file {
path => "D:\logs\test.log"
codec => line
}
}
我这里先配置输出到文件,后面再修改,创建文件:D:\logs\test.log
启动:
打开cmd(不要使用powershell),进入bin:
D:\exe\logstash-6.6.1\logstash-6.6.1\bin>logstash -f logstash.conf
然后在我们的项目中进行相应的配置: 按这个来:https://github.com/logstash/logstash-logback-encoder
加入pom并指定logback版本:
1.2.3
net.logstash.logback
logstash-logback-encoder
5.3
ch.qos.logback
logback-classic
1.2.3
配置apppender和logger
127.0.0.1:9100
true
true
测试:
RestController
@RequestMapping("/public/log")
public class LogTestController {
private static final Logger log = LoggerFactory.getLogger("logstash");
@Autowired
LoggingEventRepository repository;
@GetMapping("/")
public String test(){
log.info("sssssssssssssss");
Iterable all = repository.findAll();
return "sss";
}
}
访问接口,logstash打印信息:
[2019-03-09T11:32:56,358][INFO ][logstash.outputs.file ] Opening file {:path=>"D:/logs/test.log"}
{
"host" => "www.xmind.net",
"level" => "INFO",
"caller_class_name" => "com.muggle.poseidon.controller.LogTestController",
"@timestamp" => 2019-03-09T03:33:03.413Z,
"logger_name" => "logstash",
"@version" => "1",
"thread_name" => "http-nio-8080-exec-9",
"message" => "sssssssssssssss",
"caller_line_number" => 22,
"port" => 58368,
"level_value" => 20000,
"caller_file_name" => "LogTestController.java",
"tags" => [
[0] "tags"
],
"caller_method_name" => "test"
}
test.log输出了文件:
2019-03-09T03:33:03.413Z www.xmind.net sssssssssssssss
接下来只要把输出路径换成ES就可以了,这属于logstash和es的整合,这里先不讲解;重新回归到我们的请求模块:
我希望我的模块,对每次请求都能记录下来(请求日志),并将记录存到数据库或者ES,同时我要对所有接口都进行一个幂等性的保障;保障接口的幂等性有多种方法,比较简单的是数据库做唯一索引或者加拦截器,我这里加了一个拦截器来保障接口幂等和拦截前端数据的重复提交(关于接口幂等性在其他文档中介绍):
@Slf4j
public class RequestLockInterceptor implements HandlerInterceptor {
RedisLock redisTool;
private int expireTime;
public RequestLockInterceptor(int expireTime, RedislockImpl redisTool) {
this.expireTime = expireTime;
this.redisTool = redisTool;
}
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if("post".equalsIgnoreCase(request.getMethod())){
String token = request.getParameter("request_key");
if (token==null||"".equals(token)){
log.error("请求非法");
// throw new PoseidonException("请求太频繁",PoseidonProperties.TOO_NUMBER_REQUEST);
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{\"code\":\"5001\",\"msg\":\"请求非法\"}");
writer.close();
return false;
}
String ipAddr = RequestUtils.getIpAddr(request);
String lockKey = request.getRequestURI() + "_" + "_" + token;
boolean lock = redisTool.lock(lockKey, ipAddr, expireTime);
if (!lock) {//
log.error("拦截表单重复提交");
// throw new PoseidonException("请求太频繁",PoseidonProperties.TOO_NUMBER_REQUEST);
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = response.getWriter();
writer.write("{\"code\":\"5001\",\"msg\":\"请求太频繁\"}");
writer.close();
return false;
}
}
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
// String requestURI = request.getRequestURI();
// String lockKey = request.getRequestURI() + "_" + RequestUtils.getIpAddr(request);
// redisTool.unlock(lockKey,getIpAddr(request));
}
}
项目使用了redis锁(redis锁原理和使用在其他文档中介绍)
对于系统异常,如果是业务的异常,正常处理,如果是系统发生的异常比如空指针,数据库异常等我希望系统能马上通知,以便排查问题,所以我配置邮件异常通知(关于springboot邮件配置其他文档介绍):
@RestControllerAdvice
@Slf4j
public class RestExceptionHandlerController {
@Autowired
EmailService emailService;
@Value("${admin.email}")
private String adminEmail;
@ExceptionHandler(value = {PoseidonException.class})
public ResultBean poseidonExceptionHandler(PoseidonException e, HttpServletRequest req) {
return new ResultBean().setMsg(e.getMsg()).setCode(e.getCode());
}
@ExceptionHandler(value = {MethodArgumentNotValidException.class})
public ResultBean MethodArgumentNotValidException(MethodArgumentNotValidException e, HttpServletRequest req) {
System.out.println(e.getMessage());
return new ResultBean().setMsg("数据未通过校验").setCode(PoseidonProperties.COMMIT_DATA_ERROR);
}
@ExceptionHandler(value = {Exception.class})
public ResultBean exceptionHandler(Exception e, HttpServletRequest req) {
log.error("系统异常:" + req.getMethod() + req.getRequestURI(), e);
try {
//
EmailBean emailBean = new EmailBean();
emailBean.setRecipient(adminEmail);
emailBean.setSubject("poseidon---系统异常");
emailBean.setContent("系统异常:" + req.getMethod() + req.getRequestURI()+"----"+e.getMessage());
// 改良
emailService.sendSimpleMail(emailBean);
} finally {
return new ResultBean().setMsg("系统异常,请联系管理员").setCode("500");
}
}
@ExceptionHandler(value = {HttpRequestMethodNotSupportedException.class})
public ResultBean notsupported(Exception e, HttpServletRequest req) {
return new ResultBean().setMsg("不支持的请求方式").setCode(PoseidonProperties.NOT_SUPPORT_METHOD);
}
@ExceptionHandler(value = {NoHandlerFoundException.class})
public ResultBean notFoundUrl(Exception e, HttpServletRequest req) {
return new ResultBean().setMsg("请求路径不存在").setCode("404");
}
}
你可能感兴趣的:(log模块配置)
Spring Boot01(注解、)---java八股
凉漠
java八股 java spring boot 开发语言
SpringBoot中常用注解及其底层实现1、@SpringBootApplication注解:@SpringBootApplication注解:这个注解标识了一个SpringBoot工程,它实际上是另外三个注解的组合,这三个注解是:a@SpringBootConfiguration:这个注解实际就是一个@Configuration,表示启动类也是一个配置类b@EnableAutoConfigur
基于pytest+requests+allure+yaml实现接口自动化测试框架
csdn950212
测试开发 pytest 接口自动化测试
1.项目背景和目标:这是一个基于Python的接口自动化测试框架,主要目标是:-提供一个稳定、可维护的接口测试解决方案-实现测试用例与测试数据的解耦-提供详细的测试报告和日志记录-支持多环境配置和灵活的用例管理2.技术栈选型框架采用了以下核心技术:pytest:作为测试执行引擎,提供了强大的用例管理和参数化能力requests:处理HTTP请求,支持各种接口调用场景allure:生成美观的测试报告
WebMvcConfigurer 介绍
boy快快长大
解决问题合集 java spring
WebMvcConfigurer介绍1.什么是WebMvcConfigurer介绍2.WebMvcConfigurer接口常用的方法3.使用WebMvcConfigurer实现跨域4.使用WebMvcConfigurer配置拦截器5.使用WebMvcConfigurer配置静态资源5.1配置外部目录(本地文件系统)详细解释6.使用WebMvcConfigurer配置视图解析6.1详细说明6.1.1
Vue.js 组件开发:构建高效、可重用的用户界面
m1chiru
vue.js
Vue.js组件开发:构建高效、可重用的用户界面Vue.js是一个流行的JavaScript框架,它致力于通过简单的API和高度响应的设计,帮助开发者构建高效、动态的前端应用。在Vue.js中,组件是构建用户界面的核心单元。它们使得应用的结构更加模块化、可维护且易于重用。在这篇博客中,我们将深入探讨Vue.js组件的开发方法,包括组件的基本概念、创建、传递数据、事件处理以及组件之间的通信等关键内容
React源码解读
程序员小续
react.js 前端 前端框架 node.js webpack react javascript
配置React源码本地调试环境本次环境构建采用了node版本为16、react-scripts版本号为3.4.4,源码下载地址react源码调试:react源码调试环境使用create-react-app脚手架创建项目npxcreate-react-appreact-test进入刚刚下载的目录,弹射create-react-app脚手架内部配置//在npmruneject之前,手动将项目packa
《炸裂!掌握这些 Spring Boot 干货,面试直接 “开挂”!》
@孤随
JAVA spring boot 面试 后端
SpringBoot重点、面试题及答案详细整理一、SpringBoot重点知识(一)核心概念1.自动配置SpringBoot自动配置基于类路径中的依赖、配置文件以及应用上下文里的Bean情况,借助条件注解来自动设置Spring应用的配置。例如,当类路径中存在spring-data-jpa和数据库驱动时,会自动配置数据源、JPA实体管理器工厂和事务管理器。可通过@EnableAutoConfigur
ubuntu18.04安装vnc
魔法白糖
ubuntu ubuntu ssh
最近很多小伙伴都在私信问怎么在ubuntu上安装vnc,下面我来给大家提供一个思路,如有不足的地方,欢迎大家私信,让我们相互学习,共同进步!一、配置VNCServer打开终端,依次输入以下指令:gsettingssetorg.gnome.Vinorequire-encryptionfalsegsettingssetorg.gnome.Vinoprompt-enabledfalsegsettings
RAM与ROM的区别
云墨丹青
系统 ram rom
[参考链接】(https://blog.csdn.net/tectrol/article/details/80638457)随机存取存储器(RandomAccessMemory,RAM)又称作“随机存储器”,是与CPU直接交换数据的内部存储器,也叫主存(内存)。它可以随时读写,而且速度很快,通常作为操作系统或其他正在运行中的程序的临时数据存储媒介。当电源关闭时RAM不能保留数据。如果需要保存数据,
Python正则
懒大王爱吃狼
python python 开发语言 Python基础 学习 python学习
正则表达式(RegularExpressions,简称regex)是一种强大的文本处理工具,可以用于搜索、替换和解析字符串。Python的re模块提供了对正则表达式的支持。以下是一些基本用法和示例:导入re模块首先,你需要导入Python的re模块:importre基本匹配匹配单个字符.匹配除换行符以外的任意单个字符。[]匹配括号内的任意一个字符。例如[abc]匹配a、b或c。[^...]匹配不在
Python网络编程
懒大王爱吃狼
python python 网络 开发语言 pycharm Python基础 pycharm安装
Python网络编程涉及使用Python语言来创建网络应用程序,这些应用程序可以在网络上与其他计算机进行通信。Python提供了多个库和框架来简化网络编程的过程,其中一些最常用的包括socket库、http.server模块、asyncio库以及第三方库如requests、Twisted和aiohttp等。以下是一些Python网络编程的基本概念和使用示例:1.套接字编程(SocketProgra
Linux:Supervisor进程管理
m0_37559973
linux 运维 服务器
目录一、Supervisor介绍二、核心组件2.1supervisord2.2supervisorctl2.3WebServer三、安装Supervisor3.1安装要求3.2安装方式3.3修改WebServer端口(可选)3.4启动服务四、Supervisor常用命令4.1supervisord命令行选项4.2supervisorctl命令行选项五、监控服务5.1创建进程管理配置文件5.2重新加
使用Docker部署Spark集群
小孩真笨
工程开发技术 Cloud Data Docker Spark
使用Docker部署Spark集群克隆包含启动脚本的git仓库启动Spark0.8.0集群并切换至SparkShell环境不带参数运行部署脚本*运行一些小的例子终止集群克隆包含启动脚本的git仓库*gitclone-bblogpostgit@github.com:amplab/docker-scripts.git当然,在这之前你必须已经配置了Github的SSH密钥认证,如果没有配置,会提示Per
在vue3中配置scss
库库林_沙琪马
# Vue组件直接使用 scss vue.js 前端
要在Vue3中使用SCSS(Sass的一个子集),你需要安装相应的依赖,并对项目进行一些配置。下面是详细的步骤:步骤1:安装依赖首先,你需要安装sass和vue-loader(如果你使用的是VueCLI)以及vite-plugin-vue2或vite-plugin-vue3(如果你使用的是Vite)。这里我们假设你使用的是Vite构建工具。打开终端并运行以下命令以安装所需的依赖包:npminsta
vue基础(十)
Goodbaibaibai
面试 vue.js
在Vue中,vue-router是官方的路由管理插件,用于在单页面应用(SPA)中实现不同页面的切换。在此介绍Vue2和Vue3中路由的使用。1.安装vue-router如果是Vue2:npminstallvue-router@3如果是Vue3:npminstallvue-router@42.基本路由配置创建router/index.js作为路由配置文件。Vue2#路由文件importVuefro
Python教学-最常用的标准库之一——OS库
懒大王爱吃狼
python python 服务器 开发语言 Python基础 python学习 python爬虫
os库是Python标准库中的一个模块,它提供了一种方便的方式来使用操作系统相关的功能。os模块提供了很多函数,可以用来处理文件和目录、访问环境变量、执行系统命令等。以下是一些常用的os模块的功能和示例:1.文件和目录操作1.1当前工作目录os.getcwd():获取当前工作目录。os.chdir(path):改变当前工作目录。importoscurrent_dir=os.getcwd()prin
智能图像处理平台:技术融合与实践的结晶
顾北辰20
智能图像处理平台 图像处理 人工智能
目录项目目标项目功能用户模块图像处理模块异步任务模块技术栈数据库设计核心功能实现方式图片上传图片处理异步任务项目扩展近日DeepSeek很火,我让他给我出了个小项目练练手,带大家一起做一下,可做为毕业设计参考。在当今数字化时代,图像处理技术广泛应用于各个领域,从医学影像到社交媒体,其重要性不言而喻。本文将介绍一个智能图像处理平台的练手项目,旨在巩固Java、MySQL、SpringBoot、Red
webpack 项目优化(一)
霹雳桃
webpack 前端 node.js
一、构建速度优化缩小文件处理范围module:{rules:[{test:/\.js$/,exclude:/node_modules/,//排除第三方库include:path.resolve(__dirname,'src'),//限定处理范围use:'babel-loader'}]}利用缓存Webpack5+内置持久化缓存(直接配置):cache:{type:'filesystem'}//大幅提
银河麒麟V10SP1下qt5-12-12编译环境配置
洛阳鱼紫怡
qt
下面的脚本在兆芯版kylin上试过,如果是本地编译,arm版应该也没有问题a.如果执行脚本安装有问题,读者可以拷贝命令多次安装b.建议在虚拟机环境下进行#!/bin/bashsudoaptupdatesudoapt-getinstallg++gccmakecmakegperfbisonflexlibdrm-devlibxcomposite-devsudoapt-getinstalllibxcurs
NVRAM配置文件,Flash系统映像,BootROM硬件初始值,SDRAM内存
顺漆自然
智能路由器
企业级路由器的初始配置文件通常保存在NVRAM(非易失性随机存取存储器)上。因此,本题的答案是B.NVRAM。NVRAM是一种特殊的内存类型,它可以在系统关闭或断电的情况下保持数据。路由器启动时,会从ROM中的Bootstrap程序开始,然后查找NVRAM中的配置文件以继续启动过程。这样可以确保即使在系统关闭或断电后,路由器也能恢复到其先前的配置状态。正确答案是B.NVRAM(非易失性随机访问存储
前端构建工具——Webpack和Vite的主要区别
像素检测仪
前端 前端 webpack
目录1.设计理念2.性能表现3.使用场景4.配置复杂度5.生态系统6.性能对比总结7.选择建议1.设计理念Webpack设计理念:Webpack是一个通用的模块打包工具,它将项目中的各种资源(如JavaScript、CSS、图片等)视为模块,并通过loader和plugin进行处理和打包。适用范围:适用于复杂项目,支持高度定制化,适合需要精细控制构建过程的场景。Vite设计理念:Vite(法语“快
python tkinter 自制文本编辑器
ocean35
tkinter python 文本编辑器 tkinter python
成品如图:代码fromtkinterimport*fromtkinterimportfiledialogfromtkinterimportmessageboximporttimedefnodefined():pass#打开文件函数defopenfile():filename=filedialog.askopenfilename()f=open(filename,'r')f2=f.read()f.c
ubuntu下安装TFTP服务器
luoqice
linux编程技巧 linux 服务器
在Ubuntu系统下安装和配置TFTP(TrivialFileTransferProtocol)服务器可以按照以下步骤进行:1.安装TFTP服务器软件包TFTP服务器通常使用tftpd-hpa软件包,你可以使用以下命令进行安装:sudoaptupdatesudoaptinstalltftpd-hpa2.配置TFTP服务器安装完成后,需要对TFTP服务器进行配置。配置文件位于/etc/default
Linux 固定 IP 地址和网关
法号:行颠
Linux linux
Linux固定IP地址和网关查看IPifconfigifconfigeth0ipaddripaddrshoweth0查看网关iprouteshowroute-nnetstat-rn设置固定IP//配置静态IP文件/etc/network/interfaces$vi/etc/network/interfacesautoeth0ifaceeth0inetstaticaddress192.168.0.2
KubeEdge 1.20.0 版本发布
laowangsen
数据库
KubeEdge1.20.0现已发布。新版本针对大规模、离线等边缘场景对边缘节点和应用的管理、运维等能力进行了增强,同时新增了多语言Mapper-Framework的支持。新增特性:支持批量节点操作多语言Mapper-Framework支持边缘keadmctl新增podslogs/exec/describe和Devicesget/edit/describe能力解耦边缘应用与节点组,支持使用Node
芯片与寄存器
jhb222
stm32
芯片与寄存器作为一个嵌入式开发人员,从接触第一块单片机的时候,就一定会听到一个此,叫做寄存器。单片机的很多功能都是通过配置修改寄存器来实现的。我们了解寄存器,也读写过很多的寄存器一、操作过得寄存器先回顾一下我们以前操作过得寄存器:这是一段51单片机初始化定时器的代码voidTIME_Init(){TMOD=0x11; TL0=0; TH0=0;TL1=0;
利用javaCV 将视频转为m3u8格式
daqinzl
流媒体 视频音频 ffmpeg javacv 视频转m3u8
参考链接https://blog.csdn.net/laow1314/article/details/128797915
在IntelliJIDEA中使用Gradle创建Web项目的步骤
大G哥
前端 intellij-idea java ide
1.打开IntelliJIDEA启动IntelliJIDEA。2.创建新项目点击File>New>Project。在弹出的窗口中,选择Gradle作为项目类型。确保Java被选中。点击Next。3.配置项目GroupId:输入你的组织或项目组ID(例如:com.example)。ArtifactId:输入项目名称(例如:mywebapp)。点击Next。4.配置项目名称和位置Projectname
Android VTS单模块测试问题处理
漫步的傻瓜
android 模块测试
目录1编译方式2.执行vts测试异常问题处理2.1解决办法2.2vts单模块测试正常运行的结果3编译报错:error:'xxxCallback::onCallback'hidesoverloadedvirtualfunction问题处理1编译方式在hardware模块下vts目录,编译命令举例如下:sourcebuild/envsetup.shlunch//这里选择要编译的项目mmmhardwar
Android系统开机时间优化-实践篇(一)
漫步的傻瓜
Android系统启动时间优化 android linux
Android系统开机时间优化目录背景正文优化内容小结产品功能:高清大屏、多路摄像头、蓝牙、WIFI、4G无线网络、收音机、语音识别等等。背景主芯片是多核处理器,高版本Android系统,启动时间相比android4.x的十几秒慢很多。优化前的状态:处理前已被优化的内容有:裁剪多余的原生apk和资源文件、部分耗时动作等,并修改log输出等级。这种情况下,启动时间,想比原生系统有较大改善,但不够理想
Webpack 基础入门
懒羊羊我小弟
前端工程化 webpack rust 前端 es6 node.js
一、Webpack是什么Webpack是一款现代JavaScript应用程序的静态模块打包工具。在Web开发中,我们的项目会包含各种类型的文件,如JavaScript、CSS、图片等。Webpack可以将这些文件打包成一个或多个文件,以便在浏览器中高效加载。它就像是一个超级管家,把项目中的各种资源整理打包,让它们能更好地协同工作。二、为什么要使用Webpack代码拆分:可以将代码拆分成多个块,实现
JAVA中的Enum
周凡杨
java enum 枚举
Enum是计算机编程语言中的一种数据类型---枚举类型。 在实际问题中,有些变量的取值被限定在一个有限的范围内。 例如,一个星期内只有七天 我们通常这样实现上面的定义:
public String monday;
public String tuesday;
public String wensday;
public String thursday
赶集网mysql开发36条军规
Bill_chen
mysql 业务架构设计 mysql调优 mysql性能优化
(一)核心军规 (1)不在数据库做运算 cpu计算务必移至业务层; (2)控制单表数据量 int型不超过1000w,含char则不超过500w; 合理分表; 限制单库表数量在300以内; (3)控制列数量 字段少而精,字段数建议在20以内
Shell test命令
daizj
shell 字符串 test 数字 文件比较
Shell test命令
Shell中的 test 命令用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试。 数值测试 参数 说明 -eq 等于则为真 -ne 不等于则为真 -gt 大于则为真 -ge 大于等于则为真 -lt 小于则为真 -le 小于等于则为真
实例演示:
num1=100
num2=100if test $[num1]
XFire框架实现WebService(二)
周凡杨
java webservice
有了XFire框架实现WebService(一),就可以继续开发WebService的简单应用。
Webservice的服务端(WEB工程):
两个java bean类:
Course.java
package cn.com.bean;
public class Course {
private
重绘之画图板
朱辉辉33
画图板
上次博客讲的五子棋重绘比较简单,因为只要在重写系统重绘方法paint()时加入棋盘和棋子的绘制。这次我想说说画图板的重绘。
画图板重绘难在需要重绘的类型很多,比如说里面有矩形,园,直线之类的,所以我们要想办法将里面的图形加入一个队列中,这样在重绘时就
Java的IO流
西蜀石兰
java
刚学Java的IO流时,被各种inputStream流弄的很迷糊,看老罗视频时说想象成插在文件上的一根管道,当初听时觉得自己很明白,可到自己用时,有不知道怎么代码了。。。
每当遇到这种问题时,我习惯性的从头开始理逻辑,会问自己一些很简单的问题,把这些简单的问题想明白了,再看代码时才不会迷糊。
IO流作用是什么?
答:实现对文件的读写,这里的文件是广义的;
Java如何实现程序到文件
No matching PlatformTransactionManager bean found for qualifier 'add' - neither
林鹤霄
java.lang.IllegalStateException: No matching PlatformTransactionManager bean found for qualifier 'add' - neither qualifier match nor bean name match!
网上找了好多的资料没能解决,后来发现:项目中使用的是xml配置的方式配置事务,但是
Row size too large (> 8126). Changing some columns to TEXT or BLOB
aigo
column
原文:http://stackoverflow.com/questions/15585602/change-limit-for-mysql-row-size-too-large
异常信息:
Row size too large (> 8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAM
JS 格式化时间
alxw4616
JavaScript
/**
* 格式化时间 2013/6/13 by 半仙 alxw4616@msn.com
* 需要 pad 函数
* 接收可用的时间值.
* 返回替换时间占位符后的字符串
*
* 时间占位符:年 Y 月 M 日 D 小时 h 分 m 秒 s 重复次数表示占位数
* 如 YYYY 4占4位 YY 占2位<p></p>
* MM DD hh mm
队列中数据的移除问题
百合不是茶
队列移除
队列的移除一般都是使用的remov();都可以移除的,但是在昨天做线程移除的时候出现了点问题,没有将遍历出来的全部移除, 代码如下;
//
package com.Thread0715.com;
import java.util.ArrayList;
public class Threa
Runnable接口使用实例
bijian1013
java thread Runnable java多线程
Runnable接口
a. 该接口只有一个方法:public void run();
b. 实现该接口的类必须覆盖该run方法
c. 实现了Runnable接口的类并不具有任何天
oracle里的extend详解
bijian1013
oracle 数据库 extend
扩展已知的数组空间,例:
DECLARE
TYPE CourseList IS TABLE OF VARCHAR2(10);
courses CourseList;
BEGIN
-- 初始化数组元素,大小为3
courses := CourseList('Biol 4412 ', 'Psyc 3112 ', 'Anth 3001 ');
--
【httpclient】httpclient发送表单POST请求
bit1129
httpclient
浏览器Form Post请求
浏览器可以通过提交表单的方式向服务器发起POST请求,这种形式的POST请求不同于一般的POST请求
1. 一般的POST请求,将请求数据放置于请求体中,服务器端以二进制流的方式读取数据,HttpServletRequest.getInputStream()。这种方式的请求可以处理任意数据形式的POST请求,比如请求数据是字符串或者是二进制数据
2. Form
【Hive十三】Hive读写Avro格式的数据
bit1129
hive
1. 原始数据
hive> select * from word;
OK
1 MSN
10 QQ
100 Gtalk
1000 Skype
2. 创建avro格式的数据表
hive> CREATE TABLE avro_table(age INT, name STRING)STORE
nginx+lua+redis自动识别封解禁频繁访问IP
ronin47
在站点遇到攻击且无明显攻击特征,造成站点访问慢,nginx不断返回502等错误时,可利用nginx+lua+redis实现在指定的时间段 内,若单IP的请求量达到指定的数量后对该IP进行封禁,nginx返回403禁止访问。利用redis的expire命令设置封禁IP的过期时间达到在 指定的封禁时间后实行自动解封的目的。
一、安装环境:
CentOS x64 release 6.4(Fin
java-二叉树的遍历-先序、中序、后序(递归和非递归)、层次遍历
bylijinnan
java
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
public class BinTreeTraverse {
//private int[] array={ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
private int[] array={ 10,6,
Spring源码学习-XML 配置方式的IoC容器启动过程分析
bylijinnan
java spring IOC
以FileSystemXmlApplicationContext为例,把Spring IoC容器的初始化流程走一遍:
ApplicationContext context = new FileSystemXmlApplicationContext
("C:/Users/ZARA/workspace/HelloSpring/src/Beans.xml&q
[科研与项目]民营企业请慎重参与军事科技工程
comsci
企业
军事科研工程和项目 并非要用最先进,最时髦的技术,而是要做到“万无一失”
而民营科技企业在搞科技创新工程的时候,往往考虑的是技术的先进性,而对先进技术带来的风险考虑得不够,在今天提倡军民融合发展的大环境下,这种“万无一失”和“时髦性”的矛盾会日益凸显。。。。。。所以请大家在参与任何重大的军事和政府项目之前,对
spring 定时器-两种方式
cuityang
spring quartz 定时器
方式一:
间隔一定时间 运行
<bean id="updateSessionIdTask" class="com.yang.iprms.common.UpdateSessionTask" autowire="byName" />
<bean id="updateSessionIdSchedule
简述一下关于BroadView站点的相关设计
damoqiongqiu
view
终于弄上线了,累趴,戳这里http://www.broadview.com.cn
简述一下相关的技术点
前端:jQuery+BootStrap3.2+HandleBars,全站Ajax(貌似对SEO的影响很大啊!怎么破?),用Grunt对全部JS做了压缩处理,对部分JS和CSS做了合并(模块间存在很多依赖,全部合并比较繁琐,待完善)。
后端:U
运维 PHP问题汇总
dcj3sjt126com
windows2003
1、Dede(织梦)发表文章时,内容自动添加关键字显示空白页
解决方法:
后台>系统>系统基本参数>核心设置>关键字替换(是/否),这里选择“是”。
后台>系统>系统基本参数>其他选项>自动提取关键字,这里选择“是”。
2、解决PHP168超级管理员上传图片提示你的空间不足
网站是用PHP168做的,反映使用管理员在后台无法
mac 下 安装php扩展 - mcrypt
dcj3sjt126com
PHP
MCrypt是一个功能强大的加密算法扩展库,它包括有22种算法,phpMyAdmin依赖这个PHP扩展,具体如下:
下载并解压libmcrypt-2.5.8.tar.gz。
在终端执行如下命令: tar zxvf libmcrypt-2.5.8.tar.gz cd libmcrypt-2.5.8/ ./configure --disable-posix-threads --
MongoDB更新文档 [四]
eksliang
mongodb Mongodb更新文档
MongoDB更新文档
转载请出自出处:http://eksliang.iteye.com/blog/2174104
MongoDB对文档的CURD,前面的博客简单介绍了,但是对文档更新篇幅比较大,所以这里单独拿出来。
语法结构如下:
db.collection.update( criteria, objNew, upsert, multi)
参数含义 参数
Linux下的解压,移除,复制,查看tomcat命令
y806839048
tomcat
重复myeclipse生成webservice有问题删除以前的,干净
1、先切换到:cd usr/local/tomcat5/logs
2、tail -f catalina.out
3、这样运行时就可以实时查看运行日志了
Ctrl+c 是退出tail命令。
有问题不明的先注掉
cp /opt/tomcat-6.0.44/webapps/g
Spring之使用事务缘由(3-XML实现)
ihuning
spring
用事务通知声明式地管理事务
事务管理是一种横切关注点。为了在 Spring 2.x 中启用声明式事务管理,可以通过 tx Schema 中定义的 <tx:advice> 元素声明事务通知,为此必须事先将这个 Schema 定义添加到 <beans> 根元素中去。声明了事务通知后,就需要将它与切入点关联起来。由于事务通知是在 <aop:
GCD使用经验与技巧浅谈
啸笑天
GC
前言
GCD(Grand Central Dispatch)可以说是Mac、iOS开发中的一大“利器”,本文就总结一些有关使用GCD的经验与技巧。
dispatch_once_t必须是全局或static变量
这一条算是“老生常谈”了,但我认为还是有必要强调一次,毕竟非全局或非static的dispatch_once_t变量在使用时会导致非常不好排查的bug,正确的如下: 1
linux(Ubuntu)下常用命令备忘录1
macroli
linux 工作 ubuntu
在使用下面的命令是可以通过--help来获取更多的信息1,查询当前目录文件列表:ls
ls命令默认状态下将按首字母升序列出你当前文件夹下面的所有内容,但这样直接运行所得到的信息也是比较少的,通常它可以结合以下这些参数运行以查询更多的信息:
ls / 显示/.下的所有文件和目录
ls -l 给出文件或者文件夹的详细信息
ls -a 显示所有文件,包括隐藏文
nodejs同步操作mysql
qiaolevip
学习永无止境 每天进步一点点 mysql nodejs
// db-util.js
var mysql = require('mysql');
var pool = mysql.createPool({
connectionLimit : 10,
host: 'localhost',
user: 'root',
password: '',
database: 'test',
port: 3306
});
一起学Hive系列文章
superlxw1234
hive Hive入门
[一起学Hive]系列文章 目录贴,入门Hive,持续更新中。
[一起学Hive]之一—Hive概述,Hive是什么
[一起学Hive]之二—Hive函数大全-完整版
[一起学Hive]之三—Hive中的数据库(Database)和表(Table)
[一起学Hive]之四-Hive的安装配置
[一起学Hive]之五-Hive的视图和分区
[一起学Hive
Spring开发利器:Spring Tool Suite 3.7.0 发布
wiselyman
spring
Spring Tool Suite(简称STS)是基于Eclipse,专门针对Spring开发者提供大量的便捷功能的优秀开发工具。
在3.7.0版本主要做了如下的更新:
将eclipse版本更新至Eclipse Mars 4.5 GA
Spring Boot(JavaEE开发的颠覆者集大成者,推荐大家学习)的配置语言YAML编辑器的支持(包含自动提示,