Count IP Address Appears Most in Log File

1.Problem

之前做运维的笔试题,有一道很经典的统计日志文件中出现最多的 IP 地址及次数的题。其日志格式为

122.224.6.43 - - [30/Oct/2012:21:29:42 +0800] "GET /phpMyAdmin/scripts/setup.php HTTP/1.1" 404 162 "-" "ZmEu"
122.224.6.43 - - [30/Oct/2012:21:29:42 +0800] "GET /MyAdmin/scripts/setup.php HTTP/1.1" 404 162 "-" "ZmEu"
122.224.6.43 - - [30/Oct/2012:21:29:42 +0800] "GET /pma/scripts/setup.php HTTP/1.1" 404 162 "-" "ZmEu"
122.224.6.43 - - [30/Oct/2012:21:29:42 +0800] "GET /myadmin/scripts/setup.php HTTP/1.1" 404 162 "-" "ZmEu"
58.251.60.248 - - [30/Oct/2012:23:05:46 +0800] "GET http://www.baidu.com/ HTTP/1.1" 200 612 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 551; .NET CLR 2.0.50727; CIBA)"
58.251.60.248 - - [30/Oct/2012:23:06:37 +0800] "" 400 0 "-" "-"

这是 nginx 日志 /var/log/nginx/access.log 中的一个片段。

2.用 Shell 解决

思路一: awk 取出第一列,然后利用 sort -u 将 IP unique 化输出到文件,再从文件按行读入再计数显示出来

具体如下:

cd /var/log/nginx

sudo zcat access.log.1.gz | awk '{print $1}' | sort -u > /tmp/ip

while read line; do
    let a++
    echo `sudo zcat access.log.1.gz | awk '{print $1}' | grep $line -c` $line
done < /tmp/ip

rm /tmp/ip

其中用的是 grep 计数的,而且会产生临时文件又需要手动删除,致命点是只能显示出各 IP 次数,但不能列出最大次数及 IP.其中为 access.log.1.gz 文件则用 zcat, 如果是普通 access.log 文件则用 cat.

思路一(改进):利用 here document 则可避免使用临时文件
cd /var/log/nginx

while read line; do
    let a++
    echo `sudo zcat access.log.1.gz | awk '{print $1}' | grep $line -c` $line
done << EOF

`sudo zcat access.log.1.gz | awk '{print $1}' | sort -u`

EOF

这里用到 here document, here document 往往用于需要输出一大段文本的地方,例如脚本的 help 函数。两个 EOF 之间产生的内容用于上面循环的输入。

思路二:上面所用的 unique 化和 count 方法太拙劣,寻求其他的便捷方法,果然找到了

之前用了循环将问题复杂化了,用 uniq 这个命令会很 cool.

cd /var/log/nginx

sudo cat access.log | awk '{print $1}' | sort | uniq -c | sort -nr | head -1

其中 uniq -c 就是 unique 化 AND count, sort -n 表示把字符串中的数字当做数字比较,-r 意为倒序。

注意:这个方法可能只能显示那些相同次数的 IP 的最前面的一个,所以适当加大为 head -3head -5 等适可而止,无需再去判断那些相同次数来增加复杂性,这样很好记的。

3.用 Python 来做

先将日志文件中所有 IP 读入一个 list, 然后将 IP 和次数统计入一个 dict, 最后输出次数最大的 IP 及次数。

li = []

with open('/var/log/nginx/access.log') as f:
    for line in f:
        li.append(line.split(' ')[0])

dic = {}

for element in li: 
    dic[element] = dic.get(element, 0) + 1 
#print(map.items())
print [(k, v) for k, v in dic.iteritems() if v == max(dic.values())]

会将所有次数最大的 IP 和次数输出,避免了思路二的问题。

4.SEE ALSO

Python Virtualenv Wrapper and Pip

1.Install

Arch 下默认是 python3, 而如果强制用软链接改为 python2 的话,一些程序会有问题的,之前都是小脚本,所以手动把第一行改为 #!/usr/bin/env python2 或者 #!/usr/bin/python2 就行,现在要做 GAE 发现手动改了好多还是不行,于是便有了 Python virtualenv 等。

Install virtualenv virtualenvwrapper and pip

# pacman -S python2-virtualenv
# pacman -S python2-virtualenvwrapper
# pacman -S python2-pip

2.Configure

Add the following lines to the ~/.bashrc or ~/.zshrc

export WORKON_HOME=~/.virtualenvs
source /usr/bin/virtualenvwrapper.sh

Then run

mkdir $WORKON_HOME
source ~/.zshrc   # or ~/.bashrc

Or manually perform the following operation

export WORKON_HOME=~/.virtualenvs
mkdir $WORKON_HOME
source /usr/bin/virtualenvwrapper.sh

3.Basic Usage

Create a virtualenv:

mkvirtualenv -p python2.7 --no-site-packages hello

We can see:

