ugrás a tartalomhoz

log analizáló pythonban (valójában a TDD alapjaival ismerkednék)

H.Z. · 2014. Ápr. 29. (K), 22.29
Van-e köztetek valaki, aki tudna segíteni abban, hogy belekezdjek az OOP + TDD témák gyakorlati kipróbálásába?
Az elképzelésem, hogy python2.7-ben összerakok egy minimalista tűzfal logot analizáló scriptet, ami az openwrt/tomato routerek logjában megjelenő üzenetekből tud kiszedni bizonyos infókat.
És itt most a log analizálása csak ürügy, hogy kicsit életszerűbb példa legyen, mint a különböző tutorialokban látható, "autó ojjektum, ennek vannak tulajdonságai, alkatrészei etc...", mert ez utóbbi alapján kicsit nehezemre esik valós problémákat megoldani.


Ugyan valaha foglalkoztam programozással, de amit csináltam, azt manapság "code monkey" néven szokás emlegetni, programtervezést nem tanultam, nem csináltam soha. Szóval érdekes lesz, na... :)


Az alap elképzelés egy paraméterezhető program, ami kiválogatja a syslogból az iptables által generált sorokat és a paraméterek alapján gyárt kimenetet.
Paraméterezhető szűrőfeltételek:
- eldobott (DROP), elfogadott (ACCEPT) csomagok
- protokoll (tcp, udp, icmp, egyéb)
- protokollon belül, ha lehetséges, port
Kimenet:
- legtöbb találat IP alapján csoportosítva
- legtöbb találat portok alapján csoportosítva (melyik portokra jött a legtöbb találat)


Ez procedurális alapokon pár óra alatt összedobható volt, csak miután kipróbáltam, megkaptam az eredményeket, egy laza mozdulattal töröltem az egészet.

Sajnos már az elején elakadtam, mert fogalmam sincs, hogy lehetne ezeket a feladatokat objektumokra szétszedni.
Szóval ha akadna valaki lelkes segítő (nem a pythonos, hanem az elméleti részben), akkor folytatom.

Illetve még az is gondot okoz, hogy egyáltalán egy ilyen téma belefér-e még a fórum kereteibe?
 
1

Először is válaszd szét a

szjanihu · 2014. Ápr. 29. (K), 23.02
Először is válaszd szét a beolvasást, a szűrést, és a kimenet gyártást.

Mit csinálna az alkalmazás?
- adatokat olvas be, amelyeket parsolni kell
- különféle módon szűrni kell az adatokat
- aggregált értékeket kell számolni
- a végeredményt ki kell írni

Ez a négy felelősség rögtön legalább négy interfészt meghatároz, valamint szükség van egy adathordozó szerepet betöltő osztályra is.

Nem tudom, mondjam-e tovább, lehet már ez is sok, de lehet, hogy csak még jobban összezavartalak. Inkább kérdezz.
2

Köszi, nem zavartál össze

H.Z. · 2014. Ápr. 29. (K), 23.23
Köszi, nem zavartál össze egyáltalán, olvasgattam elég sokat (és elég régen :( ) OOP témában készült írásokat, tutorialokat, csak valahogy a nem túl életszerű példákat nem tudtam valós problémákkal összekapcsolni.

Mára kivonom magam a forgalomból, holnap megpróbálom leírni az első pár gondolatot, ahogy nekiesnék az egésznek (Ez majd ott indul, hogy paraméter feldolgozás - amíg nincsenek paramétereim, addig úgysem tudom, mit kell feldolgozni és hogyan)

Érdekes... a fotózásban is a kompozíció volt mindig a fő problémám. Itt is valami hasonló :)
3

egy laza mozdulattal töröltem

spapp · 2014. Ápr. 30. (Sze), 08.11
egy laza mozdulattal töröltem az egészet


- használj valamilyen verziókezelőt (pl.: git, mercurial)
- még jobb, ha nem csak a saját gépeden van a kód (pl.: github, bitbucket)
4

Félreértesz: szándékosan

H.Z. · 2014. Ápr. 30. (Sze), 08.52
Félreértesz: szándékosan töröltem, ha beraktam volna a githubra, onnan is töröltem volna. :)
Arra lett volna jó, hogy demonstráljam, hogy nem szabad szkriptet írni. :)

