Apacheのアクセスログ解析方法(AWStats編)



●AWStatsの設定(RockyLinux)

 参考URL:AWStatsの設定とオプションの指定方法

 AWStatsのインストール

 AWStatsをインストールします。
# dnf --enablerepo=epel,powertools -y install awstats
  awstats.<解析対象サイト名>.confが自動作成されているので編集します。
# vi /etc/awstats/awstats.www.bigbang.mydns.jp.conf
# 解析対象のログファイル
LogFile="/var/log/httpd/access_log"
↓
LogFile="/var/log/httpd/ssl_access_log"

# httpd のログフォーマットが combinedの場合は 1、commonの場合は 4
LogFormat=1

# ホスト名を確認
SiteDomain="www.bigbang.mydns.jp"

# アクセス元から内部(例:192.168.1.X)を除外する
HostAliases="localhost 127.0.0.1 REGEX[^192\.168\.1\.]"

# アクセス元の名前解決を行う(アクセス元をIPアドレスではなくホスト名で表示する)
DNSLookup=2
↓
DNSLookup=1

# 週の始まりを日曜日にする
FirstDayOfWeek=1
 ↓
FirstDayOfWeek=0

SkipHosts="localhost 127.0.0.1 REGEX[^1\.0\.0\.] REGEX[^192\.168\.0\.]"

# サーバが返したHTTPステータスコードが有効なもの(200 OK、304 NotModified、302 Found)を成功したアクセスと見なし、ログファイルから情報を取り込む
ValidHTTPCodes="200 304"
 ↓
ValidHTTPCodes="200 304 302"
 レポートの日付形式を「月 9月 2016」から「2016年 9月」となるように変更します。
# vi `rpm -ql awstats|grep "awstats\.pl"` ← awstats.pl編集
if ( $MonthRequired eq 'all' ) {
        print "$Message[6] $YearRequired";
}
else {
        print
          #"$Message[5] $MonthNumLib{$MonthRequired} $YearRequired";
      ↑ 行頭に#を追加してコメントアウト
          "$YearRequired$Message[6] ".$MonthNumLib{$MonthRequired};
      ↑ 追加(日付をYYYY年 MM月形式にする)
}

        print( $MonthRequired eq 'all'
                ? "$Message[6] $YearRequired"
                #: "$Message[5] "  行頭に#を追加してコメントアウト
                  #. $MonthNumLib{$MonthRequired} ← 行頭に#を追加してコメントアウト
                  #. " $YearRequired" ← 行頭に#を追加してコメントアウト
                : "$YearRequired$Message[6] $MonthNumLib{$MonthRequired}"
         ↑ 追加(日付をYYYY年 MM月形式にする)
        );

                        #print "$MonthNumLib{$monthix}<br />$YearRequired";
                         ↑ 行頭に#を追加してコメントアウト
                        print "$YearRequired$Message[6]<br />$MonthNumLib{$monthix}";
                         ↑ 追加(日付をYYYY年 MM月形式にする)
        
                        #print "$MonthNumLib{$monthix} $YearRequired";
                         ↑ 行頭に#を追加してコメントアウト
                        print "$YearRequired$Message[6] $MonthNumLib{$monthix}";
                         ↑ 追加(日付をYYYY年 MM月形式にする)
 AWStatsへのアクセス制限
# vi /etc/httpd/conf.d/awstats.conf
Require ip 192.168.0.

# systemctl restart httpd


 日本の検索エンジン対応

 日本の検索エンジンに対応させるためには、下記URLを参照してください。
 日本の検索エンジンへの対応

 awstats.plのアクセス制限

 awstats.plを実行するたびにAWStatsのデータベースからログ情報を入力してアクセス統計ページを作成するため、サーバに負荷がかかることから、awstats.plは内部からのみ実行できるようにします。

 インターネット側からアクセスされないよう/etc/httpd/conf.d/awstats.confを下記のように設定します。
<Directory "/usr/share/awstats/wwwroot">
    DirectoryIndex awstats.pl
    Options None
    #Options ExecCGI
    AllowOverride None
    <IfModule mod_authz_core.c>
        # Apache 2.4	
        Require local
        Require ip 192.168.100.
        Require ip 192.168.200.
    </IfModule>
    <IfModule !mod_authz_core.c>
        # Apache 2.2
        Order allow,deny
        Allow from 127.0.0.1
        Allow from ::1
    </IfModule>
</Directory>
# Additional Perl modules


 AWStatsデータベース初期作成

 Apacheのログファイルを過去分も含めて全てAWStatsのデータベースに取り込みます。
 一度でもデータベースを作成済みの場合、過去ログファイルを処理しませんので、下記コマンドを実行します。
# rm /var/lib/awstats/awstats112021.<解析対象サイト名>.txt
 データベースを取り込むためのスクリプトは下記のとおりです。
■スクリプトその1
# vi awstatsinit.sh
#!/bin/sh

# データベース作成先ディレクトリ作成
mkdir -p `grep ^DirData /etc/awstats/awstats.$1.conf | awk -F= '{print $2}'|tr -d \"`

# AWStats設定ファイルよりApacheログファイル名取得
logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p' -e d`
#### 場合によっては下記行に変更しないと動作しない
#logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p'`

# 過去ログを古い順に処理
grep dateext /etc/logrotate.conf > /dev/null 2>&1
if [ $? -eq 0 ]; then
ls $logfile-* > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 過去ログファイル名に日付が設定されている場合(CentOS6以降)
for log in `ls $logfile-*|sort`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done
fi
else
ls $logfile.* > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 過去ログファイル名に通番が設定されている場合(CentOS5以前)
for log in `ls $logfile.*|sort -r`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done
fi
fi

# 現在ログ処理
for log in `ls $logfile|sort`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
# -config=$1 -update -showdropped -logfile=$log
done


■スクリプトその2
# vi awstatsinit.sh
#!/bin/sh

logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p' -e d`
#### 場合によっては下記行に変更しないと動作しない
#logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p'`
for log in `ls $logfile*|sort -r`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done


 スクリプトを実行します。ログサイズ及びログ数により処理時間を要します。
# sh awstatsinit.sh <解析対象サイト名>
例:# sh awstatsinit.sh www.serverA.com
Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=<解析対象サイト名> -configdir="/etc/awstats"' \
to update config www.data.jma.go.jp
Create/Update database for config"/etc/awstats/awstats.<解析対象サイト名>.conf" by AWStats version 7.4 (build 20150714)
From data in log file "/var/log/httpd/ssl_access_log|"...
Phase 1 : First bypass old records, searching new record...
Searching new records from beginning of log file...
  :
(途中省略)
  :
Flush history file on disk (unique hosts reach flush limit of 20000)
Flush history file on disk (unique hosts reach flush limit of 20000)
Jumped lines in file: 0
Parsed lines in file: 1010166052
 Found 454 dropped records,
 Found 0 comments,
 Found 0 blank records,
 Found 23627196 corrupted records,
 Found 28849736 old records,
 Found 957688666 new qualified records.
 統計情報を見るには、http://Webサーバ名/awstats/awstats.plでアクセスします。
 この場合、Webサーバ名での統計情報が表示されます(デフォルト)。
 Webサーバ名とは異なるサイト名で解析した場合は、http://Webサーバ名/awstats/awstats.pl?config=<解析対象サイト名>でアクセスすると表示されます。

 今後、ログを解析するには既に作成されている過去のデータベース(/var/lib/awstats/awstatsMMYYYY.<解析対象サイト名>.txt)を削除することがないように下記を実行します。
# データベース初期作成時と異なっている場合、解析対象ログファイルを変更
# vi /etc/awstats/awstats.<解析対象サイト名>.conf
# 解析対象のログファイル
LogFile="/var/log/httpd/access_log"

