目录
一、日志系统
日志系统是基于ELK + Storm完成,解决分布式中多节点日志处理。Nginx日志也纳入到日志系统处理,可以解决Nginx问题查看、总流量、本地缓存命中率等问题。
Nginx的日志分为两方面:access_log与error_log。其中access_log可以自定义格式及内容;而error_log是基于Linux系统日志(不方便修改)。所以导致两种日志格式不同,但不影响filebeat对打印日志的收集。
随着Nginx的日志量变大,需要Linux的定时任务crondtab,定时分割Nginx日志(按天分割),这样查询本地日志很方便。
二、Nginx日志
1. access_log
语法:access_log path [format [buffer=size [flush=time]]];
默认值:access_log logs/access.log combined;
配置段:http, server,location;
http {
# 定义日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
log_format json '$time_iso8601 | {"timestamp": "$time_iso8601", '
'"remoteAddr": "$remote_addr", '
'"remoteUser": "$remote_user", '
'"bodyBytesSent": "$body_bytes_sent", '
'"requestTime": "$request_time", '
'"upstreamResponseTime": "$upstream_response_time", '
'"status": "$status", '
'"host": "$host", '
'"request": "$request", '
'"requestMethod": "$request_method", '
'"uri": "$uri", '
'"httpReferrer": "$http_referer", '
'"size": "$body_bytes_sent", '
'"userIp": "$http_x_forwarded_for", '
'"mobile": "$http_Mobile", '
'"token": "$http_UserToken", '
'"channel": "$http_Channel", '
'"appVersion": "$http_AppVersion", '
'"userAgent": "$http_user_agent" '
'}';
# 定义访问日志
access_log logs/access.log json;
# 关闭访问记录
# access_log off;
}
2. error_log
语法:error_log file | stderr | syslog:server=address[,parameter=value] [debug | info | notice |
warn | error | crit | alert | emerg];
默认值:error_log logs/error.log error;
配置段:main,http,server,location;
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
http {
# 自定义日志
error_log logs/LOG_COMMON.log debug;
error_log logs/LOG_INFO.log info;
}
三、Nginx日志分割
Nginx长期使用,导致Nginx日志文件越来越大打开很慢,这就需要日志分割。思路是Linux定时器定时执行启动Nginx,复制日志到其他文件中即可。
1. 分割脚本nginxLogRotate.sh
#!/bin/bash
#设置日志文件存放目录
LOG_HOME="/usr/local/openresty/nginx/logs"
PID=$LOG_HOME/nginx.pid
#备分文件名称
LOG_DATE=$(date -d yesterday +"%Y-%m-%d-%H-%M")
# 获取当前年信息和月信息
LOG_DIR=$(date -d yesterday +"%Y%m")
# 获取昨天的日信息
# day=$(date -d yesterday +"%d")
# 按年月创建文件夹
mkdir -p $LOG_HOME/$LOG_DIR
#重命名日志文件
mv $LOG_HOME/access.log $LOG_HOME/$LOG_DIR/access.$LOG_DATE.log
#等3秒后执行下一条
sleep 3
mv $LOG_HOME/error.log $LOG_HOME/$LOG_DIR/error.$LOG_DATE.log
sleep 3
mv $LOG_HOME/LOG_COMMON.log $LOG_HOME/$LOG_DIR/LOG_COMMON.$LOG_DATE.log
sleep 3
mv $LOG_HOME/LOG_INFO.log $LOG_HOME/$LOG_DIR/LOG_INFO.$LOG_DATE.log
sleep 3
# 通过Nginx信号量控制重读日志
kill -USR1 `cat $PID`
2. Linux安装crontab
yum install vixie-cron crontabs
3. 添加定时任务
修改定时任务:crontab -e
任务内容:定时启动nginx:*/2 * * * * /usr/local/openresty/nginx/logs/nginxLogRotate.sh
查看所有定时任务:crontab -l
4. 启动定时任务并验证
crond start/stop/reload/restart
四、Nginx日志纳入日志系统
1. 定义日志文件
访问日志:access_log logs/access.log json;
错误日志:error_log logs/error.log error;
通用日志:error_log logs/LOG_COMMON.log debug;
INFO日志:error_log logs/LOG_INFO.log info;
注意:access_log可以自定义格式,但error_log不能;LOG_COMMON是通用日志,不搜集。
2. access_log日志格式及内容
log_format json '$time_iso8601 | {"timestamp": "$time_iso8601", '
'"remoteAddr": "$remote_addr", '
'"remoteUser": "$remote_user", '
'"bodyBytesSent": "$body_bytes_sent", '
'"requestTime": "$request_time", '
'"upstreamResponseTime": "$upstream_response_time", '
'"status": "$status", '
'"host": "$host", '
'"request": "$request", '
'"requestMethod": "$request_method", '
'"uri": "$uri", '
'"httpReferrer": "$http_referer", '
'"size": "$body_bytes_sent", '
'"userIp": "$http_x_forwarded_for", '
'"mobile": "$http_Mobile", '
'"token": "$http_UserToken", '
'"channel": "$http_Channel", '
'"appVersion": "$http_AppVersion", '
'"userAgent": "$http_user_agent" '
'}';
3. filebeat配置
# Nginx日志
- type: log
enabled: true
paths:
- /usr/local/openresty/nginx/logs/access.log
fields:
log_type: NGINX_LOG_ACCESS
- type: log
enabled: true
paths:
- /usr/local/openresty/nginx/logs/error.log
fields:
log_type: NGINX_LOG_ERROR
- type: log
enabled: true
paths:
- /usr/local/openresty/nginx/logs/LOG_INFO.log
fields:
log_type: NGINX_LOG_INFO
4. storm解析日志
a. 实体类
package com.migu.storm.entity.nginx;
import com.migu.storm.entity.LogFileBeatContent;
import lombok.Data;
/**
* @description Nginx日志FileBeat信息
* @author tcm
* @version 1.0.0
* @date 2022/1/25 10:02
**/
@Data
public class NginxLogFileBeat {
// 日志FileBeat信息
private LogFileBeatContent logFileBeatContent;
}
package com.migu.storm.entity.nginx;
import lombok.Data;
/**
* @description Nginx的access日志
* @author tcm
* @version 1.0.0
* @date 2022/1/25 9:26
**/
@Data
public class NginxLogAccess extends NginxLogFileBeat {
// 请求时间戳
private String timestamp;
// 请求IP
private String remoteAddr;
// 请求用户
// private String remoteUser;
// 请求服务器主机:IP + PORT
private String host;
// 请求信息:请求方式 + 请求URI + HTTP版本号
private String request;
// 请求方式
private String requestMethod;
// 请求URI
private String uri;
// 请求引用页面地址
// private String httpReferrer;
// 用户真实IP地址
// private String userIp;
// 手机号
private String mobile;
// token
private String token;
// 终端类型
private String channel;
// 版本号
private String appVersion;
// 请求代理
private String userAgent;
// 从upstream获取响应的处理时间
private String upstreamResponseTime;
// 响应状态
private String status;
// 响应体字节数
private String bodyBytesSent;
// 请求处理时间
private String requestTime;
// 日志内容
private String message;
}
package com.migu.storm.entity.nginx;
import lombok.Data;
/**
* @description Nginx的error日志
* @author tcm
* @version 1.0.0
* @date 2022/1/25 10:07
**/
@Data
public class NginxLogError extends NginxLogFileBeat {
// 时间戳
private String timestamp;
// 日志级别
private String logLevel;
// 日志内容
private String message;
}
package com.migu.storm.entity.nginx;
import lombok.Data;
/**
* @description Nginx的INFO日志
* @author tcm
* @version 1.0.0
* @date 2022/1/25 10:16
**/
@Data
public class NginxLogInfo extends NginxLogFileBeat{
// 时间戳
private String timestamp;
// 日志级别
private String logLevel;
// 日志内容
private String message;
}
b. Bolt类
package com.migu.storm.bolt;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.migu.storm.config.ESConfig;
import com.migu.storm.entity.LogFileBeatContent;
import com.migu.storm.entity.nginx.NginxLogAccess;
import com.migu.storm.entity.nginx.NginxLogInfo;
import com.migu.storm.entity.nginx.NginxLogError;
import com.migu.storm.enumeration.NginxLogType;
import com.migu.storm.util.DateUtil;
import org.apache.storm.task.TopologyContext;
import org.apache.storm.topology.BasicOutputCollector;
import org.apache.storm.topology.OutputFieldsDeclarer;
import org.apache.storm.topology.base.BaseBasicBolt;
import org.apache.storm.tuple.Tuple;
import org.elasticsearch.action.bulk.BulkProcessor;
import org.elasticsearch.action.index.IndexRequest;
import java.util.Date;
import java.util.Map;
import java.util.Properties;
/**
* @description 索引index格式:nginx-日期,如:nginx-2021-03-19
* @author tcm
* @version 1.0.0
* @date 2022/1/25 10:29
**/
public class NginxLogESBolt extends BaseBasicBolt {
private Properties properties;
// ES批量写入:线程安全的批量处理类
private BulkProcessor bulkProcessor;
public NginxLogESBolt(Properties properties) {
this.properties = properties;
}
@Override
public void execute(Tuple tuple, BasicOutputCollector collector) {
try {
// 日志公用字段
String logFileBeatStr = tuple.getStringByField("logFileBeatContent");
LogFileBeatContent logFileBeatContent = JSON.parseObject(logFileBeatStr, LogFileBeatContent.class);
// 日志内容
String message = tuple.getStringByField("message");
// 索引 - 服务应用名称
Date timestamp = new Date();
String index = properties.getProperty("NGINX_LOG") + "-" + DateUtil.dateToString(timestamp);
// access日志,格式:"日期 | 对象{}"
if (NginxLogType.NGINX_LOG_ACCESS.name().equals(logFileBeatContent.getLogType())){
// 转access对象
String[] messageArray = message.split("\\|");
if (messageArray.length > 0) {
message = messageArray[1].trim();
NginxLogAccess nginxLogAccess = JSON.parseObject(message, NginxLogAccess.class);
nginxLogAccess.setMessage(message);
nginxLogAccess.setLogFileBeatContent(logFileBeatContent);
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(nginxLogAccess));
bulkProcessor.add(new IndexRequest(index).source(jsonObject));
}
}
// error日志
else if (NginxLogType.NGINX_LOG_ERROR.name().equals(logFileBeatContent.getLogType())) {
// 转error对象
NginxLogError nginxLogError = new NginxLogError();
nginxLogError.setMessage(message);
nginxLogError.setLogFileBeatContent(logFileBeatContent);
// 内容
String[] messageArray = message.split("\\[");
if (messageArray.length > 0) {
// 时间戳
Date date = DateUtil.stringToDate(messageArray[0].trim(), "yyyy/MM/dd HH:mm:ss");
nginxLogError.setTimestamp(DateUtil.dateToString(date, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
// 日志级别
String logLevel = messageArray[1].split("\\]")[0].trim();
nginxLogError.setLogLevel(logLevel);
}
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(nginxLogError));
bulkProcessor.add(new IndexRequest(index).source(jsonObject));
}
// info日志
else if (NginxLogType.NGINX_LOG_INFO.name().equals(logFileBeatContent.getLogType())) {
// 转common对象
NginxLogInfo nginxLogInfo = new NginxLogInfo();
nginxLogInfo.setMessage(message);
nginxLogInfo.setLogFileBeatContent(logFileBeatContent);
// 内容
String[] messageArray = message.split("\\[");
if (messageArray.length > 0) {
// 时间戳
Date date = DateUtil.stringToDate(messageArray[0].trim(), "yyyy/MM/dd HH:mm:ss");
nginxLogInfo.setTimestamp(DateUtil.dateToString(date, "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"));
// 日志级别
String logLevel = messageArray[1].split("\\]")[0].trim();
nginxLogInfo.setLogLevel(logLevel);
}
JSONObject jsonObject = JSON.parseObject(JSON.toJSONString(nginxLogInfo));
bulkProcessor.add(new IndexRequest(index).source(jsonObject));
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void declareOutputFields(OutputFieldsDeclarer declarer) {
}
/**
* execute前,准备操作
* @param stormConf
* @param context Topology的基本信息
*/
@Override
public void prepare(Map stormConf, TopologyContext context) {
super.prepare(stormConf, context);
// ES批量写入:BulkProcessor
this.bulkProcessor = new ESConfig(properties).getBulkProcessor();
// this.bulkProcessor = bulkProcessor();
}
}
5. ES并Kibana查看
五、参考资料
https://www.jb51.net/article/117879.htm
https://www.jb51.net/article/52573.htm
Nginx修改日志格式 日志分析 字段分离 ES数据格式的转换及nginx日志参数详解_wpskdsl的博客-CSDN博客