2016-05-20 8 views
0

Ich führe einige sehr gesprächige Programme, die so viele wichtige Dinge ausstrahlen, dass ich ihre Ausgabe in mehrere Dateien teilen möchte, basierend auf Mustern in dem, was sie sagen.Gibt es in Python/Java ein dateiähnliches Objekt, das Zeilen basierend auf dem Regex-Mustervergleich filtern kann?

Also ich glaube, ich brauche ein Datei-ähnliches Objekt, das Zeilen abfangen kann, die bestimmten regulären Ausdrücken entsprechen und sie in verschiedene tatsächliche Dateien schreiben. Nicht übereinstimmende Zeilen würden in die Standarddatei übertragen. Die googlenets sind nicht hilfreich.

ich mir vorstellen, es wie folgt aus:

outputFile = FilteringFile('unfiltered-output.txt', 'w') 
outputFile.addFilter(re.compile(r'spam'), 'regarding-spam.txt') 
outputFile.addFilter(re.compile(r'eggs'), 'regarding-eggs.txt') 

Jetzt kann ich outputFile.write('...') und alle Linien mit spam zu tun hat, wird sicher in nur regarding-spam.txt geschrieben werden, und alle, die Linien mit eggs zu tun wird sicher nur in regarding-eggs.txt geschrieben. Alle diese Zeilen, die weder mit spam noch eggs zu tun haben, würden sicher in unfiltered-output.txt übergeben werden. Abgesehen von dieser inneren Magie verhält sich outputFile wie eine gewöhnliche Datei.

Hat Python oder Java schon so etwas? Wenn nicht, wie würden Sie solche Dinge effizient erledigen? Ich werde eine Lösung in beiden Sprachen akzeptieren, da mein Code auf Jython läuft.

Antwort

0

Ich würde in Verwendung Log4J 2 in Java suchen. Sie legen eine Konfigurationsdatei in src/main/resources/log4j2.xml up:

<?xml version="1.0" encoding="UTF-8"?> 
<Configuration status="warn" name="MyApp" packages=""> 
    <Appenders> 
    <RollingFile name="RollingFile" fileName="logs/app.log" 
       filePattern="logs/app-%d{MM-dd-yyyy}.log.gz"> 
     <RegexFilter regex=".* test .*" onMatch="ACCEPT" onMismatch="DENY"/> 
     <PatternLayout> 
     <pattern>%d %p %c{1.} [%t] %m%n</pattern> 
     </PatternLayout> 
     <TimeBasedTriggeringPolicy /> 
    </RollingFile> 
    </Appenders> 
    <Loggers> 
    <Root level="info"> 
     <AppenderRef ref="RollingFile"/> 
    </Root> 
    </Loggers> 
</Configuration> 

dann in Java-Code, können Sie tun Dinge wie:

Logger log = LogManager.getLogger(); 
log.info("Here is some info about a test object: {}", object); 

auf den verschiedenen Appen Je und Logger, die Sie in Ihrer Konfigurationsdatei definiert haben Jede geloggte Zeile wird in die entsprechenden Dateien geschrieben. In diesem Fall protokolliert unser Logger Nachrichten mit dem Schweregrad INFO oder höher, und die Nachricht stimmt mit dem RegexFilter überein, sodass diese Zeile in logs/app.log übergeht.

0

Hier ist Hacky Weg, dies in Python mit Hilfe von Modul von stdlib zu tun (und es ist erstaunliche Filterfähigkeiten). Grundsätzlich müssen Sie zwei separate Handler erstellen und den beiden die richtigen Filter hinzufügen.

Filter müssen vom Entwickler implementiert werden, und sie geben einfach 1 zurück, wenn die Nachricht die Filterung passiert, und 0, wenn dies nicht der Fall ist. Sie können more about Filter objects auf der offiziellen Dokumentationsseite des Protokollierung Moduls lesen.

Im folgenden Code habe ich einen einfachen Filter implementiert, der einen regulären Ausdruck als eines der init-Methodenargumente verwendet und dann jeden Datensatz dieser Regex zuordnet. Leider ist der Code wegen der notwendigen Konfiguration des Loggerobjektes ziemlich wortreich.

