"Fossies" - the Fresh Open Source Software Archive

Member "AdGuardHome-0.104.3/internal/querylog/querylog_search.go" (19 Nov 2020, 4672 Bytes) of package /linux/misc/dns/AdGuardHome-0.104.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Go source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "querylog_search.go": 0.104.1_vs_0.104.3.

    1 package querylog
    2 
    3 import (
    4     "io"
    5     "time"
    6 
    7     "github.com/AdguardTeam/AdGuardHome/internal/util"
    8     "github.com/AdguardTeam/golibs/log"
    9 )
   10 
   11 // search - searches log entries in the query log using specified parameters
   12 // returns the list of entries found + time of the oldest entry
   13 func (l *queryLog) search(params *searchParams) ([]*logEntry, time.Time) {
   14     now := time.Now()
   15 
   16     if params.limit == 0 {
   17         return []*logEntry{}, time.Time{}
   18     }
   19 
   20     // add from file
   21     fileEntries, oldest, total := l.searchFiles(params)
   22 
   23     // add from memory buffer
   24     l.bufferLock.Lock()
   25     total += len(l.buffer)
   26     memoryEntries := make([]*logEntry, 0)
   27 
   28     // go through the buffer in the reverse order
   29     // from NEWER to OLDER
   30     for i := len(l.buffer) - 1; i >= 0; i-- {
   31         entry := l.buffer[i]
   32         if !params.match(entry) {
   33             continue
   34         }
   35         memoryEntries = append(memoryEntries, entry)
   36     }
   37     l.bufferLock.Unlock()
   38 
   39     // limits
   40     totalLimit := params.offset + params.limit
   41 
   42     // now let's get a unified collection
   43     entries := append(memoryEntries, fileEntries...)
   44     if len(entries) > totalLimit {
   45         // remove extra records
   46         entries = entries[:totalLimit]
   47     }
   48 
   49     if params.offset > 0 {
   50         if len(entries) > params.offset {
   51             entries = entries[params.offset:]
   52         } else {
   53             entries = make([]*logEntry, 0)
   54             oldest = time.Time{}
   55         }
   56     }
   57 
   58     if len(entries) > 0 && len(entries) <= totalLimit {
   59         // Update oldest after merging in the memory buffer.
   60         oldest = entries[len(entries)-1].Time
   61     }
   62 
   63     log.Debug("QueryLog: prepared data (%d/%d) older than %s in %s",
   64         len(entries), total, params.olderThan, time.Since(now))
   65 
   66     return entries, oldest
   67 }
   68 
   69 // searchFiles reads log entries from all log files and applies the specified search criteria.
   70 // IMPORTANT: this method does not scan more than "maxSearchEntries" so you
   71 // may need to call it many times.
   72 //
   73 // it returns:
   74 // * an array of log entries that we have read
   75 // * time of the oldest processed entry (even if it was discarded)
   76 // * total number of processed entries (including discarded).
   77 func (l *queryLog) searchFiles(params *searchParams) ([]*logEntry, time.Time, int) {
   78     entries := make([]*logEntry, 0)
   79     oldest := time.Time{}
   80 
   81     r, err := l.openReader()
   82     if err != nil {
   83         log.Error("Failed to open qlog reader: %v", err)
   84         return entries, oldest, 0
   85     }
   86     defer r.Close()
   87 
   88     if params.olderThan.IsZero() {
   89         err = r.SeekStart()
   90     } else {
   91         err = r.Seek(params.olderThan.UnixNano())
   92         if err == nil {
   93             // Read to the next record right away
   94             // The one that was specified in the "oldest" param is not needed,
   95             // we need only the one next to it
   96             _, err = r.ReadNext()
   97         }
   98     }
   99 
  100     if err != nil {
  101         log.Debug("Cannot Seek() to %v: %v", params.olderThan, err)
  102         return entries, oldest, 0
  103     }
  104 
  105     totalLimit := params.offset + params.limit
  106     total := 0
  107     oldestNano := int64(0)
  108     // By default, we do not scan more than "maxFileScanEntries" at once
  109     // The idea is to make search calls faster so that the UI could handle it and show something
  110     // This behavior can be overridden if "maxFileScanEntries" is set to 0
  111     for total < params.maxFileScanEntries || params.maxFileScanEntries <= 0 {
  112         entry, ts, err := l.readNextEntry(r, params)
  113 
  114         if err == io.EOF {
  115             // there's nothing to read anymore
  116             break
  117         }
  118 
  119         oldestNano = ts
  120         total++
  121 
  122         if entry != nil {
  123             entries = append(entries, entry)
  124             if len(entries) == totalLimit {
  125                 // Do not read more than "totalLimit" records at once
  126                 break
  127             }
  128         }
  129     }
  130 
  131     if oldestNano != 0 {
  132         oldest = time.Unix(0, oldestNano)
  133     }
  134     return entries, oldest, total
  135 }
  136 
  137 // readNextEntry - reads the next log entry and checks if it matches the search criteria (getDataParams)
  138 //
  139 // returns:
  140 // * log entry that matches search criteria or null if it was discarded (or if there's nothing to read)
  141 // * timestamp of the processed log entry
  142 // * error if we can't read anymore
  143 func (l *queryLog) readNextEntry(r *QLogReader, params *searchParams) (*logEntry, int64, error) {
  144     line, err := r.ReadNext()
  145     if err != nil {
  146         return nil, 0, err
  147     }
  148 
  149     // Read the log record timestamp right away
  150     timestamp := readQLogTimestamp(line)
  151 
  152     // Quick check without deserializing log entry
  153     if !params.quickMatch(line) {
  154         return nil, timestamp, nil
  155     }
  156 
  157     entry := logEntry{}
  158     decodeLogEntry(&entry, line)
  159 
  160     // Full check of the deserialized log entry
  161     if !params.match(&entry) {
  162         return nil, timestamp, nil
  163     }
  164 
  165     return &entry, timestamp, nil
  166 }
  167 
  168 // openReader - opens QLogReader instance
  169 func (l *queryLog) openReader() (*QLogReader, error) {
  170     files := make([]string, 0)
  171 
  172     if util.FileExists(l.logFile + ".1") {
  173         files = append(files, l.logFile+".1")
  174     }
  175     if util.FileExists(l.logFile) {
  176         files = append(files, l.logFile)
  177     }
  178 
  179     return NewQLogReader(files)
  180 }