[Android / Java] Conditioneel loggen van zware functie

Pagina: 1
Acties:

Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 14-10 21:05
Ik ben bezig met een Android app die een HTML document moet parsen en de relevante delen aan de gebruiker moet laten zien. Tijdens het debuggen wil ik graag op veel plaatsen loggen waar de parser mee bezig is (bijv een stukje HTML) om te kunnen zien wat er mis gaat.

Het parsen van de HTML gaat echter VEEL langzamer met deze logs omdat ik vaak een heel stuk HTML wil loggen (bijvoorbeeld de contents van een bepaalde div) terwijl ik voor de applicatie zelf (als het eenmaal werkt) enkel een span nodig heb. Het opzoeken van die span gaat super snel (ik gebruik Jsoup voor het parsen), maar het ophalen van de hele parent-parent-parent-parent div (ik zeg maar iets) duurt veel langer. Dat maakt tijdens debug niet uit, maar zodra de app klaar is wil ik die debug statements met een simpele conditie uit kunnen zetten zodat dit in release mode niet de applicatie langzaam maakt.

Klinkt simpel, maar ik krijg het niet voor elkaar....


Ik heb een simpele conditionele log gemaakt met zoiets:
Java:
1
2
3
4
5
6
7
8
private static final boolean DEBUG = true; // of false tijdens release

private void log(String message)
{
    if (!DEBUG) return; // in release hoeft dit niks te doen
    
    Log.d(TAG, message);
}


Simpel genoeg, en het doet wel wat het moet doen (het stuurt geen log als ik DEBUG = false zet), maar het probleem is dat ik deze functie wil aanroepen met de functie die heel lang duurt.

Stel dat ik een functie heb "getParentDivHtml" die 3 seconden er over doet om de html op te halen, wat ik alleen tijdens debug nodig heb. Ik wil dit dan op deze manier aanroepen:
code:
1
log(getParentDivHtml())


Als DEBUG op false staat zou ik verwachten dat de compiler het hele 'log' statement hier uit zou halen. Dat doet het niet, zelfs in release mode duurt het nog steeds 3 seconden, ook al komt er geen log. Dit is ook wel logisch want 'getParentDivHtml' moet eerst draaien, dat duurt 3 seconden, en daarna komt pas de check of hij wel moet loggen. Die check wil ik echter wel graag in de log functie hebben anders moet ik die alsnog overal apart gaan checken en dat is natuurlijk niet handig.


Dit heb ik simpel gechecked met deze test:
Java:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private static final boolean DEBUG = false;

private void log(String message)
{
    if (!DEBUG) return; // in release hoeft dit niks te doen
    
    Log.d(TAG, message);
}

private String testLongDebug()
{
    try
    {
        Thread.sleep(3000);
        return "X after 3000 sleep";
    }
    catch (InterruptedException ex)
    {
        return "X interrupted";
    }
}


Aanroepen met
Java:
1
log(testLongDebug());


zorgt er voor dat hij zelfs in release mode nog 3 seconden wacht.


Volgens mij moet de compiler toch wel slim genoeg zijn om te snappen dat dit niet nodig is? Of overschat ik nu de compiler?
Edit: ok, dat is natuurlijk niet waar, de compiler kan vast niet inschatten dat "testLongDebug" niks belangrijks doet. Maar m'n vraag blijft hetzelfde:

Hoe kan ik dit simpel oplossen? Wat ik dus wil is een enkele 'log' functie die ik overal waar ik maar wil kan aanroepen zonder eerst steeds een aparte check te hoeven doen op "!DEBUG", en dat moet vanzelf (zodra ik DEBUG = false zet) niet meer draaien, ook niet als het argument van de log functie log nodig heeft.

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • CodeCaster
  • Registratie: Juni 2003
  • Niet online

CodeCaster

Can I get uhm...

log(testLongDebug()) roept eerst testLongDebug() aan, zet de geretourneerde waarde op de stack en roept vervolgens log() aan, die de waarde weer van de stack pakt.

Java ondersteunt vast wel lambda's / delegates. In C# zou je zoiets doen:

C#:
1
2
3
4
5
6
7
8
9
public void Log(Func<string> logFunction)}
{
  if (!DEBUG)
  {
    return;
  }

  DoLog(logFunction())
}

Aanroepen met Log(testLongDebug);.

Waarbij logFunction() dus enkel wordt uitgevoerd indien DEBUG true is.

Of je zet de if(DEBUG) gewoon in de getParentDivHtml-functie.

@Snake :*

https://oneerlijkewoz.nl
Op papier is hij aan het tekenen, maar in de praktijk...


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Right, ik ben even lui, dus ik vertel hoe je het doet in C#:

