2010-10-26

De ce nu m-am dus la Google


De ce nu m-am dus la Google

 Ca să fie clar de la început, nu am parcurs decât primele 3 interviuri telefonice pentru Google, nu pot spune că sunt "material Google" cu acte în regulă.

Povestea

 Am fost contactat de Google prin Linkedin şi am avut 3 interviuri telefonice la care se pare că am răspuns mulţumitor. Am fost invitat la o zi de interviu la unul dintre centrele Google din London, Dublin sau Zurich.

 Pe de o parte să lucrez la Google era un vis al meu mai vechi, un fel de firmă de soft ideală. Pe de altă parte nu-mi doream să lucrez în afara ţării (scrisesem şi în CV). Înainte de a-i pune la cheltuială (călatoria era plătită de Google) am simţit nevoia să mă hotărăsc într-un fel, şi am decis să refuz politicos invitaţia.

Călătoria în sine nu m-ar fi încântat neapărat, m-am cam săturat de vizitat tări străine. Mi-ar fi plăcut totuşi să vizitez Google, să discut cu inginerii de acolo, dar nu era corect din partea mea să nu le spun.

 Poate nu aş fi trecut nici interviul, ştiu un programator cu mai multă experienţă decât mine în programare care nu a fost angajat pâna la urmă. Pe de altă parte era un post care îmbina 3 domenii în care am o experienţa îndelungată : Software, Linux/Unix, Networking.

Mai puţin de 0.5% dintre aplicanţi sunt angajaţi. Este deci un grup foarte elitist, ceea ce la mine s-ar contoriza un pic la minus, nu-mi place să mă bulucesc unde se înghesuie toată lumea. Firmele foarte curtate sunt probabil şi mai capricioase. Totuşi dacă jobul ar fi fost în România probabil mi-ar fi plăcut să lucrez la Google.

 M-am gândit şi la posibilitatea că aş fi putut fi supracalificat pentru post. Până la urmă din 4000 angajaţi pe an, sigur sunt şi nişte posturi la munca mai de jos.
Visul meu ar fi să inovez ceva foarte important în zona algoritmilor. Mi-e realmente greu să-mi dovedesc chiar şi mie că aş fi în stare de aşa ceva, mai ales că proiectele mele personale cam stau în stadiul de proiect din lipsă de timp, energie şi chef.


 De ce ?

  Am lucrat în afara tării şi sincer nu m-a dat pe spate. M-am acomodat greu cu mâncarea de exemplu, şi sunt multe lucruri pe care trebuie să le iei de la zero, să cunoşti legile, obiceiurile, unde poţi obţine un anumit serviciu. De multe ori mi-e greu să comunic şi în română, darămite într-o limbă străină...

 Relocarea vine cu nişte costuri ascunse pe care nu toată lumea le ia în seama. Trebuie renunţat la multe investiţii materiale şi afective. Este relativ uşor dacă eşti pe val, dacă eşti sănătos şi nu ai nevoie decât de mâncare şi acoperiş, dar poate deveni mult mai dificil dacă ai nevoie de ajutor, de exemplu medical.

 Odata relocat, firma are o putere de "negociere" mult mai mare asupra ta, depinzi de ea pentru viză, nu îţi poţi găsi altceva de lucru foarte rapid. Trebuie să ai puşi de o parte banii de întoarcere, iar dacă trebuie să te întorci pe neaşteptate pierzi destul de mult (avion mai scump, lucruri cumpărate în ideea că vei rămâne acolo etc).

  Ar mai fi un fapt, mai subtil. Psihicul uman evaluează nivelul de trai destul de relativ. După un anumit nivel de bunăstare în sus contează mai mult comparaţia cu cei din jur. Depinde de la om la om, dar poţi fi mai nefericit având mai mult într-o ţară bogată, pentru că ceilalţi au şi mai mult.

  Pentru mine contează şi un factor spiritual, cred că pot aduce mai multă valoare adaugată pe lume trăind în Romania decât în altă ţară. Nu e vorba doar de rezultatul muncii, e vorba şi de clădirea unei societăţi la care mă simt dator să contribui.


 Incheiere


 Nu vreau să fac apologia rămasului în ţară, am scris acest post doar ca să se ştie că există şi alte atitudini decât trendul la modă. Prea des oamenii nu îşi exprimă parerile contrare a ceea ce ... pare opinia generală.

 Am mai scris şi ca să explic cumva postul criptic anterior : Jurnal de calatorie, Paris 2005


"The more I say, the more I know" . Republicarea articolelor este permisă cu citarea autorului

2010-10-18

Jurnal de calatorie, Paris 2005

 

Jurnal de calatorie,
Paris 2005
 
 Am scris prin 2005 despre impresiile mele după ce am stat vreo 3 luni la Paris. Articolul era publicat pe bătrâna mea pagina personală în format Word, m-am gândit să-i dau un paste și aici, așa fără diacritice. Este un text de dinainte de blog, deci concurs : cine găsește mai multe erori de ortografie. Câștigătorul primește venitul meu din reclamă pe următoarea săptamână :P


 Alt concurs, același premiu (tot atăt revine fiecăruia). Cum era bancul acela cu Ion care iși dorea s-o refuze o dată pe Claudia Schiffer ?



Paris: orasul femeilor anorexice si prost incaltate
-         jurnal (subiectiv) de calatorie –

        De indata ce ajungi in aeroport, te izbeste o lume pestrita, pigmentata cu fizionomii asiatice si de culoare. Pe turistii tarand dupa ei trollere mari si prafuite ii vei intalni apoi si in metrou sau (fara trollere) discutand in diferite limbi langa obiective turistice, cu o harta a Paris-ului in mana (se distribuie gratuit).

E greu in Pris sa distingi localnicii de turisti. Chiar daca nu poarta cu ei insemnele amintite, iti poti da seama ca sunt francezi doar auzindu-i vorbind franceza, uneori rapida si temperamentala (precum italienii), alteori apatica si superprescurtata. Si totusi nu poti fi sigur ca sunt parizieni.

 In general francezii vivace sunt oamenii de culoare, care in multe locuri depasesc 25% din locuitori. Veniti majoritatea din fostele colonii franceze, ei au gasit aici un mediu destul de tolerant fata de ei. Totusi, nu poti sa nu observi ca de multe ori ei sunt preponderent cei care executa munci ingrate : taximetrist, room-service, paznic magazin, curatenia orasului. Cred ca si « politist » este o profesie ingrata pt ca majoritatea politistilor din Paris sunt de culoare. Au si avantajul ca nu se poate plange nimeni ca discrimineaza rasial. Se pare ca fiecare societate « avansata » are « negrii ei », in Franta negrii lor fiind in majoritate de culoare.

In ciuda acestei stratificari sociale, societatea franceza pare sa se impace destul de bine cu relatiile mixte inter-rasiale. Am vazut femei albe cu barbati negrii, femei asiatice cu barbati albi, dar si viceversa : ) Poate nu am observat eu, dar nu am remarcat relatii intre persoane asiatice si de culoare. Explicatia ar putea fi ca relatiile sunt polarizate de catre populatia autohtona « alba ». Din partea naturalizatilor relatiile mixte cu albii are putea fi puse pe seama nevoii de acceptare ca egali, de parvenire poate. Din partea populatiei autohtone albe as indrazni insa sa speculez despre ceva mai complex, de o nevoie inconstrienta de reimprospatare si altoire a fondului genetic.

Francezii « pur-sange » par a fi inghetat intr-un timp trecut, in care Franta avea putere de dominare asupra regiuniim cand regi puternici duceau razboaie glorioase. Ei nu inteleg ce se intampla, ce este cu acesti straini care vin din afara si vor sa se simta egali cu ei. De la ei vine acel xenofobism francez de care se vorbeste. S-a apreciat ca votul impotriva constitutiei europene a fost unul impotriva clasei politice. Eu unul cred insa ca aici se ascunde si ceva din orgoliul unei natiuni care crede (sau spera) ca inca are un cuvand greu de spus in Europa. In afara de aceasta rabufnire insa, francezii albi sunt destul de blazati si neinteresati de politica. Sunt ca niste razboinici ramasi fara cauza, traind din vremurile de glorie, adaptandu-se cu greu realitatii. Cand au cate o problema dau vina pe « negrii si arabi si alti imigranti », chiar si pe turisti, desi acestia practic le sustin economia. Probabil ca undeva neconstientizat realizeaza totusi ca sunt in pericol de a degenera genetic si spiritual, asta fiind singura explicatie pentru faptul ca in ciuda declaratelor xenofobii au totusi extrem de multi emigranti, care le aduc forta de munca dar si vitalitate.

