Seite 1 von 1

Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Donnerstag 23. Juli 2020, 20:56
von test.py
Hi,

ich benutze in meinen Projekten häufig farbigen Output (mit colorama) und will darauf nicht verzichten. Problem: Es gibt Situationen, in denen die gleiche Nachricht ohne die Farbcodes benötigt wird, zB beim Loggen in eine Datei, beim Beschriften von Graphen oder wenn man einem anderen Tool einen solchen Text mitgeben will, das mit Farbcodes nicht gut kann.

Hier ein Beispiel:

Code: Alles auswählen

import colorama as col
col.init(autoreset=True)

BLUE, YELLOW, RED, TEXT = col.Fore.BLUE, col.Fore.YELLOW, col.Fore.RED, col.Style.RESET_ALL

def message(obj, filename, n=None):
    s = f'Lese {BLUE}{obj}{TEXT} aus {YELLOW}{filename}{TEXT} ein ...'
    if n is not None:
        s += f' ({RED}{n}. Versuch{TEXT})'
    return s

msgA = message('Fisch', 'Urzeitmeer', 2000000)
print(msgA)
Konsolenausgabe:
Lese Fisch aus Urzeitmeer ein ... (2000000. Versuch)
Die sieht aus wie gewünscht, aber wenn ich das nun zB in eine Logdatei schreibe, steht da, wenn ich sie öffne (und nicht etwa auf der Konsole ausgebe):

Code: Alles auswählen