code:
1
var toLog = () => getDifficultFormationThatTakes3Seconds();


dan roep je dat op ergens:
code:
1
log(toLog);

en in je log function:

code:
1
2
3
4
5
6
private static void log(Action getInfoToBeLogged)
{
    if(!DEBUG) return; 
    
    logger.log(getInfoToBeLogged() /* notice the () for executing */);
}


@hierboven: ARG

Edit:
Dus ofwel neem je Java 8, maar aangezien het in de titel staat dat je Android wil gebruiken zul je iets moeten doen a la

code:
1
2
3
4
5
6
7
log(new LogAction() {
    public void GetData()
        {
            return getDifficultFormationThatTakes3Seconds(); 
        }
    }
);


En een klasse:

code:
1
2
3
4
class LogAction 
{
     public abstract object GetData() { } 
}


En je log methode:


code:
1
2
3
4
5
6
private static void log(LogAction logAction)
{
    if(!DEBUG) return; 
    
    logger.log(logAction.GetData());
}

[ Voor 42% gewijzigd door Snake op 24-03-2015 19:55 ]

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 14-10 21:05
In C# klinkt het logisch... was het maar C# want Java en lambdas / funcs / actions heb ik nooit echt gesnapt. Maar het zal vast wel iets vergelijkbaars kunnen... bedankt, ik ga eens zoeken hoe dit in Java eruit zou zien.
CodeCaster schreef op dinsdag 24 maart 2015 @ 19:51:

Of je zet de if(DEBUG) gewoon in de getParentDivHtml-functie.
Dat wil ik dus juist voorkomen want ten eerste zijn de meeste van die lange functies ingebouwd in Jsoup, en ten tweede moet ik dus die debug statement overal erbij gaan gooien. Het parsen is vrij ingewikkeld en ik gok dat er wel 200 losse log statements staan. Ik krijg er spontaan rsi van :+

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Ik heb mijn post uitgebreid ;)

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • NickThissen
  • Registratie: November 2007
  • Laatst online: 14-10 21:05
Ik zie het, bedankt... Helaas lijkt het nog steeds niet erg handig zo.

Stel dat ik zoiets heb:

Java:
1
2
3
4
Element element = getSomeElement("div[blabla]");

// Log parent of parent of parent 
log(element.parent().parent().parent().html());


Dit duurt lang omdat parent() steeds de parent moet gaan zoeken en uiteindelijk html weer de html moet uitspugen. Ik ga dit niet sneller maken want het is puur voor debug.

Maar nu moet ik dus nog steeds bij ELKE log statement (die zoiets doet) die hele reeks functies gaan vervangen door een "new LogAction() { ... }":
Java:
1
2
3
4
5
6
7
log(new LogAction() 
{
    public String getData()
    {
        return element.parent().parent().parent().html())
    }
});


In dat geval is het nog veel makkelijker om gewoon voor elke log functie de DEBUG te checken, toch?
Java:
1
if (!DEBUG) log(...);


Dat wil ik dus niet als ik het even kan helpen, maar het lijkt er dus op dat ik pech heb?

Of mis ik iets...

[ Voor 17% gewijzigd door NickThissen op 24-03-2015 20:04 ]

Mijn iRacing profiel


Acties:
  • 0 Henk 'm!

  • Snake
  • Registratie: Juli 2005
  • Laatst online: 07-03-2024

Snake

Los Angeles, CA, USA

Op het eerste:

Je log is dan:

code:
1
log(new LogAction() { public abstract object GetData() { return element.parent().parent().parent().html(); } });


Om te antwoorden op je 2de vraag: Inderdaad :)

[ Voor 3% gewijzigd door Snake op 25-03-2015 08:36 ]

Going for adventure, lots of sun and a convertible! | GMT-8


Acties:
  • 0 Henk 'm!

  • Janoz
  • Registratie: Oktober 2000
  • Laatst online: 14-10 22:14

Janoz

Moderator Devschuur®

!litemod

Sowieso zou ik niet een 'DEBUG' constante gebruiken. Gebruik gewoon wat android je al biedt. met Log.isLoggable kun je bepalen of iets op een bepaald niveau gelogd moet worden.

Verder kun je heel lastig met lambdas en/of actions gaan doen, maar waarom niet gewoon:

Java:
1
2
3
if (Log.isLoggable(TAG, Log.DEBUG)) {
  Log.d(TAG,testLongDebug());
}

Ken Thompson's famous line from V6 UNIX is equaly applicable to this post:
'You are not expected to understand this'

Pagina: 1