Am fost uimit sa vad o reglama la ciorapi in care se afisa o femeie cu picioare anormal de subtiri. Practic acesta este specificul frantuzoaicelor « pur-sange », sunt in general foarte slabe si destul de lipsite de forme. Am observat ca este un reper destul de bun de a le deosebii de turiste. Sunt in general mici de staturam probabil pe langa dispozitia genetica isi mentin silueta nemancand paine. Desi Franta este vestita pentru sortimentele ei de paine, ospatarii au fost deseori uimiti cum devoram noi cos dupa cos de paine. Am vazut si extrema, o femeie in varsta cu pielea zbarcita pe oase care nu putea nici sa mearga neajutata. 

Barbatii fracezi « pur sange » sunt in general de doua tipuri : tipul mai nou, astenic, slabut, dar ceva mai vivace si tipul clasic, mai solid, cu o detasare nobiliara, foarte cautata in politica. Acest ultim tip pare sa fie amator de vin, avand niste pometi ai obrajilor specifici.

In contrast cu aceste tipuri « bastinase » exista francezii naturalizatim venind cu ambitia si energia care i-a adus pe stramosii lor din locuri indepartate. Constitutia lor este in general atletica (mai ales la cei de culoare). Am vazut femei de culoare de aproximativ 2 metrii, pe langa cele traditionale « big mamma ».
Parisul este un oras al utilitarismului. Pretul unei marfi nu este dat de pretul de productie, ci de cat de util (sau dorit) este acel produs in acel loc. Daca cauti suficient, aceleasi « amintiri » le poti gasi cu pana la 6 ori mai ieftine decat langa obiectivul turistic.

Totul este organizat atent pentru a te putea deplasa cat mai usor oriunde. Principalul mijloc de locomotie este metroul (subteran si suprateran). Practic poti ajunge de oriunde in mai putin de 5 minute la o statie de metrou. Indicatoarele de directie sunt foarte bine facute, astfel incat in scurt timp te poti descurca destul de bine in hatisul de linii care se intersecteaza. Pe langa metrou mai exista si RER - un fel de tren urban care are corespondenta cu metroul. Acestea au nume in loc de numere si sincer mi s-a parut mult mai complicat.

Parisul nu este un oras extrem de mare, este cam de 1.5-2 ori mai mare decat Bucurestiul daca luam in calcul si suburbiile. Transportul rapid face sa para mult mai mic. Este impartit in 8 zone concentrice, in functie de zona existand tarif diferentiat in transportul public si taxi (creste spre periferie). Abonamentul este de baza, daca folosesti bilete dai faliment rapid. O ciudatenie a lor este ca abonamentul pe saptamana incepe luni iar cel lunar incepe pe 1. Daca iei abonament saptamanal joi, mergi doar 4 zile cu el. La abonamente mai cer sa ai si un « carte orange » cu poza si serie, serie pe care o scrii cu pixul pe micutul bilet – abonament. Daca nu ai acest numar de legitimatie pe bilet poti lua o amenda maricica. Sunt destul de paranoici cu frauda, usile de intrare si iesire din metrou se inchid de sus pana jos, fiind imposibil de sarit.

Parizienii tin mult la confortul lor. Aspectul pariziencelor reprezinta triumful comoditatii asupra esteticului. In primul rand sunt aproape de loc aranjate. Machiajul lipseste de regula, parul deseori neingrijit. Bluze usor sifonate sau desuete, dar lejere. Cel mai socant este insa aspectul incaltarilor. Aproape toate poarta papuci sau sandale tip papuc, dar obligatoriu fara toc. Papucii sunt deseori facuti dintr-o talpa flexibila si  doua baretute unite intre degetul mare si celelalte, amintind uneori de floraresele din Romania. Sandalele sunt din piele sau inlocuitor, acoperind doar varful piciorului. De obicei sunt prafuite si uzate, in cateva lucuri am vazut talpa usor dezlipita. Ca regula generala papucul sau sandaua lasa sa se vada usoare julituri sau cicatrice de la lovituri sau calcat pe picioare. Aspectul este de « lucratoare a campului ». Am vazut si cazuri de incaltari care acopera piciorul dar in general erau uzate ca aspect si total neasortate (tenisi galbeni cu fusta). Inexplicabil, in doua cazuri am vazut cizmulite, desi erau 25-30 grade, probabil comoditatea de a gasi altceva.

Se poate spune ca francezii nu mai simt nevoie sa dovedeasca ceva ca indivizi. Poate de aceea ii gasesc usor blazati. Au abandonat majoritatea responsabilitatilor lor civice organizatiilor (de stat, locale), nu mai simt nevoia sa ia atitudine. In acelasi timp sunt foarte increzatori in faptul ca sistemul functioneaza. Eu am fost foarte ingrijorat de ceea ce a fost denumit “un grav accident de metrou”. Francezii erau neinteresati de subiect, au banuit ca vreun sinucigas s-a aruncat in fata metroului. Am intrebat « daca la asa ceva se zice grav accident, cum se zice cand un intreg metrou pateste ceva ». Raspunsul a fost « asta nu se poate intampla ».

Totusi, la nivelul institutiilor exista o ingrijorare privind spectrul terorismului. In majoritatea obiectivelor turistice este interzis accesul cu bagaje voluminoase in care ai putea avea un dispozitiv exploziv.

Primaria incearca sa pastreze viu spiritul paris-ului, organizand diferite evenimente (« ziua muzicii » de exemplu). Cred ca o fac in special pentru turistii care platesc 7-10 euro pentru fiecare obiectiv turistic in care intra. Locuitorii autohtoni nu sunt foarte interesati (in afara de naturalizati). Cei batrani isi mai omoara timpul cu diferite actiuni in slujba comunitatii. Un strop de viata regasesti atunci cand diferiti muzicieni francezi canta in metrou pantru a-si promova/vinde CD-ul (cu voie de la metrou, bineinteles). Am ascultat ieri un superb rectal de coarde executat de un ansamblu de peste 10 muzicieni. In alta statie se canta muzica clasica la acordeon - de catre un francez.  « Muzicienii » romani s-au imputinat, probabil pentru ca francezii nu prea dau bani cersetorilor.

Impresia generala despre francezi este ca parca isi cauta inca telul, au nevoie sa gaseasca un nou sistem de valori, un nou tel care sa ii faca sa viseze la maretie. Pana atunci, cei mai multi se complac in a sustine o industrie cvasi-turistica, cu monumente inalte si majestuoase care sa ne ia ochii de la frantuzoaicele anorexice si prost incaltate.


***

Alte impresii

Un alt lucru notabil despre femeile din Paris este ca au obiceiul sa stea cu degetul in gura. Am vazut si un (singur) barbat cu acest obicei oral, dar nu constituie inca un esantion reprezentativ. Probabil unul din motive este faptul ca francezii isi duc copii in carucior chiar si la 4-5 ani, poate tot din comoditate (pentru a nu avea grija de ei). Este mult mai greu sa tii degetul in gura in timp ce mergi decat in carut, unde obiceiul se poate prelungi, ramanand activ si la varste adulte.

***