Lese [34mFisch[0m aus [33mUrzeitmeer[0m ein ... ([31m2000000. Versuch[0m)
... was ziemlich unleserlich ist. Kennt ihr für dieses Szenario eine praktikable Lösung?

Folgende Einfälle hatte ich bis jetzt:
  • String grundsätzlich zweimal zusammensetzen, nämlich einmal mit und einmal ohne Farbcodes --> bläht Code auf, fehleranfällig wegen Dopplung
  • String-Einzelteile einzeln zusammensetzen, im if-Teil mit, im else-Teil ohne Farbcodes --> bläht Code auf, unschön
  • Klasse für Farbstrings, die bei str() nur plain Text zurückgibt, aber bei zB repr() oder .colored() den farbigen String. Der Konstruktor bekäme in dem Fall ein Tupel aus Strings und Objekten einer String-Subklasse, die nur dazu da ist, um Farbcodes als solche zu kennzeichnen und mittels isinstance() von normalen Strings unterscheiden zu können --> man muss vermutlich vieles nachimplementieren, wenn man die Objekte wie Strings benutzen können will (oder immer explizit str() machen)
  • eine Funktion schreiben, die per regulärem Ausdruck alle Farbcodes herausfiltert --> nicht performant
Habt ihr sonst Ideen?

LG

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Donnerstag 23. Juli 2020, 21:14
von Sirius3
Wie viele Gigabytes an Text möchtest Du denn ausgeben, dass Performance eine Rolle spielt?

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Donnerstag 23. Juli 2020, 22:59
von __deets__
"nicht perfomant" ist Unfug. Das ist linear in der Menge der auszugebenden Daten, und damit im Zweifel eh von der IO selbst dominiert. Das waere mein Vorgehen: statt ueberall im Code Verzweigungen zu haben, die Ausgabe filtern, und einfach die escape-codes rauswerfen. Dafuer sind die ja da.

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Freitag 24. Juli 2020, 12:39
von test.py
OK, das ist ja interessant!
Also zur Erklärung: Ich meinte mit "nicht performant" nicht, dass das für den Benutzer einen spürbaren Unterschied macht, sondern eher dass es nicht effizient, da unnütze Arbeit ist, die Farbcodes reinzumachen und dann wieder extra rauszufiltern, wenn man sie nicht möchte. Sagen wir, es ist nicht elegant. :P
Hintergrund ist, dass das ganze ein Testsystem für externe Hardware ist und ich zeitweise viele Log-Einträge pro Sekunde generiere, ich durch das Loggen aber eigentlich keinen zeitlichen Fehler reinbringen will, der die Synchronizität mit der externen Hardware beeinflusst.

Ich hab die Aussagen mal mit timeit untersucht, und zwar basierend auf https://stackoverflow.com/a/14693789.
Erstmal dachte ich, dass meine Einschätzung des relativen Rechenaufwands wohl sehr daneben war, als ich Folgendes rausgefunden hab (Angaben in µs, 100.000 Durchläufe):

Code: Alles auswählen

Methode             cmd      PS       VScode-cmd   VScode-PS
print_plainStr      240,0    252,6    737,3        477,5
print_colorStr      1328,0   1346,3   1753,8       1771,8
print_filteredStr   236,9    252,4    450,0        443,6
filtering           6,3      6,7      6,6          7,4
Fand ich verblüffend, dass Konsolenausgaben, insbesondere farbige, dermaßen aufwendig zu sein scheinen, und dass es in VScode deutlich länger dauert. Irritierend finde ich, dass Filtern+Ausgeben schneller geht als gleich den blanken String auszugeben ... kann ich mir zwar nicht erklären, ist aber auch nicht der Fokus gerade.
(btw: ohne Ausführen von colorama.init() ist farbige Ausgabe etwa genauso schnell (oder sogar geringfügig schneller?) wie nicht-farbige, und in VScode wird dabei dennoch farbig ausgegeben.)

Dann hab ich aber mal in eine Datei geschrieben, was ja dem eigentlichen Anwendungsfall viel näher kommt (1.000.000 Durchläufe, keine Unterschiede bzgl. der verwendeten Konsole):

Code: Alles auswählen

Methode             µs
write_plainStr      1,3
write_colorStr      1,6
write_filteredStr   8,1
Plötzlich schien das Ergebnis eher meiner Einschätzung zu entsprechen: es dauert mit Filtern mehr als 6-mal so lange.

Schließlich hab ich es dann so gemacht, wie es eigentlich gedacht ist, nämlich die Strings geloggt (100.000 Durchläufe, keine Unterschiede bzgl. der verwendeten Konsole):

Code: Alles auswählen

Methode             µs
log_plainStr        34,2
log_colorStr        34,6
log_filteredStr     42,8
Dadurch, dass das Loggen an sich schon recht aufwendig ist, macht das Filtern nur so 25 % Mehraufwand. Zum einen ist das relativ betrachtet vertretbar, zum anderen sind auch die absoluten Ausführungszeiten für die nötige Synchronizität meines Systems verkraftbar. Ich sollte wohl eher auf die ein oder andere Print-Ausgabe verzichten. :D

Zwischenergebnis:
Der zusätzliche Rechenaufwand bei Filterung der Farbcodes ist zwar signifikant, aber längst kein Problem.
Ob der Ansatz schön oder elegant ist, ist eine andere Frage. Ich finde ihn zumindest deutlich schöner und übersichtlicher als Schachtelcode, um den korrekten String zusammenzubauen.
Gibt es noch andere Ideen, Anregungen oder Meinungen?

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Freitag 24. Juli 2020, 12:49
von __deets__
Da du den Code zu deinen Experimenten nicht zeigst, kann man da auch nur so viel zu sagen.

Und wenn das ganze hier die Performance eines angeschlossenen anderen Systems betrifft, wuerde ich das eh anders loesen: das loggen sollte dann gebuffert und parallel erfolgen. Da ist Python ggf. schwierig (weil durch GIL nicht wirklich parallelisierbar), aber je nachdem wie das System aufgebaut ist, kann man entweder mit multiprocessing arbeiten, oder sogar nur einer dequeue von Log-Meldungen, die dann in Pausen abgearbeitet werden.

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Freitag 24. Juli 2020, 12:54
von sparrow
@test.py: Also wenn mir Performance wichtig wäre, würde ich keine Ausgaben an die Standardausgabe senden. Das ist nun wirklich langsam.

Re: Bedingte Farbausgabe (zB mit Farbe auf Konsole, ohne Farbe im Log)

Verfasst: Freitag 24. Juli 2020, 13:21
von test.py
sparrow hat geschrieben: Freitag 24. Juli 2020, 12:54 @test.py: Also wenn mir Performance wichtig wäre, würde ich keine Ausgaben an die Standardausgabe senden. Das ist nun wirklich langsam.
Das habe ich daraus auch geschlossen. :lol:

Ich hatte schonmal an Multithreading gedacht, aber nicht wegen Logging, sondern wegen Live-Plotting mit matplotlib, das ist nämlich wirklich langsam! :D
Wenn der Einfluss des Loggings < 5 ms/s bleibt, ist das für mich tolerierbar.
(btw: wenn man keine 8-bit-Sequenzen rausfiltert (s. Link von vorhin), ist das Filtern etwa doppelt so schnell)

Aber gibt es noch Alternativansätze oder Ideen zur bedingten Farbausgabe?