# `rpm -ql awstats|grep "awstats_updateall\.pl"` now -confdir="/etc/awstats" -awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`"


 解析が上手く行かない場合の対処

 何度、解析を実行してもcorrupted recordsの件数が50%以上(あるいは、50%程度)と表示されてしまうことがありました。
 ログサイズが何百GBもあり、あまりにも大きすぎて、正常に日付順にソートされていないような感じでした。
 そのため下記のような手順をとりました。
 作業は通常awstatsを解析しているサーバとは異なるサーバで実施したほうがより安全です。
  • 複数のサーバ上にあるログをサーバごとに整理
  • サーバごとに整理したログを月単位でマージ
  • logresolvemerge.plを使用して、月単位でマージされたサーバごとのログを1つのログに統合
  • その1つのログで前後の月の情報を削除(2020年11月のログならば、2020年10、12月を削除、serverA_202011.txt)
  • awstats.<解析対象サイト名>.confのLogFileディレクティブに/var/log/serverA_202011.txtを指定
  • ※補足(手動でのログ解析時は下記の設定変更作業(解析対象のファイル名が変更する場合)が必須)
    サーバのURLが「test.mydns.jp」(URL名でなくても構わない)の場合、解析時に参照するファイル「/etc/awstats/awstats.test.mydns.jp.conf」を事前に作成しておく必要があります。
    このファイル「/etc/awstats/awstats.test.mydns.jp.conf」に解析するログを指定おく必要があります。
    LogFile="/var/log/serverA_202011.txt"
  • /var/lib/awstatsディレクトリにあるAWStatsデータベース(awstats.<解析対象サイト名>.txt)を全て退避
  • AWStatsデータベース初期作成を参照し解析
  • 解析するたびにAWStatsデータベース(awstats.<解析対象サイト名>.txt)を全て退避し、/var/lib/awstatsディレクトリ内を空にする
  • 上記を繰り返し実施<
  • 作業終了後、退避した全AWStatsデータベース(awstats.<解析対象サイト名>.txt)を/var/lib/awstatsディレクトリ内に戻す
  • 該当URLにアクセス
  • 例:https://www.bigbang.mydns.jp/awstats/awstats.pl?config=test.mydns.jp
 時間はかかりますが、上記方法によりcorrupted recordsの件数が激減し、正常に解析できました。

 Apacheのログローテーションの設定

 Apacheログファイルのローテーション時、AWStatsのデータベースに取り込んでからローテーションするように設定します。
# vi /etc/logrotate.d/httpd
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    ---- 追加(ここから) ----
    prerotate
        `rpm -ql awstats|grep "awstats_updateall\.pl"` now -confdir="/etc/awstats" \
        -awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" >/dev/null
    endscript
    ---- 追加(ここまで) ----
    postrotate
        /bin/kill -HUP `cat /var/run/httpd.pid 2>/dev/null` 2> /dev/null || true
    endscript
}
 httpで接続するWebサーバ名とhtmlで表示されるアクセス統計ページのドメイン名が異なると「awstats.<解析対象サイト名>.confと一致しません」というようなエラーが表示されますので注意してください。
 この場合、http://Webサーバ名/awstats/awstats.pl?config=<解析対象サイト名>でアクセスします。

 アクセス統計ページHTML版の作成

 awstats.plを実行するたびにAWStatsのデータベースからログ情報を入力してアクセス統計ページを作成するため、サーバに負荷がかかることから、awstats.plは内部からのみ実行できるようにします。
 また、万が一、AWStatsのデータベースが失われた場合、アクセス統計が見れなくなってしまうため、アクセス統計ページをHTMLで作成するようにします。
 CentOS 7でのアクセス統計ページの保存ディレクトリ先を/usr/share/awstats/wwwrootとしました。これは既に該当ディレクトリがWebサーバで公開されているからです。
 また、インターネットからの不要なアクセスを防ぐためアクセス制限を実施します。
# vi /etc/httpd/conf.d/awstatsreport.conf
Alias /awstatsreport "/usr/share/awstats/wwwroot"
インターネット側からアクセスされないように設定
<Location "/awstatsreport">
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
    Allow from 192.168.1.0/24  ← 内部ネットワークアドレスを指定
</Location>
# systemctl reload httpd
 アクセス統計ページHTML版作成ためのスクリプト(awstatsreport.sh)を作成します。

# vi awstatsreport.sh
#!/bin/bash

site=$1
dir=$2