Egyébként itt az újraírás borzalmainak nyomát még megtalálod: https://github.com/haa-zee/ :)
Csak félbehagytam már az elején és most jött rám, hogy újra kellene kezdeni :)
5

OOP?

Hidvégi Gábor · 2014. Ápr. 30. (Sze), 09.04
Azt meg tudod mondani, hogy az adott problémára miért pont OOP módon keresel megoldást? Miben lesz szerinted hatékonyabb/jobb a procedurálisnál?

(Nagy mennyiségű) adat feldolgozására inkább a funkcionális paradigmát szokták ajánlani.
6

Nem a problémát akarom

H.Z. · 2014. Ápr. 30. (Sze), 10.02
Nem a problémát akarom megoldani, csak a gyakorlatban szeretném látni az OOP és a TDD működését.
És ez akadt a kezembe, mint számomra hasznos/érdekes, mégis egyszerű feladvány.
7

Arra próbáltam célozni, hogy

Hidvégi Gábor · 2014. Ápr. 30. (Sze), 10.42
Arra próbáltam célozni, hogy a feladat önmagában szerintem még nem elégséges az OOP gyakorlásához, legalábbis sok hasznot nem fogsz belőle látni. Életszerűbb lenne, ha mondjuk egynél több be- vagy kimenettel dolgoznál, például az utóbbi lehetne egy HTML oldal és egy email, amit minden reggel elküld neked a rendszer.
8

Gyakorlás még nagyon messze

H.Z. · 2014. Ápr. 30. (Sze), 11.06
Gyakorlás még nagyon messze van. Egyelőre indulásról van szó. Ahhoz bőven elég egy-két osztály, pár rövidke metódussal. (egyelőre mobilon vagyok, ezért van csak szöveg, semmi kód és hasonlók :) )
9

Életszerűbb lenne, ha mondjuk

kuka · 2014. Ápr. 30. (Sze), 14.07
Életszerűbb lenne, ha mondjuk egynél több be- vagy kimenettel dolgoznál
Napló állományok esetében ez éppen kézenfekvő felvetés. Ugyanis a napló állományokat rotate-olni (anyanyelven ezt nem tudom mi :() szokták. Amibe tartozhat átnevezés, törlés és tömörítés. Tehát ha nem csak nagyjából éjféltől érdekelnek az adatok, szükséges lesz betekinteni a régebbi napló állományokba is.

Itt alkalom nyílik egy pici OOP-re öröklődésre:
class NaploOlvaso(object):
# ...

class TomoritettNaploOlvaso(NaploOlvaso):
# ...


Meg egy kis design patternre, hogy factory példányosítsa az olvasót, annak függvényében, hogy éppen milyen állományt kell feldolgozni.

Illetve a komolyabb napló feldolgozók szoktak olyant is tudni, hogy ha fél órája már feldolgoztattad a napló állományt, akkor most csak az utolsó fél órát dolgozza fel, a régebbi adatokat pedig előkapja valami gyorsítótárból.

Ahol a gyorsítótár egy újabb bemenet illetve kimenet típus, amely ugyanazokkal az adatokkal dolgozik, csak picit másképp. Tehát lehetőséget ad újabb adag öröklődés beiktatására.
10

Mondjuk a rotate nem annyira

H.Z. · 2014. Ápr. 30. (Sze), 14.41
Mondjuk a rotate nem annyira ennek a feladata (némelyik log író intézi önállóan, máshol meg ott a logrotate)
11

Mondjuk a rotate nem annyira

kuka · 2014. Ápr. 30. (Sze), 14.58
Mondjuk a rotate nem annyira ennek a feladata (némelyik log író intézi önállóan, máshol meg ott a logrotate)
Ez gáz. Mármint hogy ennyire zagyván fejezem ki magam.

Én úgy értettem, hogy rotate-en már átesett állományok feldolgozására is készítsd fel az elemző szkriptedet. Valami ilyesmi:

class NaploOlvaso(object):
  def __init__(self, nev):
    self.nev = nev

  def olvas(self):
    with open(self.nev) as allomany:
      for sor in allomany:
        # csinal valamit a sorral

class TomoritettNaploOlvaso(NaploOlvaso):
  def olvas(self):
    # kitomorit
    self.nev = '/tmp/ahova/kitomoritette'

    super(TomoritettNaploOlvaso, self).olvas()
12

Nem gáz, pillanatnyilag egy

H.Z. · 2014. Május. 1. (Cs), 11.04
Nem gáz, pillanatnyilag egy zombival társalogsz, pedig mindössze két órát gyalogoltam. Szóval a felfogásom a szokottnál is gyengébb. ;)

