diff --git a/adguard-exporter b/adguard-exporter
deleted file mode 160000
index 1113358..0000000
--- a/adguard-exporter
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 1113358cac51354eaf1280d940a50e344a4e045b
diff --git a/adguard-exporter/.env.example b/adguard-exporter/.env.example
new file mode 100644
index 0000000..3b4b13a
--- /dev/null
+++ b/adguard-exporter/.env.example
@@ -0,0 +1,19 @@
+# Contoh konfigurasi, silakan sesuaikan
+#Fill up your adguard url, if didn't works try docker ips (172.x.x.x) (required)
+ADGUARD_HOST=http://172.x.x.x:20271
+
+#Your AdGuard username and password for authentication that you setup during the init (required)
+ADGUARD_USER=
+ADGUARD_PASS=
+
+#Your exporter port (optional, default 9617)
+EXPORTER_PORT=9200
+
+#Scrape Interval by prometheus (optional, default 15s)
+SCRAPE_INTERVAL=15s
+
+#Fill up your LogLevel you want it to, (DEBUG,WARN,ERROR,INFO)
+LOG_LEVEL=DEBUG
+
+#GEOIP_DB File download => wget https://raw.githubusercontent.com/P3TERX/GeoLite.mmdb/download/GeoLite2-City.mmdb
+GEOIP_DB=/path/to/your/GeoLite2-City.mmdb
diff --git a/adguard-exporter/.github/renovate.json b/adguard-exporter/.github/renovate.json
new file mode 100644
index 0000000..01c9f06
--- /dev/null
+++ b/adguard-exporter/.github/renovate.json
@@ -0,0 +1,29 @@
+{
+ "$schema": "https://docs.renovatebot.com/renovate-schema.json",
+ "extends": [
+ "config:recommended"
+ ],
+ "labels": [
+ "renovate::dependencies",
+ "{{#if category}}renovate::{{category}}{{/if}}",
+ "{{#if updateType}}renovate::{{updateType}}{{/if}}",
+ "{{#if datasource}}renovate::{{datasource}}{{/if}}",
+ "{{#if manager}}renovate::{{manager}}{{/if}}",
+ "{{#if vulnerabilitySeverity}}renovate::{{vulnerabilitySeverity}}{{/if}}",
+ "renovate::{{#if isVulnerabilityAlert}}vulnerability{{else}}not-vulnerability{{/if}}"
+ ],
+ "pinDigests": false,
+ "enabled": true,
+ "separateMajorMinor": true,
+ "automerge": true,
+ "packageRules": [
+ {
+ "matchManagers": [
+ "gomod"
+ ],
+ "postUpdateOptions": [
+ "gomodTidy"
+ ]
+ }
+ ]
+}
diff --git a/adguard-exporter/.github/workflows/go-ci.yml b/adguard-exporter/.github/workflows/go-ci.yml
new file mode 100644
index 0000000..36ad5c7
--- /dev/null
+++ b/adguard-exporter/.github/workflows/go-ci.yml
@@ -0,0 +1,44 @@
+name: Go CI
+
+on:
+ push:
+ branches: [ "main" ]
+ pull_request:
+ branches: [ "main" ]
+ workflow_dispatch:
+
+jobs:
+ test:
+ name: Run Tests & Linter
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v7
+
+ - name: Set up Go
+ uses: actions/setup-go@v6
+ with:
+ go-version: "1.26"
+
+ # - name: Cache Go modules
+ # uses: actions/cache@v3
+ # with:
+ # path: |
+ # ~/.cache/go-build
+ # ~/go/pkg/mod
+ # key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ # restore-keys: |
+ # ${{ runner.os }}-go-
+
+ - name: Install dependencies
+ run: go mod tidy
+
+ - name: Run unit tests
+ run: go test -v ./...
+
+ - name: Run linter (golangci-lint)
+ uses: golangci/golangci-lint-action@v9
+ with:
+ version: latest
+ args: --timeout=3m
diff --git a/adguard-exporter/.github/workflows/release.yml b/adguard-exporter/.github/workflows/release.yml
new file mode 100644
index 0000000..6b0b805
--- /dev/null
+++ b/adguard-exporter/.github/workflows/release.yml
@@ -0,0 +1,32 @@
+name: Release
+
+on:
+ push:
+ tags:
+ - "v*"
+
+permissions:
+ contents: write
+ packages: write
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source
+ uses: actions/checkout@v7
+
+ - name: Set up Go
+ uses: actions/setup-go@v6
+ with:
+ go-version: 1.26
+
+ - name: Login to GitHub Container Registry
+ run: echo "${{ secrets.CR_PAT }}" | docker login ghcr.io -u znand-dev --password-stdin
+ - name: Run GoReleaser
+ uses: goreleaser/goreleaser-action@v7
+ with:
+ version: latest
+ args: release --clean
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/adguard-exporter/.gitignore b/adguard-exporter/.gitignore
new file mode 100644
index 0000000..10ab642
--- /dev/null
+++ b/adguard-exporter/.gitignore
@@ -0,0 +1,13 @@
+# Binaries
+adguardexporter
+dist/
+# Env file
+.env
+
+# OS / Editor files
+.DS_Store
+*.swp
+
+#db
+GeoLite2-City.mmdb
+geolite2-db/GeoLite2-City.mmdb
diff --git a/adguard-exporter/.goreleaser.yml b/adguard-exporter/.goreleaser.yml
new file mode 100644
index 0000000..8bb91e7
--- /dev/null
+++ b/adguard-exporter/.goreleaser.yml
@@ -0,0 +1,79 @@
+project_name: adguard-exporter
+
+before:
+ hooks:
+ - go mod tidy
+
+builds:
+ - id: universal
+ main: main.go
+ binary: adguard-exporter
+ env: [CGO_ENABLED=0]
+ goos:
+ - linux
+ - windows
+ - darwin
+ goarch:
+ - amd64
+ - arm64
+ - arm
+
+archives:
+ - format: tar.gz
+ name_template: "{{ .ProjectName }}_{{ .Version }}_{{ .Os }}_{{ .Arch }}"
+ files:
+ - LICENSE
+ - README.md
+
+checksum:
+ name_template: 'checksums.txt'
+
+dockers:
+ - id: docker-amd64
+ ids: [universal]
+ goos: linux
+ goarch: amd64
+ use: buildx
+ dockerfile: Dockerfile
+ build_flag_templates:
+ - "--platform=linux/amd64"
+ image_templates:
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-amd64
+ - ghcr.io/znand-dev/adguardexporter:latest-amd64
+
+ - id: docker-arm64
+ ids: [universal]
+ goos: linux
+ goarch: arm64
+ use: buildx
+ dockerfile: Dockerfile
+ build_flag_templates:
+ - "--platform=linux/arm64"
+ image_templates:
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-arm64v8
+ - ghcr.io/znand-dev/adguardexporter:latest-arm64v8
+
+ - id: docker-arm
+ ids: [universal]
+ goos: linux
+ goarch: arm
+ use: buildx
+ dockerfile: Dockerfile
+ build_flag_templates:
+ - "--platform=linux/arm/v6"
+ image_templates:
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-armv6
+ - ghcr.io/znand-dev/adguardexporter:latest-armv6
+
+docker_manifests:
+ - name_template: ghcr.io/znand-dev/adguardexporter:{{ .Tag }}
+ image_templates:
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-amd64
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-arm64v8
+ - ghcr.io/znand-dev/adguardexporter:{{ .Tag }}-armv6
+
+ - name_template: ghcr.io/znand-dev/adguardexporter:latest
+ image_templates:
+ - ghcr.io/znand-dev/adguardexporter:latest-amd64
+ - ghcr.io/znand-dev/adguardexporter:latest-arm64v8
+ - ghcr.io/znand-dev/adguardexporter:latest-armv6
diff --git a/adguard-exporter/CHANGELOG.md b/adguard-exporter/CHANGELOG.md
new file mode 100644
index 0000000..698c93e
--- /dev/null
+++ b/adguard-exporter/CHANGELOG.md
@@ -0,0 +1,20 @@
+# Changelog
+
+## v1.1.0 - 2025-06-18
+### β¨ Added
+- `adguard_replaced_parental`: total queries replaced due to parental control
+- `adguard_safe_search`: total queries forced into Safe Search (YouTube, Google, Bing)
+- `adguard_safe_browsing`: total Safe Browsing protections triggered
+- `adguard_top_upstreams`: top DNS upstreams based on query count
+- `adguard_top_clients`: top client IPs querying AdGuard
+- `adguard_top_queried_domains`: most frequently queried domains
+- `adguard_upstream_avg_response_time_seconds`: average DNS upstream response time
+
+### π Fixed
+- Fixed: missing `upstream` and `client` labels due to empty maps or parsing errors
+- Fixed: more reliable parsing of nested JSON from `/control/stats` and `/control/status`
+- Fixed: exporter now exposes partial metrics even if part of the JSON fails
+
+### π Refactored
+- Cleaned up metric and label names to follow Prometheus naming conventions
+- Fixed Dockerfile lint warning: unified casing of `FROM ... AS` keyword for best practices
diff --git a/adguard-exporter/Dockerfile b/adguard-exporter/Dockerfile
new file mode 100644
index 0000000..467eec7
--- /dev/null
+++ b/adguard-exporter/Dockerfile
@@ -0,0 +1,31 @@
+FROM --platform=$BUILDPLATFORM golang:1.26-alpine AS build
+WORKDIR /app
+
+RUN apk add --no-cache ca-certificates git
+
+COPY go.mod go.sum ./
+RUN go mod download
+
+COPY . .
+
+ARG TARGETOS
+ARG TARGETARCH
+
+RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH \
+ go build -ldflags="-s -w" -o adguard-exporter .
+
+FROM alpine:3.24 AS certs
+RUN apk add --no-cache ca-certificates
+
+FROM scratch
+WORKDIR /
+
+COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
+COPY --from=build /app/adguard-exporter /adguard-exporter
+COPY --from=build /app/GeoLite2-City.mmdb /GeoLite2-City.mmdb
+
+USER 65532:65532
+
+EXPOSE 9200
+
+ENTRYPOINT ["/adguard-exporter"]
diff --git a/adguard-exporter/LICENSE b/adguard-exporter/LICENSE
new file mode 100644
index 0000000..c9eab41
--- /dev/null
+++ b/adguard-exporter/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 Januar Ismunandar
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/adguard-exporter/README.md b/adguard-exporter/README.md
new file mode 100644
index 0000000..e4b4706
--- /dev/null
+++ b/adguard-exporter/README.md
@@ -0,0 +1,307 @@
+# AdGuard Exporter for Prometheus
+
+A lightweight Prometheus exporter written in Go that exposes detailed metrics from your AdGuard Home instance β including DNS statistics, blocked domains, upstreams, and client data.
+
+---
+
+## Features
+
+| Category | Capabilities |
+|----------|--------------|
+| API Integration | β’ Authenticated access to AdGuard Home API
β’ Supports `/control/status`, `/control/stats`, and `/control/querylog` endpoints |
+| DNS Analytics | β’ Total DNS queries metrics
β’ Blocked DNS queries tracking
β’ Upstream DNS statistics
β’ Per-client query statistics
β’ Query reason statistics
β’ DNS upstream latency histograms |
+| Query Log Processing | β’ Real-time query log analysis
β’ Query deduplication to prevent metric inflation
β’ Domain and query reason tracking |
+| GeoIP Enrichment | β’ MaxMind GeoLite2 IP geolocation lookup
β’ DNS client geographic visualization (Grafana Geomap)
β’ DNS threat map for blocked queries
β’ GeoIP caching for improved performance |
+| Traffic Analytics | β’ DNS queries grouped by ISP organization
β’ Country-level DNS traffic insights
β’ ISP-based DNS traffic monitoring |
+| Exporter Monitoring | β’ Deduplication cache metrics
β’ GeoIP cache usage monitoring
β’ Deduplication hit counters
β’ Exporter scrape performance metrics |
+| Deployment | β’ Customizable scrape interval
β’ Lightweight single binary deployment
β’ Docker-friendly deployment |
+---
+
+## Built With
+
+- [Go (Golang)](https://golang.org/)
+- [Prometheus Client Library](https://github.com/prometheus/client_golang)
+- [AdGuard Home](https://github.com/AdguardTeam/AdGuardHome)
+
+---
+## Prerequisites
+
+Before running this exporter, make sure:
+
+- AdGuard Home is up and running
+- You have a valid AdGuard username & password (without special character (!,@,`,* etc.))
+- Prometheus is configured to scrape this exporter
+- Docker installed (or alternatively Go 1.20+ for building from source)
+- Port `9617` is available on your system
+
+To make sure the endpoint is valid, you can check the endpoints via curl:
+
+```
+curl -v -u : :/control/stats
+curl -v -u : :/control/status
+curl -v -u : :/control/querylog
+```
+After that, you should got the output like this:
+
+```
+* Trying 172.31.20.12:80...
+* Connected to 172.31.20.12 (172.31.20.12) port 80 (#0)
+* Server auth using Basic with user 'admin'
+> GET /control/stats HTTP/1.1
+> Host: 172.31.20.12
+> Authorization: Basic Xxxxxxxxxxxxxxxxxxxxxxxxx
+> User-Agent: curl/7.88.1
+> Accept: */*
+>
+< HTTP/1.1 200 OK
+< Access-Control-Allow-Origin: http://172.31.20.12
+< Content-Type: application/json
+< Server: AdGuardHome/v0.107.62
+< Vary: Origin
+< Vary: Accept-Encoding
+< Date: Tue, 08 Jul 2025 16:45:56 GMT
+< Transfer-Encoding: chunked
+<
+```
+
+---
+
+---
+## Environment Variables
+
+| Variable | Description | Required | Example |
+|------------------|---------------------------- | -------------------|-------------------
+| `ADGUARD_HOST`| AdGuard Home base URL | β
| `http://192.168.1.1:3000,` |
+| `ADGUARD_USER`| AdGuard Home username | β
| `admin` |
+| `ADGUARD_PASS`| AdGuard Home password | β
| `secretpassword` |
+| `EXPORTER_PORT` | Port to expose metrics (default: 9617) | β | `9200` |
+| `SCRAPE_INTERVAL` | How often to scrape (default: 15s) | β | `30s` |
+| `LOG_LEVEL` | Log Level to analyze, INFO, WARN, DEBUG | β | `DEBUG`,`WARN`,`INFO` |
+
+---
+
+### Quick Start
+
+## Run via Docker Compose
+
+### 1. Create docker-compose.yml in your root dir
+
+```yaml
+version: '3.8'
+
+services:
+ adguard-exporter:
+ image: ghcr.io/znandev/adguardexporter:latest
+ container_name: adguard_exporter
+ restart: unless-stopped
+ ports:
+ - "9617:9617"
+ volumes:
+ - .//GeoLite2-City.mmdb:/GeoLite2-City.mmdb
+ environment:
+ - ADGUARD_HOST=
+ - ADGUARD_USER=
+ - ADGUARD_PASS=
+ - EXPORTER_PORT=9617
+ - SCRAPE_INTERVAL=15s
+ - LOG_LEVEL=DEBUG
+```
+
+### 2. Run with Docker Compose
+
+```bash
+docker-compose up -d
+```
+
+---
+
+## Metrics Endpoint
+
+Once running, your exporter will be available at:
+
+```
+http://:9617/metrics
+```
+
+Ready to scrape by Prometheus!
+
+---
+
+## Example Prometheus Job
+
+```yaml
+- job_name: 'adguard-exporter'
+ scrape_interval: 15s
+ static_configs:
+ - targets: ['adguard-exporter:9617']
+```
+---
+## Available Prometheus Metrics
+
+This exporter exposes the following metrics from AdGuard Home.
+
+---
+
+### Core Metrics
+
+* `adguard_dns_queries_total`
+ Total DNS queries processed by AdGuard Home.
+
+* `adguard_blocked_filtering_total`
+ Total DNS queries blocked by filtering rules.
+
+* `adguard_replaced_parental`
+ Queries replaced by parental filtering.
+
+* `adguard_avg_processing_time`
+ Average DNS query processing time in milliseconds.
+
+---
+
+### AdGuard Status
+
+* `adguard_protection_enabled`
+ Whether DNS filtering protection is enabled (1 = enabled, 0 = disabled).
+
+* `adguard_running`
+ Whether the AdGuard Home DNS service is running.
+
+* `adguard_dhcp_available`
+ Indicates if DHCP functionality is available.
+
+* `adguard_protection_disabled_duration_seconds`
+ Time since DNS protection was disabled.
+
+---
+
+### Top Queries
+
+Metrics with labels:
+
+* `adguard_top_queried_domain_total{domain="example.com"}`
+* `adguard_top_blocked_domain_total{domain="ads.example.com"}`
+* `adguard_top_client_total{client="192.168.1.2"}`
+* `adguard_top_upstream_total{upstream="8.8.8.8"}`
+* `adguard_upstream_avg_response_time_seconds{upstream="8.8.8.8"}`
+
+---
+
+### Query Log Metrics
+
+Metrics generated from AdGuard query log analysis.
+
+* `adguard_query_domain_total{domain="example.com"}`
+* `adguard_query_reason_total{reason="FilteredBlackList"}`
+* `adguard_query_type_total{type="A"}`
+* `adguard_query_upstream_total{upstream="8.8.8.8"}`
+* `adguard_query_client_reason_total{client="192.168.1.10",reason="FilteredBlackList"}`
+
+---
+
+### Upstream Latency Metrics
+
+Histogram metrics that track DNS upstream response time distribution.
+
+* `adguard_upstream_latency_seconds`
+
+This metric allows latency percentile calculations such as **p95 or p99 upstream DNS latency**.
+
+Generated metrics:
+
+* `adguard_upstream_latency_seconds_bucket`
+* `adguard_upstream_latency_seconds_sum`
+* `adguard_upstream_latency_seconds_count`
+
+Example:
+
+```
+adguard_upstream_latency_seconds_bucket{upstream="1.1.1.1",le="0.005"} 50
+```
+
+---
+
+### GeoIP Metrics
+
+#### `adguard_client_geo_queries`
+
+Total DNS queries per client enriched with geographic information.
+
+Labels:
+
+* `client` β Client IP address
+* `country` β ISO country code
+* `lat` β Latitude
+* `lon` β Longitude
+
+Example:
+
+```
+adguard_client_geo_queries{client="118.99.94.204",country="ID",lat="-2.969000",lon="104.744200"} 1000
+```
+
+---
+
+#### `adguard_blocked_geo_queries`
+
+Counts DNS queries that were blocked by filtering rules and enriches them with geographic information.
+
+Labels:
+
+* `client`
+* `country`
+* `lat`
+* `lon`
+
+Example:
+
+```
+adguard_blocked_geo_queries{client="118.99.94.204",country="ID",lat="-2.969000",lon="104.744200"} 306
+```
+
+---
+
+### Exporter Health Metrics
+
+Metrics used to monitor the exporter itself.
+
+* `adguard_exporter_up` β Indicates if the exporter successfully scraped the AdGuard API.
+* `adguard_exporter_scrape_duration_seconds` β Time taken for the exporter to scrape AdGuard API data.
+* `adguard_exporter_scrape_errors_total` β Total number of errors encountered during scraping.
+* `adguard_exporter_query_cache_size` β Number of cached DNS query entries used for deduplication.
+* `adguard_exporter_geo_cache_size` β Number of cached GeoIP lookup entries.
+* `adguard_exporter_dedup_hits_total` β Total number of duplicate DNS queries skipped by the exporter.
+
+---
+
+## License
+
+This project is licensed under the MIT License β see the [LICENSE](LICENSE) file for details.
+
+---
+
+## Screenshots
+
+Grafana Dashboard Preview
+
+[Live preview and monitoring, click here](https://stats.znand.my.id/public-dashboards/a0b05deb37464a17b01cbc69eb65ac5a?orgId=1&refresh=5s)
+
+[Deploy yours here](https://grafana.com/grafana/dashboards/23579-adguard-metrics-statistics/)
+β new dashboard.json for grafana will be uploaded soon
+
+
+
+
+
+
+
+
+
+
+
+---
+
+## π¬ Feedback
+
+Pull Requests, Issues, and Suggestions are always welcome!
+
+**Made with β€οΈ by [znanddev](https://github.com/znand-dev)**
diff --git a/adguard-exporter/docker-compose.yml b/adguard-exporter/docker-compose.yml
new file mode 100644
index 0000000..7cee3a3
--- /dev/null
+++ b/adguard-exporter/docker-compose.yml
@@ -0,0 +1,21 @@
+version: '3.8'
+
+services:
+ adguard-exporter:
+ image: ghcr.io/znand-dev/adguardexporter:latest
+ container_name: adguard_exporter
+ restart: unless-stopped
+
+ ports:
+ - "9617:9617"
+
+ volumes:
+ - .//GeoLite2-City.mmdb:/GeoLite2-City.mmdb # your geolite2 db file
+
+ environment:
+ - ADGUARD_HOST= # try docker ips if didn't works http://172.xx.x.x for example
+ - ADGUARD_USER= # required
+ - ADGUARD_PASS= # required
+ - EXPORTER_PORT=9200 # optional, default 9617
+ - SCRAPE_INTERVAL=15s # optional
+ - LOG_LEVEL=DEBUG # optional
diff --git a/adguard-exporter/geolite2-db/README.md b/adguard-exporter/geolite2-db/README.md
new file mode 100644
index 0000000..96d3a10
--- /dev/null
+++ b/adguard-exporter/geolite2-db/README.md
@@ -0,0 +1,8 @@
+Download GeoLite2 City database from MaxMind:
+
+https://dev.maxmind.com/geoip/geolite2-free-geolocation-data
+
+Place the file here:
+
+geoip/GeoLite2-City.mmdb
+
diff --git a/adguard-exporter/go.mod b/adguard-exporter/go.mod
new file mode 100644
index 0000000..5b13801
--- /dev/null
+++ b/adguard-exporter/go.mod
@@ -0,0 +1,25 @@
+module github.com/znand-dev/adguardexporter
+
+go 1.24.0
+
+toolchain go1.26.4
+
+require (
+ github.com/joho/godotenv v1.5.1
+ github.com/oschwald/geoip2-golang v1.13.0
+ github.com/prometheus/client_golang v1.23.2
+)
+
+require (
+ github.com/beorn7/perks v1.0.1 // indirect
+ github.com/cespare/xxhash/v2 v2.3.0 // indirect
+ github.com/kr/text v0.2.0 // indirect
+ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/oschwald/maxminddb-golang v1.13.0 // indirect
+ github.com/prometheus/client_model v0.6.2 // indirect
+ github.com/prometheus/common v0.66.1 // indirect
+ github.com/prometheus/procfs v0.16.1 // indirect
+ go.yaml.in/yaml/v2 v2.4.2 // indirect
+ golang.org/x/sys v0.38.0 // indirect
+ google.golang.org/protobuf v1.36.8 // indirect
+)
diff --git a/adguard-exporter/go.sum b/adguard-exporter/go.sum
new file mode 100644
index 0000000..a8aa348
--- /dev/null
+++ b/adguard-exporter/go.sum
@@ -0,0 +1,52 @@
+github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
+github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
+github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
+github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
+github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
+github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
+github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
+github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
+github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
+github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
+github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
+github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
+github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
+github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
+github.com/oschwald/geoip2-golang v1.13.0 h1:Q44/Ldc703pasJeP5V9+aFSZFmBN7DKHbNsSFzQATJI=
+github.com/oschwald/geoip2-golang v1.13.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
+github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
+github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
+github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
+github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
+github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
+github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
+github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
+github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
+github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
+github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
+github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
+github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
+go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
+go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
+go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
+go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
+golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
+golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
+google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
+gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/adguard-exporter/grafana/dashboard+node.json b/adguard-exporter/grafana/dashboard+node.json
new file mode 100644
index 0000000..e679962
--- /dev/null
+++ b/adguard-exporter/grafana/dashboard+node.json
@@ -0,0 +1,1540 @@
+{
+ "annotations": {
+ "list": [
+ {
+ "builtIn": 1,
+ "datasource": {
+ "type": "grafana",
+ "uid": "-- Grafana --"
+ },
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "type": "dashboard"
+ }
+ ]
+ },
+ "description": "A powerful Grafana dashboard for monitoring AdGuard Home DNS performance using Prometheus as the data source.",
+ "editable": true,
+ "fiscalYearStartMonth": 0,
+ "gnetId": 23579,
+ "graphTooltip": 0,
+ "id": 7,
+ "links": [],
+ "liveNow": true,
+ "panels": [
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 0
+ },
+ "id": 23,
+ "panels": [],
+ "title": "Node Exporter",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "description": "Displays the total CPU utilization percentage (average across all cores). \nIt is calculated as 100% minus the average idle time of the CPU \nover the last 30 seconds.",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-YlRd"
+ },
+ "custom": {
+ "axisBorderShow": true,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 4,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 0,
+ "y": 1
+ },
+ "id": 21,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "100 - (avg(rate(node_cpu_seconds_total{mode=\"idle\"}[30s])) * 100)",
+ "instant": false,
+ "legendFormat": "CPU Usage (%)",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "CPU Usage (%)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-BlPu"
+ },
+ "custom": {
+ "axisBorderShow": true,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 4,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 6,
+ "x": 6,
+ "y": 1
+ },
+ "id": 22,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "avg(100 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes * 100))",
+ "instant": false,
+ "legendFormat": "RAM Usage (%)",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "RAM Usage (%)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-GrYlRd"
+ },
+ "custom": {
+ "axisBorderShow": true,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 5,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "KiBs"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 9,
+ "x": 12,
+ "y": 1
+ },
+ "id": 25,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "rate(node_network_transmit_bytes_total{device=\"eth0\",instance=\"172.31.18.251:9100\"}[5m]) * 8 / 1e3",
+ "instant": false,
+ "legendFormat": "TX Rate",
+ "range": true,
+ "refId": "A"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "rate(node_network_receive_bytes_total{device=\"eth0\",instance=\"172.31.18.251:9100\"}[5m]) * 8 / 1e3",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "RX Rate",
+ "range": true,
+ "refId": "B"
+ }
+ ],
+ "title": "Network Traffic (KiB/s)",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "default": true,
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 5,
+ "w": 3,
+ "x": 21,
+ "y": 1
+ },
+ "id": 24,
+ "options": {
+ "minVizHeight": 75,
+ "minVizWidth": 75,
+ "orientation": "auto",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showThresholdLabels": false,
+ "showThresholdMarkers": true,
+ "sizing": "auto"
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "(avg(node_memory_SwapTotal_bytes - node_memory_SwapFree_bytes) / avg(node_memory_SwapTotal_bytes)) * 100",
+ "instant": false,
+ "legendFormat": "Swap Usage (%)",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Swap Usage (%)",
+ "type": "gauge"
+ },
+ {
+ "collapsed": false,
+ "gridPos": {
+ "h": 1,
+ "w": 24,
+ "x": 0,
+ "y": 10
+ },
+ "id": 18,
+ "panels": [],
+ "title": "AdGuard Statistics",
+ "type": "row"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [
+ {
+ "options": {
+ "0": {
+ "color": "semi-dark-red",
+ "index": 1,
+ "text": "Disabled"
+ },
+ "1": {
+ "color": "semi-dark-green",
+ "index": 0,
+ "text": "Enabled"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 4,
+ "x": 0,
+ "y": 11
+ },
+ "id": 4,
+ "options": {
+ "colorMode": "background",
+ "graphMode": "none",
+ "justifyMode": "center",
+ "orientation": "vertical",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "adguard_protection_enabled",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "Protection Status",
+ "range": true,
+ "refId": "B"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "adguard_running",
+ "hide": false,
+ "instant": false,
+ "legendFormat": "Service Status",
+ "range": true,
+ "refId": "C"
+ }
+ ],
+ "title": "π‘Protection & Service",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [
+ {
+ "options": {
+ "0": {
+ "color": "semi-dark-red",
+ "index": 1,
+ "text": "Stopped"
+ },
+ "1": {
+ "color": "semi-dark-green",
+ "index": 0,
+ "text": "Running"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 2,
+ "x": 4,
+ "y": 11
+ },
+ "id": 1,
+ "options": {
+ "colorMode": "background",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "adguard_running",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "πStatus",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 3,
+ "x": 6,
+ "y": 11
+ },
+ "id": 13,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": true,
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "(adguard_blocked_filtering_total / adguard_dns_queries_total) * 100",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Block Rate (%)",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "decimals": 2,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 4,
+ "w": 3,
+ "x": 9,
+ "y": 11
+ },
+ "id": 5,
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "text": {},
+ "textMode": "auto",
+ "wideLayout": true
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "adguard_avg_processing_time * 1000",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "Avg Processing Time",
+ "type": "stat"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "description": "\nThis panel displays the **real-time DNS query rate per second**, calculated over the last 1 minute.\n\n- Based on the metric: `adguard_dns_queries_total`\n- Uses the Prometheus `rate()` function:\n\n",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-BlPu",
+ "seriesBy": "min"
+ },
+ "custom": {
+ "axisBorderShow": true,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "left",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 29,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 2,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "fieldMinMax": false,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "req/s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 13,
+ "w": 12,
+ "x": 12,
+ "y": 11
+ },
+ "id": 3,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "rate(adguard_dns_queries_total[1m])",
+ "legendFormat": "DNS QPS",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "β‘ π Total DNS Query Rate",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "description": "This panel shows the **average number of DNS queries per second**, grouped by query type (A, AAAA, PTR, etc.).\n\n- Based on the metric: `adguard_query_type_total`\n- Uses the Prometheus `rate()` function with a 5-minute range:",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 5,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 9,
+ "w": 12,
+ "x": 0,
+ "y": 15
+ },
+ "id": 6,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum(rate(adguard_query_type_total[5m])) by (type)",
+ "format": "time_series",
+ "instant": false,
+ "legendFormat": "{{type}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π DNS Query Types",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "description": "This panel displays the **average response time of DNS upstream servers**, in milliseconds.\n\n- Based on metric: `adguard_upstream_avg_response_time_seconds`\n- Converted to milliseconds by multiplying with 1000:",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 6,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 0,
+ "y": 24
+ },
+ "id": 20,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "adguard_upstream_avg_response_time_seconds * 1000",
+ "legendFormat": "{{upstream}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π Avg Upstream Response Time",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "description": "This panel displays the **real-time DNS query block rate per second**, grouped by client IP and the reason why the query was blocked or allowed.\n\n- Based on metric: `adguard_query_client_reason_total`\n- Uses Prometheus query:",
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "left",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 15,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "log": 2,
+ "type": "symlog"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "fieldMinMax": false,
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": null
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "o/s"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 12,
+ "y": 24
+ },
+ "id": 14,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "right",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "sum(rate(adguard_query_client_reason_total[1m])) by (client, reason)",
+ "legendFormat": "{{client}} - {{reason}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π Query Blocks - Real-Time per Client/Reason",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 9,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "count"
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 0,
+ "y": 35
+ },
+ "id": 11,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "topk(10, sum(adguard_query_client_reason_total) by (client))",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π‘Top Clients",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "scaleDistribution": {
+ "type": "linear"
+ }
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 11,
+ "w": 12,
+ "x": 12,
+ "y": 35
+ },
+ "id": 8,
+ "options": {
+ "calculate": false,
+ "cellGap": 1,
+ "color": {
+ "exponent": 0.5,
+ "fill": "dark-orange",
+ "mode": "scheme",
+ "reverse": false,
+ "scale": "exponential",
+ "scheme": "BrBG",
+ "steps": 64
+ },
+ "exemplars": {
+ "color": "rgba(255,0,255,0.7)"
+ },
+ "filterValues": {
+ "le": 1e-9
+ },
+ "legend": {
+ "show": true
+ },
+ "rowsFrame": {
+ "layout": "auto"
+ },
+ "tooltip": {
+ "mode": "single",
+ "showColorScale": false,
+ "yHistogram": false
+ },
+ "yAxis": {
+ "axisPlacement": "right",
+ "reverse": false
+ }
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "topk(10, adguard_query_domain_total)",
+ "legendFormat": "{{domain}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π Top Queried Domains",
+ "type": "heatmap"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic-by-name"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 12,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 1,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "mappings": [],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green"
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 12,
+ "x": 0,
+ "y": 46
+ },
+ "id": 7,
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ },
+ "pluginVersion": "12.0.1+security-01",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "sum(adguard_query_reason_total) by (reason)\r\n",
+ "legendFormat": "__auto",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π« Block Reasons",
+ "type": "timeseries"
+ },
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "scaleDistribution": {
+ "type": "linear"
+ }
+ }
+ },
+ "overrides": []
+ },
+ "gridPos": {
+ "h": 12,
+ "w": 12,
+ "x": 12,
+ "y": 46
+ },
+ "id": 10,
+ "options": {
+ "calculate": false,
+ "cellGap": 1,
+ "color": {
+ "exponent": 0.5,
+ "fill": "dark-orange",
+ "mode": "scheme",
+ "reverse": false,
+ "scale": "exponential",
+ "scheme": "Oranges",
+ "steps": 64
+ },
+ "exemplars": {
+ "color": "rgba(255,0,255,0.7)"
+ },
+ "filterValues": {
+ "le": 1e-9
+ },
+ "legend": {
+ "show": true
+ },
+ "rowsFrame": {
+ "layout": "auto"
+ },
+ "tooltip": {
+ "mode": "single",
+ "showColorScale": false,
+ "yHistogram": false
+ },
+ "yAxis": {
+ "axisPlacement": "right",
+ "reverse": false
+ }
+ },
+ "pluginVersion": "11.2.2",
+ "targets": [
+ {
+ "datasource": {
+ "type": "prometheus",
+ "uid": "cew4qvofsq48wf"
+ },
+ "editorMode": "code",
+ "expr": "topk(10, adguard_top_blocked_domain_total)",
+ "legendFormat": "{{domain}}",
+ "range": true,
+ "refId": "A"
+ }
+ ],
+ "title": "π« Top Blocked Domains",
+ "type": "heatmap"
+ }
+ ],
+ "refresh": "5s",
+ "schemaVersion": 39,
+ "tags": [
+ "DNS",
+ "AdGuard",
+ "Server"
+ ],
+ "templating": {
+ "list": []
+ },
+ "time": {
+ "from": "now-1h",
+ "to": "now"
+ },
+ "timepicker": {
+ "nowDelay": "5s"
+ },
+ "timezone": "browser",
+ "title": "AdGuard Metrics Statistics",
+ "uid": "5fd5f4c0-d0b3-446b-a5b0-9bc11c02b1e02",
+ "version": 6,
+ "weekStart": ""
+}
\ No newline at end of file
diff --git a/adguard-exporter/grafana/dashboard.json b/adguard-exporter/grafana/dashboard.json
new file mode 100644
index 0000000..c82ac23
--- /dev/null
+++ b/adguard-exporter/grafana/dashboard.json
@@ -0,0 +1,3470 @@
+{
+ "apiVersion": "dashboard.grafana.app/v2beta1",
+ "kind": "DashboardWithAccessInfo",
+ "metadata": {
+ "name": "5fd5f4c0-d0b3-446b-a5b0-9bc11c02b1e02",
+ "generation": 71,
+ "creationTimestamp": "2026-03-09T14:51:34Z",
+ "labels": {},
+ "annotations": {}
+ },
+ "spec": {
+ "annotations": [
+ {
+ "kind": "AnnotationQuery",
+ "spec": {
+ "builtIn": true,
+ "enable": true,
+ "hide": true,
+ "iconColor": "rgba(0, 211, 255, 1)",
+ "name": "Annotations & Alerts",
+ "query": {
+ "group": "grafana",
+ "kind": "DataQuery",
+ "spec": {},
+ "version": "v0"
+ }
+ }
+ }
+ ],
+ "cursorSync": "Off",
+ "description": "A powerful Grafana dashboard for monitoring AdGuard Home DNS performance using Prometheus as the data source.",
+ "editable": true,
+ "elements": {
+ "panel-1": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_running",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 1,
+ "links": [],
+ "title": "πStatus",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [
+ {
+ "options": {
+ "0": {
+ "color": "semi-dark-red",
+ "index": 1,
+ "text": "Stopped"
+ },
+ "1": {
+ "color": "semi-dark-green",
+ "index": 0,
+ "text": "Running"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "background",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-10": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "topk(10, adguard_top_blocked_domain_total)",
+ "legendFormat": "{{domain}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 10,
+ "links": [],
+ "title": "π« Top Blocked Domains",
+ "vizConfig": {
+ "group": "heatmap",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "scaleDistribution": {
+ "type": "linear"
+ }
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "calculate": false,
+ "cellGap": 1,
+ "color": {
+ "exponent": 0.5,
+ "fill": "dark-orange",
+ "mode": "scheme",
+ "reverse": false,
+ "scale": "exponential",
+ "scheme": "Oranges",
+ "steps": 64
+ },
+ "exemplars": {
+ "color": "rgba(255,0,255,0.7)"
+ },
+ "filterValues": {
+ "le": 1e-9
+ },
+ "legend": {
+ "show": true
+ },
+ "rowsFrame": {
+ "layout": "auto"
+ },
+ "tooltip": {
+ "mode": "single",
+ "showColorScale": false,
+ "yHistogram": false
+ },
+ "yAxis": {
+ "axisPlacement": "right",
+ "reverse": false
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-11": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "topk(10, sum(adguard_query_client_reason_total) by (client))",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 11,
+ "links": [],
+ "title": "π‘Top Clients",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "queries"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-13": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "(adguard_blocked_filtering_total / adguard_dns_queries_total) * 100",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 13,
+ "links": [],
+ "title": "Block Rate (%)",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "percent"
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": true,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-14": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "topk(5, sum by (reason) (\r\n rate(adguard_query_client_reason_total[5m])\r\n))",
+ "legendFormat": "{{reason}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "This panel displays the **real-time DNS query block rate per second**, grouped reason why the query was blocked or allowed.\n\n- Based on metric: `adguard_query_client_reason_total`\n- Uses Prometheus query:",
+ "id": 14,
+ "links": [],
+ "title": "DNS Query Reasons",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 1,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 4,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "log": 2,
+ "type": "symlog"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "fieldMinMax": false,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "req/s"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-20": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_upstream_avg_response_time_seconds * 1000",
+ "legendFormat": "{{upstream}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "This panel displays the **average response time of DNS upstream servers**, in milliseconds.\n\n- Based on metric: `adguard_upstream_avg_response_time_seconds`\n- Converted to milliseconds by multiplying with 1000:",
+ "id": 20,
+ "links": [],
+ "title": "π Avg Upstream Response Time",
+ "vizConfig": {
+ "group": "bargauge",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "max": 300,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "#EAB839",
+ "value": 70
+ },
+ {
+ "color": "red",
+ "value": 150
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "options": {
+ "displayMode": "lcd",
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": false
+ },
+ "maxVizHeight": 300,
+ "minVizHeight": 34,
+ "minVizWidth": 8,
+ "namePlacement": "top",
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "text": {
+ "titleSize": 15
+ },
+ "valueMode": "color"
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-21": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum(adguard_client_geo_queries) by (lat, lon, client, country)",
+ "format": "table",
+ "instant": true,
+ "legendFormat": "__auto",
+ "range": false
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": [
+ {
+ "kind": "labelsToFields",
+ "spec": {
+ "id": "labelsToFields",
+ "options": {}
+ }
+ },
+ {
+ "kind": "convertFieldType",
+ "spec": {
+ "id": "convertFieldType",
+ "options": {
+ "conversions": [
+ {
+ "destinationType": "number",
+ "targetField": "lat"
+ },
+ {
+ "destinationType": "number",
+ "targetField": "lon"
+ }
+ ],
+ "fields": {}
+ }
+ }
+ }
+ ]
+ }
+ },
+ "description": "",
+ "id": 21,
+ "links": [],
+ "title": "GeoIP Clients",
+ "vizConfig": {
+ "group": "geomap",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "basemap": {
+ "config": {},
+ "name": "Layer 0",
+ "noRepeat": false,
+ "type": "default"
+ },
+ "controls": {
+ "mouseWheelZoom": true,
+ "showAttribution": true,
+ "showDebug": false,
+ "showMeasure": true,
+ "showScale": true,
+ "showZoom": true
+ },
+ "layers": [
+ {
+ "config": {
+ "arrow": 0,
+ "style": {
+ "color": {
+ "fixed": "blue"
+ },
+ "lineWidth": 2,
+ "opacity": 0.4,
+ "rotation": {
+ "fixed": 0,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "fixed": 5,
+ "max": 15,
+ "min": 2
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "text": {
+ "field": "",
+ "fixed": "",
+ "mode": "field"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "filterData": {
+ "id": "byRefId",
+ "options": "A"
+ },
+ "layer-tooltip": true,
+ "location": {
+ "latitude": "lat",
+ "longitude": "lon",
+ "mode": "coords"
+ },
+ "name": "GarisKoneksi",
+ "opacity": 0.4,
+ "tooltip": true,
+ "type": "route"
+ },
+ {
+ "config": {
+ "showLegend": true,
+ "style": {
+ "color": {
+ "fixed": "dark-red"
+ },
+ "opacity": 1,
+ "rotation": {
+ "field": "Value",
+ "fixed": 3,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "fixed": 5,
+ "max": 15,
+ "min": 2
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "text": {
+ "fixed": "",
+ "mode": "field"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "filterData": {
+ "id": "byRefId",
+ "options": "A"
+ },
+ "location": {
+ "latitude": "lat",
+ "longitude": "lon",
+ "mode": "coords"
+ },
+ "name": "Layer 1",
+ "tooltip": true,
+ "type": "markers"
+ }
+ ],
+ "tooltip": {
+ "mode": "details"
+ },
+ "view": {
+ "allLayers": true,
+ "id": "coords",
+ "lat": 19.5,
+ "lon": 80,
+ "noRepeat": false,
+ "shared": false,
+ "zoom": 4
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-22": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_exporter_up",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "If exporter crashed or failed to scrape",
+ "id": 22,
+ "links": [],
+ "title": "AdGuard Exporter Status",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "fieldMinMax": false,
+ "mappings": [
+ {
+ "options": {
+ "0": {
+ "color": "dark-red",
+ "index": 1,
+ "text": "Down"
+ },
+ "1": {
+ "color": "semi-dark-green",
+ "index": 0,
+ "text": "Up"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "background",
+ "graphMode": "area",
+ "justifyMode": "center",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-24": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_exporter_scrape_duration_seconds",
+ "legendFormat": "Scrape Duration",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "How long the exporter scrapes the AdGuard API",
+ "id": 24,
+ "links": [],
+ "title": "Exporter Scrape Duration",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic-by-name"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "#EAB839",
+ "value": 0.5
+ },
+ {
+ "color": "dark-red",
+ "value": 1
+ }
+ ]
+ },
+ "unit": "s"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "timezone": [
+ "browser"
+ ],
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-25": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "increase(adguard_exporter_scrape_errors_total[5m])",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "Count how much API scrapes failure",
+ "id": 25,
+ "links": [],
+ "title": "Exporter Scrape Errors",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "fieldMinMax": false,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "semi-dark-green",
+ "value": 0
+ },
+ {
+ "color": "#EAB839",
+ "value": 10
+ },
+ {
+ "color": "dark-red",
+ "value": 20
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "background",
+ "graphMode": "area",
+ "justifyMode": "center",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "value",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-26": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "(\r\nsum by (upstream) (rate(adguard_upstream_latency_seconds_sum[5m]))\r\n/\r\nsum by (upstream) (rate(adguard_upstream_latency_seconds_count[5m]))\r\n) * 1000",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 26,
+ "links": [],
+ "title": "Average Latency per Upstream",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "#EAB839",
+ "value": 80
+ },
+ {
+ "color": "dark-red",
+ "value": 200
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-27": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "histogram_quantile(\r\n0.95,\r\nsum(rate(adguard_upstream_latency_seconds_bucket[5m])) by (le, upstream)\r\n) * 1000",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 27,
+ "links": [],
+ "title": "P95 Latency",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-28": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "histogram_quantile(\r\n0.99,\r\nsum(rate(adguard_upstream_latency_seconds_bucket[5m])) by (le, upstream)\r\n) * 1000",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "Detect spike DNS",
+ "id": 28,
+ "links": [],
+ "title": "P99 Latency",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 1,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "#EAB839",
+ "value": 200
+ },
+ {
+ "color": "dark-red",
+ "value": 500
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "median"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-29": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "sum by (upstream) (\r\nrate(adguard_upstream_latency_seconds_count[5m])\r\n)",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 29,
+ "links": [],
+ "title": "Requests to upstream",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 0,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "single",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-3": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "rate(adguard_dns_queries_total[1m])",
+ "legendFormat": "DNS QPS",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "\nThis panel displays the **real-time DNS query rate per second**, calculated over the last 1 minute.\n\n- Based on the metric: `adguard_dns_queries_total`\n- Uses the Prometheus `rate()` function:\n\n",
+ "id": 3,
+ "links": [],
+ "title": "β‘ π Total DNS Query Rate",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-BlPu",
+ "seriesBy": "min"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "left",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 5,
+ "gradientMode": "opacity",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineStyle": {
+ "fill": "solid"
+ },
+ "lineWidth": 3,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": true,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "fieldMinMax": false,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "req/s"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-31": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "increase(adguard_exporter_dedup_hits_total[5m])",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "Number of duplicate DNS queries skipped by exporter deduplication.",
+ "id": 31,
+ "links": [],
+ "title": "Deduplicated Queries (last 5m)",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "dark-green",
+ "value": 0
+ },
+ {
+ "color": "orange",
+ "value": 250
+ },
+ {
+ "color": "dark-red",
+ "value": 1000
+ }
+ ]
+ },
+ "unit": "short"
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": true,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-32": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_exporter_query_cache_size",
+ "legendFormat": "Query Cache",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ },
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_exporter_geo_cache_size",
+ "instant": false,
+ "legendFormat": "GeoIP Cache",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "B"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "Number of query entries currently stored in exporter deduplication cache.",
+ "id": 32,
+ "links": [],
+ "title": "Exporter Cache Size",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "value",
+ "graphMode": "area",
+ "justifyMode": "auto",
+ "orientation": "auto",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-33": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "sum(rate(adguard_query_isp_total[5m])) by (isp)",
+ "legendFormat": "{{isp}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 33,
+ "links": [],
+ "title": "DNS Queries by ISP (QPS)",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "none",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 4,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "auto",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "max": 3,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "qps"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-34": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum(adguard_query_isp_total) by (country, isp)",
+ "format": "time_series",
+ "instant": true,
+ "legendFormat": "__auto",
+ "range": false
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": [
+ {
+ "kind": "labelsToFields",
+ "spec": {
+ "id": "labelsToFields",
+ "options": {
+ "mode": "rows",
+ "valueLabel": "country"
+ }
+ }
+ },
+ {
+ "kind": "merge",
+ "spec": {
+ "id": "merge",
+ "options": {}
+ }
+ }
+ ]
+ }
+ },
+ "description": "",
+ "id": 34,
+ "links": [],
+ "title": "DNS Traffic Map by ISP Country",
+ "vizConfig": {
+ "group": "geomap",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "fixed"
+ },
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "basemap": {
+ "config": {},
+ "name": "Layer 0",
+ "noRepeat": true,
+ "type": "default"
+ },
+ "controls": {
+ "mouseWheelZoom": true,
+ "showAttribution": true,
+ "showDebug": false,
+ "showMeasure": true,
+ "showScale": true,
+ "showZoom": true
+ },
+ "layers": [
+ {
+ "config": {
+ "arrow": 1,
+ "style": {
+ "color": {
+ "fixed": "dark-orange"
+ },
+ "lineWidth": 2,
+ "opacity": 0.4,
+ "rotation": {
+ "fixed": 0,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "fixed": 5,
+ "max": 15,
+ "min": 2
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "text": {
+ "field": "",
+ "fixed": "",
+ "mode": "field"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "layer-tooltip": false,
+ "location": {
+ "lookup": "value",
+ "mode": "lookup"
+ },
+ "name": "Layer 2",
+ "opacity": 0.4,
+ "tooltip": true,
+ "type": "route"
+ },
+ {
+ "config": {
+ "showLegend": true,
+ "style": {
+ "color": {
+ "fixed": "dark-red"
+ },
+ "opacity": 1,
+ "rotation": {
+ "fixed": 1,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "field": "value",
+ "fixed": 15,
+ "max": 5,
+ "min": 5
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "text": {
+ "fixed": "",
+ "mode": "field"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "filterData": {
+ "id": "byRefId",
+ "options": "merge-A-A-A-A-A-A-A"
+ },
+ "layer-tooltip": true,
+ "location": {
+ "gazetteer": "public/build/gazetteer/countries.json",
+ "lookup": "value",
+ "mode": "lookup"
+ },
+ "name": "Layer 1",
+ "tooltip": true,
+ "type": "markers"
+ }
+ ],
+ "tooltip": {
+ "mode": "details"
+ },
+ "view": {
+ "allLayers": true,
+ "id": "zero",
+ "lat": 0,
+ "lon": 0,
+ "noRepeat": true,
+ "shared": true,
+ "zoom": 1
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-35": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum(adguard_blocked_geo_queries) by (lat, lon, client, country)",
+ "format": "table",
+ "instant": true,
+ "legendFormat": "__auto",
+ "range": false
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": [
+ {
+ "kind": "labelsToFields",
+ "spec": {
+ "id": "labelsToFields",
+ "options": {}
+ }
+ },
+ {
+ "kind": "convertFieldType",
+ "spec": {
+ "id": "convertFieldType",
+ "options": {
+ "conversions": [
+ {
+ "destinationType": "number",
+ "targetField": "lat"
+ },
+ {
+ "destinationType": "number",
+ "targetField": "lon"
+ }
+ ],
+ "fields": {}
+ }
+ }
+ }
+ ]
+ }
+ },
+ "description": "",
+ "id": 35,
+ "links": [],
+ "title": "GeoIP Blocked Queries by AdGuard",
+ "vizConfig": {
+ "group": "geomap",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ }
+ },
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "basemap": {
+ "config": {},
+ "name": "Layer 0",
+ "noRepeat": false,
+ "type": "default"
+ },
+ "controls": {
+ "mouseWheelZoom": true,
+ "showAttribution": true,
+ "showDebug": false,
+ "showMeasure": true,
+ "showScale": true,
+ "showZoom": true
+ },
+ "layers": [
+ {
+ "config": {
+ "showLegend": true,
+ "style": {
+ "color": {
+ "fixed": "dark-red"
+ },
+ "opacity": 1,
+ "rotation": {
+ "field": "Value",
+ "fixed": 3,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "fixed": 5,
+ "max": 15,
+ "min": 2
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "filterData": {
+ "id": "byRefId",
+ "options": "A"
+ },
+ "location": {
+ "latitude": "lat",
+ "longitude": "lon",
+ "mode": "coords"
+ },
+ "name": "Layer 1",
+ "tooltip": true,
+ "type": "markers"
+ },
+ {
+ "config": {
+ "arrow": 0,
+ "style": {
+ "color": {
+ "fixed": "dark-purple"
+ },
+ "lineWidth": 2,
+ "opacity": 0.4,
+ "rotation": {
+ "fixed": 0,
+ "max": 360,
+ "min": -360,
+ "mode": "mod"
+ },
+ "size": {
+ "fixed": 5,
+ "max": 15,
+ "min": 2
+ },
+ "symbol": {
+ "fixed": "img/icons/marker/circle.svg",
+ "mode": "fixed"
+ },
+ "symbolAlign": {
+ "horizontal": "center",
+ "vertical": "center"
+ },
+ "textConfig": {
+ "fontSize": 12,
+ "offsetX": 0,
+ "offsetY": 0,
+ "textAlign": "center",
+ "textBaseline": "middle"
+ }
+ }
+ },
+ "location": {
+ "latitude": "lat",
+ "longitude": "lon",
+ "mode": "coords"
+ },
+ "name": "GarisKoneksi",
+ "opacity": 0.4,
+ "tooltip": true,
+ "type": "route"
+ }
+ ],
+ "tooltip": {
+ "mode": "details"
+ },
+ "view": {
+ "allLayers": true,
+ "id": "coords",
+ "lat": 19.5,
+ "lon": 80,
+ "noRepeat": false,
+ "shared": false,
+ "zoom": 4
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-4": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_protection_enabled",
+ "instant": false,
+ "legendFormat": "Protection Status",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "B"
+ }
+ },
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_running",
+ "instant": false,
+ "legendFormat": "Service Status",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "C"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 4,
+ "links": [],
+ "title": "π‘Protection & Service",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "mappings": [
+ {
+ "options": {
+ "0": {
+ "color": "semi-dark-red",
+ "index": 1,
+ "text": "Disabled"
+ },
+ "1": {
+ "color": "semi-dark-green",
+ "index": 0,
+ "text": "Enabled"
+ }
+ },
+ "type": "value"
+ }
+ ],
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "background",
+ "graphMode": "none",
+ "justifyMode": "center",
+ "orientation": "vertical",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-5": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "adguard_avg_processing_time * 1000",
+ "legendFormat": "__auto",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 5,
+ "links": [],
+ "title": "Avg Processing Time",
+ "vizConfig": {
+ "group": "stat",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "thresholds"
+ },
+ "decimals": 2,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "ms"
+ },
+ "overrides": []
+ },
+ "options": {
+ "colorMode": "value",
+ "graphMode": "none",
+ "justifyMode": "auto",
+ "orientation": "horizontal",
+ "percentChangeColorMode": "standard",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showPercentChange": false,
+ "text": {},
+ "textMode": "auto",
+ "wideLayout": true
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-6": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "exemplar": false,
+ "expr": "sum(increase(adguard_query_type_total[5m])) by (type)",
+ "format": "time_series",
+ "instant": false,
+ "legendFormat": "{{type}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "This panel shows the **number of DNS queries per second**, grouped by query type (A, AAAA, PTR, etc.).\n\n- Based on the metric: `adguard_query_type_total`\n- Uses the Prometheus `rate()` function with a 5-minute range:",
+ "id": 6,
+ "links": [],
+ "title": "π DNS Query Types",
+ "vizConfig": {
+ "group": "timeseries",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "palette-classic"
+ },
+ "custom": {
+ "axisBorderShow": false,
+ "axisCenteredZero": false,
+ "axisColorMode": "text",
+ "axisGridShow": true,
+ "axisLabel": "",
+ "axisPlacement": "auto",
+ "barAlignment": 0,
+ "barWidthFactor": 0.6,
+ "drawStyle": "line",
+ "fillOpacity": 2,
+ "gradientMode": "hue",
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "insertNulls": false,
+ "lineInterpolation": "linear",
+ "lineWidth": 4,
+ "pointSize": 5,
+ "scaleDistribution": {
+ "type": "linear"
+ },
+ "showPoints": "never",
+ "showValues": false,
+ "spanNulls": false,
+ "stacking": {
+ "group": "A",
+ "mode": "none"
+ },
+ "thresholdsStyle": {
+ "mode": "off"
+ }
+ },
+ "max": 500,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "queries"
+ },
+ "overrides": []
+ },
+ "options": {
+ "legend": {
+ "calcs": [
+ "min",
+ "max",
+ "mean"
+ ],
+ "displayMode": "table",
+ "placement": "bottom",
+ "showLegend": true
+ },
+ "tooltip": {
+ "hideZeros": false,
+ "mode": "multi",
+ "sort": "none"
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-7": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "sum(adguard_query_reason_total) by (reason)\r\n",
+ "legendFormat": "{{reason}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 7,
+ "links": [],
+ "title": "π« Block Reasons",
+ "vizConfig": {
+ "group": "bargauge",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "color": {
+ "mode": "continuous-GrYlRd"
+ },
+ "fieldMinMax": false,
+ "max": 470000,
+ "min": 0,
+ "thresholds": {
+ "mode": "absolute",
+ "steps": [
+ {
+ "color": "green",
+ "value": 0
+ },
+ {
+ "color": "red",
+ "value": 80
+ }
+ ]
+ },
+ "unit": "queries"
+ },
+ "overrides": []
+ },
+ "options": {
+ "displayMode": "lcd",
+ "legend": {
+ "calcs": [],
+ "displayMode": "list",
+ "placement": "bottom",
+ "showLegend": false
+ },
+ "maxVizHeight": 203,
+ "minVizHeight": 0,
+ "minVizWidth": 8,
+ "namePlacement": "top",
+ "orientation": "horizontal",
+ "reduceOptions": {
+ "calcs": [
+ "lastNotNull"
+ ],
+ "fields": "",
+ "values": false
+ },
+ "showUnfilled": true,
+ "sizing": "auto",
+ "valueMode": "color"
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ },
+ "panel-8": {
+ "kind": "Panel",
+ "spec": {
+ "data": {
+ "kind": "QueryGroup",
+ "spec": {
+ "queries": [
+ {
+ "kind": "PanelQuery",
+ "spec": {
+ "hidden": false,
+ "query": {
+ "group": "prometheus",
+ "kind": "DataQuery",
+ "spec": {
+ "editorMode": "code",
+ "expr": "topk(10, adguard_query_domain_total)",
+ "legendFormat": "{{domain}}",
+ "range": true
+ },
+ "version": "v0"
+ },
+ "refId": "A"
+ }
+ }
+ ],
+ "queryOptions": {},
+ "transformations": []
+ }
+ },
+ "description": "",
+ "id": 8,
+ "links": [],
+ "title": "π Top Queried Domains",
+ "vizConfig": {
+ "group": "heatmap",
+ "kind": "VizConfig",
+ "spec": {
+ "fieldConfig": {
+ "defaults": {
+ "custom": {
+ "hideFrom": {
+ "legend": false,
+ "tooltip": false,
+ "viz": false
+ },
+ "scaleDistribution": {
+ "type": "linear"
+ }
+ }
+ },
+ "overrides": []
+ },
+ "options": {
+ "calculate": false,
+ "cellGap": 1,
+ "color": {
+ "exponent": 0.5,
+ "fill": "dark-orange",
+ "mode": "scheme",
+ "reverse": false,
+ "scale": "exponential",
+ "scheme": "BrBG",
+ "steps": 64
+ },
+ "exemplars": {
+ "color": "rgba(255,0,255,0.7)"
+ },
+ "filterValues": {
+ "le": 1e-9
+ },
+ "legend": {
+ "show": true
+ },
+ "rowsFrame": {
+ "layout": "auto"
+ },
+ "tooltip": {
+ "mode": "single",
+ "showColorScale": false,
+ "yHistogram": false
+ },
+ "yAxis": {
+ "axisPlacement": "right",
+ "reverse": false
+ }
+ }
+ },
+ "version": "12.4.0"
+ }
+ }
+ }
+ },
+ "layout": {
+ "kind": "RowsLayout",
+ "spec": {
+ "rows": [
+ {
+ "kind": "RowsLayoutRow",
+ "spec": {
+ "collapse": false,
+ "layout": {
+ "kind": "GridLayout",
+ "spec": {
+ "items": [
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-4"
+ },
+ "height": 4,
+ "width": 4,
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-1"
+ },
+ "height": 4,
+ "width": 2,
+ "x": 4,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-13"
+ },
+ "height": 4,
+ "width": 3,
+ "x": 6,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-5"
+ },
+ "height": 4,
+ "width": 3,
+ "x": 9,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-31"
+ },
+ "height": 4,
+ "width": 12,
+ "x": 12,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-20"
+ },
+ "height": 6,
+ "width": 12,
+ "x": 0,
+ "y": 4
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-7"
+ },
+ "height": 6,
+ "width": 12,
+ "x": 12,
+ "y": 4
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-6"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 0,
+ "y": 10
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-33"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 12,
+ "y": 10
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-11"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 0,
+ "y": 21
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-3"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 12,
+ "y": 21
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-10"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 0,
+ "y": 32
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-8"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 12,
+ "y": 32
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-21"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 0,
+ "y": 43
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-14"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 12,
+ "y": 43
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-35"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 0,
+ "y": 54
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-34"
+ },
+ "height": 11,
+ "width": 12,
+ "x": 12,
+ "y": 54
+ }
+ }
+ ]
+ }
+ },
+ "title": "AdGuard Statistics"
+ }
+ },
+ {
+ "kind": "RowsLayoutRow",
+ "spec": {
+ "collapse": false,
+ "layout": {
+ "kind": "GridLayout",
+ "spec": {
+ "items": [
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-29"
+ },
+ "height": 10,
+ "width": 12,
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-26"
+ },
+ "height": 10,
+ "width": 12,
+ "x": 12,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-27"
+ },
+ "height": 10,
+ "width": 12,
+ "x": 0,
+ "y": 10
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-28"
+ },
+ "height": 10,
+ "width": 12,
+ "x": 12,
+ "y": 10
+ }
+ }
+ ]
+ }
+ },
+ "title": "Latency Data Monitoring for Upstream DNS"
+ }
+ },
+ {
+ "kind": "RowsLayoutRow",
+ "spec": {
+ "collapse": false,
+ "layout": {
+ "kind": "GridLayout",
+ "spec": {
+ "items": [
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-22"
+ },
+ "height": 4,
+ "width": 4,
+ "x": 0,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-32"
+ },
+ "height": 3,
+ "width": 20,
+ "x": 4,
+ "y": 0
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-24"
+ },
+ "height": 5,
+ "width": 20,
+ "x": 4,
+ "y": 3
+ }
+ },
+ {
+ "kind": "GridLayoutItem",
+ "spec": {
+ "element": {
+ "kind": "ElementReference",
+ "name": "panel-25"
+ },
+ "height": 4,
+ "width": 4,
+ "x": 0,
+ "y": 4
+ }
+ }
+ ]
+ }
+ },
+ "title": "Exporter Healths"
+ }
+ }
+ ]
+ }
+ },
+ "links": [],
+ "liveNow": true,
+ "preload": false,
+ "tags": [
+ "DNS",
+ "AdGuard",
+ "Server"
+ ],
+ "timeSettings": {
+ "autoRefresh": "5s",
+ "autoRefreshIntervals": [
+ "5s",
+ "10s",
+ "30s",
+ "1m",
+ "5m",
+ "15m",
+ "30m",
+ "1h",
+ "2h",
+ "1d"
+ ],
+ "fiscalYearStartMonth": 0,
+ "from": "now-1h",
+ "hideTimepicker": false,
+ "nowDelay": "5s",
+ "timezone": "browser",
+ "to": "now"
+ },
+ "title": "AdGuard Metrics Statistics",
+ "variables": []
+ },
+ "status": {}
+}
\ No newline at end of file
diff --git a/adguard-exporter/grafana/dashboard01.png b/adguard-exporter/grafana/dashboard01.png
new file mode 100644
index 0000000..4a6c632
Binary files /dev/null and b/adguard-exporter/grafana/dashboard01.png differ
diff --git a/adguard-exporter/grafana/dashboard02.png b/adguard-exporter/grafana/dashboard02.png
new file mode 100644
index 0000000..32ae91e
Binary files /dev/null and b/adguard-exporter/grafana/dashboard02.png differ
diff --git a/adguard-exporter/grafana/dashboard03.png b/adguard-exporter/grafana/dashboard03.png
new file mode 100644
index 0000000..cf41ed2
Binary files /dev/null and b/adguard-exporter/grafana/dashboard03.png differ
diff --git a/adguard-exporter/grafana/dashboard04.png b/adguard-exporter/grafana/dashboard04.png
new file mode 100644
index 0000000..815e0fa
Binary files /dev/null and b/adguard-exporter/grafana/dashboard04.png differ
diff --git a/adguard-exporter/grafana/dashboard06.png b/adguard-exporter/grafana/dashboard06.png
new file mode 100644
index 0000000..7d23884
Binary files /dev/null and b/adguard-exporter/grafana/dashboard06.png differ
diff --git a/adguard-exporter/grafana/dashboard1.png b/adguard-exporter/grafana/dashboard1.png
new file mode 100644
index 0000000..65868b0
Binary files /dev/null and b/adguard-exporter/grafana/dashboard1.png differ
diff --git a/adguard-exporter/grafana/dashboard2.png b/adguard-exporter/grafana/dashboard2.png
new file mode 100644
index 0000000..d879696
Binary files /dev/null and b/adguard-exporter/grafana/dashboard2.png differ
diff --git a/adguard-exporter/grafana/dashboard3.png b/adguard-exporter/grafana/dashboard3.png
new file mode 100644
index 0000000..7a4fee1
Binary files /dev/null and b/adguard-exporter/grafana/dashboard3.png differ
diff --git a/adguard-exporter/grafana/dashboard4.png b/adguard-exporter/grafana/dashboard4.png
new file mode 100644
index 0000000..5b5f647
Binary files /dev/null and b/adguard-exporter/grafana/dashboard4.png differ
diff --git a/adguard-exporter/main.go b/adguard-exporter/main.go
new file mode 100644
index 0000000..3659adc
--- /dev/null
+++ b/adguard-exporter/main.go
@@ -0,0 +1,860 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io"
+ "log"
+ "net"
+ "net/http"
+ "os"
+ "strconv"
+ "sync"
+ "time"
+
+ "github.com/joho/godotenv"
+ "github.com/oschwald/geoip2-golang"
+ "github.com/prometheus/client_golang/prometheus"
+ "github.com/prometheus/client_golang/prometheus/promhttp"
+)
+
+/*
+ π¦ AdGuard Exporter for Prometheus
+ ----------------------------------
+ Author : @znand-dev
+ License : MIT
+ Repo : https://github.com/znand-dev/adguardexporter
+
+ This Go application fetches stats from AdGuard Home via API endpoints
+ and exposes them as Prometheus metrics at `/metrics`.
+
+ Required ENV variables:
+ - ADGUARD_HOST : AdGuard Home base URL (e.g. http://192.168.1.1:3000)
+ - ADGUARD_USER : API username (your adguard user)
+ - ADGUARD_PASS : API password (your adguard pass)
+ - EXPORTER_PORT : Port to expose metrics (default: 9617)
+ - SCRAPE_INTERVAL : Interval (in seconds) to fetch new stats (default: 15)
+ - LOG_LEVEL : Logging level (options: DEBUG, INFO, WARN, ERROR β default: INFO)
+ - GEOIP_DB : GeoLite2-City.mmdb
+*/
+
+var logLevelMap = map[string]int{"ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4}
+var currentLogLevel = 3
+
+func initLogger() {
+ level := os.Getenv("LOG_LEVEL")
+ if level == "" {
+ level = "INFO"
+ }
+ if val, ok := logLevelMap[level]; ok {
+ currentLogLevel = val
+ }
+}
+
+func logX(level string, format string, args ...interface{}) {
+ if logLevelMap[level] <= currentLogLevel {
+ log.Printf("[%s] %s", level, fmt.Sprintf(format, args...))
+ }
+}
+
+//function hash query
+func buildQueryKey(client, domain, upstream, reason, elapsed string) string {
+ return client + "|" + domain + "|" + upstream + "|" + reason + "|" + elapsed
+}
+
+type GeoCache struct {
+ Country string
+ Lat float64
+ Lon float64
+ TS int64
+}
+
+var (
+ geoDB *geoip2.Reader
+ geoCache = map[string]GeoCache{}
+ geoMutex sync.RWMutex
+ // cache query hash
+ querySeen = map[string]int64{}
+ queryMutex sync.Mutex
+ queryTTL = int64(300) // 5 menit
+ geoTTL = int64(86400) // 24 jam
+ dedupHits int64
+)
+
+type AdGuardStats struct {
+ NumDNSQueries float64 `json:"num_dns_queries"`
+ NumBlockedFiltering float64 `json:"num_blocked_filtering"`
+ NumReplacedParental float64 `json:"num_replaced_parental"`
+ AvgProcessingTime float64 `json:"avg_processing_time"`
+ TopQueriedDomains []map[string]float64 `json:"top_queried_domains"`
+ TopBlockedDomains []map[string]float64 `json:"top_blocked_domains"`
+ TopClients []map[string]float64 `json:"top_clients"`
+ TopUpstream []map[string]float64 `json:"top_upstreams_responses"`
+ TopUpstreamTime []map[string]float64 `json:"top_upstreams_avg_time"`
+}
+
+type AdGuardStatus struct {
+ Version string `json:"version"`
+ Language string `json:"language"`
+ DNSAddresses []string `json:"dns_addresses"`
+ DNSPort int `json:"dns_port"`
+ HTTPPort int `json:"http_port"`
+ ProtectionDisabledDuration int `json:"protection_disabled_duration"`
+ ProtectionEnabled bool `json:"protection_enabled"`
+ DHCPAvailable bool `json:"dhcp_available"`
+ Running bool `json:"running"`
+}
+
+type AdGuardQueryLog struct {
+ Data []struct {
+ Question struct {
+ Type string `json:"type"`
+ Name string `json:"name"`
+ } `json:"question"`
+ Answer []interface{} `json:"answer"`
+ Reason string `json:"reason"`
+ Client string `json:"client"`
+ Elapsed string `json:"elapsedMs"`
+ Upstream string `json:"upstream"`
+
+ ClientInfo struct {
+ Whois struct {
+ Country string `json:"country"`
+ OrgName string `json:"orgname"`
+ } `json:"whois"`
+ } `json:"client_info"`
+
+ } `json:"data"`
+}
+
+var (
+
+ dnsQueries = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_dns_queries_total", Help: "Total DNS queries received",
+ })
+
+ blockedFiltering = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_blocked_filtering_total", Help: "Total DNS queries blocked",
+ })
+
+ replacedParental = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_replaced_parental", Help: "Total parental replaced queries",
+ })
+
+ avgProcessingTime = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_avg_processing_time", Help: "Avg DNS processing time",
+ })
+
+ statusProtectionEnabled = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_protection_enabled", Help: "Protection enabled (1/0)",
+ })
+
+ statusRunning = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_running", Help: "AdGuard running",
+ })
+
+ statusDHCPAvailable = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_dhcp_available", Help: "DHCP available",
+ })
+
+ statusDisabledDuration = prometheus.NewGauge(prometheus.GaugeOpts{
+ Name: "adguard_protection_disabled_duration_seconds",
+ Help: "Time since protection disabled",
+ })
+
+ versionInfo = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_version_info",
+ Help: "AdGuard version",
+ },
+ []string{"version"},
+ )
+
+ topQueriedDomains = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_top_queried_domain_total",
+ Help: "Top queried domains",
+ },
+ []string{"domain"},
+ )
+
+ topBlockedDomains = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_top_blocked_domain_total",
+ Help: "Top blocked domains",
+ },
+ []string{"domain"},
+ )
+
+ topClients = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_top_client_total",
+ Help: "Top client IPs",
+ },
+ []string{"client"},
+ )
+
+ topUpstreams = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_top_upstream_total",
+ Help: "Top upstream servers",
+ },
+ []string{"upstream"},
+ )
+
+ topUpstreamTime = prometheus.NewGaugeVec(
+ prometheus.GaugeOpts{
+ Name: "adguard_upstream_avg_response_time_seconds",
+ Help: "Avg response time per upstream",
+ },
+ []string{"upstream"},
+ )
+
+ queryCountByReason = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_reason_total",
+ Help: "Queries by reason",
+ },
+ []string{"reason"},
+ )
+
+ queryCountByType = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_type_total",
+ Help: "Queries by DNS type",
+ },
+ []string{"type"},
+ )
+
+ queryHistogramByClient = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "adguard_query_elapsed_ms",
+ Help: "Query duration by client",
+ Buckets: prometheus.LinearBuckets(1, 5, 10),
+ },
+ []string{"client"},
+ )
+
+ queryCountByUpstream = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_upstream_total",
+ Help: "Queries per upstream",
+ },
+ []string{"upstream"},
+ )
+
+ queryCountByDomain = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_domain_total",
+ Help: "Queries per domain",
+ },
+ []string{"domain"},
+ )
+
+ queryCountClientReason = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_client_reason_total",
+ Help: "Queries per client per reason",
+ },
+ []string{"client", "reason"},
+ )
+
+ clientGeoQueries = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_client_geo_queries",
+ Help: "Queries per client with geographic info",
+ },
+ []string{"client", "country", "lat", "lon"},
+ )
+
+ blockedGeoQueries = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_blocked_geo_queries",
+ Help: "Blocked DNS queries per client with geographic info",
+ },
+ []string{"client", "country", "lat", "lon"},
+ )
+
+ upstreamLatencyHistogram = prometheus.NewHistogramVec(
+ prometheus.HistogramOpts{
+ Name: "adguard_upstream_latency_seconds",
+ Help: "Latency distribution per upstream DNS server",
+ Buckets: prometheus.ExponentialBuckets(
+ 0.001, // 1ms
+ 2,
+ 10,
+ ),
+ },
+ []string{"upstream"},
+ )
+
+ exporterUp = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "adguard_exporter_up",
+ Help: "Exporter scrape success",
+ },
+ )
+
+ exporterScrapeDuration = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "adguard_exporter_scrape_duration_seconds",
+ Help: "Exporter scrape duration",
+ },
+ )
+
+ exporterErrors = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Name: "adguard_exporter_scrape_errors_total",
+ Help: "Total exporter errors",
+ },
+ )
+
+ exporterQueryCacheSize = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "adguard_exporter_query_cache_size",
+ Help: "Current number of entries in query deduplication cache",
+ },
+ )
+
+ exporterGeoCacheSize = prometheus.NewGauge(
+ prometheus.GaugeOpts{
+ Name: "adguard_exporter_geo_cache_size",
+ Help: "Current number of cached GeoIP entries",
+ },
+ )
+
+ exporterDedupHits = prometheus.NewCounter(
+ prometheus.CounterOpts{
+ Name: "adguard_exporter_dedup_hits_total",
+ Help: "Total number of duplicate queries skipped by deduplication",
+ },
+ )
+
+ queryCountByISP = prometheus.NewCounterVec(
+ prometheus.CounterOpts{
+ Name: "adguard_query_isp_total",
+ Help: "DNS queries grouped by ISP organization",
+ },
+ []string{"isp", "country"},
+ )
+)
+
+func init() {
+ _ = godotenv.Load()
+ initLogger()
+
+ prometheus.MustRegister(
+ dnsQueries,
+ blockedFiltering,
+ replacedParental,
+ avgProcessingTime,
+ statusProtectionEnabled,
+ statusRunning,
+ statusDHCPAvailable,
+ statusDisabledDuration,
+ versionInfo,
+ topQueriedDomains,
+ topBlockedDomains,
+ topClients,
+ topUpstreams,
+ topUpstreamTime,
+ queryCountByReason,
+ queryCountByType,
+ queryHistogramByClient,
+ queryCountByUpstream,
+ queryCountByDomain,
+ queryCountClientReason,
+ clientGeoQueries,
+ blockedGeoQueries,
+ exporterUp,
+ exporterScrapeDuration,
+ exporterErrors,
+ upstreamLatencyHistogram,
+ exporterQueryCacheSize,
+ exporterGeoCacheSize,
+ exporterDedupHits,
+ queryCountByISP,
+ )
+}
+
+func resolveGeo(ipStr string) (GeoCache, bool) {
+ if geoDB == nil {
+ return GeoCache{}, false
+ }
+
+ ip := net.ParseIP(ipStr)
+ if ip == nil || ip.IsPrivate() || ip.IsLoopback() {
+ return GeoCache{}, false
+ }
+
+ now := time.Now().Unix()
+
+ geoMutex.RLock()
+ if val, ok := geoCache[ipStr]; ok {
+
+ // cache valid
+ if now-val.TS < geoTTL {
+ geoMutex.RUnlock()
+ return val, true
+ }
+
+ }
+ geoMutex.RUnlock()
+
+ record, err := geoDB.City(ip)
+ if err != nil {
+ return GeoCache{}, false
+ }
+
+ cache := GeoCache{
+ Country: record.Country.IsoCode,
+ Lat: record.Location.Latitude,
+ Lon: record.Location.Longitude,
+ TS: now,
+ }
+
+ geoMutex.Lock()
+ geoCache[ipStr] = cache
+ geoMutex.Unlock()
+
+ return cache, true
+}
+
+func fetchQueryLog() (*AdGuardQueryLog, error) {
+
+ host := os.Getenv("ADGUARD_HOST")
+ user := os.Getenv("ADGUARD_USER")
+ pass := os.Getenv("ADGUARD_PASS")
+
+ url := host + "/control/querylog"
+
+ req, _ := http.NewRequest("GET", url, nil)
+ req.SetBasicAuth(user, pass)
+
+ client := &http.Client{Timeout: 10 * time.Second}
+
+ resp, err := client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logX("WARN", "failed to close response body: %v", err)
+ }
+ }()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var logData AdGuardQueryLog
+
+ err = json.Unmarshal(body, &logData)
+
+ return &logData, err
+}
+
+func fetchStats() (*AdGuardStats, error) {
+
+ host := os.Getenv("ADGUARD_HOST")
+ user := os.Getenv("ADGUARD_USER")
+ pass := os.Getenv("ADGUARD_PASS")
+
+ url := host + "/control/stats"
+
+ req, _ := http.NewRequest("GET", url, nil)
+ req.SetBasicAuth(user, pass)
+
+ client := &http.Client{Timeout: 10 * time.Second}
+
+ resp, err := client.Do(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logX("WARN", "failed to close response body: %v", err)
+ }
+ }()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var stats AdGuardStats
+
+ err = json.Unmarshal(body, &stats)
+
+ return &stats, err
+}
+
+func fetchStatus() (*AdGuardStatus, error) {
+
+ host := os.Getenv("ADGUARD_HOST")
+ user := os.Getenv("ADGUARD_USER")
+ pass := os.Getenv("ADGUARD_PASS")
+
+ url := host + "/control/status"
+
+ req, _ := http.NewRequest("GET", url, nil)
+ req.SetBasicAuth(user, pass)
+
+ client := &http.Client{Timeout: 10 * time.Second}
+
+ resp, err := client.Do(req)
+
+ if err != nil {
+ return nil, err
+ }
+
+ defer func() {
+ if err := resp.Body.Close(); err != nil {
+ logX("WARN", "failed to close response body: %v", err)
+ }
+ }()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ var status AdGuardStatus
+
+ err = json.Unmarshal(body, &status)
+
+ return &status, err
+}
+
+func updateStatsMetrics() {
+
+ stats, err := fetchStats()
+
+ if err != nil {
+ logX("ERROR", "Failed fetch stats: %v", err)
+ return
+ }
+
+ dnsQueries.Set(stats.NumDNSQueries)
+ blockedFiltering.Set(stats.NumBlockedFiltering)
+ replacedParental.Set(stats.NumReplacedParental)
+ avgProcessingTime.Set(stats.AvgProcessingTime)
+
+ // reset gauge vectors
+ topQueriedDomains.Reset()
+ topBlockedDomains.Reset()
+ topClients.Reset()
+ topUpstreams.Reset()
+ topUpstreamTime.Reset()
+
+ // top queried domains
+ for _, d := range stats.TopQueriedDomains {
+ for domain, count := range d {
+ topQueriedDomains.WithLabelValues(domain).Set(count)
+ }
+ }
+
+ // top blocked domains
+ for _, d := range stats.TopBlockedDomains {
+ for domain, count := range d {
+ topBlockedDomains.WithLabelValues(domain).Set(count)
+ }
+ }
+
+ // top clients
+ for _, c := range stats.TopClients {
+ for client, count := range c {
+ topClients.WithLabelValues(client).Set(count)
+ }
+ }
+
+ // upstream responses
+ for _, u := range stats.TopUpstream {
+ for upstream, count := range u {
+ topUpstreams.WithLabelValues(upstream).Set(count)
+ }
+ }
+
+ // upstream avg response time
+ for _, u := range stats.TopUpstreamTime {
+ for upstream, time := range u {
+ topUpstreamTime.WithLabelValues(upstream).Set(time)
+ }
+ }
+
+ logX(
+ "DEBUG",
+ "Fetched stats: queries=%.0f blocked=%.0f replaced=%.0f avgTime=%.2fms topDomains=%d",
+ stats.NumDNSQueries,
+ stats.NumBlockedFiltering,
+ stats.NumReplacedParental,
+ stats.AvgProcessingTime,
+ len(stats.TopQueriedDomains),
+ )
+}
+
+func updateStatusMetrics() {
+
+ status, err := fetchStatus()
+
+ if err != nil {
+ logX("ERROR", "Failed fetch status: %v", err)
+ return
+ }
+
+ if status.Running {
+ statusRunning.Set(1)
+ } else {
+ statusRunning.Set(0)
+ }
+
+ if status.ProtectionEnabled {
+ statusProtectionEnabled.Set(1)
+ } else {
+ statusProtectionEnabled.Set(0)
+ }
+
+ if status.DHCPAvailable {
+ statusDHCPAvailable.Set(1)
+ } else {
+ statusDHCPAvailable.Set(0)
+ }
+
+ statusDisabledDuration.Set(float64(status.ProtectionDisabledDuration))
+
+ versionInfo.WithLabelValues(status.Version).Set(1)
+
+ logX(
+ "DEBUG",
+ "Fetched status: running=%v protection=%v DHCP=%v version=%s",
+ status.Running,
+ status.ProtectionEnabled,
+ status.DHCPAvailable,
+ status.Version,
+ )
+}
+
+func updateQueryLogMetrics() {
+
+ scanned := 0
+ processed := 0
+ skipped := 0
+ geoResolved := 0
+ privateClients := 0
+ publicClients := 0
+
+ logData, err := fetchQueryLog()
+
+ if err != nil {
+ logX("ERROR", "Failed querylog: %v", err)
+ return
+ }
+
+ for _, q := range logData.Data {
+
+ scanned++
+
+ ip := net.ParseIP(q.Client)
+
+ if ip != nil {
+ if ip.IsPrivate() || ip.IsLoopback() {
+ privateClients++
+ } else {
+ publicClients++
+ }
+
+ }
+
+ // dedup logic
+ key := buildQueryKey(
+ q.Client,
+ q.Question.Name,
+ q.Upstream,
+ q.Reason,
+ q.Elapsed,
+ )
+
+ now := time.Now().Unix()
+
+ queryMutex.Lock()
+
+ if ts, exists := querySeen[key]; exists {
+ if now-ts < queryTTL {
+ queryMutex.Unlock()
+ skipped++
+ exporterDedupHits.Inc()
+ continue
+ }
+ }
+
+ querySeen[key] = now
+ queryMutex.Unlock()
+
+ processed++
+
+ queryCountByReason.WithLabelValues(q.Reason).Inc()
+ queryCountByType.WithLabelValues(q.Question.Type).Inc()
+
+ elapsedMs, err := strconv.ParseFloat(q.Elapsed, 64)
+
+ if err == nil {
+
+ queryHistogramByClient.WithLabelValues(q.Client).Observe(elapsedMs)
+
+ if q.Upstream != "" {
+ upstreamLatencyHistogram.WithLabelValues(q.Upstream).Observe(elapsedMs / 1000)
+ }
+ }
+
+ queryCountByUpstream.WithLabelValues(q.Upstream).Inc()
+ queryCountByDomain.WithLabelValues(q.Question.Name).Inc()
+ queryCountClientReason.WithLabelValues(q.Client, q.Reason).Inc()
+
+ geo, ok := resolveGeo(q.Client)
+
+ if ok {
+
+ geoResolved++
+
+ clientGeoQueries.WithLabelValues(
+ q.Client,
+ geo.Country,
+ fmt.Sprintf("%f", geo.Lat),
+ fmt.Sprintf("%f", geo.Lon),
+ ).Inc()
+
+ if q.Reason == "FilteredBlackList" ||
+ q.Reason == "FilteredSafeBrowsing" ||
+ q.Reason == "FilteredParental" {
+
+ blockedGeoQueries.WithLabelValues(
+ q.Client,
+ geo.Country,
+ fmt.Sprintf("%f", geo.Lat),
+ fmt.Sprintf("%f", geo.Lon),
+ ).Inc()
+ }
+ }
+
+ isp := q.ClientInfo.Whois.OrgName
+ country := q.ClientInfo.Whois.Country
+
+ if isp != "" {
+ queryCountByISP.WithLabelValues(isp, country).Inc()
+
+ }
+
+ }
+
+ logX("DEBUG",
+ "Querylog: scanned=%d new=%d skipped=%d geoip=%d dedupHits=%d",
+ scanned,
+ processed,
+ skipped,
+ geoResolved,
+ dedupHits,
+ )
+}
+
+//cleanup memory
+func cleanupQueryCache() {
+
+ now := time.Now().Unix()
+
+ queryMutex.Lock()
+
+ for k, v := range querySeen {
+
+ if now-v > queryTTL {
+ delete(querySeen, k)
+ }
+
+ }
+
+ queryMutex.Unlock()
+}
+
+func main() {
+
+ dbPath := os.Getenv("GEOIP_DB")
+
+ if dbPath == "" {
+ dbPath = "GeoLite2-City.mmdb"
+ }
+
+ db, err := geoip2.Open(dbPath)
+
+ if err != nil {
+ logX("WARN", "GeoIP DB not found (%v) β geo metrics disabled", err)
+ } else {
+ geoDB = db
+ logX("INFO", "GeoIP database loaded")
+ }
+
+ scrapeIntervalStr := os.Getenv("SCRAPE_INTERVAL")
+ port := os.Getenv("EXPORTER_PORT")
+
+ if port == "" {
+ port = "9617"
+ }
+
+ interval, err := strconv.Atoi(scrapeIntervalStr)
+
+ if err != nil || interval < 1 {
+ interval = 15
+ }
+
+ logX("INFO", "Scrape interval set to %ds", interval)
+
+ go func() {
+
+ for {
+
+ start := time.Now()
+
+ updateStatsMetrics()
+ updateStatusMetrics()
+ updateQueryLogMetrics()
+
+ cleanupQueryCache()
+
+ duration := time.Since(start).Seconds()
+
+ exporterScrapeDuration.Set(duration)
+ exporterUp.Set(1)
+
+ exporterQueryCacheSize.Set(float64(len(querySeen)))
+ exporterGeoCacheSize.Set(float64(len(geoCache)))
+
+ logX(
+ "DEBUG",
+ "Exporter caches: query_cache_entries=%d geo_cache_entries=%d dedup_hits_total=%d",
+ len(querySeen),
+ len(geoCache),
+ dedupHits,
+ )
+
+ logX("DEBUG", "Scrape finished in %.3fs", duration)
+
+ time.Sleep(time.Duration(interval) * time.Second)
+
+ }
+
+ }()
+
+ http.Handle("/metrics", promhttp.Handler())
+
+ logX("INFO", "Starting exporter at :%s ..", port)
+
+ err = http.ListenAndServe(":"+port, nil)
+
+ if err != nil {
+ logX("ERROR", "Server failed: %v", err)
+ os.Exit(1)
+ }
+
+}