reportbuild() {
# AWStatsデータベース更新中断時ロックファイル残存対処(ここから)
if [ -f /tmp/awstats.$site.lock ]; then
ps -p `cat /tmp/awstats.$site.lock |awk '{print $6}'`
if [ $? -ne 0 ]; then
echo /tmp/awstats.$site.lock removed >&2
rm -f /tmp/awstats.$site.lock
fi
fi
# AWStatsデータベース更新中断時ロックファイル残存対処(ここまで)
`rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` \
-awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`"\
-config=$site -update -lang=jp -dir=$dir \
-year=$YEAR -month=$MONTH -builddate=$YEAR$MONTH
# 個別ページリンク切れ対処(ここから)
sed -i "s/href=\"${site}/href=\"awstats.${site}/g" \
$dir/awstats.$site.$YEAR$MONTH.html
# 個別ページリンク切れ対処(ここまで)
if [ "$YEAR$MONTH" = $(date +%Y%m) ]; then
mv $dir/awstats.$site.$YEAR$MONTH.html $dir/index.html
else
mv $dir/awstats.$site.$YEAR$MONTH.html $dir/$YEAR$MONTH.html
fi
}

ls $dir/* > /dev/null 2>&1
if [ $? -eq 0 ]; then
YEAR=`date --date '1 days ago' +%Y`
MONTH=`date --date '1 days ago' +%m`
reportbuild
else
DirData=`grep ^DirData /etc/awstats/awstats.model.conf|awk -F= '{print $2}'|tr -d \"`
for log in `ls $DirData/awstats*.$site.txt`
do
YEAR=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats..\([^ ]*\)/\1/p' -e d`
MONTH=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats\([^ ]*\)..../\1/p' -e d`
#YEAR=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats.\([^ ]*\)/\1/p' -e d`
#MONTH=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats\([^ ]*\)./\1/p' -e d`
reportbuild
done
fi


 実行権限を与え、アクセス統計ページHTML版作成スクリプトを実行します。
# chmod 700 awstatsreport.sh
# ./awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot
 実行すると下記のようなログが出力されました。

# ./awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot
Launch update process : "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -update -configdir=
Build main page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output
Build alldomains page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=alldomains
Build allhosts page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=allhosts
Build lasthosts page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=lasthosts
Build unknownip page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=unknownip
Build allrobots page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=allrobots
Build lastrobots page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=lastrobots
Build session page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=session
Build urldetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=urldetail
Build urlentry page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=urlentry
Build urlexit page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=urlexit
Build osdetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=osdetail
Build unknownos page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=unknownos
Build browserdetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=browserdetail
Build unknownbrowser page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=unknownbrowser
Build downloads page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=downloads
Build refererse page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=refererse
Build refererpages page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=refererpages
Build keyphrases page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=keyphrases
Build keywords page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=keywords
Build errors400 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=errors400
Build errors403 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=errors403
Build errors404 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象サイト名> -staticlinks=awstats.<解析対象サイト名>.202111 -lang=jp -month=11 -year=2021 -output=errors404
23 files built.
Main HTML page is 'awstats.<解析対象サイト名>.202111.html'.


 http://www.bigbang.mydns.jp/awstatsreport/へアクセスして今月分のアクセス統計ページが表示されることを確認します。

 過去のアクセス統計ページはhttp://Webサーバ名/awstatsreport/年月(YYYYMM).html(例:http://Webサーバ名/awstatsreport/awstats.<解析対象サイト名>.201607.html)のようにアクセスします。
 上記スクリプトがうまく動作せず今月分しか静的なアクセス統計ページが作成されない場合、下記いずれかの方法により作成することが可能です。いずれも静的なアクセス統計ページは/tmpに出力されます。

 ■設定ファイル(/etc/awstats/awstats.<解析対象サイト名>.conf)内のDirDataで定義されたディレクトリ内にデータベース(awstatsMMYYYY<解析対象サイト名>.txt)が存在する・しないに関わらず新たにデータベースを作成しつつ静的なアクセス統計ページを作成する場合

 データベースも再作成しますので解析するログを絞った上で実行することにより短時間に静的な統計ページを作成出来ます。
# `rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` -awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" \
    -config=<解析対象サイト名> -update -lang=jp -dir=/usr/share/awstats/wwwroot -year=2016 -month=07 -builddate=201607

 ■既存のデータベース(awstatsMMYYYY<解析対象サイト名>.txt)を再使用し静的なアクセス統計ページを作成する場合
# `rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" \
    -config=<解析対象サイト名> -lang=jp -dir=/usr/share/awstats/wwwroot -year=2016 -month=07 -builddate=201607
 最後にcronで毎日00:00にアクセス統計ページHTML版作成スクリプトが動作するように設定します。
# vi /etc/cron.d/awstatsreport
# 00 00 * * * root /root/awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot > /dev/null
 以上で設定は完了です。

 アクセスログの統合について

 参考URL:複数サーバーのアクセスログを統合するのにAWStatsが便利だった件

 一つのサーバで毎月一回、毎週一回アクセスログをローテートしている場合や複数サーバのアクセスログを扱う場合、解析する際に一度日付順に並び替えて統合したい場合があります。Awstatsの中にあるツール「logresolvemerge.pl」を利用すると非常に便利です。
logresolvemerge allows you to get one unique output log file, sorted on date,
built from particular sources:
 - It can read several input log files,
 - It can read .gz/.bz2/.xz log files,
 - It can also makes a fast reverse DNS lookup to replace
   all IP addresses into host names in resulting log file.
logresolvemerge comes with ABSOLUTELY NO WARRANTY. It's a free software
distributed with a GNU General Public License (See COPYING.txt file).
logresolvemerge is part of AWStats but can be used alone as a log merger
or resolver before using any other log analyzer.

Usage:
  logresolvemerge.pl [options] file
  logresolvemerge.pl [options] file1 ... filen
  logresolvemerge.pl [options] *.*
  logresolvemerge.pl [options] addfolder=dirname
  perl logresolvemerge.pl [options] *.* > newfile
Options:
  -dnslookup      make a reverse DNS lookup on IP adresses
  -dnslookup=n    same with a n parallel threads instead of serial requests
  -dnscache=file  make DNS lookup from cache file first before network lookup
  -showsteps      print on stderr benchmark information every 8192 lines
  -addfilenum     if used with several files, file number can be added in first
  -addfilename    if used with several files, file name can be added in first
                  field of output file. This can be used to add a cluster id
                  when log files come from several load balanced computers.
  -stoponfirsteof Stop processing when any logfile reaches end-of-file.
  -printfields    For IIS or W3C logs, prints the latest field header for
                  the currentlog file when switching between log file entries
                  so that the parsercan automatically determine which fields
                  are avaiable.
  -ignoremissing  will not fail if a log file is missing
(以下、省略)
 logresolvemerge.plがインストールされていない場合、下記のようにインストールします。
# dnf install awstats -y
 統合するには下記のようにします。
# perl /usr/share/awstats/tools/logresolvemerge.pl /var/log/httpd/access_log*.gz > /tmp/access_log_all.txt
# perl /usr/share/awstats/tools/logresolvemerge.pl /var/log/httpd/webA_access_log.gz /var/log/httpd/webB_access_log.gz > /tmp/access_log_all.txt
 月始めから月末までを統合するに下記のようにします。
時間でソート後に、2022年9月のログを抽出
# perl /usr/share/awstats/tools/logresolvemerge.pl \
    /var/log/access_log-20220828.gz,access_log-202209*.gz,access_log-20221002.gz} > /tmp/hcloud_origin_log_sort_202209.txt
# sed -e '/"Aug/2022"/d' /tmp/access_log_sort_202209.txt > /tmp/access_log_sort_202209_.txt
# sed -e '/"Oct/2022"/d' /tmp/access_log_sort_202209_.txt &gy; /tmp/access_log_sort_202209.txt
# fm -f /tmp/access_log_sort_202209_.txt
 残った/tmp/access_log_sort_202209.txtが、2022年9月1日00:00:00から2022年9月30日23:59:59を含んだログになります。

●Apacheのログ解析について

 Apacheのログ解析をしたいと思い調べていたところAWStatsなるものがありました。そういえば、以前、職場でも利用していたことを思い出し勉強がてらインストールしてみることにしました。

 参考URL:Apacheアクセスログ解析(AWStats)
 参考URL:AWStats ログファイル解析ツール 7.4 ドキュメント
 参考URL:CentOS 7 Awstatsインストール

 Apacheアクセスログ解析(AWStats)を参考に作業を進めます。

●Awatatsのインストール

 Awstatsをインストールし、少し環境を変更します。
# yum -y install awstats ← AWStatsインストール
# vi `rpm -ql awstats|grep "awstats\.pl"` ← awstats.pl編集
※レポートの日付形式を「月 9月 2016」から「2016年 9月」へ変更
if ( $MonthRequired eq 'all' ) {
        print "$Message[6] $YearRequired";
}
else {
        print
          #"$Message[5] $MonthNumLib{$MonthRequired} $YearRequired";
      ↑ 行頭に#を追加してコメントアウト
          "$YearRequired$Message[6] ".$MonthNumLib{$MonthRequired};
      ↑ 追加(日付をYYYY年 MM月形式にする)
}

        print( $MonthRequired eq 'all'
                ? "$Message[6] $YearRequired"
                #: "$Message[5] "  行頭に#を追加してコメントアウト
                  #. $MonthNumLib{$MonthRequired} ← 行頭に#を追加してコメントアウト
                  #. " $YearRequired" ← 行頭に#を追加してコメントアウト
                : "$YearRequired$Message[6] $MonthNumLib{$MonthRequired}"
         ↑ 追加(日付をYYYY年 MM月形式にする)
        );

                        #print "$MonthNumLib{$monthix}<br />$YearRequired";
                         ↑ 行頭に#を追加してコメントアウト
                        print "$YearRequired$Message[6]<br />$MonthNumLib{$monthix}";
                         ↑ 追加(日付をYYYY年 MM月形式にする)
        
                        #print "$MonthNumLib{$monthix} $YearRequired";
                         ↑ 行頭に#を追加してコメントアウト
                        print "$YearRequired$Message[6] $MonthNumLib{$monthix}";
                         ↑ 追加(日付をYYYY年 MM月形式にする)


●Awstatsの設定

 Awstatsの設定をします。
# rm -f /etc/awstats/awstats.localhost.localdomain.conf
 ↑ 不要な設定ファイルを削除
# rm -f /etc/awstats/awstats.`hostname`.conf
 ↑ 不要な設定ファイルを削除
# cp /etc/awstats/awstats.model.conf /etc/awstats/awstats.www.serverA.com.conf
 ↑ 設定ファイルをサンプルよりコピー