multifilelogging.py

import logging 
import re 

# Creating a filter object that will match records based on supplied regex 
class re_filter(logging.Filter): 
    def __init__(self, name='', regex_str=''): 
     self.regex = re.compile(regex_str) 
     super().__init__(name=name) 

    def filter(self, record): 
     matched = 1 if self.regex.match(record.msg) else 0 
     return matched 

# Instantiating the logger 
logger = logging.getLogger(__name__) 
logger.setLevel(logging.DEBUG) 
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') 

# Creating two handlers 
spam_handler = logging.FileHandler("spam.txt") 
eggs_handler = logging.FileHandler("eggs.txt") 

# Setting a loglevel for them 
spam_handler.setLevel(logging.DEBUG) 
eggs_handler.setLevel(logging.DEBUG) 

# Setting a formatter for them 
spam_handler.setFormatter(formatter) 
eggs_handler.setFormatter(formatter) 

# Adding the filters 
spam_handler.addFilter(re_filter(regex_str=r'.*spam.*')) 
eggs_handler.addFilter(re_filter(regex_str=r'.*eggs.*')) 

# Finally, attaching the handlers to the logger 
logger.addHandler(spam_handler) 
logger.addHandler(eggs_handler) 

# This goes to spam.txt 
logger.debug("Here's the spam") 
# This goes to eggs.txt 
logger.info("Here's the eggs") 

Hier ist der Ausgang:

Spam. txt

2016-05-20 23:16:04,731 - INFO - Here's the spam 

eggs.txt

2016-05-20 23:16:04,731 - INFO - Here's the eggs 
0

Ich finde es interessant, dass ich zwei Lösungen bekam Protokollierung basiert. Vielleicht sollte ich loggen statt an stdout und stderr zu schreiben, aber das scheint jetzt eine zu große Veränderung zu sein, wenn man die Menge an Code berücksichtigt.

Dies ist, was ich gerade benutze, aber ich habe ein quälendes Gefühl, dass es unter bestimmten Umständen schrecklich ineffizient sein kann. Ich bin mir sicher, dass jemand da draußen es besser machen kann.

''' 
For Splitting File Output into Multiple Files 
''' 

class OutputFileFilter(object): 
    '''A (somewhat) file-like object that sends different lines to 
    different actual files based upon regular expression matching. 

    As you can see, much of the file interface is missing, and 
    attempts to use those methods will raise ``AttributeError``. 
    ''' 

    def __init__(self, defaultOutputFile): 
     '''Initialize this :class:`OutputFileFilter` with a 
     `defaultOutputFile`. 

     All lines that do not match any regular expression patterns 
     will be written to `defaultOutputFile`.''' 

     self.defaultOutputFile = defaultOutputFile 
     self.patternOntoFile = {} 

    def addPatternAndFile(self, pattern, anotherFile): 
     '''Add a regular-expression `pattern` and a `anotherFile` for 
     the lines matching that expression. 

     The `pattern` will be used in an ``re.search()`` operation. 
     If the search result is ``not None``, that is the file that 
     will receive the output.''' 

     self.patternOntoFile[pattern] = anotherFile 

    def write(self, text): 
     '''Write some `text` to the appropriate output files.''' 

     if '\n' not in text: 
      self._dispatchLine(text) 
     else: 
      for line in text.splitlines(): 
       self._dispatchLine(line, addNewline=True) 

    def writelines(self, lines): 
     '''Write an iterable sequence of `lines` to the appropriate 
     output files.''' 

     for line in lines: 
      self._dispatchLine(line, addNewline=True) 

    def _dispatchLine(self, line, addNewline=False): 
     '''Write a single line of text to the appropriate file. 

     This is where the magic happens.''' 

     for pattern, anotherFile in self.patternOntoFile.iteritems(): 
      searchResult = pattern.search(line) 
      if searchResult is not None: 
       anotherFile.write(line) 
       if addNewline: 
        anotherFile.write('\n') 
       break 
     else: 
      self.defaultOutputFile.write(line) 
      if addNewline: 
       self.defaultOutputFile.write('\n')