Compare commits

...

11 Commits

Author SHA1 Message Date
bf0d3369f6 docs: fix release url
Some checks failed
test / test (push) Has been cancelled
2024-09-24 12:46:13 +02:00
8112cccc04 ci: archlist from taskfile options
Some checks failed
test / test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-24 12:34:29 +02:00
2b23870670 ci: adds missing binfmt setup to test
Some checks failed
test / test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-24 10:35:45 +02:00
95cf80c71d build: multi arch docker images
Some checks are pending
test / test (push) Waiting to run
2024-09-24 10:33:40 +02:00
0ef3d0e890 chore: flake update 2024-09-24 10:29:59 +02:00
de9e78ee26 ci: setup qemu and binfmt 2024-09-24 10:29:26 +02:00
cccad390d6 docs: adds more screenshots
Some checks are pending
test / test (push) Waiting to run
2024-09-23 14:17:19 +02:00
7c5c1d6406 fix: only write metrics if there is a useragent
Some checks failed
test / test (push) Waiting to run
release / release (push) Has been cancelled
2024-09-23 12:48:35 +02:00
757d47fc41 build: no test caching 2024-09-23 12:48:11 +02:00
192391f52c docs: adds screenshot
Some checks are pending
test / test (push) Waiting to run
2024-09-23 12:26:59 +02:00
b022ebdc33 docs: readme
Some checks are pending
test / test (push) Waiting to run
2024-09-23 12:23:32 +02:00
8 changed files with 150 additions and 35 deletions

View File

@ -10,4 +10,5 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v27
- run: nix develop . -c task ci-publish GITHUB_USERNAME=${{ github.actor }} GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }} GIT_TAG=${{ github.ref_name }}
- uses: docker/setup-qemu-action@v3
- run: nix develop . -c task ci-publish GITHUB_USERNAME=${{ github.actor }} GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }} GIT_TAG=${{ github.ref_name }} ARCH_LIST=x86_64-linux,aarch64-linux

2
.gitignore vendored
View File

@ -1,4 +1,4 @@
.direnv
result
result-*
dist/

76
README.md Normal file
View File