# vi /etc/awstats/awstats.www.serverA.com.conf
# "SiteDomain" must contain the main domain name, or the main intranet web
# server name, used to reach the web site.
# If you share the same log file for several virtual web servers, this
# parameter is used to tell AWStats to filter record that contains records for
# this virtual host name only (So check that this virtual hostname can be
# found in your log file and use a personalized log format that include the
# %virtualname tag).
# But for multi hosting a better solution is to have one log file for each
# virtual web server. In this case, this parameter is only used to generate
# full URL's links when ShowLinksOnUrl option is set to 1.
# If analysing mail log, enter here the domain name of mail server.
# Example: "myintranetserver"
# Example: "www.domain.com"
# Example: "ftp.domain.com"
# Example: "domain.com"
#
SiteDomain="localhost.localdomain"
 ↓
SiteDomain="www.serverA.com" ← Webサーバ名を指定
# Enter here all other possible domain names, addresses or virtual host
# aliases someone can use to access your site. Try to keep only the minimum
# number of possible names/addresses to have the best performances.
# You can repeat the "SiteDomain" value in this list.
# This parameter is used to analyze referer field in log file and to help
# AWStats to know if a referer URL is a local URL of same site or an URL of
# another site.
# Note: Use space between each value.
# Note: You can use regular expression values writing value with REGEX[value].
# Note: You can also use @/mypath/myfile if list of aliases are in a file.
# Example: "www.myserver.com localhost 127.0.0.1 REGEX[mydomain\.(net|org)$]"
#
HostAliases="localhost 127.0.0.1 REGEX[^192\.168\.1\.]"
 ↑ アクセス元から内部(例:192.168.1.X)を除外する
# If you want to have hosts reported by name instead of ip address, AWStats
# need to make reverse DNS lookups (if not already done in your log file).
# With DNSLookup to 0, all hosts will be reported by their IP addresses and
# not by the full hostname of visitors (except if names are already available
# in log file).
# If you want/need to set DNSLookup to 1, don't forget that this will reduce
# dramatically AWStats update process speed. Do not use on large web sites.
# Note: Reverse DNS lookup is done on IPv4 only (Enable ipv6 plugin for IPv6).
# Note: Result of DNS Lookup can be used to build the Country report. However
# it is highly recommanded to enable the plugin 'geoipfree' or 'geoip' to
# have an accurate Country report with no need of DNS Lookup.
# Possible values:
# 0 - No DNS Lookup
# 1 - DNS Lookup is fully enabled
# 2 - DNS Lookup is made only from static DNS cache file (if it exists)
# Default: 2
#
DNSLookup=2
↓
DNSLookup=1
 ↑ アクセス元の名前解決を行う(アクセス元をIPアドレスではなくホスト名で表示する)
# Plugin: GeoIPfree
# Perl modules required: Geo::IPfree version 0.2+ (from Graciliano M.P.)
# Country chart is built from an Internet IP-Country database.
# This plugin is useless for intranet only log files.
# Note: You must choose between using this plugin (need Perl Geo::IPfree
# module, database less up to date) or the GeoIP plugin (need Perl Geo::IP
# module from Maxmind, database more up to date).
# Note: Activestate provide a corrupted version of Geo::IPfree 0.2 Perl
# module, so install it from elsewhere (from www.cpan.org for example).
# This plugin reduces AWStats speed of 10% !
#
#LoadPlugin="geoipfree"
 ↓
LoadPlugin="geoipfree" ← コメント解除(アクセス元国情報を詳細に取得)
# PLUGIN: GeoIP
# REQUIRED MODULES: Geo::IP or Geo::IP::PurePerl (from Maxmind)
# PARAMETERS: [GEOIP_STANDARD | GEOIP_MEMORY_CACHE] [/pathto/geoip.dat[+/pathto/override.txt]]
# DESCRIPTION: Builds a country chart and adds an entry to the hosts 
# table with country name
# Replace spaces in the path of geoip data file with string "%20".
#
#LoadPlugin="geoip GEOIP_STANDARD /pathto/GeoIP.dat"
LoadPlugin="geoip GEOIP_STANDARD /usr/share/GeoIP/GeoIP-initial.dat"


※perl-Geo-IPfreeをインターネットからダウンロード出来なかった(みつからない)
# wget http://pkgs.repoforge.org/perl-Geo-IPfree/perl-Geo-IPfree-0.8-1.el6.rf.noarch.rpm
 ↑ perl-Geo-IPfreeダウンロード※CentOS6の場合
# wget http://pkgs.repoforge.org/perl-Geo-IPfree/perl-Geo-IPfree-0.8-1.el5.rf.noarch.rpm
 ↑ perl-Geo-IPfreeダウンロード※CentOS5の場合
 ↑※最新版のURLはダウンロードページで確認すること

# yum -y localinstall --nogpgcheck perl-Geo-IPfree-*.rpm
# rm -f perl-Geo-IPfree-*.rpm
そのためGeoIPをインストールしました。
# yum -y install GeoIP-1.5.0-11.el7.x86_64

※Rocky Linux 9の場合
# rpm -ivh http://www6.atomicorp.com/channels/atomic/centos/9/x86_64/RPMS/atomic-release-1.0-23.el9.art.noarch.rpm
# dnf install GeoIP -y


●日本の検索エンジン対応

 日本の検索エンジンが検索エンジンとして集計されるようにします。
# vi /usr/share/awstats/lib/search_engines.pm

search_engines.pm

 「Your web site, virtual server or profile name:」では、自分の管理するサイト名を記載してください。のちのち、統計画面の左上にも表示されることになります。
 Awstatsのディレクティブは、下記を参照してください。

 参考URL:AWStats構成ディレクティブ

 解析対象のログファイルを変更したい場合は、下記のようにします。
# /etc/awstats/awstats.<解析対象サイト名>.conf

#LogFile="/var/log/httpd/access_log"
 ↓
LogFile="/var/log/httpd/ssl_access_log"
 ログ解析ページが日本語表示されない場合は、下記のようにします。
#Lang="auto"
 ↓
Lang="jp"
 週の始まりを日曜日にする場合は、下記のようにします。
#FirstDayOfWeek=1
 ↓
FirstDayOfWeek=0
 解析情報から自サイト内からのアクセスであるIPアドレス及びドメインを除外する場合は、下記のようにします。

 ----- リンク対策 ここから -----
 バーチャルホスト等を設定していてサイト名とログ解析画面へのアクセス時のURLのホスト名(Webサーバ名)が異なる場合は、「■AWStats設定(1)AWStats設定」作業後作成されるファイル名に対してリンクを作成します。
Awstatsのログ解析画面への接続URLが次のような場合:http://www.serverA.com/…
# ln -s /etc/awstats/awstats.<解析対象サイト名>.conf /etc/awstats/awstats.www.serverA.com.conf
 ----- リンク対策 ここまで -----

 解析対象サイト名とWebサーバ名が異なるとログ解析画面へのアクセス時に「Error: Couldn't open config file …」のようなエラーが表示されます。上記リンク対策を実施しなくても下記のようにすればエラーにならずに表示されるようになります。
http://<Webサーバ名>/awstats/awstats.pl?config=<解析対象サイト名>

 解析画面のアクセスホストの一覧から内部のホストを表示させないようにするには下記のように設定します。
 ただし、IPアドレスの除外はログにIPアドレスで記録済みであること、ホスト名(FQDN等)の場合はログに名前解決済みのホスト名(FQDN等)で記録済みであることが条件となります。
