Ankündigung

Einklappen
Keine Ankündigung bisher.

memLog Ersatz mit dem Standard Logger

Einklappen
X
 
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

    memLog Ersatz mit dem Standard Logger

    Hallo zusammen,
    ich habe eine Frage, wie ich das mit mehreren Loggern geschickt lösen kann. Alle meine Logiken loggen in die Standardausgabe [shng_details_file]. Das soll auch so bleiben. Weiterhin habe ich aber die ein oder andere Ausgabe auch in die VISU geloggt und hierzu memLog genutzt. Das ist aber deprecated und kann mit standard Mechanismen gelöst werden. Soweit so gut.

    Ich habe mir nun einen Logger angelegt in der logging.yaml
    Code:
    handlers:
        handler_messagesystem:
            (): lib.log.ShngMemLogHandler
            logname: messagesystem
            maxlen: 60
            level: INFO
            cache: True
    
    loggers:
        messagesystem:
            handlers: [handler_messagesystem]
            level: INFO
    Ich habe hier bewusst nicht logics.<meineLogik> in die logging.yaml unter loggers eingetragen, da das Standard Logging ja weiterlaufen soll und ich nur bestimmte Logeinträge in das messagesystem eintragen möchte.

    In der Logik kann ich dann selbst entscheiden, ob ich in den Standard Logfile loggen möchte oder in das Messagesystem, welches in der VISU angezeigt wird

    Standard
    Code:
    logger.info('Meldung in den Standardlogfile')
    Messagesystem
    Code:
    mylogger = logging.getLogger("messagesystem")
    mylogger.info('Meldung in das Messagesystem (dargestellt in der VISU)')


    Soweit so gut!

    Nun aber zur eigentlichen Frage:
    Das Logging will ich gern über eine Funktion machen, die nicht Bestandteil dieser Phyton Logik ist. Hintergrund ist, dass ich noch ein paar Formatierungen vornehmen will, die immer gleich sind und dann einfach aus jeder Logik immer die gleiche Funktion aufrufen kann. Dazu habe ich ein Python Skript mit den Funktionen, welches ich dann einfach in jede Logik per import einbinde. So muss ich nur an einer Stelle ändern.

    modMessageSystem.py
    Code:
    #!/usr/bin/env python
    
    def logMessage(msgclass, textmessage):
        messagesystemlogger = logging.getLogger("messagesystem")
        # Meldeklasse prüfen und ggf. anpassen
        
        # weitere Anpassungen
        # ...
    
        if msgclass == 'ERROR':
            messagesystemlogger.error(textmessage)
        elif ((msgclass == 'WARNING') or (msgclass == 'WARN')):
            messagesystemlogger.warn(textmessage)
        elif msgclass == 'INFO':
            messagesystemlogger.info(textmessage)
        else:
            messagesystemlogger.error(textmessage)

    Logik.py
    Code:
    #!/usr/bin/env python
    
    import logics.modMessageSystem as msg
    
    logger.info('Meldung in den Standardlogfile')
    
    msg.logMessage('INFO', 'Meldung in das Messagesystem (dargestellt in der VISU)')

    Leider kennt modMessageSystem.py das Objekt "logging" nicht und kann so auch das messagesystem nicht ermitteln. Natürlich kann ich das übergeben, aber das wollte ich eigentlich nicht. Wenn ich das richtig gesehen habe, ist das ja eigentlich auch nicht mehr notwendig.

    https://github.com/smarthomeNG/smart...42ef7c71a719c9

    Wie wäre aus eurer Sicht eine gute Implementierung?
    Vermutlich denke ich schon wieder viel zu kompliziert.
    Mein Wunsch wäre es aus dem modMessageSystem.py einfach in den richtigen Log schreiben zu können (hier in den messagesystem).

    System
    + PRETTY_NAME="Debian GNU/Linux 12 (bookworm)"
    + NAME="Debian GNU/Linux"
    + VERSION_ID="12"
    + VERSION="12 (bookworm)"
    + VERSION_CODENAME=bookworm
    + SmartHomeNG Version:v1.10.0-master (4b25822a0)
    + SmartHomeNG Plugins Version:v1.10.0-master (7e00e4ee)
    + Python Version:3.10.12 final (virtual environment)
    + Virtuelle Umgebung wie in der Dokumentation mit 3.10.12 eingerichtet​
    Zuletzt geändert von loeserman; 03.03.2024, 18:44. Grund: Durch den Hinweis von bmx ist das aus meiner Sicht gelöst. Danke nochmal.

    #2
    Irgendwie drängt sich mir auf das Du es mal mit den Userfunctions probieren solltest. Dann musst Du das nicht überall hinein importieren.

    Kommentar


      #3
      Cool. Das probiere ich gleich morgen mal aus.

      Kommentar


        #4
        Also das war ein super Tipp von Dir bmx. Habe es als user function umgebaut und klappt prima. Hänge es mal hier mit rein, damit auch andere vielleicht bei gleiche Problemen eine Lösung haben.

        Auszug logging.yaml
        Code:
        handlers:
            loghandler_visu_messagesystem:
                # LOESERHOME - Handler for message system to display messages within the visualization (used as old memlog plugin)
                (): lib.log.ShngMemLogHandler
                logname: loghandler_visu_messagesystem
                maxlen: 60
                level: INFO
                cache: True
        
        loggers:
            loglogger_visu_messagesystem:
                # LOESERHOME - Message system to display messages within the visualization (used as old memlog plugin)
                handlers: [loghandler_visu_messagesystem]
                level: INFO
        \functions\fctMsg.py
        Code:
        #!/usr/bin/env python
        import logging
        import time
        import os
        import urllib3
        
        
        # ------------------------------------------------------------------------------------------------------------------
        # FUNCTION Beschreibung
        # Funktionen rund um das Melden von Nachrichten
        # 
        # logMessage      Eintragen einer Meldung in die Logdatei
        # writeMessage    Schreiben einer Meldung in das Meldesystem und die Logdatei
        # sendMessage     Senden einer Meldung (alle Meldungen per Telegram werden auch in das Meldesystem und die Logdatei eingetragen)
        # sendPhoto       Sendet ein Bild (Fehler werden auch in den Log geschrieben)
        # ------------------------------------------------------------------------------------------------------------------
        
        _VERSION     = '1.1.0'
        _DESCRIPTION = 'LOESERHOME - Funktion für das Handling von Meldungen (im Log, oder in der Visu, oder per Telegram)'
        
        
        # Standard logger für diese Funktion (wird im Standard Logging geschrieben, siehe: logging.yaml>loggers>functions)
        _logger = logging.getLogger(__name__)
        # Logger für messages, die in der Visualisierung angezeigt werden sollen (früher mit dem Plugin memlog gelöst, welches depercated ist)
        _messagesystemlogger = logging.getLogger("loglogger_visu_messagesystem")
        
        # Telegram Bot Objekt mit dem API Key anlegen
        # Plugin "telegram" muss installiert sein und in den Items muss folgendes vorhanden sein
        # %YAML 1.1
        # ---
        # SERVICE:
        #     TELEGRAM:
        #         chat_ids:
        #             type: dict
        #             telegram_chat_ids: 'True'
        #             cache: yes
        #
        #         info:
        #             message:
        #                 type: str
        #                 enforce_updates: yes
        #                 telegram_text: 'True'
        #
        # API Keys und ChatIDs (können ggf. auch später in die items überführt werden)
        
        _botChatIdBot = <<geheim>>     # CHAT ID vom Bot-Account 
        _botChatIdAll = <<geheim>>     # CHAT ID von der Gruppe "ALL"
        _botChatIdAlarm = <<geheim>>   # CHAT ID von der Gruppe "ALARM"
        _botChatIdWarning = <<geheim>> # CHAT ID von der Gruppe "WARNING"
        
        
        
        
        # ------------------------------------------------------------------------------------------------------------------
        # Konvertierungen
        # ------------------------------------------------------------------------------------------------------------------
        # Prüfung der Meldeklasse (Long)
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO'
        # return      : 'ERROR', 'WARNING', 'INFO' 
        def checkMessageClassLong(msgclass): 
            # Übergabewert in Großbuchstaben umwandeln
            msgclass = str(msgclass)
            msgclass = msgclass.upper()
            # Meldeklasse ggf. korrigieren
            if msgclass == 'WARN':
                msgclass = 'WARNING'
            elif not ((msgclass == 'ERROR') or (msgclass == 'INFO') or (msgclass == 'WARNING')):
                msgclass = 'ERROR'
            # Korrigierte Meldeklasse zurückgeben
            return msgclass
        
        
        # Prüfung der Meldeklasse (Short)
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO' 
        # return      : 'ERROR', 'WARN', 'INFO'
        def checkMessageClassShort(msgclass): 
            # Übergabewert in Großbuchstaben umwandeln
            msgclass = str(msgclass)
            msgclass = msgclass.upper()
            # Meldeklasse ggf. korrigieren
            if msgclass == 'WARNING':
                msgclass = 'WARN'
            elif not ((msgclass == 'ERROR') or (msgclass == 'INFO') or (msgclass == 'WARN')):
                msgclass = 'ERROR'
            # Korrigierte Meldeklasse zurückgeben
            return msgclass
        
        
        
        
        # ------------------------------------------------------------------------------------------------------------------
        # Senden / Schreiben in die Logger
        # ------------------------------------------------------------------------------------------------------------------
        # Eintragen einer Meldung in die Logdatei
        # Returnzeichen werden entfernt
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO'
        # textmessage : 'Messagetext to send'
        def logMessage(msgclass, textmessage):
            # Meldeklasse prüfen und ggf. anpassen
            msgclasslong = checkMessageClassLong(msgclass)
            # Lösche die Zeilenumbrüche
            textmessage = textmessage.replace('\r\n', ' ')
            # Füge eine Info vorne an, dass es sich um eine eigens erstellte Meldung handelt.
            textmessage = 'MELDUNG: {}'.format(textmessage)
            # Erzeuge die Meldung in der Logdatei (abhängig von der Meldeklasse)
            if msgclass == 'ERROR':
                _logger.error(textmessage)
            elif ((msgclass == 'WARNING') or (msgclass == 'WARN')):
                _logger.warn(textmessage)
            elif msgclass == 'INFO':
                _logger.info(textmessage)
            else:
                _logger.error(textmessage)
        
        
        # Schreiben einer Meldung in das Meldesystem und die Logdatei
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO'
        # textmessage : 'Messagetext to send'
        def writeMessage(msgclass, textmessage):
            # Meldeklasse prüfen und ggf. anpassen
            msgclasslong = checkMessageClassLong(msgclass)
            # Meldungs in den Logfile eintragen
            logMessage(msgclasslong, textmessage)
            # Meldung in online Meldesystem eintragen
            if msgclass == 'ERROR':
                _messagesystemlogger.error(textmessage)
            elif ((msgclass == 'WARNING') or (msgclass == 'WARN')):
                _messagesystemlogger.warn(textmessage)
            elif msgclass == 'INFO':
                _messagesystemlogger.info(textmessage)
            else:
                _messagesystemlogger.error(textmessage)
        
        
        
        
        # ------------------------------------------------------------------------------------------------------------------
        # Senden / Schreiben von Telegram Nachrichten
        # ------------------------------------------------------------------------------------------------------------------
        # Senden einer Meldung (alle Meldungen per Telegram werden auch in das Meldesystem und die Logdatei eingetragen)
        # sh          : Smarthome Klasse, damit das Objekt bekannt ist (unsaubere Programmierung, aber so sollte es gehen)
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO'
        # textmessage : 'Messagetext to send'
        def sendMessage(sh, msgclass, textmessage):
            # Meldeklasse prüfen und ggf. anpassen
            msgclasslong = checkMessageClassLong(msgclass)
            msgclassshort =  checkMessageClassShort(msgclass)
            # Meldung in online Meldesystem eintragen (VISU und log)
            writeMessage(msgclasslong, textmessage)
            # ZeitString erzeugen
            dateandtimestring = "" + time.strftime("%d.%m.%Y %H:%M:%S")
            # Meldung an Telegram versenden
            try:
                sh.telegram.msg_broadcast('{} [{}]\r\n{}'.format(dateandtimestring, msgclassshort, textmessage), _botChatIdAll)
                if (msgclassshort == 'ERROR'):
                    sh.telegram.msg_broadcast('ALARM!\r\n{} [{}]\r\n{}'.format(dateandtimestring, msgclassshort, textmessage), _botChatIdAlarm)
                elif (msgclassshort == 'WARN'):
                    sh.telegram.msg_broadcast('WARNING!\r\n{} [{}]\r\n{}'.format(dateandtimestring, msgclassshort, textmessage), _botChatIdWarning)
            except Exception as e:
                # Fehler im Meldesystem eintragen (VISU und log, damit man die fehler direkt sieht in die visu)
                writeMessage('ERROR', 'Fehler beim Text Versand per Telegram. Meldetext: {}'.format(textmessage))
                # Genaue Fehlermeldung in Logfile eintragen
                s = "Error {0}".format(str(e)) # string
                utf8str = s.encode("utf-8") # byte array, representing utf8-encoded text
                _logger.error('{}'.format(utf8str))
        
        
        # Senden eines Bildes (alle Bilder per Telegram werden auch in die Logdatei eingetragen)
        # sh          : Smarthome Klasse, damit das Objekt bekannt ist (unsaubere Programmierung, aber so sollte es gehen)
        # msgclass    : 'ERROR', 'WARN', 'WARNING', 'INFO'
        # textmessage : 'Messagetext to send'
        def sendPhoto(sh, msgclass, photolink):
            # Meldeklasse prüfen und ggf. anpassen
            msgclasslong = checkMessageClassLong(msgclass)
            msgclassshort =  checkMessageClassShort(msgclass)
            # Meldungs in den Logfile eintragen (nur log)
            logMessage(msgclasslong, 'Sende Photo per Telegram {}'.format(photolink))
            try:
                # Bild an Telegram versenden
                sh.telegram.photo_broadcast(photolink, '', _botChatIdAll)
                if (msgclassshort == 'ERROR'):
                    sh.telegram.photo_broadcast(photolink, '', _botChatIdAlarm)
                elif (msgclassshort == 'WARN'):
                    sh.telegram.photo_broadcast(photolink, '', _botChatIdWarning)
            except Exception as e:
                # Fehler im Meldesystem eintragen (VISU und log, damit man die fehler direkt sieht in die visu)
                writeMessage('ERROR', 'Fehler beim Bild Versand per Telegram {}'.format(photolink))
                # Genaue Fehlermeldung in Logfile eintragen
                s = "Error {0}".format(str(e)) # string
                utf8str = s.encode("utf-8") # byte array, representing utf8-encoded text
                _logger.error('{}'.format(utf8str))
        Aufrufe in den Logiken
        Code:
        uf.fctMsg.logMessage('INFO', 'Meldetext')
        ...
        uf.fctMsg.writeMessage('WARN', 'Meldetext')
        ...
        uf.fctMsg.sendMessage(sh, 'INFO', 'Meldetext')
        ...
        myPicture = '../pictures/MeinZuSendendesBild.jpg'
        uf.fctMsg.sendPhoto(sh, 'WARN', myPicture)
        Aufuf in den VISU Seiten, um das Meldesystem anzuzeigen (mittels des Namen des Logging Handlers)
        Code:
        {{ status.log('', 'loghandler_visu_messagesystem', 50) }}
        Klappt wunderbar. Werde nun noch ein paar mehr Funktionen auf userfunctions umstellen.

        Ein sehr großer Vorteil ist auch, dass diese geändert und dann online nachgeladen werden können. Das macht es nochmal etwas einfacher.

        Kommentar

        Lädt...
        X