Compare commits
No commits in common. "main" and "0.1.0-rc6" have entirely different histories.
3
.github/workflows/release.yml
vendored
3
.github/workflows/release.yml
vendored
@ -10,5 +10,4 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v27
|
||||
- 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
|
||||
- run: nix develop . -c task ci-publish GITHUB_USERNAME=${{ github.actor }} GITHUB_PASSWORD=${{ secrets.GITHUB_TOKEN }} GIT_TAG=${{ github.ref_name }}
|
||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
||||
.direnv
|
||||
result
|
||||
result-*
|
||||
|
||||
dist/
|
||||
|
76
README.md
76
README.md
@ -1,76 +0,0 @@
|
||||
# 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:
|
||||
|
||||

|
||||
|
||||
Here in grafana:
|
||||
|
||||

|
||||
|
||||
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`
|
52
Taskfile.yml
52
Taskfile.yml
@ -2,14 +2,12 @@ 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:
|
||||
@ -17,54 +15,27 @@ tasks:
|
||||
- task: clean
|
||||
- task: lint
|
||||
- task: test-all
|
||||
- task: docker-publish-all
|
||||
- task: docker-publish
|
||||
|
||||
clean:
|
||||
cmds:
|
||||
- rm -rf result-*
|
||||
- rm -rf result
|
||||
|
||||
docker-build:
|
||||
requires:
|
||||
vars:
|
||||
- ARCH
|
||||
status:
|
||||
- test -f result-{{.ARCH}}
|
||||
- test -f result
|
||||
cmds:
|
||||
- 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}}"
|
||||
- nix build .#docker
|
||||
|
||||
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-{{.ARCH}}) \
|
||||
docker-archive:$(readlink -f result) \
|
||||
docker-daemon:caddy-log-exporter:NOTUSE
|
||||
- |
|
||||
{{.NIX_CMD_BASE}} \
|
||||
@ -75,22 +46,19 @@ 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-{{.ARCH}}) \
|
||||
docker://ghcr.io/xsteadfastx/caddy-log-exporter-{{.ARCH}}:{{.GIT_TAG}}
|
||||
docker-archive:$(readlink -f result) \
|
||||
docker://ghcr.io/xsteadfastx/caddy-log-exporter:{{.GIT_TAG}}
|
||||
|
||||
docker-inspect:
|
||||
cmds:
|
||||
@ -110,16 +78,14 @@ tasks:
|
||||
cmds:
|
||||
- |
|
||||
{{.NIX_CMD_BASE}} \
|
||||
go test -v -race -count=1 ./internal/...
|
||||
go test -v -race ./internal/...
|
||||
|
||||
test-integration:
|
||||
cmds:
|
||||
- task: docker-local-import
|
||||
vars:
|
||||
ARCH: x86_64-linux
|
||||
- |
|
||||
{{.NIX_CMD_BASE}} \
|
||||
go test -v -race -count=1 ./test/integration/...
|
||||
go test -v -race ./test/integration/...
|
||||
|
||||
test-all:
|
||||
cmds:
|
||||
|
@ -20,11 +20,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1726937504,
|
||||
"narHash": "sha256-bvGoiQBvponpZh8ClUcmJ6QnsNKw0EMrCQJARK3bI1c=",
|
||||
"lastModified": 1726755586,
|
||||
"narHash": "sha256-PmUr/2GQGvFTIJ6/Tvsins7Q43KTMvMFhvG6oaYK+Wk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9357f4f23713673f310988025d9dc261c20e70c6",
|
||||
"rev": "c04d5652cfa9742b1d519688f65d1bbccea9eb7e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
BIN
grafana.png
BIN
grafana.png
Binary file not shown.
Before Width: | Height: | Size: 145 KiB |
@ -39,31 +39,27 @@ func parseFunc(t *tail.Tail) {
|
||||
continue
|
||||
}
|
||||
|
||||
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.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)
|
||||
} else {
|
||||
slog.Warn("got empty user agent", "headers", l.Request.Headers)
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user