Logstash 实时收集 Nginx 日志
说明
logstash 需要和 nginx 部署到一台机器
需要修改 nginx 的日志格式
nginx.config
更改日志记录的格式
1 | log_format json '{ "@timestamp": "$time_iso8601", ' |
log-file.config
input 里添加 file 类型
1 | input { |
logstash 需要和 nginx 部署到一台机器
需要修改 nginx 的日志格式
更改日志记录的格式
1 | log_format json '{ "@timestamp": "$time_iso8601", ' |
input 里添加 file 类型
1 | input { |
略
1 | input { |
总的来说,配置文件里由 input,filter,output,这里我没有特别复杂的需求,filter就没有配置
我这里有三个input,但是都是 tcp 类型的
意思配置了三个input,分别监听192.168.10.78(就是安装logstash的机器)的4560,4561,和4568端口,有数据发送过来的话就进行output处理
这里我配置了3个type,这个type也就是elasticsearch里索引的type,并且该type可作为参数在output里判断进行不同的处理
codec 是的对日志数据进行处理的插件,这里是 json_lines
所以需要安装插件
1 | sh bin/logstash-plugin install logstash-codec-json_lines |
elasticsearch:hosts es的http地址和端口
index 是创建的索引名
如果要配置索引模板的话,可以添加以下配置
manage_template => true
template_name => “template_name”
template_overwrite => true
template => “/usr/local/path.json”
配置好了,我们检验下配置文件是否正确
1 | sh /app/logstash-5.3.0/bin/logstash -f /app/logstash-5.3.0/config/log.config -t |
没有问题的话就可启动了,后台启动的就用 nohup
1 | sh /app/logstash-5.3.0/bin/logstash -f /app/logstash-5.3.0/config/log.config |
启动成功的话,9600端口可以获取到 logstash 的相关信息
1 | <dependency> |
1 | <?xml version="1.0" encoding="UTF-8"?> |
这里我是使用的是 SpringBoot 自带的 logback 日志
SpringBoot 默认会读取 resources 目录下的 logback.xml 作为配置文件,别问我怎么知道的(我特地查看了源码:org.springframework.boot.logging.logback.LogbackLoggingSystem,”logback-test.groovy”, “logback-test.xml”, “logback.groovy”, “logback.xml”这些文件检测到都会读取其中的配置的)
配置文件里我只配置了 一个Appender,就是net.logstash.logback.appender.LogstashTcpSocketAppender,用来输出日志到logstash的,并且级别是 INFO
destination 指的就是 logstash 的地址
encoder 就配置LogstashEncoder不要变
再把 SpringBoot默认的配置引入base.xml
好了,SpringBoot 集成 Logstash 完毕
注 :后来我想用 javaConfig 去配置 SpringBoot和Logstash,不过没有成功,哪位大佬看到这个信息,可以给我留言下怎么配置
xml,也很方便,打包部署后可以作为配置文件修改
那么,这个时候启动项目,elasticsearch里面就会看到有新的索引数据了
1 | cluster.name: joylau-es |
剩下没贴出配置的都是默认配置
依照改配置,在各个节点上修改节点名称及network.publish_host,要保证集群名称一样就可以了。
主要配置
-Xms1400m
-Xmx1400m
我这里的机器是2G的运存,经过我的反复调试,能给出elasticsearch最大的内存空间就是1400m了,给多了跑步起来,给少了有不能完全发挥elasticsearch的性能优势
机器差,没办法
还有一点注意的是初始化内存大小个最大内存大小的配置数值要是一样的,否则会启动出错
GarageBand,这个是系统上的模拟乐器,一般都使用不到
1 | rm -rf /Library/Application\ Support/GarageBand |
但是有些系统文件显示占用的空间很大,该怎么看呢
1 | du -sh * |
这个命令用来查看根目录下,所有文件的大小分布
比如,我的电脑 Library 文件路径最大
那就在进入 Library 文件路径,再执行 du -sh *
直至找到占用内存最大的文件,然后结合实际情况,进行删减
在 SpringSecurity 中,我想配置一个关于session并发的控制,于是我是这样配置的
1 | @Override |
上下文的配置我在此省略了
这里设置 maximumSessions 为 -1,表示不限制同一账号登录的客户端数
session过期后执行的逻辑是进入我自定义的类 expiredSessionStrategy() 中
因为我是构建的 rest 服务,所以我是返回的 http 状态码
1 | public class ExpiredSessionStrategyImpl implements SessionInformationExpiredStrategy { |
在这里,问题就来了
我测试的时候,把 -1 改成了 1,之后登录同一个用户,后面登录的用户会把前面一个已经登录的用户挤下线,就是说之前登录的那个用户的session 会过期
就是说他所在的页面再发送任何请求的话会收到我返回的 405 状态码
在这里是没问题的
问题就在发完一个请求后,在发一个请求,在浏览器的 network 上会看到发出的请求会被重定向的 /login 请求上
后续再发任何请求都会被重定向到 /login 上
为什么会出现这样的情况呢?
为什么会第一个请求会收到405的状态码,后续的请求会被重定向到 /login 呢?
通过 debug 断点,我定位到过滤器的前置执行方法 beforeInvocation() 上
1 | protected InterceptorStatusToken beforeInvocation(Object object) { |
问题出在了 SecurityContextHolder.getContext().getAuthentication() == null
getAuthentication() 为 null,于是进入了credentialsNotFound(),抛出了 AuthenticationCredentialsNotFoundException 异常
确实,在控制台上也能看到抛出的异常信息
AuthenticationCredentialsNotFoundException 是 AuthenticationException 异常的子类
不仅仅是 AuthenticationCredentialsNotFoundException 还有其他很多异常都是异常的子类
既然抛出了异常,猜测肯定是被某个处理器给处理了而且处理的默认机制是重定向到 /login
于是继续搜索 SpringSecurity 异常处理器
我找到的答案是 ExceptionTranslationFilter
ExceptionTranslationFilter 是Spring Security的核心filter之一,用来处理AuthenticationException和AccessDeniedException两种异常(由FilterSecurityInterceptor认证请求返回的异常)
ExceptionTranslationFilter 对异常的处理是通过这两个处理类实现的,处理规则很简单:
规则1. 如果异常是 AuthenticationException,使用 AuthenticationEntryPoint 处理
规则2. 如果异常是 AccessDeniedException 且用户是匿名用户,使用 AuthenticationEntryPoint 处理
规则3. 如果异常是 AccessDeniedException 且用户不是匿名用户,如果否则交给 AccessDeniedHandler 处理。
1 | private void handleSpringSecurityException(HttpServletRequest request, |
我们这里的异常是 AuthenticationException ,紧接着就找 sendStartAuthentication() 方法
1 | protected void sendStartAuthentication(HttpServletRequest request, |
上面的方法是先保存请求,之后执行 authenticationEntryPoint.commence(request, response, reason), 再深入来看
默认实现 commence 接口的是 LoginUrlAuthenticationEntryPoint 类
1 | public void commence(HttpServletRequest request, HttpServletResponse response, |
我们看到了 redirectUrl = buildRedirectUrlToLoginPage(request, response, authException)
这下总算是知道了为什么会重定向了 /login 请求了
知道问题的原因了,解决问题就很简单了,重新实现 commence 接口,返回http 状态码就可以了,于是加上这样的配置
1 | @Override |
1 | public class UnauthenticatedEntryPoint implements AuthenticationEntryPoint { |
再次重试,发现会返回 405状态码了,不会在重定向到 /login 了
问题解决
1 | <dependency> |
1 | @EqualsAndHashCode(callSuper = true) |
1 | public class UserService implements UserDetailsService { |
1 | @Data |
1 | public class AuthorizationFilter extends AbstractSecurityInterceptor implements Filter { |
1 | public class AuthorizationAccessDecisionManager implements AccessDecisionManager { |
1 | public class AuthorizationMetadataSource implements FilterInvocationSecurityMetadataSource { |
1 | public class SecurityUtils { |
主要的实现类都列举在内了,还有一些成功和失败的处理类,再次没有列举出来
因为该项目为构建纯restful风格的后台项目,这些成功或失败的处理类基本都是返回的http状态码
首先需要说明一点 ,和 Example 使用相同的还有 Condition 类 该类继承自 Example,使用方法和 Example 完全一样,只是为了避免语义有歧义重命名的一个类,这里我们都用 Example 来说明
1 | Example example = new Example(XXX.class); |
其中构造方法为生成的 model 实体类,还有 2 个构造方法
1 |
|
然后可以对 example 的实体类的单表进行查询了
1 | Example example = new Example(XXX.class); |
以上查询的条件是,查询 id 大于 100 并且小于 151 或者 id 小于 41 的记录
还可以写成 sql 的方式:
1 | Example example = new Example(XXX.class); |
andCondition() 有2中使用方法:
andCondition(String condition) : 手写条件,例如 “length(name)<5”
andCondition(String condition, Object value) : 手写左边条件,右边用value值,例如 “length(name)=” “5”
orCondition() 也是类似的
example 里有很多 mysql 常用的方法,使用方法和 elasticsearch 的 java api 很类似,这里列举几个
Set<String> selectColumns
: 查询的字段Set<String> excludeColumns
: 排除的查询字段Map<String, EntityColumn> propertyMap
: 属性和列对应还有一些一看就知道意思的
上面是以 and 条件举例 ,or的条件也是一样的
我们知道 PageHelper.startPage(pageNum, pageSize); 可以对 后面的一个 select 进行分页
那么我们可以对 example 进行一个分页查询的封装
1 |
|
SpringBoot 默认有2种打包方式,一种是直接打成 jar 包,直接使用 java -jar 跑起来,另一种是打成 war 包,移除掉 web starter 里的容器依赖,然后丢到外部容器跑起来。
第一种方式的缺点是整个项目作为一个 jar,部署到生产环境中一旦有配置文件需要修改,则过程比较麻烦
linux 下可以使用 vim jar 包,找到配置文件修改后再保存
window 下需要使用 解压缩软件打开 jar 再找到配置文件,修改后替换更新
第二种方式的缺点是需要依赖外部容器,这无非多引入了一部分,很多时候我们很不情愿这么做
spring boot 项目启动时 指定配置有2种方式:一种是启动时修改配置参数,像 java -jar xxxx.jar –server.port=8081 这样;另外一种是 指定外部配置文件加载,像 java -jar xxxx.jar -Dspring.config.location=applixxx.yml这样
我们希望打包成 tomcat 或者 maven 那样的软件包结构,即
--- bin
--- start.sh
--- stop.sh
--- restart.sh
--- start.bat
--- stop.bat
--- restart.bat
--- boot
--- xxxx.jar
--- lib
--- conf
--- logs
--- README.md
--- LICENSE
就像这样
bin
目录放一些我们程序的启动停止脚本boot
目录放我们自己的程序包lib
目录是我们程序的依赖包conf
目录是项目的配置文件logs
目录是程序运行时的日志文件README.md
使用说明LICENSE
许可说明1 | <build> |
下面重要的是 assembly.xml 配置文件了,这个文件才是把我们的程序打成标准的目录结构
1 | <assembly> |