SkipHosts="127.0.0.1 REGEX[^192\.168\.] REGEX[^1\.0\.0\.] REGEX[^.*\.intra\.net\.com$]"
 過去のログに対しても上記を反映させるには、DirDataディレクティブで記載されているディレクトリ内の該当テキストファイルを一度削除する必要があるようです。
 したがって、過去ログが残っていない場合その部分は反映されないことになります。

 AWStatsデータベース作成するため、Apacheアクセスログ解析(AWStats)を参照してスクリプトを用意します。
 ただし、このスクリプトを使用してデータベース作成前に自前サーバのログの形式を確認しておく必要があります。自宅のサーバログは下記のような形式でした。

192.168.0.1 - - [01/Sep/2016:17:13:41 +0900] "GET / HTTP/1.1" 200 40132 "http://www.from.com/from.html" "Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Firefox/45.0"


 このため、LogFormatはデフォルトのままで問題ありませんでした。
# /etc/awstats/awstats.<解析対象サイト名>.conf
LogFormat=W


●awstats.plのアクセス制限

 インターネット側からアクセスされないよう/etc/httpd/conf.d/awstats.confを下記のように設定します。
<Directory "/usr/share/awstats/wwwroot">
    DirectoryIndex awstats.pl
    Options None
    #Options ExecCGI
    AllowOverride None
    <IfModule mod_authz_core.c>
        # Apache 2.4	
        Require local
        Require ip 192.168.100.
        Require ip 192.168.200.
    </IfModule>
    <IfModule !mod_authz_core.c>
        # Apache 2.2
        Order allow,deny
        Allow from 127.0.0.1
        Allow from ::1
    </IfModule>
</Directory>
# Additional Perl modules


●AWStatsデータベース初期作成

 Apacheのログファイルを過去分も含めて全てAWStatsのデータベースに取り込みます。

 データベースを取り込むためのスクリプトは下記のとおりです。
■スクリプトその1
# vi awstatsinit.sh
#!/bin/sh

# データベース作成先ディレクトリ作成
mkdir -p `grep ^DirData /etc/awstats/awstats.$1.conf | awk -F= '{print $2}'|tr -d \"`

# AWStats設定ファイルよりApacheログファイル名取得
logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p' -e d`
#### 場合によっては下記行に変更しないと動作しない
#logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p'`

# 過去ログを古い順に処理
grep dateext /etc/logrotate.conf > /dev/null 2>&1
if [ $? -eq 0 ]; then
ls $logfile-* > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 過去ログファイル名に日付が設定されている場合(CentOS6以降)
for log in `ls $logfile-*|sort`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done
fi
else
ls $logfile.* > /dev/null 2>&1
if [ $? -eq 0 ]; then
# 過去ログファイル名に通番が設定されている場合(CentOS5以前)
for log in `ls $logfile.*|sort -r`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done
fi
fi

# 現在ログ処理
for log in `ls $logfile|sort`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
# -config=$1 -update -showdropped -logfile=$log
done


■スクリプトその2
# vi awstatsinit.sh
#!/bin/sh

logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p' -e d`
#### 場合によっては下記行に変更しないと動作しない
#logfile=`grep ^LogFile /etc/awstats/awstats.$1.conf|sed -e 's/LogFile="\([^ ]*\)"/\1/p'`
for log in `ls $logfile*|sort -r`
do
`rpm -ql awstats|grep "awstats\.pl"` \
-config=$1 -update -logfile=$log
done


 スクリプトを実行します。ログサイズ及びログ数により処理時間を要します。
# sh awstatsinit.sh <解析対象サイト名>
例:# sh awstatsinit.sh www.serverA.com
Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=<解析対象サイト名> -configdir="/etc/awstats"' \
to update config www.data.jma.go.jp
Create/Update database for config"/etc/awstats/awstats.<解析対象サイト名>.conf" by AWStats version 7.4 (build 20150714)
From data in log file "/v/ar/log/httpd/ssl_access_log|"...
Phase 1 : First bypass old records, searching new record...
Searching new records from beginning of log file...
  :
(途中省略)
  :
Flush history file on disk (unique hosts reach flush limit of 20000)
Flush history file on disk (unique hosts reach flush limit of 20000)
Jumped lines in file: 0
Parsed lines in file: 1010166052
 Found 454 dropped records,
 Found 0 comments,
 Found 0 blank records,
 Found 23627196 corrupted records,
 Found 28849736 old records,
 Found 957688666 new qualified records.


 参考URL:ログ解析スクリプトAWStats 7.1ドキュメント

 Dropped recordsは、「ユーザHTTPリクエスト」でなかったかAWStatsフィルタ条件により捨てられたレコードの数です。(SkipHosts、SkipUserAgents、SkipFiles、OnlyHosts、OnlyUserAgentsおよびOnlyFilesパラメタを参照ください。)どのような条件で破棄されたか確認する場合、コマンドラインで-showdroppedオプションを付加します。
 Corrupted recordsはAWStats構成ファイルの"LogFormat"パラメタによって定義されたログ書式に合っていないレコードです。すべてが正しく動作していても、すべてのウェブサーバにはわずかなCorrupted records(<5%)を出力するのが普通です。これはいくつかの理由があります。
 1)ウェブサーバーの内部のバグ
 2)不正なブラウザからの不正なリクエスト
 3)サーバのプラグを抜くことなどの汚いウェブサーバーシャットダウン等。
 すべてがCorrupted recordsとなり、AWStats構成ファイルのLogFormatパラメタが正しいなら、ウェブサーバーログ形式に関するセットアップ問題があるかもしれません。AWStats構成ファイルのLogFormatパラメタは、分析するサーバログファイル形式に合わせる必要があります。。どの行がCorrupted recordsとなったかを見たいなら、コマンドラインで-showcorruptedオプションを付加することができます。
 Old recordsは単に前の更新セッションで既に処理されたレコードです。各更新処理の後にあなたのログファイルを削除するのは必須ではありませんが、できる限り削除するのをお勧めします。
 New recordsは統計データベースを作成及び更新に問題なく使用されたログファイルのレコードです。

 awstats.plのオプション

 統計情報更新のためのオプション

 -update
  統計情報を更新します(これが既定値)
 -showsteps
  8192行処理するごとにベンチマーク情報を付加する。
 -showcorrupted
  処理がうまく行かなかった行があった場合、その理由と合わせてうまく行かなかったことを出力する。
 -showdropped
  破棄された行があった場合、その理由と合わせて破棄されたことを出力する。
 -showunknownorigin
  解析不能な参照元を含む行があった場合、そのことを出力する。
 -showdirectorigin
  別のページからリンクをたどってきたのではなく、直接このページにアクセスされていた場合にそのことを出力する。
 -updatefor=行数
  行数に指定した行数を処理したところで更新処理を停止する。
 -LogFile=ログファイル名
  解析対象のログファイルを指定する。この指定は設定ファイルの LogFile ディレクティブによる指定より優先される。なお、更新処理を行う際、時系列順にログファイルを処理するようにして下さい(既にデータベースにある情報よりも新しいもののみが更新対象となるため)。

 統計情報閲覧のためのオプション

 -output
  メインのHTMLレポートを出力する(-updateオプションも合わせて指定しない限り、更新処理は行わない)。
 -output=出力項目
  指定した項目のHTMLレポートを出力する。指定できる項目と意味は以下の通り
  出力項目名 内容
