Apache服务器日志Log解析

------------本文笔记整理自《Hadoop海量数据处理:技术详解与项目实战》范东来

一、Apache服务器日志信息组成说明

1. group-0 是一条初始的log日志信息;

2. group-1 ~ group-15 是对该条日志正则解析后的结果。

一条原始log日志:
	group-0 : 120.196.145.58 - - [11/Dec/2013:10:00:32 +0800] "GET /__utm.gif HTTP/1.1" 200 35 "http://easternmiles.ceair.com/flight/index.html" "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" "BIGipServermu_122.119.122.14=192575354.20480.0000;Webtrends=120.196.145.58.1386724976245806;uuid=5eb501f9-3586-4239-a15c-fe89aa14d624;userId=099;st=1" 1482 352 - easternmiles.ceair.com 794
远端主机(访客IP地址):
	group-1 : 120.196.145.58
远端登录名:
	group-2 : -
远端用户名:
	group-3 : -
服务器接收请求时间:
	group-4 : 11/Dec/2013:10:00:32 +0800
请求的第一行(请求方式、请求的URL、请求所用协议):
	group-5 : GET /__utm.gif HTTP/1.1
响应码(最后的请求状态):
	group-6 : 200
返回的数据流量(以CLF格式显示的除HTTP头以外传送的字节数):
	group-7 : 35
上一个访问的页面(访客来源):
	group-8 : http://easternmiles.ceair.com/flight/index.html
访客浏览器信息:
	group-9 : Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36
Cookie信息(包含uuid/userId/sessiontime):
	group-10 : BIGipServermu_122.119.122.14=192575354.20480.0000;Webtrends=120.196.145.58.1386724976245806;uuid=5eb501f9-3586-4239-a15c-fe89aa14d624;userId=099;st=1
接收的字节数(包括请求头的数据,并且不能为零):
	group-11 : 1482
发送的字节数(包括请求头的数据,并且不能为零):
	group-12 : 352
%{X-Forwarded-For}i:
	group-13 : -
访问主机地址:
	group-14 : easternmiles.ceair.com
服务器处理本请求所用时间,以微秒为单位:
	group-15 : 794

二、正则解释

参考正则教程链接:Java 正则表达式 | 菜鸟教程

1.用到的正则表达式解释:

"(.+)": 字符串中用一对括号括起来的表示 一个组
"(.+)(.+)":这个就表示 两个组,因为有两对括号
"(.+)":括号中的 . 代表 匹配除 \r\n 之外的任何单个字符
        括号中的 + 代表 一次或多次匹配前面的字符或子表达式,也就是多次匹配 . 
"\\s":代表字符串中转义"\\",最终转换成正则表达式时,
       即为"\s",代表匹配任何空白字符,包括空格、制表符、换页符等,如:\f\n\r\t\v
