"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 }