alldomains 全ドメイン・国名一覧ページを作成する
  allhosts  全ホスト一覧ページを作成する
  lasthosts  各ホストの最終アクセス時刻ページを作成する
  unknownip  アクセス元IPアドレスが不明であるアクセスの情報ページを作成する
  allemails  (メールログのみ)全ての電子メール送信先一覧ページを作成する
 -staticlinks
  固定リンクをHTMLレポートページに埋め込む。
 -staticlinksext=拡張子
  固定リンクをHTMLレポートページに埋め込む。但し拡張子を.htmlから指定した拡張子に置き換える。
 -lang=言語コード
  指定した言語でHTMLレポートを作成する。言語の指定は言語の2文字コード(ja、en、de、es、fr、it、nlなど)で行う。
 -month=月
  過去の指定月のレポートを作成する。月は1〜12の整数で指定。
 -year=年
  過去の指定年のレポートを作成する。年は4桁の整数で西暦を指定。

 以降、AWStatsデータベースの更新は/etc/cron.hourly/awstatsにより1時間毎に自動実行されます。
 http://Webサーバ名/awstats/awstats.plへアクセスしてアクセス統計ページが表示されることを確認します。

●Apacheのログローテーションの設定

 Apacheログファイルのローテーション時、AWStatsのデータベースに取り込んでからローテーションするように設定します。
# vi /etc/logrotate.d/httpd
/var/log/httpd/*log {
    missingok
    notifempty
    sharedscripts
    ---- 追加(ここから) ----
    prerotate
        `rpm -ql awstats|grep "awstats_updateall\.pl"` now -confdir="/etc/awstats" \
        -awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" >/dev/null
    endscript
    ---- 追加(ここまで) ----
    postrotate
        /bin/kill -HUP `cat /var/run/httpd.pid 2>/dev/null` 2> /dev/null || true
    endscript
}
 httpで接続するWebサーバ名とhtmlで表示されるアクセス統計ページのドメイン名が異なると「awstats.<解析対象サイト名>.confと一致しません」というようなエラーが表示されますので注意してください。この場合、http://Webサーバ名/awstats/awstats.pl?config=<解析対象サイト名>でアクセスします。

●アクセス統計ページHTML版の作成

 awstats.plを実行するたびにAWStatsのデータベースからログ情報を入力してアクセス統計ページを作成するため、サーバに負荷がかかることから、awstats.plは内部からのみ実行できるようにします。また、万が一、AWStatsのデータベースが失われた場合、アクセス統計が見れなくなってしまうため、アクセス統計ページをHTMLで作成するようにします。
 Rocky Linux 8でのアクセス統計ページの保存ディレクトリ先を/usr/share/awstats/wwwrootとしました。これは既に該当ディレクトリがWebサーバで公開されているからです。
 また、インターネットからの不要なアクセスを防ぐためアクセス制限を実施します。
# vi /etc/httpd/conf.d/awstatsreport.conf
Alias /awstatsreport "/usr/share/awstats/wwwroot"
インターネット側からアクセスされないように設定
<Location "/awstatsreport">
    Order deny,allow
    Deny from all
    Allow from 127.0.0.1
    Allow from 192.168.1.0/24  ← 内部ネットワークアドレスを指定
</Location>
# systemctl reload httpd
 アクセス統計ページHTML版作成ためのスクリプト(awstatsreport.sh)を作成します。

# vi awstatsreport.sh
#!/bin/bash

site=$1
dir=$2

reportbuild() {
# AWStatsデータベース更新中断時ロックファイル残存対処(ここから)
if [ -f /tmp/awstats.$site.lock ]; then
ps -p `cat /tmp/awstats.$site.lock |awk '{print $6}'`
if [ $? -ne 0 ]; then
echo /tmp/awstats.$site.lock removed >&2
rm -f /tmp/awstats.$site.lock
fi
fi
# AWStatsデータベース更新中断時ロックファイル残存対処(ここまで)
`rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` \
-awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`"\
-config=$site -update -lang=jp -dir=$dir \
-year=$YEAR -month=$MONTH -builddate=$YEAR$MONTH
# 個別ページリンク切れ対処(ここから)
sed -i "s/href=\"${site}/href=\"awstats.${site}/g" \
$dir/awstats.$site.$YEAR$MONTH.html
# 個別ページリンク切れ対処(ここまで)
if [ "$YEAR$MONTH" = $(date +%Y%m) ]; then
mv $dir/awstats.$site.$YEAR$MONTH.html $dir/index.html
else
mv $dir/awstats.$site.$YEAR$MONTH.html $dir/$YEAR$MONTH.html
fi
}

ls $dir/* > /dev/null 2>&1
if [ $? -eq 0 ]; then
YEAR=`date --date '1 days ago' +%Y`
MONTH=`date --date '1 days ago' +%m`
reportbuild
else
DirData=`grep ^DirData /etc/awstats/awstats.model.conf|awk -F= '{print $2}'|tr -d \"`
for log in `ls $DirData/awstats*.$site.txt`
do
YEAR=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats..\([^ ]*\)/\1/p' -e d`
MONTH=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats\([^ ]*\)..../\1/p' -e d`
#YEAR=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats.\([^ ]*\)/\1/p' -e d`
#MONTH=`echo $log|cut -d / -f 5|cut -d . -f 1|sed -e 's/awstats\([^ ]*\)./\1/p' -e d`
reportbuild
done
fi


 実行権限を与え、アクセス統計ページHTML版作成スクリプトを実行します。
# chmod 700 awstatsreport.sh
# ./awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot
 実行すると下記のようなログが出力されました。

# ./awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot
Launch update process : "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -update -configdir=
Build main page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output
Build alldomains page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=alldomains
Build allhosts page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=allhosts
Build lasthosts page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=lasthosts
Build unknownip page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=unknownip
Build allrobots page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=allrobots
Build lastrobots page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=lastrobots
Build session page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=session
Build urldetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=urldetail
Build urlentry page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=urlentry
Build urlexit page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=urlexit
Build osdetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=osdetail
Build unknownos page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=unknownos
Build browserdetail page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=browserdetail
Build unknownbrowser page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=unknownbrowser
Build downloads page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=downloads
Build refererse page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=refererse
Build refererpages page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=refererpages
Build keyphrases page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=keyphrases
Build keywords page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=keywords
Build errors400 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=errors400
Build errors403 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=errors403
Build errors404 page: "/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -config=<解析対象ファイル名> -staticlinks=awstats.<解析対象ファイル名>.201609 -lang=jp -month=09 -year=2016 -output=errors404
23 files built.
Main HTML page is 'awstats.<解析対象ファイル名>.201609.html'.


 http://Webサーバ名/awstatsreport/へアクセスして今月分のアクセス統計ページが表示されることを確認します。
 過去のアクセス統計ページはhttp://Webサーバ名/awstatsreport/年月(YYYYMM).html(例:http://Webサーバ名/awstatsreport/200601.html)のようにアクセスします。
 上記スクリプトがうまく動作せず今月分しか静的なアクセス統計ページが作成されない場合、下記いずれかの方法により作成することが可能です。いずれも静的なアクセス統計ページは/tmpに出力されます。

 ■設定ファイル(/etc/awstats/awstats.<解析対象サイト名>.conf)内のDirDataで定義されたディレクトリ内にデータベース(awstatsMMYYYY<解析対象サイト名>.txt)が存在する・しないに関わらず新たにデータベースを作成しつつ静的なアクセス統計ページを作成する場合

 データベースも再作成しますので解析するログを絞った上で実行することにより短時間に静的な統計ページを作成出来ます。
# `rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` -awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" \
-config=<解析対象サイト名> -update -lang=jp -dir=/tmp -year=2016 -month=07 -builddate=201607

 ■既存のデータベース(awstatsMMYYYY<解析対象サイト名>.txt)を再使用し静的なアクセス統計ページを作成する場合
# `rpm -ql awstats|grep "awstats_buildstaticpages\.pl"` awstatsprog="`rpm -ql awstats|grep "awstats\.pl"`" \
-config=<解析対象サイト名> -lang=jp -dir=/tmp -year=2016 -month=07 -builddate=201607
 最後にcronで毎日00:00にアクセス統計ページHTML版作成スクリプトが動作するように設定します。
# vi /etc/cron.d/awstatsreport
# 00 00 * * * root /root/awstatsreport.sh <解析対象サイト名> /usr/share/awstats/wwwroot > /dev/null
 以上で設定は完了です。

●データベース更新のcronが動作しない

 デフォルトで1時間毎に更新されるはずのcronが動作しない現象が発生しました。

# cat /etc/corn.hourly/awstats
#!/bin/bash
exec /usr/share/awstats/tools/awstats_updateall.pl now -configdir="/etc/awstats" -awstatsprog="/usr/share/awstats/wwwroot/cgi-bin/awstats.pl"
exit 0


 上記のコマンドをbash -x /etc/corn.hourly/awstatsのようにデバッグモードで動作させましたが特にエラーは表示されませんでした。
 検証のためスクリプトを作成し試験してみました。
 /etc/corn.hourly/awstatsを別の場所に保存し、crondを再起動し動作しないようにします。
 検証用のスクリプトを/etc/cron.dに保存します。

# cat /etc/cron.d/awstats
MAILTO=root

*/2 * * * * root /root/awstats