Update: ez a kód nincs ellentétben a S.O.L.I.D. L betűjével?
(A leszármazott szerintem nem használható a szülő helyett)
13

A leszármazott szerintem nem

Hidvégi Gábor · 2014. Május. 1. (Cs), 13.25
A leszármazott szerintem nem használható a szülő helyett
Miért nem?
15

Ha jól értem azt az L betűt,

H.Z. · 2014. Május. 1. (Cs), 13.43
Ha jól értem azt az L betűt, akkor a leszármazottat a szülő helyén használva ugyanazt a működést kell kapnod, mintha a szülőt raktad volna oda.
Itt viszont nem ez a helyzet, mert a leszármazottnak kötelezően tömörített inputot kell biztosítani, míg a szülő kizárólag plain texttel dolgozik.
14

(Nekem az a meglátásom, hogy

kuka · 2014. Május. 1. (Cs), 13.30
(Nekem az a meglátásom, hogy elvekből nem lehet megélni. Ezért nem is sokat foglalkozok velük. Tehát a következőkben minden esély adott, hogy bődületes marhaságot írjak.)

Az alábbi kódrészletben hol szeretnéd helyettesíteni a NaploOlvaso osztályt TomoritettNaploOlvaso osztállyal? Sőt, egyáltalán honnan tudod, hogy hol melyik van használva?

olvaso = OlvasoGyar.adj_olvasot('valami.log')
olvaso.olvas()

olvaso = OlvasoGyar.adj_olvasot('valami-001.log.zip')
olvaso.olvas()
Az az érzésem, hogy az az „L”-mélet nem erre a helyzetre vonatkozhat.
16

Ez csak a factory pattern, ha

H.Z. · 2014. Május. 1. (Cs), 13.46
Ez csak a factory pattern, ha jól látom, itt a "gyár" dönti el, hogy milyen osztályból gyártja le kért objektumot.

Én úgy emlékszem (nem kizárt, hogy rosszul vagy rosszul értelmezem), hogy az utódnak mindig azonos működést kell produkálnia a szülő helyére helyezve.
Magyarán, ahol a NaploOlvaso típusú objektum használható, ott a TomoritettNaploOlvaso is használható kellene, hogy legyen és ez itt nem teljesül, mert tömörítetlen állománnyal hibára fut a Tomoritett...
17

Hát az „azonos működés”

kuka · 2014. Május. 1. (Cs), 14.08
Hát az „azonos működés” szerintem kissé meredek. Akkor minek leszármaztatni? Részemről bőven elegendő a hasonló működés: a beolvasást mindkét osztály esetében az olvas() metódus végzi.
20

Nekem az a meglátásom, hogy

Hidvégi Gábor · 2014. Május. 2. (P), 12.30
Nekem az a meglátásom, hogy elvekből nem lehet megélni. Ezért nem is sokat foglalkozok velük.
Kukába az elvekkel! (Bocs : ))

Szépek az ilyen elvek, elméletek, de szerintem ezek csak bizonyos problémáknál hatékonyak. Ráadásul soknál még az alapokban sem tudnak megegyezni a használóik, lásd OOP és unit testing stb.
18

Egyelőre lezárva...

