Pull request 2145: 5992-stats-qlog-custom-dir

Updates #5992.

Squashed commit of the following:

commit 39d3df705ef68672ec9406d81e00daf52a3b3c70
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 12 15:25:22 2024 +0300

    all: fix typo

commit 21e03e4d5a7624a68add53734a127652053845a2
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 12 15:22:35 2024 +0300

    all: upd docs

commit 11180061619f4352774d4bc8c85b481ae28f0d0b
Merge: ac5fd8dc8 7f8370744
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 12 14:57:35 2024 +0300

    Merge branch 'master' into 5992-stats-qlog-custom-dir

commit ac5fd8dc82c9c6e88a182cd6e6aed07bf3548639
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 7 14:55:51 2024 +0300

    all: upd chlog

commit fe00652e158db65e0e735a19cf88aa999ece3e62
Merge: 21ad1ecf7 56b98080f
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 7 14:53:24 2024 +0300

    Merge branch 'master' into 5992-stats-qlog-custom-dir

commit 21ad1ecf7b30c3c8f45d54210d7297966126b0f7
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Wed Feb 7 14:52:12 2024 +0300

    home: imp docs

commit 739b158de77e673ef80efdaa523044939ea879d5
Author: Stanislav Chzhen <s.chzhen@adguard.com>
Date:   Mon Feb 5 20:15:50 2024 +0300

    home: add stats qlog custom dir
This commit is contained in:
Stanislav Chzhen 2024-02-12 18:45:51 +03:00
parent 7f83707449
commit d338451faf
11 changed files with 78 additions and 18 deletions

View File