Running virtualenv with interpreter /usr/bin/python2.7
New python executable in hello/bin/python2.7
Also creating executable in hello/bin/python
Installing setuptools............................done.
Installing pip...............done.

We can see them in ~/.virtualenvs/hello
and find (hello) in the Prompt. It means we already activate the virtualenv.
Then we could do something we want, like install some package inside the virtualenv (like, Django):

(hello)$ pip install django

We could use deactivate to leave the virtualenv.

But we also could virtualenv any directory you want, for instance:

cd ~/gae
mkdir test
virtualenv2 -p python2.7 --no-site-packages test

then we could activate the virtualenv.

cd test
source bin/activate

then we could do something we want, like install some package inside the virtualenv (like, Django):

(hello)$ pip install django

Finally, we could similarly use deactivate to leave the virtualenv.

4.Virtualenvwrapper

We could use Virtualenvwrapper activate a virtualenv:

$ workon hello

The hello is a virtualenv directory in the $WORKON_HOME(~/.virtualenvs).
所以我们可以在 $WORKON_HOME(~/.virtualenvs) 中做个软链接,然后就能用 workon 命令激活

cd ~/.virtualenvs
ln -s ~/gae/test .
workon test

如果频繁切换,容易搞混的话,可以用下面命令来查看自己所处的环境

which pip
which python
which python2
which python3   # and so on

5.Summary

一般来说,一个虚拟环境就放在 $WORKON_HOME 就好了,不要和要做的项目工程的源码放在一起,配置好之后就相当于一个稳定的环境了,用的时候切换过去就行了。

See Also

Python VirtualEnv

VirtualEnv 和Pip 构建Python的虚拟工作环境

Python开发工具virtualenv的使用 - [Python]

OpenCV Basis

1.Load an image

In [1]: import cv

In [2]: im1 = cv.LoadImageM('foo.jpg')

In [3]: print type(im1)
<type 'cv2.cv.cvmat'>

In [4]: im2 = cv.LoadImage('foo.jpg')

In [5]: print type(im2)
<type 'cv2.cv.iplimage'>

In [6]: cv.SaveImage('foo1.png', im1)

In [7]: cv.SaveImage('foo2.png', im2)

可以看到LoadImageM得到的是一个cvmat图像,而LoadImage得到的是一个iplimage图像,两者均可以输出为图像

2.About DPI PPI and Resolution(分辨率)

  • DPI 是指每一英吋长度中,取样或可显示或输出点的数目,英文为 Dots Per Inch(点每英寸)。主要说的是打印机。
  • PPI 是指每英寸的长度中所具有的像素,英文为 Pixels Per Inch(像素每英寸)。主要说的是显示器。
  • 分辨率(resolution)则泛指量测或显示系统对细节的分辨能力。
  • 由分辨率中X或Y轴的数字除以该轴的长度(英寸),可得该轴的像素每英寸密度。一般的像素是方形或接近方形,X与Y轴像素密度相同,但也有不相同的显示器。
  • 同样分辨率的一张图像,在越小的屏幕上显示或者在越小的尺寸上打印出来,PPI或者DPI就越高。看起来就越清晰,越细腻。
  • 也就是说,如果想要把一张大小为 800X600 且 PPI/DPI 为 200 图像,降低到 100 PPI/DPI ,在显示或打印尺寸不变的情况下,只需把大小变为原来的一半 400X300(假设X与Y轴像素密度相同)。
  • 同时也可以说,单纯的高分辨率与 DPI/PPI 是扯不上关系的,只有高分辨率的图像体现在具体尺寸上,才能说它的 DPI/PPI。一张 2880x1800 图像在 15.4 英寸的显示器上会有很高的 PPI, 但在 42 英寸的显示器上 PPI 则很低。

以下halvedimage.py是将图像大小变为一半

#!/usr/bin/env python2
# -*- coding: utf-8 -*-

import cv
import sys

def main(img):
    im = cv.LoadImageM(img)
    halved = cv.CreateMat(im.rows / 2, im.cols / 2, cv.CV_8UC3)
    cv.Resize(im, halved)
    cv.SaveImage('halved_'+img, halved)

if __name__=='__main__':
    try:
        main(sys.argv[1])
    except IndexError:
        print '''Usage:
        halvedimage.py image.file'''

Install OpenEXR for Python

Using OpenEXR’s Python bindings we can make a simple image viewer.
So, we should install OpenEXR first, following OpenEXR bindings for Python
Make sure you have already installed OpenEXR’s Prerequisite: Python 2.5+ and the OpenEXR C++ library(openexr).

wget http://excamera.com/files/OpenEXR-1.2.0.tar.gz

tar zxvf OpenEXR-1.2.0.tar.gz

cd OpenEXR-1.2.0

(sudo python2 setup.py build)

sudo python2 setup.py install

Then, we can follow OpenCV and OpenEXR

References: Python how to install setup.py