# cat /root/awstats
#!/bin/sh

exec /usr/share/awstats/tools/awstats_updateall.pl now -configdir="/etc/awstats" -awstatsprog="/usr/share/awstats/wwwroot/cgi-bin/awstats.pl"

exit 0


 コマンドのパス等を確認しましたが、やはりコマンドでは正常に動作するもののcronでは動作しませんでした。
 そこでcron動作時にどのように動作しているのかをログに書き出す(「●cronが実行されないときの確認方法」参照)ことにしました。

# cat /root/awstats
#!/bin/sh

exec /usr/share/awstats/tools/awstats_updateall.pl now -configdir="/etc/awstats" -awstatsprog="/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" >>/tmp/exec.log 2>>/tmp/exec_error.log

exit 0


 下記のようなログが記録されていました。

Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=www.serverA.com -configdir="/etc/awstats"' to update config www.serverA.com
Error: Plugin load for plugin 'geoipfree' failed with return code: Error: Can't locate Geo/IPfree.pm in @INC (@INC contains: /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl /usr/share/perl5/vendor_perl /usr/lib64/perl5 /usr/share/perl5 . /usr/share/awstats/lib /usr/share/awstats/plugins /usr/share/awstats/wwwroot/cgi-bin/plugins) at (eval 4) line 1.

Setup ('/etc/awstats/awstats.www.serverA.com.conf' file, web server or permissions) may be wrong.
Check config file, permissions and AWStats documentation (in 'docs' directory).


 何とインストールしたはずのGeo/IPfreeのパスが通っていませんでした。
 早速対策を実施します(●rootでcpanコマンドを実行するとモジュールが/root/perl5にインストールされる)。
 再度ログを確認します。今度は違うエラーが記録されました。

Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=www.serverA.com -configdir="/etc/awstats"' to update config www.serverA.com
Error: Plugin init for plugin 'geoipfree' failed with return code: Can't load database, blank or not there: at /usr/share/awstats/plugins/geoipfree.pm line 53.
(A module required by plugin might be missing).
Setup ('/etc/awstats/awstats.www.serverA.com.conf' file, web server or permissions) may be wrong.
Check config file, permissions and AWStats documentation (in 'docs' directory).


 何かしらのデータベースを読み込めなさそうです。

 参考URL:Linux/log解析/http logの解析/アクセス元国情報を取得

 GeoIPのデータベースをアップデートしてみました。

# geoipupdate /usr/share/GeoIP/GeoLiteCountry.dat can't be opened, proceeding to download database
Updating /usr/share/GeoIP/GeoLiteCountry.dat
Updated database
/usr/share/GeoIP/GeoLiteCity.dat can't be opened, proceeding to download database
Updating /usr/share/GeoIP/GeoLiteCity.dat
Updated database


 状況は変わりませんでした。
 Awstatsの設定ファイルを変更してみました。
# vi /etc/awstats/awstats.www.serverA.com.conf
LoadPlugin="geoipfree"
 ↓
#LoadPlugin="geoipfree"
#LoadPlugin="geoip GEOIP_STANDARD /pathto/GeoIP.dat"
 ↓
LoadPlugin="geoip GEOIP_STANDARD /usr/share/GeoIP/GeoLiteCity.dat"


Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=www.serverA.com -configdir="/etc/awstats"' to update config www.serverA.com
Create/Update database for config "/etc/awstats/awstats.www.serverA.com.conf" by AWStats version 7.4 (build 20150714)
From data in log file "/var/log/httpd/ssl_access_log"...
Phase 1 : First bypass old records, searching new record...
Direct access after last parsed record (after line 20536)
Jumped lines in file: 20536
Found 20536 already parsed records.
Parsed lines in file: 350
Found 318 dropped records,
Found 0 comments,
Found 0 blank records,
Found 0 corrupted records,
Found 0 old records,
Found 32 new qualified records.
Invalid database type GeoIP City Edition, Rev 1, expected GeoIP Country Edition
Invalid database type GeoIP City Edition, Rev 1, expected GeoIP Country Edition


 「Invalid database type…」と言うのが表示されたので、ここで再度データベースの初期化を実行しました。
 再度、ログを確認します。

Running '"/usr/share/awstats/wwwroot/cgi-bin/awstats.pl" -update -config=www.serverA.com -configdir="/etc/awstats"' to update config www.serverA.com
Create/Update database for config "/etc/awstats/awstats.www.serverA.com.conf" by AWStats version 7.4 (build 20150714)
From data in log file "/var/log/httpd/ssl_access_log"...
Phase 1 : First bypass old records, searching new record...
Direct access after last parsed record (after line 20891)
Jumped lines in file: 20891
Found 20891 already parsed records.
Parsed lines in file: 1
Found 1 dropped records,
Found 0 comments,
Found 0 blank records,
Found 0 corrupted records,
Found 0 old records,
Found 0 new qualified records.


 問題なく更新されるようになりました。バックアップしてあった/etc/cron.hourly/awstasを元に戻しても正常に動作することを確認しました。

●logwatchにおけるGeoIP

 logwatchで下記のようなエラー(?)が記録されていました。実際には/var/log/messagesで記録されたものでした。

--------------------- Named Begin ------------------------


Received control channel commands
reload: 1 Time(s)

**Unmatched Entries**
GeoIP AS (type 9) DB not available: 1 Time(s)
GeoIP City (IPv4) (type 2) DB not available: 1 Time(s)
GeoIP City (IPv4) (type 6) DB not available: 1 Time(s)
GeoIP City (IPv6) (type 30) DB not available: 1 Time(s)
GeoIP City (IPv6) (type 31) DB not available: 1 Time(s)
GeoIP Country (IPv4) (type 1) DB not available: 1 Time(s)
GeoIP Country (IPv6) (type 12) DB not available: 1 Time(s)
GeoIP Domain (type 11) DB not available: 1 Time(s)
GeoIP ISP (type 4) DB not available: 1 Time(s)
GeoIP NetSpeed (type 10) DB not available: 1 Time(s)
GeoIP Org (type 5) DB not available: 1 Time(s)
GeoIP Region (type 3) DB not available: 1 Time(s)
GeoIP Region (type 7) DB not available: 1 Time(s)
all zones loaded: 1 Time(s)
sizing zone task pool based on 8 zones: 1 Time(s)
using built-in DLV key for view _default: 1 Time(s)

---------------------- Named End -------------------------


 参考URL:GeoIPをPHPで使うためにインストール

 上記URLを参考に設定してみました。
 果たして効果あるでしょうか。