Desi sunt mai degraba rigid fizic, imi vine foarte usor sa prind ritmul unei noi  melodii. De obicei trec la noul ritm fara a ma opri, uneori fara a observa ca s-a schimbat melodia. La fel un nou loc, alti oameni, nu ii percep ca ceva nou, ci mai degraba ii mulez pe patternuri cunoscute, actionand in mod obisnuit. Aceasta abordare are si dezavantaje, desi adaptarea este mai rapida, ea este probabil de o calitate mai scazuta. Sunt in Paris si ma astept sa gasesc aceeasi atmosfera, in care trebuie sa ti mana pe geanta ca sa nu fi furat. Este drept ca am auzit si un episod intamplat unuia dintre colegi in care s-a organizat o ambuscada (cineva isi aduna monezile cazute in metrou) si s-a trezit ca odata pus in miscare metroul, i se arunca portofelul pe geam inapoi, dupa ce a fost golit de ceva maruntis, in special bani romanesti. Se pare ca erau totusi hoti autohtoni (francezi). Au aruncat portofelul inapoi pentru a nu fi motivat sa anunti politia. Am mai vazut intr-un magazin de adidasi un negru inghesuit discret de alt negru pe o usa de serviciu, asteptand sa vina politistii (care erau de culoare). Furase un tricou se pare. Poate ca e bine sa fii atent totusi.

Pe de alta parte, mi-am uitat haina pe spatarul scaunului la un restaurant chinezesc si dupa ceteva zile cand am reusit sa-i gasesc deschisi (nu aveau program afisat) am reusit s-o recuperez, dupa ce am spus culoarea.

Nu ca toti chinezii ar fi foarte cinstiti. Am avut surpriza ca la alt chinezesc, cerand inca o ceasca la ceainicul de ceai deja comandat sa fiu taxat pentru doua ceaiuri (de fapt colegul care vroia sa guste). Si un ceai costa in jur de 2 euro…Oricum, este plin de restaurante chinezesti care in general au si specific tailandez si japonez, asa ca respectivului i-am declarat embargou. Im pare rau ca am lasat bacsis, mi-am dat seama de inselatorie dupa achitare. Tanti chinezoaica ne-a explicat zambind ca la ei o ceasca egal un ceai, desi nu ne-a adus doua ceainice. Francezii au ceva numit «bistro», deosebit de restaurant prin faptul ca e o afacere familiala, unde platesti la casa si nu prea lasi bacsis (chelnerul e si patron). Restaurantul asta parea ceva de genul asta, deci in mod normal nu ar fi trebuit lasat bacsis oricum iar eu am lasat cat pentru inca un ceai.

In Paris e foarte la moda mancarea cu maioneza si un sos de otet (~vinegrette). Asta ca ornare a unor salate considerate fel principal. O salata vine pe un platou urias cu tot felul de chestii, incluzand ou fiert, cubulete de cartofi prajiti, salata, morcov ras, bucatele de carne sau peste, branza « bleu » (mucegaita) – in functie de ce comanzi. Sa va feriti de ceva numit « jambon traditional » care este o carne aproape cruda, preparata cumva sa aiba gust acceptabil, dar iti  ramane pe gat si in dinti. Exista si salate mai mici (de antreu) din care cea mai buna mi s-a parut cea de ton (din pacate are maioneza si vinegrette care contin otet  care sunt iritabile pentru digestia mea).

Aproape toate restaurantele au asa numita « Formule », sau « Meniu » care este o combinatie prestabilita de 3 feluri la un pret unitar, mai mic decat suma preturilor fiecareia. Uneori ai de ales intre cateva variante. Totusi, portiile de la meniu sunt si ele mai mici decat cele comandate separat, deci nu este mare afacere.

Pe langa chinezescuri si bistrouri cu salate gasesti foarte des pizzerii unde pizza este ca la ea acasa, adica fara ketchup. Probabil daca insisti neapart iti pot aduce dar te vor privi piezis.

Vinul frantuzesc atat de renumit nu e este extraordinar. Destul de sec si uneori acrisor. Nu stiu cum sunt cele de peste 5-10euro dar ce am gustat erau mult sub cele romanesti. Proababil foarte renumita este inclinatia francezilor pentru vinuri, pentru ca si ei par sa gaseasca vinurile lor cam seci, asa ca il beau de obicei cu putin sirop concentrat (de visine de exemplu). Chiar la un chinezesc am fost servit din partea casei cu un fel de sampanie cu sirop. Ca veni vorba (din nou) de chinezesc, nu am vazut restaurant chinezesc in care sa nu fie in exclusivitate personal chinez (sau asiatic, nu stiu sa ii deosebesc).

Bacsisul la restaurant se lasa dupa ce ai primit restul. Si chiar iti dau restul pana la 5 centi. Inca nu mi-e clar care sunt cuantumurile obisnuite si situatiile in care ai putea sa nu lasi bacsis, aici recunosc ca actionez cam haotic de obicei sub un prag de 10%.

M-am obisnuit usor sa aud in jur oameni vorbind in alta limba. E ca in Romania, percep doar 25% din ce aud. Cand ma intreaba cineva ceva pe strada sau in metrou si nu inteleg am renuntat sa mai incerc sa il inteleg sau sa-l lamuresc ca nu inteleg. Daca este grabit l-as face sa piarda timp, daca vrea sa afle ceva probabil nu stiu, daca vrea sa coboare la prima am grija sa-i fac loc. Daca este important, insista.

La firma insa oamenii mai stiu ceva engleza, spre deosebire de majoritatea francezilor, inclusiv chelneri si vanzatori. Engleza e mai utila pentru a intelege ei ce vreau sa spun, de multe ori as prefera sa vorbeasca franceza pentru ca probabilitatea sa nu stim unul din noi un cuvant in engleza este mai mare decat cea de a nu intelege eu un cuvant in franceza. Si apoi engleza francezilor este foarte frantzuzita, ii intelegi mai usor daca ai idee cum au ei obiceiul sa poceasca cuvintele. Am incercat sa vorbim eu-engleza si ei franceza dar nu prea a mers, oficial sunt obligati sa stie engleza si cred ca se feresc sa para ca nu stiu. In plus, probabil trec automat in regim de engleza cand aud engleza. Eu o mai franglezesc uneori atat de tare incat ma trezesc ca nu mai stiu in ce limba am vorbit.

Cand cineva vorbeste engleza bine este atat de placut si de simplu de inteles incat ai impresia ca vorbeste limba materna. Asa ca m-am trezit la un moment dat raspunzandu-i in romana. Normal ca s-a blocat.

Cateodata am impresia ca nici francezii nu stiu bine sa vorbeasca franceza. Ii auzi cateodata vorbind la telefon si cautandu-si indelungat cuvintele ca si cand nu ar stii limba. In plus, folosesc tot felul de prescurtari la cuvinte, astfel ca limba lor de conversatie nu seamana de loc cu franceza « literara », parand a poci cuvintele. Oui (UI) de exemplu este transformat de obicei in ceva de genul «OEI ».

***

            A trecut mai mult de o luna. Impresiile s-au mai echilibrat, am inceput sa vad si frantuzoaicele « fast food », si politistii albi, si destul de multe femei insarcinate (probabil fiind cu cateva luni inainte de aniversarea a 9 luni de la sarbatorile de iarna. Este semn ca se mai intampla cate ceva si in Franta.

            In ajunul zilei Frantei am ajuns intamplator intr-un parc public, atras de pocnitorile si muzica americana cantata cu un accent specific. La intrare erau copii care aruncau petarde spre disperarea adultilor care treceau pe langa. In spate, pe o scena mobila canta o formatie de muzica usoara slagare mai vechi si mai noi. Langa ea, peste iarba care umplea acea mica dumbrava era instalat un ring de dans cu cativa centimetrii mai inalt decat pamantul. Era ca o serbare campeneasca, asa cum vezi in filmele de epoca. Copii care trageau de parinti pentru a-i face sa danseze, si copii adusi de parinti pentru a-si aduce aminte de propria copilarie. Ce m-a impresionat este atitudinea pe care o afisau, de calm si pace, ca si cum stiau ca nimic rau nu se poate intampla, ca au tot timpul inainte si nu trebuie sa lupte pentru a subzista. Altfel oameni simplii, ca pe la noi, doar ca mai putine tipete, mai putina agitatie.

Mi-am scos papucii si am mers pe iarba un pic uda. M-am asezat apoi pe iarba si am privit. Alti francezi stateau si ei pe iarba si priveau sau discutau linistit. Cei mai curajosi erau pe ring si se miscau in ritmul muzicii, fiecare dupa pricepere. Un barbat care mi-a sugerat un  superman de vreo 50 ani, cu ochelari cu rama groasa, in blugi pana la genunchi si bretele dansa singur miscand energic din bratem putin caraghios. Nimeni nu isi punea insa problema de parerea celorlalti, fiecare gusta clipa. Retras la cativa metrii de ringul de dans, un batranel improviza un tangou cu sotia pe o melodie oarecare. Incarcat de aceasta atomosfera am plecat usor spre hotel, printre oamenii care veneau in continuare spre parc si primii politisti albi pe care i-am remarcat. Doreau sa vada atificiile, care au fost aruncate in acel parc si in alte parcuri din Paris. Probabil cu asta se ocupa chinezii din Paris care nu au restaurante :)

