Running¶
The tool polls each configured device, prints new log entries to stdout and its own
messages to stderr. It keeps a state file per device in state_dir, so that a restart does
not print the whole log again; give it a place where that file survives.
Preparing the box¶
Create a user on the Fritz!Box for the tool (System → FRITZ!Box Users, German FRITZ!Box-Benutzer),
for example logextractor, and give it the right FRITZ!Box settings (FRITZ!Box
Einstellungen). Fritz!OS has no finer rights for this; it is the same right fritz_exporter
needs.
You may reuse the user of fritz_exporter. A separate one lets you change or revoke either tool's access on its own, and the box's log tells their logins apart.
Use a long random password of at most 32 characters: TR-064 logs in with MD5 digest authentication, and Fritz!OS accepts no longer passwords there.
Mesh
In a mesh, poll the master only: it shows the entries of its repeaters too. Polling a repeater as well gives its entries twice. See Output.
Container¶
Images for linux/amd64 and linux/arm64 are on Docker Hub as
pdreker/fritz_logextractor:
| Tag | Built from |
|---|---|
develop |
every push to main |
<version>, <major>.<minor>, <major>, latest |
each release, also for linux/arm/v6 and linux/arm/v7 |
The image runs as nobody (UID and GID 65534) and keeps its state in
/var/lib/fritzlogextractor, which is also the default state_dir.
Mount a volume for the state
The image declares /var/lib/fritzlogextractor as a volume. Without -v, Podman and
Docker create an anonymous volume, which is gone when the container is created anew, for
example on an update of the image. The tool then prints the whole log of the box once more.
Always mount a named volume or a host directory there.
Podman or Docker¶
With one device, the environment is enough:
podman run -d --name fritzlogextractor --restart unless-stopped \
-v fritzlogextractor-state:/var/lib/fritzlogextractor \
-e FRITZ_HOSTNAME=fritz.box \
-e FRITZ_USERNAME=logreader \
-e FRITZ_PASSWORD_FILE=/etc/fritz/password \
-v ./password:/etc/fritz/password:ro \
docker.io/pdreker/fritz_logextractor:latest
docker run takes the same options. Arguments after the image name go to the tool, for example
--once or --config /etc/fritz/config.yaml (the FRITZ_* variables are then ignored, see
Configuration):
podman run -d --name fritzlogextractor --restart unless-stopped \
-v fritzlogextractor-state:/var/lib/fritzlogextractor \
-v ./fritz:/etc/fritz:ro \
docker.io/pdreker/fritz_logextractor:latest --config /etc/fritz/config.yaml
Files mounted into the container must be readable by UID 65534. A named volume takes over the
owner of the directory in the image, so it is writable right away. A host directory mounted on
/var/lib/fritzlogextractor must be writable by 65534: with rootless Podman, add :U to the
mount (-v ./state:/var/lib/fritzlogextractor:U), elsewhere chown 65534:65534 ./state.
Compose¶
services:
fritzlogextractor:
image: docker.io/pdreker/fritz_logextractor:latest
restart: unless-stopped
command: ["--config", "/etc/fritz/config.yaml"]
volumes:
- ./config.yaml:/etc/fritz/config.yaml:ro
- state:/var/lib/fritzlogextractor
secrets:
- fritz_password
volumes:
state:
secrets:
fritz_password:
file: ./password
with password_file: /run/secrets/fritz_password in config.yaml.
Kubernetes¶
Run a single replica: two copies would each print every entry. Recreate makes sure the old pod
is gone before the new one starts, and fsGroup makes the volume writable for UID 65534.
apiVersion: v1
kind: Secret
metadata:
name: fritzlogextractor
stringData:
password: "..."
config.yaml: |
devices:
- hostname: fritz.box
username: logreader
password_file: /etc/fritz/password
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: fritzlogextractor-state
spec:
accessModes: [ReadWriteOnce]
resources:
requests:
storage: 10Mi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: fritzlogextractor
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: fritzlogextractor
template:
metadata:
labels:
app: fritzlogextractor
spec:
securityContext:
runAsUser: 65534
runAsGroup: 65534
fsGroup: 65534
containers:
- name: fritzlogextractor
image: docker.io/pdreker/fritz_logextractor:latest
args: ["--config", "/etc/fritz/config.yaml"]
volumeMounts:
- name: config
mountPath: /etc/fritz
readOnly: true
- name: state
mountPath: /var/lib/fritzlogextractor
volumes:
- name: config
secret:
secretName: fritzlogextractor
- name: state
persistentVolumeClaim:
claimName: fritzlogextractor-state
The state file takes about 150 bytes per row of the box's log: 85 KB for the 567 rows a FRITZ!Box 7690 held after two weeks.
Without a container¶
Install it from PyPI with
uv (or pipx install fritz-logextractor), or run it from a clone:
uv tool install fritz-logextractor
fritzlogextractor --config config.yaml
git clone https://github.com/pdreker/fritz_logextractor && cd fritz_logextractor
uv run fritzlogextractor --config config.yaml
The default state_dir, /var/lib/fritzlogextractor, is usually not writable for a normal
user. Set state_dir to a directory of your own; otherwise the tool warns, keeps its state in
memory only and prints the whole log again on every start.
Options¶
| Option | Meaning |
|---|---|
--config FILE |
Read the configuration from FILE instead of the environment |
--once |
Read each device once, print what is new and exit |
--log-level LEVEL |
Override the log level from the configuration |
--log-format FORMAT |
Format of the tool's own log lines on stderr: json (default) or text for reading in a terminal |
--capture DIR |
Also store the raw log of each poll, see Capturing snapshots |
--version |
Print the version and exit |
--once uses the state file as well, so a run from cron prints only what came since the last
run.
Keeping events and diagnostics apart¶
Events go to stdout, the tool's own log lines to stderr. Both are JSON lines with a kind field,
event or log, so they can be told apart even where the two streams end up mixed, as in a
container's log or a terminal. A log stack keeps the lines with "kind": "event" as the box's
log and the rest as the tool's own; see Output.
Ready-made configs for Grafana Alloy, Vector, Fluentd and Logstash are in Log collectors.
In a terminal, the classic format is easier to read, and redirecting keeps the events apart:
fritzlogextractor --config config.yaml --log-format text > events.jsonl # events to a file
fritzlogextractor --config config.yaml --log-format text 2> tool.log # own log to a file