H.Z. · 2014. Május. 1. (Cs), 20.36
Sajnos képtelen vagyok értelmesen megfogalmazni, mi is az, amivel elakadtam, úgyhogy bocs mindenkitől, egyelőre részemről fel is függesztem a témát. :(

(már ott megakadtam, hogy a paraméterek feldolgozását hogyan lehetne egyrészt objektum orientáltan megoldani az argparse modul segítségével, másrészt - ez lett volna a fontosabb - hogyan és milyen unit teszteket írhatnék hozzá)
19

a paraméterek feldolgozását

kuka · 2014. Május. 2. (P), 12.05
a paraméterek feldolgozását hogyan lehetne egyrészt objektum orientáltan megoldani az argparse modul segítségével
Az argparse szerintem már kimerítette a paraméterkezelés OOP-asítási lehetőségeit, ehhez már tök fölösleges bármi továbbit hozzátenni.
hogyan és milyen unit teszteket írhatnék hozzá
Én ilyennel próbálkoznék (az argparse dokumentáció alapján, fantáziaszegényen):

import argparse
import unittest
import sys

class ParameterTeszt(unittest.TestCase):

    def setUp(self):
        self.parser = argparse.ArgumentParser(description='Process some integers.')
        self.parser.add_argument('integers', metavar='N', type=int, nargs='+',
                           help='an integer for the accumulator')
        self.parser.add_argument('--sum', dest='accumulate', action='store_const',
                           const=sum, default=max,
                           help='sum the integers (default: find the max)')

    def test_semmi(self):
        sys.argv = (__name__, )
        with self.assertRaises(SystemExit):
            args = self.parser.parse_args()

    def test_hibas(self):
        sys.argv = (__name__, 'ize', 'bigyo')
        with self.assertRaises(SystemExit):
            args = self.parser.parse_args()

    def test_szamok(self):
        sys.argv = (__name__, '1', '2', '3', '4')
        args = self.parser.parse_args()
        self.assertEqual(args.accumulate(args.integers), 4)

    def test_szamok_es_muvelet(self):
        sys.argv = (__name__, '1', '2', '3', '4', '--sum')
        args = self.parser.parse_args()
        self.assertEqual(args.accumulate(args.integers), 10)

if __name__ == '__main__':
    unittest.main()
(Jó, tudom, lezárva. De nekem csak piszkálja az oldalamat, mert a közelmúltban foglalkoztatott engem is a téma. És én is ott akadtam el, hogy rendben, tanultam OOP, de most mihez kezdjek vele.)
21

:) Azért köszi.

H.Z. · 2014. Május. 2. (P), 18.53
:)
Azért köszi.

Apropó: jól értem, hogy nem foglalkozol pythonnal, csak doksiból szedted össze az ehhez szükséges infókat?
23

jól értem, hogy nem

kuka · 2014. Május. 9. (P), 16.07
jól értem, hogy nem foglalkozol pythonnal
Hivatalosan nem, csak műkedvelésből. (A Python közel áll az OOP világnézetemhez: nincsenek láthatóság módosítók, minden publikus. Így buzgón érdeklődök iránta, de Python fejlesztőként nem sikerült elhelyezkedni.)
csak doksiból szedted össze az ehhez szükséges infókat?
Az argparse részét igen. A készen kapható paraméter feldolgozók általában nem tetszenek. Többségük kiválóan alkalmas a dokumentációjuk példáihoz, de a gyakorlatban nekem kevésnek bizonyulnak. Úgyhogy általában „kézzel” dolgozom fel a paramétereket. (De ez az argparse jóval átlag fölöttinek néz ki. Legközelebb kap egy esélyt a gyakorlati bizonyításra.)
22

Ugyan kissé offtopic, de a

H.Z. · 2014. Május. 9. (P), 00.45
Ugyan kissé offtopic, de a témához kapcsolódva: megint előszedtem Uncle Bob írásait és... A metódusok felülírhatóságát (method overriding) és a SOLID-ból az L-t (Liskov Substitution Principle) nem tudom összeegyeztetni.
Utóbbi, ha jól értem, arról szól, hogy egy "gyerek" objektumnak a szülő helyén épp úgy kell viselkednie, mintha a szülő osztályából létrehozott objektum lenne.
(erre szokták hozni a kör és az ellipszis példáját, ha jól emlékszem: geometriai szempontból a kör egy speciális ellipszis, OOP szempontból viszont az ellipszist kell úgy megalkotni, mint a kör osztály leszármazottját)