In drumul spre hotel am trecut pe langa un sediu de politie, care arata la fel de darapanat ca unul din Romania. Putin mai departe insa niste politisti pe motociclete aratoase chestionau un pusti pe scuter care avea o figura foarte incurcate si incerca sa sune de pe mobil probabil un parinte. Nimic care sa iasa din normal, totusi mult mai mult calm. Este ceea ce ai nevoie ca sa faci planuri pe termen lung, sa nu te preocupi doar de ziua de maine, carpind ceva ce va trebui carpit din nou poimaine.

***
Mergand cu metroul am avut revelatia vastului sistem circulator care iriga Paris-ul si il face sa functioneze – sistemul ferat de transport, metrou, RER, tren (mai putin autobuze si autoturisme). Acest sistem, care s-a intins cand in subteran, cand la suprafata, care trece si pe sub Sena, care ajunge si pe Ille de la Cite (insulita de pe Sena) si se prelungeste spre alte orase cu TJV-ul (tren rapid)  asigura oxigenul pentru ca aceast oras sa traiasca. Nu totul arata impecabil. Mai vezi fire atarnand din tavan de luni de zile, mai vezi colturi rupte si nereparate sau doar uzate si reparate carpit. Dar timpul trece si probabil lucruri mai greu de reparat se vor strica. Magistrale vor deveni neincapatoare pentru volumul de trafic crescut - deja se intampla pe unele linii, la orele de varf sa fie aglomeratie. Cum vor rezolva oare francezii (sau « negrii » lor) aceste probleme ? Vor reusi sa tina pasul cu invechirea infrastructurii si cu marirea traficului, sau timpul nemilos va scleroza aceste vase sanguine, iar ele nu vor mai reusi sa isi gaseasca noi cai pe langa cele supra-aglomerate, printre cladiri si conducte de canalizare. Oare va veni o vreme cand orasul va deveni pur si simplu nelocuibil, iar oamenii vor cauta alte locuri in care sa construiasca orase mai bine structurate, sau se vor gasi poate mijloace alternative de transport, poate aeriene. Dar aceste raspunsuri sunt poate inca departe, orasul abia incepe sa cucereasca cea de-a treia dimensiune, magistrale de metrou, sosele si chiar un aeroport suprapuse. Dureaza mii de ani ca un oras sa se naruiasca sub greutatea propriei supra-dezvoltari si de obicei intervin alti factori care reusesc asta inaintea termenului de expirare.
 
***

Romania mai are mult pana sa ajunga la aceste probleme filosofice. Infrastructura abia se construieste. Am fost placut surprins ca au inceput si la noi sa se puna la punct sistemul de transport ferat de suprafata (adica se construiesc noi linii de tranvai). Se circula foarte prost din cauza lucrarilor, dar e un lucru bun pentru viitor. Pentru ca lucrez departe am indraznit sa dau o sansa transportului comun bucurestean. Pana acum sunt multumit, parca ajung mai odihnit decat cand merg cu masina. Metroul este chiar mai aratos decat cele franceze. Mai poti citi si cateva pagini de carte uneori. Cred ca mersul exclusiv cu masina personala dezumanizeaza putin. Probabil resorturi ascunse reactioneaza negativ la lipsa variatiei de fizionomii, alte mecanisme de relationare se blocheaza nefolosite. Probabil este una din problemele care afecteaza pe multe din persoanele publice (politicieni, oameni de afaceri) care ajung pana la urma sa cedeze nervos sau sa piarda energia si idealurile care i-au animat la inceput.

            Inca tresar cand aud vorbindu-se romaneste in jur, mai ales in locuri publice (restaurant, metrou). Imi dau seama de diferenta cand stau la coada si trebuie sa am grija sa nu imi intre cineva in fata. Mancarea e mai buna, chiar si shaorma. Asta este, sunt iremediabil si inadaptabil la conditii «mai bune».

Daca este sa spun ceva care mi-a placut in mod deosebit in Franta este valea Loarei, cu salba ei de castelele …medievale cred. Nu castelele in special, platesc si ele tribut unui spirit usor mercantil de tara care a invatat sa-si vand frumusetile. Mi-a placut cerul de un albastru clar si adanc. Mi-a placut apa vazuta de la inaltimea unui castelul. Mi-a placut vinul rosu discret parfumat si usor aspru, baut intr-o atmosfera de pace si liniste. Asta as vrea sa pastrez din Franta.

Fiecare loc isi are farmecul si rostul lui, avantajele si dezavantajele lui. Cred ca fiecare om isi are rostul undeva si conteaza mai mult ce poti aduce intr-un loc decat ce iti poate aduce acel loc.


Mihai Voicu 2005



"The more I say, the more I know" . Republicarea articolelor este permisă cu citarea autorului

2010-10-10

Traceuri/Loguri in Java


 Îndrumar folosire trace-uri / log-uri in Java
- articol pentru programatori -

 Pe scurt: aproape orice program merită să aibă un sistem centralizat de trace-uri/log-uri, de exemplu log4j. Nivelele de trace se aleg astfel :
  •  FATAL : eroare gravă, programul nu mai poate continua, urmează exit(-1). SLF4J recomanda folosirea tot a nivelului ERROR în acest caz.
  •  ERROR : eroare gravă, "așa ceva nu trebuia să se întâmple"
  •  WARNING : semnalează o posibilă eroare, dar care poate fi comportament normal în unele cazuri
  •  INFO : afişeaza informaţii utile pentru utilizatorul programului. Nivelul nu trebuie să fie prea verbose, trebuie să poată fi citit de un utilizator uman indiferent de volulumul inputului.
  •  DEBUG : dă informații utile programatorului aplicaţiei în cazul în care apar probleme de funcţionare. Este echivalent cu "verbose".
  •  TRACE : dă informații foarte detaliate despre fiecare pas important al programului (util în depanarea de către programator). Pentru că pe nivelul TRACE pot fi foarte multe loguri, trace-urile pot consuma foarte mult procesor chiar dacă nu sunt afișate. Cauza este faptul că stringul de afișat se calculează chiar dacă nu va fi afișat. Performanța se poate ameliora testând întâi nivelul de trace înainte de face apelul de log. Alte loggere (SLF4J) permit trimiterea obiectelor separat, urmând a fi evaluate (.toString()) doar dacă trebuie afișate.
P.S. : am modificat căteva lucruri pe măsură ce mi-a crescut experiența : adăugat nivel TRACE, mutat unele loguri de la INFO la DEBUG și de la DEBUG la TRACE. Nu am refăcut restul textului cu noua abordate.


Introducere

   Am scris mai demult un articol despre excepții în Java. O sa prezint acum câteva concluzii la care am ajuns despre folosirea trace-urilor/logurilor în general și cu aplicații în limbajul de programare Java.