●データ移行

 参考URL:サーバ移行時の AWStats の移行方法

 サーバを更新するためデータを移行する作業が必要になりました。
 awstatsを稼働させるための基本的な作業(設定ファイルの修正作業等)は完了しているものとします。

 旧サーバから新サーバへログ分析データ(awstats012017.<解析対象サイト名>.txtのようなファイル)をすべてコピーします。
# scp /var/lib/awstats/awstats* root@serverA:/var/lib/awstats
 下記のコマンドを実行し完了です。
# /usr/share/awstats/tools/awstats_updateall.pl now -awstatsprog=/var/www/awstats/awstats.pl -configdir=/etc/awstats
 これ移行、通常の運用に入ることができます。

●AWStatsデータベースの再作成

 参考URL:AWStatsの再集計方法について

 何故か7月~9月までの統計情報が作成されていない。
 作業を開始する前にバックアップしておきます。
 AWStatsデータベースはCentOS 7では/var/lib/awstatsでした。
 その他のOSについては、/etc/awstats/awstats.<解析対象サイト名>.confファイル内のDirDataを参照してください。
# cp -p /var/lib/awstats/*.txt /awstats_data_bk/
 バックアップが完了したら、マージファイルを作成します。
 アクセスログは1日1回作成していますので、月単位にまとめます(下記は2019年8月のアクセスログを/tmpフォルダに出力)。
# /usr/share/awstats/tools/logresolvemerge.pl /var/log/httpd/access_log-201908* > /tmp/access_log-201908
 ローテンションの関係上、月初めのログには前月分のログが記録されています。
 したがって、上記で作成した/tmp/access_log-201908には7月31日分のログが混じっていますので、これを削除します。
# sed -e /'31\/Jul\/2019'/d /tmp/access_log-201908 > /tmp/access_log-201908_cor
 更に、/tmp/access_log-201909のログに8月31日分のログが混じっていますので、これを抜き出してマージします。
# cat /tmp/access_log-201909|grep "31/Aug/2019" > /tmp/ccess_log-201908_add
# cat /tmp/access_log-201908_add >> /tmp/access_log-201908_cor
 これらの作業後、作成したデータベースをもとに集計ファイルを生成します。
# /usr/share/awstats/wwwroot/cgi-bin/awstats.pl -updates -config=awstats.<解析対象サイト名>.conf \
-logfile=/tmp/access_log-201908_cor
Create/Update database for config "/etc/awstats/awstats.<解析対象サイト名>.conf" by AWStats version 7.7 (build 20180105)
From data in log file "/tmp/access_log-201908_cor"...
Phase 1 : First bypass old records, searching new record...
Direct access to last remembered record has fallen on another record.
So searching new records from beginning of log file...
Phase 2 : Now process new records (Flush history on disk after 20000 hosts)...
Jumped lines in file: 0
Parsed lines in file: 99702
 Found 81984 dropped records,
 Found 0 comments,
 Found 0 blank records,
 Found 17718 corrupted records,
 Found 0 old records,
 Found 0 new qualified records.
 すると、/var/lib/awstatsフォルダにawstats082019.txtが出来上がりますので、これをmvします。
# mv /var/lib/awstats/awstats082019.txt /var/lib/awstats/awstats082019.<解析対象サイト名>.txt
 後はWebでawstatsにアクセスして統計情報を見ます。

●統計データが作成されない

 参考URL:AWStatsのインストール、構成、レポート

 後で気がついた事ですが、2019年7月中旬ころから訪問者数が減っていました。
 詳しく調査したところ、DirData(/var/www/awstats)にデータベース(awstatsMMYYYY.<解析対象サイト名>.txt)が作成されていませんでした。
 そのため、2019年7月中旬以降の訪問者数が激減したように見えます。
 ところがアクセスログを確認したところ、それなりに訪問者数があることが分かりました。 
 AwstatsやHTTPのアクセスログの設定変更はしておらず原因をなかなか突き止めることが出来ませんでした。
 DirData(/var/www/awstats)に存在するデータベースを一時的に他のフォルダに移動し、原因を突き止めることにしました。
 ログファイルを指定してどのような挙動を振る舞うか確認します。
# `rpm -ql awstats|grep "awstats\.pl"` -config=<解析対象サイト名> -update -logfile=/var/log/httpd/access_log-20190714
Jumped lines in file: 0
Parsed lines in file: 385
 Found 67 dropped records,
 Found 0 comments,
 Found 0 blank records,
 Found 318 corrupted records,
 Found 0 old records,
 Found 0 new qualified records.
 corrupted recordsが多い感じがします。corrupted recordsはAWStats構成ファイルの"LogFormat"パラメタによって定義されたログ書式に合っていないレコードを意味します。
 したがって、apacheで設定しているログ形式とawstatsで解析する時のログが合っていないことになります。
 でも、2019年7月中旬頃に設定変更した記憶は無く、確認したところどちらの設定ファイルでもcombined形式で設定されていました。
 そこで「-showcorrupted」を付加して実行し、どのログが弾かれているのか確認することにしました。
# `rpm -ql awstats|grep "awstats\.pl"` -config=<解析対象サイト名> -update -logfile=/var/log/httpd/access_log-20190714 -showcorrupted
 すると、弾かれているログは、awstatsの設定ファイル(/etc/awstats/awstats.<解析対象サイト名>.conf)に記載されている形式と異なっていました(\"%{Referer}i\" \"%{User-Agent}i\"の記録が脱落している。)。
 apacheのログには「\"%{Referer}i\" \"%{User-Agent}i\"」を取得するよう設定されているのですが、ログには記録される場合とそうでない場合のログとなっていました。
 何故かインターネット側からアクセスするログのほとんどが「\"%{Referer}i\" \"%{User-Agent}i\"」の情報が無いログとなっていました。
 試しにawstatsの設定ファイル(/etc/awstats/awstats.<解析対象サイト名>.conf)のログ形式を下記のように変更しました。
# vi /etc/awstats/awstats.<解析対象サイト名>.conf
LogFormat = 1
  ↓
LogFormat = "%host %other %logname %time1 %methodurl %code %bytesd"
 この設定で下記コマンドを実行しました。
# rm -f /var/lib/awstats/*
# `rpm -ql awstats|grep "awstats\.pl"` -config=<解析対象サイト名> -update -logfile=/var/log/httpd/ssl_access_log-20190714
Jumped lines in file: 0
Parsed lines in file: 385
 Found 67 dropped records,
 Found 0 comments,
 Found 0 blank records,
 Found 27 corrupted records,
 Found 0 old records,
 Found 291 new qualified records.
 corrupted recordsの数が激減し、DirData(/var/www/awstats)にデータベース(awstatsMMYYYY.<解析対象サイト名>.txt)が作成されるようになりました。
 何故、apacheのログで「\"%{Referer}i\" \"%{User-Agent}i\"」の情報が取得できなくなったかは不明ですが、コメントアウトしていたLogFormat行をコメントインし、再起動したことろ正常に取得できるようになりました。

●user-agentが"-"の場合でも"empty user agent string"と表示させずに解析させる

 user-agentが"-"の場合、解析せず"empty user agent string"にまとめられてしまいます。
 user-agentが"-"の場合でも"empty user agent string"と表示させずに解析させるには下記のようにします。
# cp -p /usr/share/awstats/wwwroot/cgi-bin/awstats.pl /usr/share/awstats/wwwroot/cgi-bin/awstats.pl.org
# vi /usr/share/awstats/wwwroot/cgi-bin/awstats.pl
下記のようにコメントアウト
if ( $UserAgent && $UserAgent eq '-' ) { $UserAgent = ''; }
 ↓
#if ( $UserAgent && $UserAgent eq '-' ) { $UserAgent = ''; }