"\\(":代表字符串中转义"\\",转成正则时,代表 \( 即对"("的转义
       这个"\\(\\)"与"()"不同:
        前一个代表匹配一对括号,后一个代表匹配一个组
"\\[\\]":代表匹配一对方括号
"[0-9.]":代表匹配任意一位数字或者一个字符,与上一个例子不同
"[0-9.]+\\s":代表匹配任意多位数字或者多个字符,并以空白字符结尾
"[^0-9]":用"^"尖角代表除0-9的任意一个字符
"\\[([^\\[\\]]+)\\]\\s":代表匹配以"["开头,以"]"和空白字符结尾的一串字符
                         其中字符是除了"["、"]"方括号以外的任意多个字符

2.将需要匹配的日志信息和浏览器信息字符串,及对应的正则匹配字符串,按组(一行一组)拆分如下:

2.1.日志解析:

待解析的一条日志:(Java字符串)
String LOG = "120.196.145.58 "
    + "- "
    + "- "
    + "[11/Dec/2013:10:00:32 +0800] "
    + "\"GET /__utm.gif HTTP/1.1\" "
    + "200 "
    + "35 "
    + "\"http://easternmiles.ceair.com/flight/index.html\" "
    + "\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36\" "
    + "\"BIGipServermu_122.119.122.14=192575354.20480.0000;Webtrends=120.196.145.58.1386724976245806;uuid=5eb501f9-3586-4239-a15c-fe89aa14d624;userId=099;st=1\" "
    + "1482 "
    + "352 "
    + "- "
    + "easternmiles.ceair.com "
    + "794";
	
使用的正则表达式:(Java字符串)
String APACHE_LOG_REGEX = "^([0-9.]+)\\s"
    +"([\\w.-]+)\\s"
    +"([\\w.-]+)\\s"
    +"\\[([^\\[\\]]+)\\]\\s"
    +"\"((?:[^\"]|\\\")+)\"\\s"
    +"(\\d{3})\\s"
    +"(\\d+|-)\\s"
    +"\"((?:[^\"]|\\\")+)\"\\s"
    +"\"((?:[^\"]|\\\")+)\"\\s"
    +"\"(.+)\"\\s"
    +"(\\d+|-)\\s"
    +"(\\d+|-)\\s"
    +"(\\d+|-)\\s"
    +"(.+)\\s"
    +"(\\d+|-)$";

2.2.浏览器解析: 

待解析的一条浏览器信息
String userAgentStr = "\"Mozilla/5.0 "
    +"(Windows NT 6.1; WOW64) "
    +"AppleWebKit/537.36 "
    +"(KHTML, like Gecko) "
    +"Chrome/31.0.1650.63 "
    +"Safari/537.36\" ";

用户浏览器信息匹配正则:(Java字符串)
String USER_AGENT_REGEX = "^(.+)\\s"
    +"\\((.+)\\)\\s"
    +"(.+)\\s"
    +"\\((.+)\\)\\s"
    +"(.+)\\s"
    +"(.+)$";

 

三、代码实现

1. 将原书中的日志解析代码从Mapper类中抽离了出来,封装成独立的功能,供Mapper调用。

2. LogParser类设计成了单例模式,以便重复使用此对象,减少由不断创建对象带来的开销。

3. 代码中有测试用例,可直接运行。

4. 其中的IP解析类在上篇博文:利用纯真IP数据库解析IP地址位置信息

package com.etl.utls;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.etl.utls.IpParser;

public class LogParser {
	
	//Apache服务器日志信息的正则表达式
	private static final String APACHE_LOG_REGEX = 
			"^([0-9.]+)\\s([\\w.-]+)\\s([\\w.-]+)\\s\\[([^\\[\\]]+)\\]\\s\"((?:[^"
			+ "\"]|\\\")+)\"\\s(\\d{3})\\s(\\d+|-)\\s\"((?:[^\"]|\\\")+)\"\\s\"((?:"
			+ "[^\"]|\\\")+)\"\\s\"(.+)\"\\s(\\d+|-)\\s(\\d+|-)\\s(\\d+|-)\\s(.+)\\"
			+ "s(\\d+|-)$";
	
	//用户浏览器信息的正则表达式
	private static final String USER_AGENT_REGEX = "^(.+)\\s\\((.+)\\)\\s(.+)\\s\\((.+)\\)\\s(.+)\\s(.+)$";
	
	//测试日志一条
	public static final String LOG = "120.196.145.58 "
			+ "- "
			+ "- "
			+ "[11/Dec/2013:10:00:32 +0800] "
			+ "\"GET /__utm.gif HTTP/1.1\" "
			+ "200 "
			+ "35 "
			+ "\"http://easternmiles.ceair.com/flight/index.html\" "
			+ "\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) "
			+ "Chrome/31.0.1650.63 Safari/537.36\" "
			+ "\"BIGipServermu_122.119.122.14=192575354.20480.0000;Webtrends=120.196.145.58.1386724976245806;"
			+ "uuid=5eb501f9-3586-4239-a15c-fe89aa14d624;userId=099;st=1\" "
			+ "1482 "
			+ "352 "
			+ "- "
			+ "easternmiles.ceair.com "
			+ "794";
	
	public static final String CANNOT_GET = "can not get";
	
	//需要解析的字段
	private String ipAddress = null; //ip地址
	private String uniqueId = null; //uuid
	private String url = null; //访问的url地址
	private String sessionId = null; //会话id
	private String sessionTimes = null; //服务器响应时间
	private String areaAddress = null; //国家
	private String localAddress = null; //地区
	private String browserType = null; //浏览器类型
	private String operationSys = null; //操作系统
	private String referUrl = null; //访问的上一个页面url
	private String receiveTime = null; //服务器接收请求时间
	private String userId = null; //用户id
	
	//保存cookies信息的map
	private static Map<String, String> cookies = new HashMap<String, String>();
	
	//实例化的LogParser计数
	private static int count = 0;
	//LogParser唯一标识
	private int index = 0;
	private LogParser() {
		count++;
		index = count;
	}
	
	//初始化Log解析器
	private void init() {
		setIpAddress(CANNOT_GET);
		setUniqueId(CANNOT_GET);
		setUrl(CANNOT_GET);
		setSessionId(CANNOT_GET);
		setSessionTimes(CANNOT_GET);
		setAreaAddress(CANNOT_GET);
		setLocalAddress(CANNOT_GET);
		setBrowserType(CANNOT_GET);
		setOperationSys(CANNOT_GET);
		setReferUrl(CANNOT_GET);
		setReceiveTime(CANNOT_GET);
		setUserId(CANNOT_GET);
		cookies.clear();
	}
	
	/*
	 * 解析一条log日志
	 * 
	 * @param log 一条日志
	 */
	public void parse(String log) {
		//初始化Log解析器
		init();
		
		String ipStr = null;
		String receiveTimeStr = null;
		String urlStr = null;
		String referUrlStr = null;
		String userAgentStr = null;
		String cookieStr = null;
		String hostNameStr = null;
		
		//正则解析日志
		Pattern pattern = Pattern.compile(APACHE_LOG_REGEX);
		Matcher matcher = pattern.matcher(LOG);
		if (matcher.find()) {
//			for (int i = 0; i <= matcher.groupCount(); i++) {
//				System.out.println("group-" + i + " : " + matcher.group(i));
//			}
			
			//根据正则表达式将日志文件断开
			ipStr = matcher.group(1); //远端主机(访客IP地址)
			receiveTimeStr = matcher.group(4); //服务器接收请求时间
			urlStr = matcher.group(5); //请求的第一行(请求方式、请求的URL、请求所用协议)
			referUrlStr = matcher.group(8); //上一个访问的页面(访客来源)
			userAgentStr = matcher.group(9); //访客浏览器信息
			cookieStr = matcher.group(10); //Cookie信息(包含uuid/userId/sessiontime)
			hostNameStr = matcher.group(14); //访问主机地址
			
			//保存IP地址
			ipAddress = ipStr;
			//解析IP地址
			IpParser ipParser = new IpParser();
			try {
				//根据IP地址得出所在区域
				areaAddress = ipParser.parse(ipStr).split(" ")[0];
				localAddress = ipParser.parse(ipStr).split(" ")[1];
			} catch (Exception e) {
				e.printStackTrace();
			}
			
			//格式化请求接受时间
			DateFormat df = new SimpleDateFormat("dd/MMM/yyyy:HH:mm:ss Z",Locale.US);
			try {
				Date date = df.parse(receiveTimeStr);
				//将时间转换为字符串
				receiveTime = Long.toString(date.getTime());
			} catch (ParseException e) {
				e.printStackTrace();
			}
			
			//将url中的无效字符串丢弃
			urlStr = urlStr.substring(5);
			//重新拼装成url字符串
			url = hostNameStr + urlStr;
			
			//解析用户浏览器信息
			pattern = Pattern.compile(USER_AGENT_REGEX);
			matcher = pattern.matcher(userAgentStr);
			if (matcher.find()) {
//				for (int i = 0; i <= matcher.groupCount(); i++) {
//					System.out.println("group-" + i + " : " + matcher.group(i));
//				}
				//获取浏览器类型
				browserType = matcher.group(5);
				//获取操作系统类型
				operationSys = matcher.group(2).split(" ")[0];
			}
			
			//保存上一个页面url
			referUrl = referUrlStr;
			
			//HashMap保存cookie信息
			String[] strs = cookieStr.split(";");
			for (int i = 0; i < strs.length; i++) {
				String[] kv = strs[i].split("=");
				String keyStr = kv[0];
				String valStr = kv[1];
				cookies.put(keyStr, valStr);
			}
			//获取uuid信息
			uniqueId = cookies.get("uuid");
			//获取账号信息
			userId = cookies.get("userId");
			//如果没有获取成功,说明用户没有登录
			if (userId == null) {
				userId = "unlog_in";
			}
			//获取sessionTimes
			sessionTimes = cookies.get("st");
			//拼装成sessionId
			sessionId = uniqueId + "|" + sessionTimes;
			
		}
	}

	public String getIpAddress() {
		return ipAddress;
	}

	public void setIpAddress(String ipAddress) {
		this.ipAddress = ipAddress;
	}

	public String getUniqueId() {
		return uniqueId;
	}

	public void setUniqueId(String uniqueId) {
		this.uniqueId = uniqueId;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getSessionId() {
		return sessionId;
	}

	public void setSessionId(String sessionId) {
		this.sessionId = sessionId;
	}

	public String getSessionTimes() {
		return sessionTimes;
	}

	public void setSessionTimes(String sessionTimes) {
		this.sessionTimes = sessionTimes;
	}

	public String getAreaAddress() {
		return areaAddress;
	}

	public void setAreaAddress(String areaAddress) {
		this.areaAddress = areaAddress;
	}

	public String getLocalAddress() {
		return localAddress;
	}

	public void setLocalAddress(String localAddress) {
		this.localAddress = localAddress;
	}

	public String getBrowserType() {
		return browserType;
	}

	public void setBrowserType(String browserType) {
		this.browserType = browserType;
	}

	public String getOperationSys() {
		return operationSys;
	}

	public void setOperationSys(String operationSys) {
		this.operationSys = operationSys;
	}

	public String getReferUrl() {
		return referUrl;
	}

	public void setReferUrl(String referUrl) {
		this.referUrl = referUrl;
	}

	public String getReceiveTime() {
		return receiveTime;
	}

	public void setReceiveTime(String receiveTime) {
		this.receiveTime = receiveTime;
	}

	public String getUserId() {
		return userId;
	}

	public void setUserId(String userId) {
		this.userId = userId;
	}

	public int getIndex() {
		return index;
	}

	/*
	 * 测试
	 */
	public static void main(String[] args) {
		//获取日志解析对象
		LogParser logParser = new LogParser();
		//解析测试用例日志
		logParser.parse(LogParser.LOG);
		
		String mapOutKey = logParser.getSessionId() + "&" + logParser.getReceiveTime();
		String mapOutValue = logParser.getIpAddress() + "\n" + logParser.getUniqueId() + "\n" 
				+ logParser.getUrl() + "\n" + logParser.getSessionId() + "\n" 
				+ logParser.getSessionTimes() + "\n" + logParser.getAreaAddress() + "\n" 
				+ logParser.getLocalAddress() + "\n" + logParser.getBrowserType() + "\n" 
				+ logParser.getOperationSys() + "\n" + logParser.getReferUrl() + "\n" 
				+ logParser.getReceiveTime() + "\n" + logParser.getUserId();
		System.out.println("mapOutKey: " + mapOutKey + "\nmapOutValue: " + mapOutValue);
	}
}

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值