Ce sunt trace-urile ?

  Realitatea este ca programele au bug-uri. Mai devreme sau mai târziu vom fi nevoiți să cercetăm de ce programul nu funcționează așa cum speram într-un anumit caz. Pentru asta avem nevoie de informații despre execuția programului, ce probleme a întâmpinat, ce s-a executat cu success si ce nu.
 
  Trace-urile sunt linii de text emise de un program care "povestesc" pașii importanți prin care trece programul. Dupa cum le spune și numele, trace-urile sunt niște "urme" care ramân în ... urma executării programului.

 De exemplu trace-urile conțin informații despre erori, situații deosebite, unde a ajuns programul cu o anumită activitate mai lungă. Trace-urile sunt un feedback util, putem să vedem că programul incă rulează chiar dacă nu poate scoate incă un output util.

 Linia de trace conţine de obicei data si nivelul de trace, si opţional threadul si fisierul/linia de la care provin. Fisierul şi linia sunt foarte utile în identificarea problemei. Rulând programul in Eclipse si făcând click pe (fisier:linie), editorul ajunge exact la linia respectivă, ceea ce optimizează mult depanarea.


2010-06-01 12:09:56,582 WARN    [main] ApplicationConfig not ready (AdsManager.java:414)
2010-06-01 12:09:56,584 DEBUG  [main] HttpClient created with maxTotalConnections=20 (AdsManager.java:418)
2010-06-01 12:09:56,584 INFO     [main] Initializating with CachePath="/cache/" (AdsManager.java:380)
Trace-urile pot fi emise direct pe ecran sau pot fi scrise intr-un fisier de "log". Trace-urile scrise pe ecran (consolă) pot fi si ele redirectate intr-un fisier de către utilizator. Se recomandă emiterea trace-urile pe "standard error" ( System.err.println() ), pentru a putea fi separate de outputul "util" al programului care va ieşi pe "standard output".

 Trace-urile nu trebuiesc confundate cu rularea "pas-cu-pas" a programului dintr-un program "debugger". Spre deosebire de debuger, trace-urile nu întrerup  funcţionarea programului. La modul ideal, trimiterea trace-urilor ar trebui să aibă un impact cât mai mic în încetinirea programului, dar pentru asta este necesară puțina atenție.


 Trace-uri, log-uri sau alarme ?

 Eu prefer să numesc trace-uri informatiile care folosesc la monitorizarea și depanarea programului. Anumite programe specializate ar putea să aibă alături de trace-uri un sistem paralel de log-uri. Log-urile sunt pentru mine nişte informaţii mai puţin legate de program ci de datele procesate. 

 De exemplu anumite aplicatii de telefonie pot "log-a" fiecare apel telefonic pentru a calcula factura pe baza lor la sfarșitul lunii. De obicei log-urile au un format mai strict decăt trace-urile. Log-urile sunt menite sa fie procesate automat iar trace-urile sunt destinate in special ochiului uman. Veti intâlni  adeseori ambele denumiri ca fiind echivalente.

 Alarmele sunt niste trace-uri active, care anunță un server de monitorizare despre evenimente speciale din timpul executiei unui program, de exemplu pornirea, oprirea, erori intâlnite. In cazul sistemelor critice, serverul de monitorizare are de obicei o persoană care supraveghează permanent alarmele, le investighează si ia măsuri de remediere când este cazul.

Trace-urile obișnuite sunt de obicei pasive, ele sunt verificate doar cănd administratorul programului vrea niște informații suplimentare.



  Cum folosim trace-urile ?

  Sistemul clasic de trace-uri folosește câteva nivele distincte de trace, in funcție de importanță/gravitate (exemplu FATAL, ERROR, WARNING, INFO, DEBUG). Cele mai grave evenimente sunt erorile fatale, iar cele mai putin importante sunt informațiile detaliate despre paşii prin care trece programul.

In funcție de necesități, administratorul poate alege să vadă doar trace-urile care depașesc o anumită gravitate, de exemplu doar erorile sau doar erorile si warning-urile (avertizările). In cazul în care se depanează programul, se poate activa nivelul maxim de trace-uri, care va trimite cât mai multe informații posibile.

Este important ca programul să aibă trace-uri pentru fiecare pas mai mare pe care îl face. Programul trebuie să poată fi configurat la rulare să trimită doar trace-urile peste o anumită gravitate, pentru a nu consuma resurse inutile atunci când nu sunt necesare trace-urile detaliate. 

Sistemele avansate de trace pot activa nivele de trace diferite pe diferite module  de interes, pentru a obtine cel mai bun compromis intre informații si performanță. Un logger profesionist poate fi configurat să trimită logurile în mai multe locuri (consolă si fisier, pe alt server, etc)


Este mai simplu dacă sistemul de trace are si o metodă de afişare a excepţiilor, cu un apel de genul log.error("Unexpected exception : ", e);




Nivelele de trace pe larg
  •  FATAL (unrecoverable) : este trimis când programul a înâlnit o eroare foarte gravă, la care trebuie să se oprească. De exemplu programul nu poate să aloce memorie, nu poate deschide un fisier de input etc. Dupa ce trimite trace-ul FATAL, de obicei programul face exit(-1). Mesajul trebuie să contină o descriere cât mai clară a motivului care a determinat programul să se oprească anormal. Daca cauza poate fi înlăturată de utilizator, se prezintă acestuia posibile soluții. Daca cauza este o exceptie "this should never happen" (cauzată de un bug)  atunci se printează obligatoriu stack-ul pentru a fi reprodusă/depanată de programator.
  • ERROR (recoverable) : este emis când programul a întâlnit o eroare gravă, dar poate totuşi continua. Daca problema este externă (a picat conexiunea la baza de date) se da un mesaj pentru utilizator cu contextul problemei (server, user, tabel). Daca eroarea provine de la o problemă de programare se printează stack-ul pentru programator.
  • WARNING (posibilă problemă) : este emis cănd programul a întălnit o situatie neobișnuită sau posibilă eroare care totuși ar putea fi normală în anumite cazuri. De exemplu utilizatorul a restartat baza de date, programul a trebuit să se reconecteze. Utilizatorul poate decide dacă situația este normală sau nu. De obicei nu se printează stack-ul intrucăt problema nu este neașteptată - daca ar fi neașteptată nu am știi că "ar putea fi normală în anumite cazuri" si ar fi "ERROR"
  • INFO (verbose) : pe acest nivel sunt semnalate informații utile utilizatorului software-ului, de exemplu operatorilor de support. Pe acest nivel se dau informatii statistice despre rularea programului, se pot semnala inceputul/sfarşitul anumitor operaţii. Se poate chiar afişa fiecare cerere şi răspuns al unui sistem, cu condiţia ca acestea să nu fie prea multe, altfel afectează performanţa. Din experienţa mea 100 requesturi/secundă pot fi logate fără un impact de performanță notabil.

  • DEBUG (depanare) : pe acest nivel se dau informatii pentru a se face depanare de catre un programator. La limită, fiecare funcție importantă poate avea un trace pe nivel debug cu inputul si output-ul său. Alte informatii utile unei eventuale depanări pot fi incluse pe parcursul funcției. Procesarea unui singur request poate genera mii de linii de DEBUG, dar de obicei rularea programului pe nivel DEBUG poate afecta performanţa, incetinind programul. Trace-urile care se efectuează de peste 1000 ori pe secundă ar putea să încetinească programul chiar dacă nivelul de trace nu este pe DEBUG. Detalii mai jos.

Exepţii de la regulă (gravitate versus importantă)


De fapt există cel puţin doua axe pe care se pot clasifica evenimentele, gravitate şi importantă. De exemplu unele mesaje sunt importante (versiunea programului, faptul că a pornit si s-a oprit la o anumită oră), dar nu sunt şi grave. Clasificarea pe o singură axă a gravitătii poate să nu fie suficientă, de aceea eu fac anumite exceptii de la regula gravitătii.

In principiu este de dorit să existe versiunea si parametrii programului in orice log, pentru a uşura depanarea. Aceste informaţii ar apartine de nivelul INFO, dar eu prefer să le dau pe nivelul WARNING pentru a le avea în trace şi atunci cănd programul rulează în mod "ne-verbose". Includ în textul mesajului un "[INFO]" pentru a preciza că nu este de fapt decăt o informaţie, chiar dacă apare pe nivel "WARNING".

Nivelul normal de rulare (ne-verbose) al unui program ar trebui sa fie WARNING. Aceste loguri ar trebui să fie suficient de rare incât să poată să "sară in ochi" utilizatorului cănd se intămplă ceva ciudat. Pe nivelul INFO sunt de obicei prea multe informaţii, utilizatorul ne-urmărindu-le decăt dacă caută ceva anume.