És ez nagyon nem tiszta: ha a szülő egy metódusát felülírom, akkor az objektum már nem fog ugyanúgy viselkedni, mintha a szülő objektum/osztály lenne...
(rémlik, hogy pár hónapja, talán éve már értetlenkedtem e témában, de nem találom azt a beszélgetést :( )



update: http://stackoverflow.com/questions/1735137/liskov-substitution-principle-no-overriding-virtual-methods - ez ugyan nem én voltam, de kb. válaszol a problémámra.
24

A kör-ellipszis (vagy

MadBence · 2014. Május. 9. (P), 22.45
A kör-ellipszis (vagy négyzet-téglalap) pont nem jó példa az öröklésre, hiszen sok gond van velük. A hagyományos értelemben az öröklés az X egy speciális Y szemantikának felel meg, az LSP az kell, hogy X egy Y, ami még tud Z-t is szemantikának feleljen meg. Ez utóbbi egy szigorúbb követelmény.
25

Elvileg ezek épp az LSP,

H.Z. · 2014. Május. 9. (P), 22.55
Elvileg ezek épp az LSP, illetve annak megsértését illusztráló példának jók.
Emberi logikával az ember úgy állna hozzá, hogy készít egy téglalap/ellipszis osztályt és abból származtatja a négyzetet/kört. Csakhogy ezzel megsérti a LSP-t, ezért meg kell fordítani és négyzetből/körből kell származtatni a téglalapot/ellipszist.
Úgy halványan értem, miről van szó, de a gyakorlati problémáimra képtelen vagyok ráhúzni ezeket az ismereteket.
Ezért is jutottam végül oda, hogy inkább feladom egyelőre.

Egészen odáig nincs gond ezekkel, míg csak újabb metódusok/változók hozzáadásával bővítem a szülő osztályt. Viszont ha már képbe kerül a metódusok felülírása... na azt nem vagyok képes élő példával összehozni.
26

SRP - ez még belefér?

H.Z. · 2014. Jún. 23. (H), 15.21
Folytatva (illetve nulláról újrakezdve) a félbehagyott témát, most csak annyival kezdtem foglalkozni, hogy olvasni akarom a logot, egyéb részletekkel nem törődöm. (hup-on is felvetettem a kérdést, ha valakinek ismerős lenne :) )
Voltaképp csak egy igen/nem válaszra lenne szükségem, magyarázatra csak akkor, ha már nagyon értetlenkedek. ;)


Azon töröm a fejem, hogy a SRP-be belefér-e, ha egy osztályt úgy készítek el, hogy a feladata: végigmenni egy paraméterként kapott fájl objektum sorain _és_ a sorok közül kiválogatni a szintén paraméterként kapott regexp-nek megfelelő sorokat _és_ a beolvasott text helyett egy objektumot visszaadni, ami a szintén paraméterként kapott parser objektum által áll elő.

Én úgy érzem, hogy a sok "_és_" miatt nem, ugyanakkor össze tudom foglalni egy feladatként is: parse-olni a kapott mintára illeszkedő sorokat.
Mindezt iterátorként.

Kicsit életszerűbb példával: például kernel logot kell végigolvasni, abból kiszűrni az iptables sorait és a sorokból objektumot gyártani a sima text helyett.

(update: így utólag visszaolvasva nem sikerült pontosan megfogalmaznom, hogy mi bajom, de értelmesebben egyelőre nem tudom... Talán estére sikerül összedobni egy olyan kódrészletet, amiből már látszani fog, hogy mi bajom)
27

Kicsit életszerűbb példával:

Hidvégi Gábor · 2014. Jún. 23. (H), 16.47
Kicsit életszerűbb példával: például kernel logot kell végigolvasni, abból kiszűrni az iptables sorait és a sorokból objektumot gyártani a sima text helyett
A hozott példa egyáltalán nem életszerű, mert ha a kimenő adatok objektumok, akkor le vagy korlátozva, hogy mit lehet velük kezdeni. Ha viszont egy adatsort kapsz vissza, akkor a fogadó fél oldalán tudod eldönteni, mit kezdj velük, például kiírod őket adatbázisba, megjeleníted képernyőn, vagy pedig elküldöd valakinek e-mail-ben.
28

Na ez az egyik dolog, amin

H.Z. · 2014. Jún. 25. (Sze), 21.07
Na ez az egyik dolog, amin elakadtam.
Igen, visszaadok egy adatsort. De a visszaadott adatoknak is van formátuma. Megtehetem, hogy egy (akár injektált) objektum végez valamit a visszaadott adatsoron, megtehetem, hogy feldolgozatlan sorokat adok vissza stringként (de ebben az esetben már a szűrő feltétel használata is kérdéses, hogy belefér-e) stb.

Ezt most mobilról, hogy el ne felejtsem, majd este próbálok kicsit értelmesebben is...

upd: LogIterator.py
Talán így érthetőbb, mit akartam.