@ -0,0 +1,76 @@
# caddy-log-exporter 📈🙈🙉🙊
Uses the [caddy](https://caddyserver.com/) [json log](https://caddyserver.com/docs/caddyfile/directives/log) and exports metrics from parsing them for [prometheus](https://prometheus.io/) or [victoriametrics](https://victoriametrics.com/).
## Why
I use the wonderful caddy server just everywhere. There are two things that i discovered. For once there is still the thing that the caddy metrics doesnt add the host as label. There are some github issues about it and im sure that this feature will be added at one point. until now its a problem if you have multiple domains behind a caddy instance.
The other point is: i run a little [gitea](https://about.gitea.com/) instance for some little projects on a small vserver from hetzner. some weeks ago i got prometheus alerts for cpu and memory exhaustion. i checked logs and everything and found out that i got a victim of this stupid fucked up ai craweler shit. so i start to check my caddy logs if i discover high load on my server and add the user agents to my `Caddyfile`. i thought it would be nice to find a top list of scraper bots that gets by my caddy server. so this exporter also adds the user agents as prometheus labels.
Here metrics after running the exporter for a few minutes:
![vm](./vm.png)
Here in grafana:
![grafana](./grafana.png)
Happily, all the stuff we need is available through the standard json logs 🥳🎉, we tail them and create metrics out of them.
## Installation
Grab the latest docker image from [here](https://github.com/xsteadfastx?tab=packages&repo_name=caddy-log-exporter).
Images are available for `x86_64-linux` and `aarch64-linux`.
## Usage
`docker run -v /var/log/caddy:/var/log/caddy -e CADDY_LOG_EXPORTER_LOG_FILES=/var/log/caddy/caddy.log ghcr.io/xsteadfastx/caddy-log-exporter:0.1.0-rc6`
### Caddyfile
Enabling the json log to file.
log {
format json
output file /var/log/caddy/caddy.log
}
### Scraping
- job_name: caddy-log-exporter
scheme: http
static_configs:
- targets:
- "caddy-log-exporter.tld:2112"
### Bonus: Blocking AI bots in caddy
git.foo.tld {
@badbots {
header User-Agent *AhrefsBot*
header User-Agent *Amazonbot*
header User-Agent *Barkrowler*
header User-Agent *Bytespider*
header User-Agent *DataForSeoBot*
header User-Agent *ImagesiftBot*
header User-Agent *MJ12bot*
header User-Agent *PetalBot*
header User-Agent *SemrushBot*
header User-Agent *facebookexternalhit*
header User-Agent *meta-externalagent*
}
abort @badbots
cache
reverse_proxy gitea:3000
}
## Configuration
There are some config values we can set through environment variables.
- `CADDY_LOG_EXPORTER_LOG_FILES`: Comma seperated paths with log files
- `CADDY_LOG_EXPORTER_ADDR`: defaults to `:2112`

View File

@ -2,12 +2,14 @@ version: "3"
vars:
NIX_CMD_BASE: nix develop . -c
ARCH_LIST: x86_64-linux
tasks:
ci:
cmds:
- task: clean
- task: lint
- task: docker-build-all
- task: test-all
ci-publish:
@ -15,27 +17,54 @@ tasks:
- task: clean
- task: lint
- task: test-all
- task: docker-publish
- task: docker-publish-all
clean:
cmds:
- rm -rf result
- rm -rf result-*
docker-build:
requires:
vars:
- ARCH
status:
- test -f result
- test -f result-{{.ARCH}}
cmds:
- nix build .#docker
- nix build .#docker --option system {{.ARCH}}
- mv result result-{{.ARCH}}
docker-build-all:
cmds:
- for:
var: ARCH_LIST
split: ","
task: docker-build
vars:
ARCH: "{{.ITEM}}"
docker-publish-all:
cmds:
- for:
var: ARCH_LIST
split: ","
task: docker-publish
vars:
ARCH: "{{.ITEM}}"
docker-local-import:
requires:
vars:
- ARCH
cmds:
- task: docker-build
vars:
ARCH: "{{.ARCH}}"
- |
{{.NIX_CMD_BASE}} \
skopeo \
--insecure-policy \
copy \
docker-archive:$(readlink -f result) \
docker-archive:$(readlink -f result-{{.ARCH}}) \
docker-daemon:caddy-log-exporter:NOTUSE
- |
{{.NIX_CMD_BASE}} \
@ -46,19 +75,22 @@ tasks:
docker-publish:
requires:
vars:
- ARCH
- GITHUB_USERNAME
- GITHUB_PASSWORD
- GIT_TAG
cmds:
- task: docker-build
vars:
ARCH: "{{.ARCH}}"
- |
{{.NIX_CMD_BASE}} \
skopeo \
--insecure-policy \
copy \
--dest-username {{.GITHUB_USERNAME}} --dest-password {{.GITHUB_PASSWORD}} \
docker-archive:$(readlink -f result) \
docker://ghcr.io/xsteadfastx/caddy-log-exporter:{{.GIT_TAG}}
docker-archive:$(readlink -f result-{{.ARCH}}) \
docker://ghcr.io/xsteadfastx/caddy-log-exporter-{{.ARCH}}:{{.GIT_TAG}}
docker-inspect:
cmds:
@ -78,14 +110,16 @@ tasks:
cmds:
- |
{{.NIX_CMD_BASE}} \
go test -v -race ./internal/...
go test -v -race -count=1 ./internal/...
test-integration:
cmds:
- task: docker-local-import
vars:
ARCH: x86_64-linux
- |
{{.NIX_CMD_BASE}} \
go test -v -race ./test/integration/...
go test -v -race -count=1 ./test/integration/...
test-all:
cmds:

View File

@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1726755586,
"narHash": "sha256-PmUr/2GQGvFTIJ6/Tvsins7Q43KTMvMFhvG6oaYK+Wk=",
"lastModified": 1726937504,
"narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "c04d5652cfa9742b1d519688f65d1bbccea9eb7e",
"rev": "9357f4f23713673f310988025d9dc261c20e70c6",
"type": "github"
},
"original": {

BIN
grafana.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

View File

@ -39,27 +39,31 @@ func parseFunc(t *tail.Tail) {
continue
}
metrics.GetOrCreateCounter(
fmt.Sprintf(
`%s_http_request_total{host="%s", proto="%s", method="%s", status="%d", user_agent="%s"}`,
metricBaseName,
l.Request.Host,
l.Request.Proto,
l.Request.Method,
l.Status,
l.Request.Headers.UserAgent[0],
),
).Inc()
if len(l.Request.Headers.UserAgent) != 0 {
metrics.GetOrCreateCounter(
fmt.Sprintf(
`%s_http_request_total{host="%s", proto="%s", method="%s", status="%d", user_agent="%s"}`,
metricBaseName,
l.Request.Host,
l.Request.Proto,
l.Request.Method,
l.Status,
l.Request.Headers.UserAgent[0],
),
).Inc()
metrics.GetOrCreateHistogram(
fmt.Sprintf(
`%s_http_request_duration_seconds{host="%s", proto="%s", method="%s", status="%d"}`,
metricBaseName,
l.Request.Host,
l.Request.Proto,
l.Request.Method,
l.Status,
),
).Update(l.Duration)
metrics.GetOrCreateHistogram(
fmt.Sprintf(
`%s_http_request_duration_seconds{host="%s", proto="%s", method="%s", status="%d"}`,
metricBaseName,
l.Request.Host,
l.Request.Proto,
l.Request.Method,
l.Status,
),
).Update(l.Duration)
} else {
slog.Warn("got empty user agent", "headers", l.Request.Headers)
}
}
}

BIN
vm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB