Je tak trochu desive, ze knihovnu, ktera slouzi k logovani, tedy takove mirne ;-) vylepsene std::cerr << "neco";, pouziva tolik softu. Potrebujeme obcas neco vrznout do logu? Tak tam prdneme log4j. Kazdy, kdo to takhle udelal a ted ma trapeni, tak si to plne zaslouzi.
Ta knihovna ma 15MB FFS (pro srovnani - tolik zabiraly na disku po instalaci Windows 3.11 ;-) ) a zdrojaky maji 27MB. Bylo by divne, kdyby ten moloch zadne kriticke chyby neobsahoval.
log4j2 má 176 tisíc řádků. Rozdělení zpráv do dvou (nebo více) streamů podle priority je práce na kolik... 100 řádků? Ani v C by to nebylo tolik. V tom log4j2 monstru bude tisíc bugů jen čistě na základě statistiky.
Jenže to rozdělení zpráv chcete udělat konfigurovatelné, bez rekompilace aplikace. A dál to není jen rozdělení zpráv do několika streamů. Někdy chcete zprávy z jedné části aplikace do jednoho streamu, ale ze zbytku aplikace jinam – když řešíte nějaký problém, zapnete si vyšší úroveň logování pro pár tříd, ne pro celou aplikaci.
Správné logování je součást kvalitního vývoje aplikace, podobně jako třeba automatizované testy. Čímž ale neobhajuju to, co všechno je v Log4j 2.
Máme vlastní log facility, která z každé součásti (většinou instance structu nebo .c soubor) sbírá zprávy. Každá má timestamp, prioritu, ID zdroje, fmt, argumenty. Fmt je buď jen číslo předdefinované zprávy z katalogu, nebo klasický printf fmt string. Většina zpráv je tak jen pár B dlouhá, přestože se pak interpretuje jako delší text, často i s linky na dokumentaci, case, atd.
Ty potom předává logu o úroveň výš (a ten zase výš, ...), a to buď sdílenou pamětí (když je to v jednom zařízení), a nebo po CANu či Ethernetu. Tím se sbírají stavy ze strojů, čidel atd. z celé výrobní linky, a postupně se asynchronně agregují do jedné chronologicky konzistentní řady.
Celé to má lehce přes 900 řádků C99, plus něco kolem tisíce řádků Pythonu na straně log vieweru. Jeden auditor to má za den verifikované. Zatáhnout si do systému na logování 176k může projít jen u aplikací kde se kašle na SLA nebo bezpečnost... kde stačí dát na dobré slovo autora knihovny a neřešit, co v tom je.
To mas pravdu ze se da napsat vlastni logovaci framework na par radku. Ale proc? Stejne pak narazis, budes pridavat a delat k tomu parseri a podobne. Proste budes vynalezat kolo od zacatku. Tohle nikdo rozumni nedela.
Ano ted se to vymstilo a vsichni jsou z toho nestastni. Ale poc? Protoze si vybrali spatny logovaci nastroj.
Ne, ani po 20 letech údržby a přidávání featur mi log facility nepřeroste zbytek codebase (176kloc).
Je to problematika inherent vs. incidental complexity: pokud jednoduchou úlohu (sběr zpráv, jejich filtrování, směrování, prezentaci) řešíte knihovnou, která svojí velikostí konkuruje operačním systémům, a nehouká vám v hlavě alarm, tak je něco hodně špatně. Lidé jako Vy, kteří tohle obhajují, jsou přesně důvodem, proč je Log4Shell globální problém.
Běžně se to dělá tak, že se to všechno loguje do souboru a pak tam běží nejaky tailf + grep (něco s podobnou funkcionalitou) a ten to rozdeluje a zpracovava. Nebo lze logovat do otevřené nazvané roury, kterou připraví nekdo jiny. Jde jen o volbu formátu. Cokoliv co lze vystrčit z původního sw je dobré. Platí pravidlo "You have one job". Toho bych se držel
To, že se něco namatlá na jeden textový řádek v nedokumentovaném formátu a bez escapování znaků nového řádku, načež se to někdo pokouší parsovat grepem, bych nepovažoval zrovna za vzor dobrého přístupu. Podstatné je také to, že pokud aplikace používá logování správně, umí toho logovat opravdu hodně (pozor, neplatí opačná implikace, že vždy, když se loguje hodně, je to správně). V drtivé většině případů ty podrobné logy nepotřebujete, takže zapisovat někam ty zprávy jenom proto, abyste je vzápětí smazal, by akorát negativně ovlivňovalo výkon aplikace.
Ano, to je hezká teorie. Jenže v praxi je to pak ten textový řádek a grep.
Navíc i kdyby si někdo opravdu dal tu práci a vypisoval z aplikace třeba LNJSON, pořád to neřeší ten problém, že by musel takhle vypisovat na úrovni debug nebo dokonce trace a pak byste zase všechny ty zprávy zahazoval. Nehledě na to, že jste psal o logování do souboru – jenže to vám pak ten soubor poroste donekonečna. Nebo musíte do aplikace implementovat rotování logovacích souborů – a to už je zase v rozporu s tím vaším principem, že každý program má dělat jenom jednu věc.
Ono to v reálném světě bohužel není tak jednoduché, nestačí se řídit primitivními poučkami a je potřeba správně navrhnout, co vše aplikace má dělat a co už má dělat někdo jiný.
Na rotování logu je odjakživa logrotate.
druhá, na rotování logů mám asi 3 řádky kódu navíc
za třetí, pokud jde o logování na úrovni debug, většinou mi zas tak o výkon nejde.
pokud jde o filtrování úrovní, pak při jiné úrovni se ten JSON ani nesestaví. Logovat lze i přes lambda funkci (která se nezavolá, když není level aktivní). Případně ve své knihovně mám funkci isXXXLevelActive, která vrací false, když je daný level vypnutý, do kdyby třeba někdo prováděl složitý výpočet pro účely zalogování.
Ale vy se řiďte čím chcete. Je to vaše víra. Protože je to víra, která teď ničí pověst programátorů a přijde mi to, jako když se liní programátoři kteří použijí děravé frameworky teď kopají do programátorů, kteří si to napíšou sami s argumentem "taky tam nasekáte takové chyby".
Problém je, že tohle vzniklo jako feature creep. Parta maníku, co si sedla a řekla si "čím bychom to ještě vylepšili". Takovou chybu tam neudělám, protože takovou featuru většinou nebudu potřebovat
logrotate, logování do souboru, tailf. Zkoušel jsi někdy tohle udělat pro vícevláknovou aplikaci? Zkoušel jsi někdy implementovat logrotate, tak abys neztratil ani řádek a aplikace začala zapisovat do nového souboru? Zkoušel jsi někdy použít tailf, tak abys dokázal proces restartovat a nic nevynechal a ani nenačetl nic dvakrát?
Ty algoritmy jsou poměrně složité, stejně jako variabilita použití, proto ty knihovny nabírají na velikosti.
Ano zkoušel, není to problém.
Narozdíl od Vás, já těm algoritmum rozumím. Pro Vás to je magie. Logrotate nikdy neztratí ani řádku. Dokud nedostanu signál, loguje se do starého souboru, i když už ho logrotate přejmenoval. Jakmile dostanu signál, soubor zavřu a otevřu nový. Tahle operace je pod zámkem - případně logování má vlastní vlákno, do kterého se posílají jednotlivé záznamy a to vlákno ho zapisuje a tam zvládne i rotate.
Další věcí samozřejmě je účel logování.
Pokud někdo do logu loguje bysnys logiku, pak asi používá špatný nástroj.
v tom případě ale musíš vědět, že to udělat napříč různymi verzemi OS či jen linux kernelu není vůbec snadné, stejně tak zajistit funkčnost napříč celým životním cyklem aplikace (co když požadavek na rotování proběhne hned při bootstrapu?). Ono vůbec použití nespolehlivého signal(2) vede k sigaction(2) a to už je kousek k implementaci vlastního přerušení a ošetřování chyb a hned za rohem tě čekají věci jako signal-safety(7), v kombinaci se zámky se v tom dá nasekat neskutečně chyb.
Poté tě čeká minové pole kolem iops, když tvoje aplikace hodlá hodně logovat, o multithread komunikaci ani nemluvě, vždyť i v c++ to není zrovna lahoda, byl jsem v počátku u vývoje pantheios.
Rád bych tvoji impelemtanci viděl, mohli bychom se naživo o těch složitých zákoutích pobavit a mohl bys vidět, že to opravdu může být složitější, proto ty frameworky vznikají, proto mají tolik řádků kódů.
Na rotování logu je odjakživa logrotate.
To jako myslíte vážně? Používat něco, co vyžaduje restart aplikace kvůli logování? A když ne restart aplikace, tak musí ta aplikace správně reagovat na signál, což ale porušuje ten váš princip, že má dělat jenom jednu věc.
druhá, na rotování logů mám asi 3 řádky kódu navíc
To bych ty tři řádky chtěl vidět. Navíc tím porušujete váš princip, že by aplikace měla dělat jen jednu věc.
za třetí, pokud jde o logování na úrovni debug, většinou mi zas tak o výkon nejde.
Asi jste nepochopil, jak logování funguje. Logování funguje tak, že ve zdrojovém kódu jsou na zajímavých místech rozmístěné příkazy pro zalogování informací podstatných v daném místě programu. Ty příkazy logují na různých úrovních podrobnosti a jsou součástí přeloženého programu. Teprve po překladu se nakonfiguruje, na jaké úrovni se má skutečně logovat – a logovací knihovna zajistí, aby se logovací zprávy, které podle konfigurace nemají být vypisovány, nebyly nikam odesílány ani ukládány.
Kdybyste to řešil vaším způsobem, musela by aplikace do souboru neustále vypisovat všechny zprávy. Teprve následně byste ty nepotřebné (třeba na úrovni debug) odstraňoval.
pokud jde o filtrování úrovní, pak při jiné úrovni se ten JSON ani nesestaví. Logovat lze i přes lambda funkci (která se nezavolá, když není level aktivní). Případně ve své knihovně mám funkci isXXXLevelActive, která vrací false, když je daný level vypnutý, do kdyby třeba někdo prováděl složitý výpočet pro účely zalogování.
Aha, takže nakonec vaše aplikace nedělá jenom jednu věc, používáte v ní také logovací knihovnu, akorát jste si tu knihovnu napsal sám.
taky tam nasekáte takové chyby
Což je pravda.
Problém je, že tohle vzniklo jako feature creep. Parta maníku, co si sedla a řekla si "čím bychom to ještě vylepšili". Takovou chybu tam neudělám, protože takovou featuru většinou nebudu potřebovat
Já jsme nikdy netvrdil, že něco takového mělo v Log4j 2 být – právě naopak, kritizuju to. Ale myslet si, že si to sám napíšete líp, je naivní. Uděláte tam jiné boty, protože to nebudete řešit, je to přece jenom pro jeden projekt a nebudete to nějak zvlášť ošetřovat, musíte přece dělat hlavně na funkcionalitě samotné aplikace, zabývat se logováním nemáte čas.
tak logrotate je potřeba vždy.. protože můžeš proces přerušit kdykoliv..
To vaše za druhé... Na to abys udělal svíčkovou, taky potřebuješ udělat přípravu a postup, aby docílil výsledku... Svíčkovou neděláš stylem, že vše syrové rozmixuješ, dáš to ohřát na sporák a podáváš... Vše má svůj postup...
Za třetí... asi bych moc nevyčetl... ale taková logovací knihovna o které nevíš kde co dělá (určitě si projíždíš kód knihovny, jako časopis playboy...). Né vždy je to správné řešení, ale to je zase věc názoru...
A k tomu konci... můžu si napsat vlastní knihovnu, kterou budu implentovat dál a dál.. s tím, že budu postupně upravovat.. Výhoda vlastní knihovny je ta, že někomu jinému to dá práci rozlušťit, protože jede práci podle papíru a nepřemýšlí nad tím..
Byl tu sice dávno článek o člověku co si napsal celou Photopea hezky doma a píše si knihovny sám.. Taky to mohl převzít od jinud.. ale takhle by to nevylepšil.. Jde o to,že můžeš něco využít, ale musí to mít hlavu a patu...
Vy ste programator a uz ste robil aj nieco vacsie ako vas vlastny kod na par 1000 riadkov. Akoze byt clenom 100 clenneho timu a robite spolocny projekt kde sa melu tisicky riadkov denne. Prezradim vam zopar tajomstiev.
- Plne logovanie znamena niekolko NASOBNE spomalenie aplikacie. Skuste si bezat nejaku spring aplikaciu na DEBUG level. Asi budete sokovany.
- Logovaci framework sa stara aj o to aby zahadzovanie sprav bolo velmi rychle (ak ho viete spravne pouzivat)
- Niekedy sa chcete zamerat na DB vrstvu, niekedy sluzby, niekedy na webovu serializaciu a potrebujete si posvietit na prislusnu cast
- No a ako finale si mozno dostudujte nieco o MDC, request tracingu v ramci microservice (branching) a ako finale logging, metriky, a tracing
Takze ak ste si mysleli ze o tom nieco viete tak sa mi zda ze ste to nikdy nevideli v akcii v enterprise prostredi.
Mám za sebou desítky let praxe a stovky tisíc řádků včetně programů pro enterprise.
Na každém místě člověk řešil dvě varianty. Napíšu si to sám vs prdnu tam knihovnu.
Velice často se ukázalo, že napsat si to sám by možná nakonec zabralo méně času a méně trápení. Člověk musí mít schopnost už při analýze umět rozhodnout, co stojí za převzetí a co stojí za přepsání. Mám za sebou několik uznání ve stylu "nečekali jsme že vaše řešeni bude na tomhle hardware tak rychlé"
Ale vždycky se dá něco zesložitit a vydávat to za velice důležitou featuru, kterou nutně potřebujete.
A o tom to celé je. Jen si nejsem jist s tím "nutně potřebujete"
Jak funguje logovací systém mi nemusíte vysvětlovat. Ten svůj mám plně v "include only" headerech pro C++ samozřejmě v šablonách. Umí i filtrovat. Jo a v enterprise kde jsem dělal já jsme používal szn-dbglog :-D
Tak to mate iste premakane dokaze to logovat aj do povedzme influxdb a umoznuje separatne logovat vybrate casti nasadenim standardnej sondy. Ano pokial ste si vy vyrobili vlastny logovaci system my sme z neho spravili standard vratane vzdialeneho nastavovania a zapinania a vypinania.
Viem viem, vy to dokaze lahko dorobit aj do toho vasho logovania ale v nasom svete to dokaze kazdy druhy admin bez toho aby sa s vami vobec bavil a dokaze si cez to a AOP komplet monitorovat a parovat logy z aplikacie napriklad z requestami (cez MDC) a detekovat napriklad nestandardne spravanie aplikacie a mnohe dalsie veci.
Viem viem aj to by ste tam dokazali pridat ale na to proste nemame cas. Pouzite LOG4J2 alebo SLF4J alebo si svoju aplikaciu nechajte. Takto to dnes chodi. A bude horsie, standardizuju sa prostredia pre beh (Docker, Kubernetes, ...) a vy proste musite tu kompatibilitu a standardizaciu splnat inac nepredate.
zajímavé, že zrovna vytahuješ rsyslog, který má 150k LOC v C a 50k LOC v makefile/bash, to je srovnatelné, co? Rozdíl je jen způsob distribuce a zabalení. Kompilovaný C kód je prostě vždy dost malý narozdíl od java byte kódu, instalačka logstash pak obsahuje i další věci.
Nejsem proti porovnávání, ale chce to trochu objektivně srovnávat stejné věci.