@ -30,6 +30,8 @@ NOTE: Add new changes BELOW THIS COMMENT.
### Added ### Added
- Ability to define custom directories for storage of query log files and
statistics ([#5992]).
- Context menu item in the Query Log to add a Client to the Persistent client - Context menu item in the Query Log to add a Client to the Persistent client
list ([#6679]). list ([#6679]).
@ -59,6 +61,7 @@ NOTE: Add new changes BELOW THIS COMMENT.
- Go 1.20 support, as it has reached end of life. - Go 1.20 support, as it has reached end of life.
[#5992]: https://github.com/AdguardTeam/AdGuardHome/issues/5992
[#6679]: https://github.com/AdguardTeam/AdGuardHome/issues/6679 [#6679]: https://github.com/AdguardTeam/AdGuardHome/issues/6679
[go-toolchain]: https://go.dev/blog/toolchain [go-toolchain]: https://go.dev/blog/toolchain

View File

@ -154,8 +154,8 @@ func pathsToPatterns(fsys fs.FS, paths []string) (patterns []string, err error)
} }
// handleEvents concurrently handles the file system events. It closes the // handleEvents concurrently handles the file system events. It closes the
// update channel of HostsContainer when finishes. It's used to be called // update channel of HostsContainer when finishes. It is intended to be used as
// within a separate goroutine. // a goroutine.
func (hc *HostsContainer) handleEvents() { func (hc *HostsContainer) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", hostsContainerPrefix)) defer log.OnPanic(fmt.Sprintf("%s: handling events", hostsContainerPrefix))

View File

@ -66,7 +66,7 @@ func NewOSWritesWatcher() (w FSWatcher, err error) {
return fsw, nil return fsw, nil
} }
// handleErrors handles accompanying errors. It used to be called in a separate // handleErrors handles accompanying errors. It is intended to be used as a
// goroutine. // goroutine.
func (w *osWatcher) handleErrors() { func (w *osWatcher) handleErrors() {
defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref)) defer log.OnPanic(fmt.Sprintf("%s: handling errors", osWatcherPref))
@ -100,7 +100,7 @@ func (w *osWatcher) Close() (err error) {
} }
// handleEvents notifies about the received file system's event if needed. It // handleEvents notifies about the received file system's event if needed. It
// used to be called in a separate goroutine. // is intended to be used as a goroutine.
func (w *osWatcher) handleEvents() { func (w *osWatcher) handleEvents() {
defer log.OnPanic(fmt.Sprintf("%s: handling events", osWatcherPref)) defer log.OnPanic(fmt.Sprintf("%s: handling events", osWatcherPref))

View File

@ -140,8 +140,7 @@ func (clients *clientsContainer) Init(
} }
// handleHostsUpdates receives the updates from the hosts container and adds // handleHostsUpdates receives the updates from the hosts container and adds
// them to the clients container. It's used to be called in a separate // them to the clients container. It is intended to be used as a goroutine.
// goroutine.
func (clients *clientsContainer) handleHostsUpdates() { func (clients *clientsContainer) handleHostsUpdates() {
for upd := range clients.etcHosts.Upd() { for upd := range clients.etcHosts.Upd() {
clients.addFromHostsFile(upd) clients.addFromHostsFile(upd)

View File

@ -259,6 +259,10 @@ type tlsConfigSettings struct {
} }
type queryLogConfig struct { type queryLogConfig struct {
// DirPath is the custom directory for logs. If it's empty the default
// directory will be used. See [homeContext.getDataDir].
DirPath string `yaml:"dir_path"`
// Ignored is the list of host names, which should not be written to log. // Ignored is the list of host names, which should not be written to log.
// "." is considered to be the root domain. // "." is considered to be the root domain.
Ignored []string `yaml:"ignored"` Ignored []string `yaml:"ignored"`
@ -278,6 +282,10 @@ type queryLogConfig struct {
} }
type statsConfig struct { type statsConfig struct {
// DirPath is the custom directory for statistics. If it's empty the
// default directory is used. See [homeContext.getDataDir].
DirPath string `yaml:"dir_path"`
// Ignored is the list of host names, which should not be counted. // Ignored is the list of host names, which should not be counted.
Ignored []string `yaml:"ignored"` Ignored []string `yaml:"ignored"`

View File

@ -46,12 +46,15 @@ func onConfigModified() {
// server and initializes it at last. It also must not be called unless // server and initializes it at last. It also must not be called unless
// [config] and [Context] are initialized. // [config] and [Context] are initialized.
func initDNS() (err error) { func initDNS() (err error) {
baseDir := Context.getDataDir()
anonymizer := config.anonymizer() anonymizer := config.anonymizer()
statsDir, querylogDir, err := checkStatsAndQuerylogDirs(&Context, config)
if err != nil {
return err
}
statsConf := stats.Config{ statsConf := stats.Config{
Filename: filepath.Join(baseDir, "stats.db"), Filename: filepath.Join(statsDir, "stats.db"),
Limit: config.Stats.Interval.Duration, Limit: config.Stats.Interval.Duration,
ConfigModified: onConfigModified, ConfigModified: onConfigModified,
HTTPRegister: httpRegister, HTTPRegister: httpRegister,
@ -75,7 +78,7 @@ func initDNS() (err error) {
ConfigModified: onConfigModified, ConfigModified: onConfigModified,
HTTPRegister: httpRegister, HTTPRegister: httpRegister,
FindClient: Context.clients.findMultiple, FindClient: Context.clients.findMultiple,
BaseDir: baseDir, BaseDir: querylogDir,
AnonymizeClientIP: config.DNS.AnonymizeClientIP, AnonymizeClientIP: config.DNS.AnonymizeClientIP,
RotationIvl: config.QueryLog.Interval.Duration, RotationIvl: config.QueryLog.Interval.Duration,
MemSize: config.QueryLog.MemSize, MemSize: config.QueryLog.MemSize,
@ -545,3 +548,50 @@ func (r safeSearchResolver) LookupIP(
return ips, nil return ips, nil
} }
// checkStatsAndQuerylogDirs checks and returns directory paths to store
// statistics and query log.
func checkStatsAndQuerylogDirs(
ctx *homeContext,
conf *configuration,
) (statsDir, querylogDir string, err error) {
baseDir := ctx.getDataDir()
statsDir = conf.Stats.DirPath
if statsDir == "" {
statsDir = baseDir
} else {
err = checkDir(statsDir)
if err != nil {
return "", "", fmt.Errorf("statistics: custom directory: %w", err)
}
}
querylogDir = conf.QueryLog.DirPath
if querylogDir == "" {
querylogDir = baseDir
} else {
err = checkDir(querylogDir)
if err != nil {
return "", "", fmt.Errorf("querylog: custom directory: %w", err)
}
}
return statsDir, querylogDir, nil
}
// checkDir checks if the path is a directory. It's used to check for
// misconfiguration at startup.
func checkDir(path string) (err error) {
var fi os.FileInfo
if fi, err = os.Stat(path); err != nil {
// Don't wrap the error, since it's informative enough as is.
return err
}
if !fi.IsDir() {
return fmt.Errorf("%q is not a directory", path)
}
return nil
}

View File

@ -49,8 +49,8 @@ func (l *queryLog) client(clientID, ip string, cache clientCache) (c *Client, er
// the total amount of records in the buffer at the moment of searching. // the total amount of records in the buffer at the moment of searching.
// l.confMu is expected to be locked. // l.confMu is expected to be locked.
func (l *queryLog) searchMemory(params *searchParams, cache clientCache) (entries []*logEntry, total int) { func (l *queryLog) searchMemory(params *searchParams, cache clientCache) (entries []*logEntry, total int) {
// We use this configuration check because a buffer can contain a single log // Check memory size, as the buffer can contain a single log record. See
// record. See [newQueryLog]. // [newQueryLog].
if l.conf.MemSize == 0 { if l.conf.MemSize == 0 {
return nil, 0 return nil, 0
} }