Alte informaţii care ar aparţine nivelului INFO dar am preferat să le dau pe nivel WARNING erau informaţii la 20-30 secunde despre încărcarea sistemului (numar de utilizatori, memorie consumată, număr de operaţii pe secundă.




Probleme de performanţă la trace-uri/log-uri


La fiecare apel al unui trace, se consuma un pic de timp. Un trace care afişeaza maxim 100 linii pe secundă nu are de obicei un impact de performanţa, peste 1000 linii pe secundă ar putea să incetinească programul, dupa caz.


Dacă trace-ul este sub nivelul de afişare se execută doar condiţia if(nivel < traceLevel). Procesoarele moderne pot executa sute de milioane de comparaţii pe secundă, deci asta nu consumă foarte mult din CPU. In Java am obtinut 250 milioane comparaţii/secundă pe un sistem 2*1.6Ghz, inclusiv bucla de comparaţie, deci doar câteva  nanosecunde per comparaţie.



Problema apare cu calculul a ceea ce dorim sa afişăm. Dacă trimitem un string constant, practic se execută doar comparaţia. Dacă insă apelăm trace-ul cu un string calculat, el va fi evaluat chiar dacă nu va fi afişat. 

Schimbând stringul constant trace("a") cu trace("a" + i), unde i era long, numărul de operatii s-a redus de la 250 milioane/secundă la 5 milioane/secundă, iar cu un string putin mai lung la doar 1 milion/secundă. Inseamnă putin per operaţie dar se poate aduna dacă trace-ul este intr-o buclă în care se apelează de milioane de ori. In acest ultim caz, 100.000 trace-uri DEBUG pe secundă ar consuma 10% din procesor, chiar dacă nivelul de trace nu le-ar face să fie afişate.


Din acest punct de vedere, implementarea trace-urilor in C/C++ cu macrouri #define este mai eficientă, stringul de afişat este calculat doar daca nivelul de trace cere afişarea acelui trace. Teoretic si masina virtuală Java ar putea să detecteze că acel string nu merită calculat pentru că condiţia va fi falsă, dar in testele mele nu o face.

In Java, pentru a evita evaluarea stringului cănd nivelul de trace nu îl va afişa există soluţia de a nu concatena bucăţile de string înaintea apelului, ci în funcţia de trace. Dacă apelez de exemplu trace("a",i) şi fac concatenarea în interiorul if-ului, mă intorc la viteza mare de 250 milioane/secundă. In versiuni java mai recente se poate defini o functie de trace cu numar variabil de argumente trace(...), care va concatena orice numar de stringuri. Problema este că dacă unele dintre stringuri sunt calculate din diferite metode, metodele respective se vor apela totuşi, chiar dacă trace-ul nu va fi afişat.

Cel mai periculos este dacă pentru a construi textul apelăm metode complexe care iau mult timp. De asemenea, afişarea fişierului si liniei este mult mai lentă decăt afişarea unui trace fără aceste informaţii. Dacă programul rulează prea lent, incercaţi si fară afişarea fişierului şi liniei. Un sistem de trace centralizat are exact acest scop : puteţi schimba configuraţia pentru tot programul într-un singur loc.

Pentru locuri in care se execută peste 1000 operaţii pe secundă există posibilitatea de a pune apelul de trace într-un if, care va face ca textul trace-ului să fie evaluat doar când este activat modul DEBUG.


Capcane la folosirea trace-urilor

 Puteţi genera o excepţie neaşteptată chiar prin incercarea de a da un trace. Daca trace-ul cheamă o metodă a unui obiect null, va genera un NullPointerException.

 Situaţia este si mai gravă când trace-ul era urmare a unei excepţii, practic excepţia iniţială se pierde si se generează alta. Folosiţi in trace doar apeluri de care sunteţi siguri că nu pot genera erori/excepţii.

 Un trace prea des poate face programul instabil. Dacă de exemplu dăm un trace când detectâm ca programul răspunde prea încet, am putea agrava problema prin faptul că dam aceste avertismente. Câteaodată masurarea modifică obiectul măsurat... Evitaţi să apară cazuri in care warning-urile/erorile sunt repetate inutil, dacă problema persistă se pot re-afişa la anumite intervale.

 In C++, este posibil ca in funcţie de nivelul de trace configurat, anumite apeluri de funcţii sa se întâmple sau nu. Nu apelati in trace metode care modifică date !


Folosire Logger (log4j)
  • Se recomandă folosirea unui sistem unitar de trace, astfel incât să putem gestiona global nivelul de trace si formatul de afişare. Dacă folosim o anumită bibliotecă, ar fi bine să folosim un sistem compatibil - de exemplu bibliotecile apache folosesc log4j.

  • Există si un sistem de logare nativ in java (java.util.logging.Logger) dar pe mine nu m-a convins. Prefer log4j.

  • In mod obişnuit fiecare clasă declară un membru
     static Logger log = Logger.getLogger( NumeClasa.class );

      In cadrul clasei putem apela apoi  (dupa ce am configurat un appender, găsiţi exemple pe Internet) :


   log.debug("mesaj debug")
   log.fatal("mesaj warning")
   log.fatal("mesaj eroare fatală", exceptie).
 
Membrul log se declară static pentru a nu se instanţia in fiecare obiect, ci doar unul per clasă. Argumentul este un string de fapt, mai exact se trimite numele clasei, se face implicit un .class.toString()

   Este preferabil să se trimită .class in loc de numele clasei intre ghilimele pentru că in primul caz schimbănd numele clasei (refactorizare) nu vom uita Logger-ul definit cu numele vechi.


 Numele clasei este trimis pentru a putea controla nivelul de logging pe fiecare clasă, de exemplu sa activăm nivelul DEBUG pe o anumită clasă dar să pastrăm doar nivelul WARNING la celelalte. Putem crea si loggere care să nu aparţină unei clase ci unui modul.




 Creaţi-vă propriul logger



 Dacă nu aveţi nevoie de setarea nivelului de trace diferit per clasă si nu vreţi sa folosiţi o bibliotecă externă precum log4j, puteţi folosi o clasă MyLogger care să aibă metode statice, care se pot apela prin MyLogger.warn("..."). 

Metodele trebuie să adauge data, nivelul de trace si eventual threadul si fisierul/linia, apoi sa apeleze System.err.println().

 Pentru a obtine fisierul si linia se poate examina stack-ul care se obtine prin "new Throwable().getStackTrace()".

 Mai târziu puteţi face această clasă să apeleze un alt logger, dar din pacate toate traceurile vor părea că vin din clasa MyLogger, nu din locul in care au fost chemate iniţial. Teoretic există o metodă de a afişa locaţia originală dar eu n-am reusit. Puteti totusi să obtineti asta dacă faceţi un wrapper peste Logger derivând o clasă din Logger si suprascriind anumite metode.




Bonus : o clasă simpla de trace-uri


O implementare de clasă care face trace-uri găsiti mai jos (cu titlu de exemplu). Nu este o capodoperă, dar vă poate da nişte idei:


package ro.mihvoi.trace;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;


/*
 * MvTrace.java
 * 
 * This class provides a static methods to do traces with different trace levels
 * You can set at any time the trace level you want to actually print
 * You don't need to instantiate the class, just use the static methods
 * Normally the trace goes to stderr to not pollute the useful output
 * For Eclipse you can force it to stdout, to avoid red text
 * 
 *  You can put MVTRACE_LEVEL in evenironment to override the program's settings
 * 
 * Created on December 17, 2007, 9:31 PM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */



/**
 *
 * @author mihvoi
 */


public class MvTrace {

    enum TraceLevel{
        DEBUG,
        INFO,
        NORMAL,
        WARNING,
        ERROR,
        FATAL
    }

    public static TraceLevel DEBUG=TraceLevel.DEBUG;
    public static TraceLevel VERBOSE=TraceLevel.INFO;
    public static TraceLevel NORMAL=TraceLevel.NORMAL;
    public static TraceLevel WARNING=TraceLevel.WARNING;
    public static TraceLevel RECOVERABLE=TraceLevel.ERROR;
    public static TraceLevel UNRECOVERABLE=TraceLevel.FATAL;


    private static String textForTraceLevel[] = {
        "UNRECOVERABLE",
        "RECOVERABLE  ",
        "WARNING      ",
        "====NORMAL===",
        "VERBOSE      ",
    "DEBUG        "};

    //private static MvTrace singletonMvTrace=null;
    private static boolean isInitializated=false;
    //private static int currentTraceLevel=MvTrace.NORMAL;
    private static TraceLevel currentTraceLevel=TraceLevel.DEBUG;
    private static TraceLevel _envTraceLevel = null; //This may override the currentTraceLevel set in the program

    //Last received trace, even not printed, to be printed with the unrecoverable errors
    private static String lastWarningTraceReceivedText;
    private static TraceLevel lastWarningTraceReceivedLevel;
    private static Calendar _calendar = Calendar.getInstance();
    private static DateFormat _dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");

    private static StringBuffer tmpStringBuffer = new StringBuffer();
    public static boolean _printFileLine = true;
    public static boolean _printDate = true;
    public static boolean _printThread = true;
    public static boolean _printColor = true;
    public static boolean _autoFlush = true;
    public static boolean _printOnStderr = true;
    public static boolean _printDebugForNormal = true;
    public static boolean _printFileLineInNormal = true;
    public static boolean _printOnStderrOnlyWarningAndErrors = false; //Set to true if you want to have DEBUG, VERBOSE on stdout instead on stderr
    public static int       _skipNbWrappersWhenPrintingStack = 0; //Use 1 if you call MvTrace from another trace wrapper, 2 for 2 wrappers

    static {
        String envLevel = System.getenv("MVTRACE_LEVEL");

        if(envLevel != null){
            TraceLevel level = getTraceLevelByName(envLevel);
            //MvTrace.printRecoverable("Level : " + level);
            if(level == null){
                MvTrace.printRecoverable("Unknows trace level in environment variable MVTRACE_LEVEL=\"" + envLevel + "\" , valid options are : " + getTraceLevelList());
            }else{
                MvTrace.printWarning("Using level set in environment MVTRACE_LEVEL=" + envLevel);
                _envTraceLevel = level;
            }
        }
    }

    public static String getTraceLevelList(){
        String values = "[";
        for(TraceLevel level : TraceLevel.values()){
            values += " " + level + ",";
        }
        values += ']';

        return values;       
    }

    public static TraceLevel getTraceLevelByName(String traceLevelString){
        //MvTrace.printRecoverable("Searching for level = " + traceLevelString);
        TraceLevel level = null;

        try{
            level  = TraceLevel.valueOf(traceLevelString);
        }catch(IllegalArgumentException e){
            //MvTrace.printRecoverable("Unknow trace level : " + traceLevelString + " valid values : " + getTraceLevelList() );
            return null;
        }

        //MvTrace.printWarning("Received " + level);
        return level;
    }

    //Slower
    public static boolean setTraceLevelSlow(String newTraceLevel){

        TraceLevel level = getTraceLevelByName(newTraceLevel);
        if(level != null){
            setTraceLevel(level);
            return true;
        }

        StringBuffer sb = new StringBuffer();
        for (int i=0; i < textForTraceLevel.length; i++) {
            if (i != 0) sb.append(", ");
            sb.append(textForTraceLevel[i].toString());
        }
        MvTrace.printRecoverable("Unknown trace level string : \"" + newTraceLevel + "\". Valid trace levels are: " + sb);
        MvTrace.printRecoverable("Trace level unchanged (" + currentTraceLevel + ")");
        return false;       
    }

    public static TraceLevel setTraceLevel(int newTraceLevel){
        TraceLevel level = TraceLevel.values()[newTraceLevel];
        return setTraceLevel(level);
    }


    public static TraceLevel setTraceLevel(TraceLevel newTraceLevel){
        TraceLevel oldTraceLevel;
        oldTraceLevel=currentTraceLevel;
        currentTraceLevel=newTraceLevel;
        return oldTraceLevel;
    }

    static void initializate(TraceLevel aTraceLevel){

        if(isInitializated){
            printRecoverable("Trace already initializated");
            System.out.println("W: Trace already initializated");
        }

        isInitializated=true;
        currentTraceLevel=aTraceLevel;
        //_dateFormat = new SimpleDateFormat("zzz"+PS+"yyyy-MM-dd"+PS+"dd-HH-mm"+PS+"ss-SSS");


        System.out.println("Trace system initialized with level: " + currentTraceLevel);
    }

    public static void debug(String aText){
        printTraceWithLevel(DEBUG, aText, null);
    }

    public static void printDebug(String aText){
        printTraceWithLevel(DEBUG, aText, null);
        //return this;
    }

    public static void printNormal(String aText){
        printTraceWithLevel(NORMAL, aText, null);
        //return this;
    }

    public static void print(String aText){
        printTraceWithLevel(NORMAL, aText, null);
        //return this;
    }

    public static void printVerbose(String aText){
        printTraceWithLevel(VERBOSE, aText, null);
        //return this;
    }

    public static void info(String aText){
        printTraceWithLevel(VERBOSE, aText, null);
        //return this;
    }

    public static void printWarning(String aText){
        printTraceWithLevel(WARNING, aText, null);
        //return this;
    }

    public static void warn(String aText){
        printTraceWithLevel(WARNING, aText, null);
        //return this;
    }
   
       public static void warn(String aText, Exception t){
            printTraceWithLevel(WARNING, aText, t);
            //return this;
        }

    public static void printWarning(String aText, Exception t){
        printTraceWithLevel(WARNING, aText, t);
        //return this;
    }

    public static void printRecoverable(String aText){
        printTraceWithLevel(RECOVERABLE, aText, null);
        //return this;
    }

    public static void error(String aText){
        printTraceWithLevel(RECOVERABLE, aText, null);
        //return this;
    }

    public static void printRecoverable(String aText, Exception t){
        printTraceWithLevel(RECOVERABLE, aText, t);
        //return this;
    }

    public static void error(String aText, Exception t){
        printTraceWithLevel(RECOVERABLE, aText, t);
        //return this;
    }

    public static void printUnrecoverable(String aText){
        printTraceWithLevel(UNRECOVERABLE, aText, null);
        //return this;
    }

    public static void fatal(String aText){
        printTraceWithLevel(UNRECOVERABLE, aText, null);
        //return this;
    }

    public static void printUnrecoverable(String aText, Exception t){
        printTraceWithLevel(UNRECOVERABLE, aText, t);
        //return this;
    }

    public static void fatal(String aText, Exception t){
        printTraceWithLevel(UNRECOVERABLE, aText, t);
        //return this;
    }


    public static void checkAssertExit(boolean aConditionThatShouldBeTrue, String aText){

        if(! aConditionThatShouldBeTrue){
            printTraceWithLevel(lastWarningTraceReceivedLevel,lastWarningTraceReceivedText, null);
            printTraceWithLevel(UNRECOVERABLE, aText, null);
            System.exit(-1);
        }
        // return this;
    }

    static protected void printTraceWithLevel(TraceLevel aTraceLevel, String aTraceText, Exception t){

        synchronized (tmpStringBuffer) {

            //Set the last warning even if it is not printed
            if( aTraceLevel.compareTo( TraceLevel.WARNING) > 0){
                lastWarningTraceReceivedText=aTraceText;
                lastWarningTraceReceivedLevel=aTraceLevel;
            }

            //Will continue printing the trace with break, will not print trace if call return
            if(aTraceLevel != TraceLevel.NORMAL){ //Always print NORMAL

                if(_envTraceLevel == null){ //No environment overwrite of trace level   
                    if( aTraceLevel.compareTo(currentTraceLevel) < 0 ){
                        return;
                    }

                }else{ //There is an evironment trace level set
                    if( aTraceLevel.compareTo(_envTraceLevel) < 0 ){
                        return;
                    }
                }
            }//if(aTraceLevel != TraceLevel.NORMAL){


            //System.out.println("DEBUG [" + textForTraceLevel[aTraceLevel] + "] : " + aTraceText + "Current "+currentTraceLevel + " Level" + aTraceLevel);

            tmpStringBuffer.setLength(0);

            if(_printColor){
                if(aTraceLevel.compareTo(WARNING) >= 0){
                    tmpStringBuffer.append("\u001B[31m");
                    //tmpStringBuffer.append("!!!!!! ");
                    //"\u001B[35m"
                }

            }

            if(_printDate){
                _calendar.setTimeInMillis(java.lang.System.currentTimeMillis());
                String date = _dateFormat.format(_calendar.getTime());
                tmpStringBuffer.append(date);
            }

            if(_printThread && ( (aTraceLevel!=NORMAL) || _printDebugForNormal ) ){
                String threadName = Thread.currentThread().getName();
                long threadId = Thread.currentThread().getId();
                tmpStringBuffer.append(" [thr");

                //String threadName = Thread.currentThread().getName();
                //tmpStringBuffer.append(threadName);

                String thread3Digits = String.format("%03d", threadId);
                tmpStringBuffer.append(thread3Digits);
                tmpStringBuffer.append("_");


                tmpStringBuffer.append(String.format("%-20s", threadName) );       
                tmpStringBuffer.append("]");
            }

            if( (_printDebugForNormal==true) || (aTraceLevel!=NORMAL)){
                tmpStringBuffer.append(" [");
                tmpStringBuffer.append( String.format("%-7s", aTraceLevel) );
                tmpStringBuffer.append("]");
            }

            tmpStringBuffer.append(" ");
            tmpStringBuffer.append(aTraceText);

            if(_printFileLine && ( (_printDebugForNormal==true) || (aTraceLevel!=NORMAL || _printFileLineInNormal)) ){
                Throwable aThrowable = new Throwable();
                StackTraceElement className[]= aThrowable.getStackTrace();
                //tmpStringBuffer.append(" <= ");
                StackTraceElement stackElement = className[2 + _skipNbWrappersWhenPrintingStack ];
                //Not working in old Eclipse
                tmpStringBuffer.append(" (" + stackElement.getFileName() + ":" + stackElement.getLineNumber() + ")");
                //tmpStringBuffer.append(" " + stackElement.toString() );

            }

            //[0].getClassName();
            //First 2 are:
            //Nume clasa: atomdatabase.MvTrace.printTraceWithLevel(MvTrace.java:84)
            //Nume clasa: atomdatabase.MvTrace.printDebug(MvTrace.java:46)
            //for (int i=0; i< className.length ; i++){
            //    function_name=function_name.concat(className[i].toString());
            //    //System.out.println("Nume clasa: " + className[i]);
            //}

            //System.out.println(  date + " [thr" + threadId + "]" + " [" + textForTraceLevel[aTraceLevel] + "] : " + aTraceText );


            if(t != null){

                //tmpStringBuffer.append("\nException message : ");
                //tmpStringBuffer.append( t.getMessage() );
                tmpStringBuffer.append("\n\n");

                //tmpStringBuffer.append("\nSTACKKKKKKKKKKKKKKKKK");
                StringBuilder fullStack = new StringBuilder();
                StringBuilder currentCauseStack = new StringBuilder();

                fullStack.setLength(0);
                Throwable cause = t;

                do{
                    currentCauseStack.setLength(0); //Set empty

                    currentCauseStack.append("\nException msg : " + cause.getMessage() + " " + cause.getClass() + "\nStack:");

                    StackTraceElement a[] = cause.getStackTrace();

                    for(int i=0; i < a.length ; i++){
                        currentCauseStack.append("\n");
                        currentCauseStack.append(a[i].toString());
                    }

                    cause=cause.getCause();

                    fullStack.append(currentCauseStack);
                    //fullStack.insert(0,currentCauseStack);

                    if(cause != null){
                        fullStack.append("\n\n...CAUSED BY:");
                    }

                }while(cause != null);

                tmpStringBuffer.append(fullStack);
                tmpStringBuffer.append("\n");
            }//if(t != null){ - on exception

            if(_printColor){
                if(aTraceLevel.compareTo( WARNING ) >= 0){
                    tmpStringBuffer.append("\u001B[0m");
                    //Sane
                }

            }

            //tmpStringBuffer.append("END\n");

            //if(aTraceLevel > WARNING){
            if( _printOnStderr &&
                    (
                            (!_printOnStderrOnlyWarningAndErrors && aTraceLevel != NORMAL) 
                            ||
                            (_printOnStderrOnlyWarningAndErrors && (aTraceLevel.compareTo(NORMAL) > 0) )
                    )
            ){

                System.err.println(tmpStringBuffer);

                if(_autoFlush){
                    System.err.flush();
                }           

            }else{

                System.out.println(tmpStringBuffer);

                if(_autoFlush){
                    System.out.flush();
                }

            }


            //return this;

        }
    };

    //Use "MvTrace.init(traceLevel)" instead of new !
    //Private constructor

    private MvTrace() {
        //currentTraceLevel=traceLevel;
        System.out.println("Trace system initializated with level: " + currentTraceLevel);
    }



    public static void main(String[] args) {
        MvTrace._printColor = false;
        MvTrace._printOnStderrOnlyWarningAndErrors = true;
        System.out.println("MvTrace 5 test");
        MvTrace.debug("Trace debug");
        MvTrace.warn("Verbose");
        MvTrace.print("A normal operation message");
        MvTrace.warn("Warning");
        MvTrace.error("Recoverable");
        MvTrace.setTraceLevel(MvTrace.VERBOSE);
        //MvTrace.setTraceLevel(newTraceLevel)
        MvTrace.debug("Trace debug will not be printed");
        MvTrace.info("Verbose will be printed");
        MvTrace.fatal("Unrecoverable2", new RuntimeException("Test exception"));
        MvTrace.fatal("Unrecoverable3");

        //This will exit
        MvTrace.checkAssertExit( 1==2 , "1==2");


        //MvTrace.printDebug("Debug trace");
    }

}



Adăugare Iunie 2011


 Am scris acest post când nu cunoşteam foarte bine bibliotecile de logging Java, precum log4j, slf4j. Nici acum nu cunosc tot, dar am mai învăţat câte ceva.


 Log4j


 Pentru a folosi log4j, în fiecare clasă se declară un membru:
 private final static Logger log = Logger.getLogger( NumeClasa.class ); 

Logurile se vor trimite cu:
 log.info("Connecting to database..."); 


Note : 
 - static asigură că se va crea un singur log pentru toate obiectele de tip NumeClasa (economiseşte memorie)
 - final poate aduce un mic avantaj de performanţă în programe multithreading (economiseşte CPU)
 - private este natural, nu avem de ce să folosim acest log din altă clasă
 - NumeClasa.class în loc de stringul "NumeClasa" asigură faptul că la schimbarea numelui clasei se va modifica şi numele declarat la logger.


 Pentru a funcţiona log4j va trebui inclus:
 import org.apache.log4j.Logger;

 Mai trebuie adăugat în classpath biblioteca log4j, ceva de genul : 
 log4j-1.2.14.jar


 Este mai laborios de folosit decât varianta cu metode statice, dar aduce câteva avantaje, în special la proiecte medii/mari:
 - nivelul de log poate fi setat separat la fiecare clasă. Adică pot seta nivel INFO ca default, apoi pot seta nivelul DEBUG sau TRACE pentru clasa sau ierarhia de clase la care fac depanare.
- Nici nu trebuie să modific acea clasă sau jar-ul, totul se poate seta exterior, modificând fisierul de configurare log4j.xml
- Formatul logului se poate seta şi el la rulare, tot în log4j.xml
- La nevoie (dar nerecomandat) se poate seta şi din din program, de exemplu în funcţia main() cu:
 Logger.getLogger("").setLevel(Level.DEBUG); //Setează nivelul de log default
- Dacă foloseşti anumite bilioteci (apache mai ales), ele vor loga oricum folosind log4j

 continuare aici

"The more I say, the more I know" . Republicarea articolelor este permisă cu citarea autorului

Facebook