案例十九:分析Tomcat日志
如果你公司服务器上跑的是java的代码,那多半会使用Tomcat,而Tomcat出现问题我们就不得不去查看Tomcat的日志。Tomcat有一个总日志叫做catalina.out,它记录了Tomcat相关的信息,包括正确的和错误的。该案例的需求背景是这样的:
服务器上跑着4个Tomcat实例,目录结构如下:
/opt/TOMCAT/
├── crontabs
├── t1
├── t2
├── t3
└── t4
而catalina.out所在路径分布如下:
/opt/TOMCAT/t1/logs/catalina.out
/opt/TOMCAT/t3/logs/catalina.out
/opt/TOMCAT/t4/logs/catalina.out
/opt/TOMCAT/t2/logs/catalina.out
具体需求如下:
1)脚本可以取Tomcat实例t1-t4的日志,通过参数指定是哪一个
2)脚本可以自定义取日志的起始位置,比如取今天早上10点之后到现在的日志,要求提供的时间为24小时制
3)脚本可以自定义取日志的起始和结束位置,比如取今天早上9点到晚上8点的日志,要求提供的时间为24小时制
4)第一个参数为哪一个Tomcat(t1,t2,t3,t4),第二个参数为起始时间点(只考虑当天的时间),第三个参数位结束时间点,可以省略,如果省略则为当前时间点
5)提供的时间点需要判断合法性,即必须为18:03:20这样的格式
日志片段如下:
Oct 29, 2018 01:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["http-bio-8080"]
Oct 29, 2018 01:52:24 PM org.apache.coyote.AbstractProtocol start
INFO: Starting ProtocolHandler ["ajp-bio-8009"]
Oct 29, 2018 01:52:24 PM org.apache.catalina.startup.Catalina start
INFO: Server startup in 2102 ms
知识点一:Tomcat介绍和安装
Tomcat是一个web容器,我们主要用它来运行servlet和JSP。Tomcat本身也是一个http服务器,它可以像Apache或者Nginx那样解析HTML网页、JS、CSS以及图片等元素,但它最主要的功能还是用来运行Servlet或JSP。关于Tomcat涉及到一些JAVA相关的概念,下面我简单做一个罗列。
JAVAEE Java Plateform Enterprise Edition 企业版本,用来做网站的
JAVASE Java Plateform Standard Edition 标准版本,用来做电脑上运行的软件的
JavaME Java Plateform Micro Edition 微型版本,做手机软件的
JDK Java Developmnet kit java的开发和运行环境,JDK= java开发工具+JRE
JRE Java Runtime Environment java程序的运行环境,包含java运行所需要的类库和JVM
JVM java虚拟机
jar(java application archive) 包含class和一些资源和配置文件的压缩包
war(web application archive)与jar基本相同,会包含全部的web应用程序,tomcat会自动将其部署
上面提到了Servlet和JSP,它们二者的区别主要有以下几点:
1)在html代码中内嵌java代码就是jsp,而servlet是纯java代码写的
2)jsp主要用来展现页面效果,而servlet主要负责逻辑控制
3)用户第一次运行jsp时,会自动转换为servlet代码,所以说jsp本质上就是一种servlet
4)第一次访问servlet时,会将其编译为类文件,后续可以直接访问类文件
关于Tomcat的安装,我简单列一下步骤,供大家参考
1.安装JDK(以下方法二选一)
方法1:yum 安装 java-1.8.0-openjdk
# yum install -y java-1.8.0-openjdk
方法2:安装oracle官方jdk
1)到 http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html 下载jdk8
2)解压并改名/usr/local/jdk1.8
3)编辑配置文件/etc/profile,增加如下内容:
JAVA_HOME=/usr/local/jdk1.8/
JAVA_BIN=/usr/local/jdk1.8/bin
JRE_HOME=/usr/local/jdk1.8/jre
PATH=$PATH:/usr/local/jdk1.8/bin:/usr/local/jdk1.8/jre/bin
CLASSPATH=/usr/local/jdk1.8/jre/lib:/usr/local/jdk1.8/lib:/usr/local/jdk1.8/jre/lib/charsets.jar
2.安装Tomcat(版本为9.0)
1)下载二进制包,下载地址为https://tomcat.apache.org/download-90.cgi
2)解压并改名为/usr/local/tomcat
3)启动 /usr/local/tomcat/bin/startup.sh
知识点二:Tomcat单机多实例
所谓单机多实例就是在一台服务器上跑多个Tomcat服务。其实要想运行Tomcat,不仅需要Tomcat的主程序文件(比如,/usr/local/tomcat/bin/下面的二进制文件),还需要配置文件等辅助类的文件。要想跑多个Tomcat服务,可以只需要一份主程序文件即可,不同的Tomcat服务使用不同的配置文件即可。所以,要想实现单机多实例,可以这样规划一下目录,如下图所示:
其中CATALINA_HOME指的是Tomcat安装目录(如果你按照我的方法安装,那么就是在/usr/local/tomcat/),CATALINA_BASE为实例所在目录。CATALINA_HOME 路径下只需要包含 bin 和 lib 目录,而 CATALINA_BASE 只存放 conf、webapps、logs 等这些文件,这样部署的好处在于升级方便,配置及安装文件间互不影响,在不影响 Tomcat 实例的前提下,替换掉 CATALINA_HOME 中的安装文件。
具体的部署步骤如下:
1.创建实例目录
# mkdir -p /data/tomcat-instance
# mkdir /data/tomcat-instance/www.123.com
# cd !$
# cp -r /usr/local/tomcat/conf /data/tomcat-instance/www.123.com/
2.创建tomcat服务相关目录
# mkdir -p /data/tomcat-instance/www.123.com/{common,logs,temp,server,shared,webapps,work}
3.创建启动和关闭脚本
# vi /data/tomcat-instance/www.123.com/start.sh //启动脚本内容如下
#!/bin/bash
export CATALINA_HOME=/usr/local/tomcat
export CATALINA_BASE=/data/tomcat-instance/www.123.com
TOMCAT_ID=`ps aux |grep "java"|grep "Dcatalina.base=$CATALINA_BASE "|grep -v "grep"|awk '{ print $2}'`
if [ -n "$TOMCAT_ID" ]
then
echo "tomcat(${TOMCAT_ID}) still running now , please shutdown it first";
exit 2;
else
$CATALINA_HOME/bin/startup.sh
if [ "$?" = "0" ]; then
echo "start succeed"
else
echo "sart failed"
fi
fi
# vi /data/tomcat-instance/www.123.com/shutdown.sh //停止脚本内容如下
#!/bin/bash
export CATALINA_HOME=/usr/local/tomcat
export CATALINA_BASE=/data/tomcat-instance/www.123.com
TOMCAT_ID=`ps aux |grep "java"|grep "Dcatalina.base=$CATALINA_BASE "|grep -v "grep"|awk '{ print $2}'`
if [ -n "$TOMCAT_ID" ] ; then
TOMCAT_STOP_LOG=`$CATALINA_HOME/bin/shutdown.sh`
if [ "$?" = "0" ]; then
echo "stop succeed"
else
echo "stop failed"
fi
else
echo "Tomcat instance not found"
exit
fi
4.编辑配置文件
# vi /data/tomcat-instance/www.123.com/conf/server.xml //修改三个端口,目的是为了不和其他实例冲突
有了第一个实例后,第二个实例可以直接复制/data/tomcat-instance/www.123.com 目录,然后修改对应的配置、启动脚本、停止脚本内容。
知识点三:shell脚本的参数个数
在前面的案例中多次用到$1,$2...,即shell脚本的参数。和参数相关的还有一个常用的概念,那就是shell脚本参数的个数。先来看示例脚本:
# vi pa_nu.sh //内容如下
#!/bin/bash
echo "脚本有$#个参数"
执行脚本,过程如下:
# sh pa_nu.sh 1 a
脚本有2个参数
# sh pa_nu.sh
脚本有0个参数
# sh pa_nu.sh a b c
脚本有3个参数
所以,结论就是在shell脚本中用$#表示脚本的参数个数。
知识点四:判断一个时间是否合法
如果使用传统的方法,去比对时、分、秒的范围是可以做到,但是这样太繁琐,其实有一个很简单的方法,如下:
# date -d "19:60:" +%s
date: 无效的日期"19:60:"
# date -d "19:59" +%s
1541678340
没错,就是用date命令来做。
知识点五:将24小时制的时间转换为12小时制
不解释,直接看命令吧,如下:
# date -d "19:59:33" +%r
07:59:33 PM
当然,我们也可以直接判断一个时间是AM还是PM
# date -d "19:59:33" +%p //注意,这个是小写的p
PM
如果想用小写的,也可以做到
# date -d "9:10:00" +%P //注意,这个是大写的P
am
知识点六:比较两个时间大小
比较时间大小,只能通过时间戳来实现,如下
# t1=`date -d "13:59:59" +%s`
# t2=`date -d "15:00:00" +%s`
# if [ $t1 -lt $t2 ]; then echo "t1比t2要早"; else echo "t1比t2要晚"; fi
t1比t2要早
# t2=`date -d "10:00:00" +%s`
# if [ $t1 -lt $t2 ]; then echo "t1比t2要早"; else echo "t1比t2要晚"; fi
t1比t2要晚
本案例参考脚本
#!/bin/bash
#截取指定Tomcat的日志片段
#作者:阿铭
#日期:2018-11-08
LANG=en
logfile="/opt/TOM/$1/logs/catalina.out"
#将当天的英文月、数字日期、数字年作为变量赋值给d_mdy
d_mdy=`date "+%b %d, %Y"`
#判断参数个数
if [ $# -ne 2 ] && [ $# -ne 3 ]
then
echo "你提供的参数个数不对,请提供2个或者3个参数。例:sh $0 t1 08:01:00 14:00:00"
exit 1
fi
#判断第一个参数是否符合要求
if ! echo $1|grep -qE '^t1$|^t2$|^t3$|^t4$'
then
echo "第一个参数必须是t1、t2、t3或t4"
exit 1
fi
#判断时间有效性
judge_time()
{
date -d "$1" +%s &>/dev/null
if [ $? -ne 0 ]
then
echo "你提供的时间$1格式不正确"
exit 1
fi
}
#判断提供的时间点是否在日志中出现
judge_time_in_log()
{
if ! grep -q "$d_mdy $(tr_24_12 $1)" $logfile
then
echo "你提供的时间$1在日志$logfile中不曾出现,请换一个时间点"
exit 1
fi
}
#将24小时制时间转换为12小时
tr_24_12()
{
date -d "$1" +%r
}
#判断第2个参数是否合法
judge_time $2
#判断起始时间点是否出现在日志里
judge_time_in_log $2
#如果提供第3个参数
if [ $# -eq 3 ]
then
#判断第3个参数是否合法
judge_time $3
#判断起始时间是否早于结束时间
t1=`date -d "$2" +%s`
t2=`date -d "$3" +%s`
if [ $t2 -lt $t1 ]
then
echo "你提供的时间$2比$3要晚,应该把早的时间放到前面"
exit
fi
#判断提供的结束时间点是否出现在日志中
judge_time_in_log $3
fi
#取起始时间所在行行号
begin_n=`grep -n "$d_mdy $(tr_24_12 $2)" $logfile|head -1|awk -F ':' '{print $1}'`
#取结束时间所在行行号,并用sed截取日志内容
if [ $# -eq 3 ]
then
n=`grep -n "$d_mdy $(tr_24_12 $3)" $logfile|tail -1|awk -F ':' '{print $1}'`
#结束日期所在行的下一行才是日志的内容
end_n=$[$n+1]
sed -n "$begin_n,$end_n"p $logfile
else
sed -n "$begin_n,$"p $logfile
fi
说明:本脚本没有考虑起始时间和结束时间包含日期的情况,请你发散思维,将需求继续复杂化,把脚本写的更加完美。