Quantcast
Channel: AFPy's Planet
Viewing all articles
Browse latest Browse all 3409

[hautefeuille] Traitement d’un fichier de log Apache et génération de svg

$
0
0

Objectif

Traiter un fichier de log Apache d’un hôte virtuel puis générer un graphique synthétique des visiteurs uniques à travers le monde.

Principes

On traite le fichier, on génère des listes de données, on géolocalise les adresses ip rencontrées puis on génère un graphique svg.

L’utilisation de ce script nécessite l’installation des modules Python suivants :

  • clize pour la gestion des arguments
  • geoip pour la géolocalisation
  • pygal pour la génération SVG.

Le script Python

Le script est largement adaptable à diverses situations. La documentation de Pygal est claire pour permettre une personnalisation aisée du graphique.

On utilise le script de cette manière :

python grabip.py log.txt

Le script, dans son état actuel, ne traite que les adresses ip, donc peu importe le formatage du fichier de log par le fichier de configuration d’Apache.

Le script produit un fichier rapport.txt et un fichier chart.svg.

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

from __future__ import print_function

"""
Usage: grabip.py [OPTIONS] filein

"""

import re
import collections

import GeoIP
import clize

import pygal
from pygal.style import NeonStyle

class Inspector(object):
    '''
    Extract IP from log file, count ip, localise with GeoIP country
    '''
    def __init__(self, in_file, **kwds):
        super(Inspector, self).__init__(**kwds)
        self.in_file = open(in_file, 'r')
        self.exclude = ["0.0.0.0", "127.0.0.1"]
        self.ip = []
        self.cnt = collections.Counter()
        self.cnt_u = collections.Counter()
        self.result = []
        self.hits = {}
        self.uniques = {}
        self.total = 0
        self.unique = 0
        self.killdoublon = set()
        self.geoip = GeoIP.new(GeoIP.GEOIP_MEMORY_CACHE)

        self.filter()
        self.count()
        self.geo()
        self.hitbycountry()
        self.totalhits()
        self.totalunique()
        self.uniquebycountry()
        self.in_file.close()

    def filter(self):
        '''
        Strip line and regex for ip

        self.ip return ['78.46.16.11', '78.46.16.11']
        '''
        for line in self.in_file:
            ipp = re.search("([\d]+\.[\d]+\.[\d]+\.[\d]+)",
                            line.rstrip('\n\r'))
            if ipp and ipp not in self.exclude:
                self.ip.append(ipp.group())

        print("=> filter : %s" % self.ip)

    def count(self):
        '''
        Add a count for all ip found

        self.cnt.items() return [('78.46.16.11', 2)]
        '''
        for ip in self.ip:
            self.cnt[ip] += 1

        print("=> count : %s" % self.cnt.items())

    def geo(self):
        '''
        Add goip information to dataset

        self.result return [('78.46.16.11', 2, 'DE')]
        '''
        for ip, i in self.cnt.items():
            country = self.geoip.country_code_by_addr(ip)
            if country:
                self.result.append((ip, i, country))

        print("=> geo : %s" % self.result)

    def hitbycountry(self):
        '''
        Count hit by country

        self.hits return {'DE': 2}
        '''
        for ip, hit, country in self.result:
            if country in self.hits:
                tmp = int(self.hits.get(country)) + int(hit)
                self.hits[country] = tmp
            else:
                self.hits[country] = hit

        print("=> hits : %s" % self.hits)

    def uniquebycountry(self):
        '''
        Count unique by country
        '''
        for ip in self.killdoublon:
            country = self.geoip.country_code_by_addr(ip)
            if country:
                self.cnt_u[country] += 1
        self.uniques = dict(self.cnt_u)

        print("=> uniques : %s" % self.uniques)

    def totalhits(self):
        '''
        Return total hits

        self.total return 2
        '''
        for value in self.hits.itervalues():
            self.total += value

        print("=> total hits : %s" % self.total)

    def totalunique(self):
        '''
        Return total unique visitor

        self.unique return 1
        '''
        for elem in self.ip:
            self.killdoublon.add(elem)
        self.unique = len(self.killdoublon)

        print("=> total unique : %s" % self.unique)

class Reporting(Inspector):
    '''
    Create a report file text
    '''
    def __init__(self, in_file, **kwds):
        super(Reporting, self).__init__(in_file, **kwds)
        self.out_file = open("rapport.txt", 'w')
        self.simplereport()
        self.out_file.close()

    def simplereport(self):
        '''
        Print result and write file report
        '''
        for ip, i, country in self.result:
            self.out_file.write(ip + ' ' + str(i) + ' ' + country + '\n')

        print("=> Report done")

class Drawing(Reporting):
    '''
    Create visual representation

    '''
    def __init__(self, in_file, **kwds):
        super(Drawing, self).__init__(in_file, **kwds)
        chart = pygal.HorizontalBar(
            width=800,
            height=600,
            x_title="Visites",
            y_title="Pays",
            legend_at_bottom=True,
            tooltip_border_radius=10,
            human_readable=True,
            no_data_text='No result found',
            pretty_print=True,
            style=NeonStyle)
        chart.title = u"Visiteurs uniques à travers le monde sur\
        le site hautefeuille.eu en 2013"
        for k, v in self.uniques.iteritems():
            if v >= 50:
                chart.add(k, [{'value': v, 'label': k}])
        chart.render_to_file('chart.svg')

@clize.clize()
def main(filein):
    '''
    Main
    '''
    grab = Drawing(filein)

if __name__ == '__main__':
    clize.run(main)

Remarque : le script ne contient pas de difficulté particulière. Ce qui est peut-être méconnu : l’utilisation des compteurs d’éléments (collections.Counter()) et l’utilisation des set() qui permettent facilement d’obtenir une liste sans doublon.

Le fichier svg produit

La version interactive en SVG.


Viewing all articles
Browse latest Browse all 3409

Trending Articles