Skip to content

Commit 15c5fc8

Browse files
committed
Use structed json logging
Signed-off-by: yubiuser <github@yubiuser.dev>
1 parent dfc1231 commit 15c5fc8

2 files changed

Lines changed: 89 additions & 67 deletions

File tree

src/bash_functions.sh

Lines changed: 70 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -28,65 +28,96 @@ setFTLConfigValue() {
2828
pihole-FTL --config "${1}" "${2}" >/dev/null
2929
}
3030

31+
__timestamp(){
32+
date "+%Y%m%dT%H%M%S"
33+
}
34+
35+
__log(){
36+
local level="$1"
37+
local service="$2"
38+
local msg="$3"
39+
40+
# Check if msg is valid JSON - if so, use --argjson to avoid escaping
41+
if echo "$msg" | jq empty 2>/dev/null; then
42+
echo '{}' | \
43+
jq --monochrome-output \
44+
--compact-output \
45+
--raw-output \
46+
--arg timestamp "$(__timestamp)" \
47+
--arg level "$level" \
48+
--arg service "$service" \
49+
--argjson msg "$msg" \
50+
'.timestamp=$timestamp|.log_level=$level|.service=$service|.message=$msg'
51+
else
52+
echo '{}' | \
53+
jq --monochrome-output \
54+
--compact-output \
55+
--raw-output \
56+
--arg timestamp "$(__timestamp)" \
57+
--arg level "$level" \
58+
--arg service "$service" \
59+
--arg msg "$msg" \
60+
'.timestamp=$timestamp|.log_level=$level|.service=$service|.message=$msg'
61+
fi
62+
}
63+
3164
set_uid_gid() {
3265

33-
echo " [i] Setting up user & group for the pihole user"
66+
__log "INFO" "pihole-docker" "Setting up user & group for the pihole user"
3467

3568
currentUid=$(id -u pihole)
3669

3770
# If PIHOLE_UID is set, modify the pihole group's id to match
3871
if [ -n "${PIHOLE_UID}" ]; then
3972
if [[ ${currentUid} -ne ${PIHOLE_UID} ]]; then
40-
echo " [i] Changing ID for user: pihole (${currentUid} => ${PIHOLE_UID})"
73+
__log "INFO" "pihole-docker" "Changing ID for user: pihole (${currentUid} => ${PIHOLE_UID})"
4174
usermod -o -u "${PIHOLE_UID}" pihole
4275
else
43-
echo " [i] ID for user pihole is already ${PIHOLE_UID}, no need to change"
76+
__log "INFO" "pihole-docker" "ID for user pihole is already ${PIHOLE_UID}, no need to change"
4477
fi
4578
else
46-
echo " [i] PIHOLE_UID not set in environment, using default (${currentUid})"
79+
__log "INFO" "pihole-docker" "PIHOLE_UID not set in environment, using default (${currentUid})"
4780
fi
4881

4982
currentGid=$(id -g pihole)
5083

5184
# If PIHOLE_GID is set, modify the pihole group's id to match
5285
if [ -n "${PIHOLE_GID}" ]; then
5386
if [[ ${currentGid} -ne ${PIHOLE_GID} ]]; then
54-
echo " [i] Changing ID for group: pihole (${currentGid} => ${PIHOLE_GID})"
87+
__log "INFO" "pihole-docker" "Changing ID for group: pihole (${currentGid} => ${PIHOLE_GID})"
5588
groupmod -o -g "${PIHOLE_GID}" pihole
5689
else
57-
echo " [i] ID for group pihole is already ${PIHOLE_GID}, no need to change"
90+
__log "INFO" "pihole-docker" "ID for group pihole is already ${PIHOLE_GID}, no need to change"
5891
fi
5992
else
60-
echo " [i] PIHOLE_GID not set in environment, using default (${currentGid})"
93+
__log "INFO" "pihole-docker" "PIHOLE_GID not set in environment, using default (${currentGid})"
6194
fi
62-
echo ""
6395
}
6496

6597
install_additional_packages() {
6698
if [ -n "${ADDITIONAL_PACKAGES}" ]; then
67-
echo " [i] Additional packages requested: ${ADDITIONAL_PACKAGES}"
68-
echo " [i] Fetching APK repository metadata."
99+
__log "INFO" "pihole-docker" "Additional packages requested: ${ADDITIONAL_PACKAGES}"
100+
__log "INFO" "pihole-docker" "Fetching APK repository metadata."
69101
if ! apk update; then
70-
echo " [i] Failed to fetch APK repository metadata."
102+
__log "ERROR" "pihole-docker" "Failed to fetch APK repository metadata."
71103
else
72-
echo " [i] Installing additional packages: ${ADDITIONAL_PACKAGES}."
104+
__log "INFO" "pihole-docker" "Installing additional packages: ${ADDITIONAL_PACKAGES}."
73105
# shellcheck disable=SC2086
74106
if ! apk add --no-cache ${ADDITIONAL_PACKAGES}; then
75-
echo " [i] Failed to install additional packages."
107+
__log "ERROR" "pihole-docker" "Failed to install additional packages."
76108
fi
77109
fi
78-
echo ""
79110
fi
80111
}
81112

82113
start_cron() {
83-
echo " [i] Starting crond for scheduled scripts. Randomizing times for gravity and update checker"
114+
__log "INFO" "pihole-docker" "Starting crond for scheduled scripts. Randomizing times for gravity and update checker"
84115
# Randomize gravity update time
85116
sed -i "s/59 1 /$((1 + RANDOM % 58)) $((3 + RANDOM % 2))/" /crontab.txt
86117
# Randomize update checker time
87118
sed -i "s/59 17/$((1 + RANDOM % 58)) $((12 + RANDOM % 8))/" /crontab.txt
88119
if ! /usr/bin/crontab /crontab.txt; then
89-
echo " [!] Failed to install crontab - scheduled tasks (gravity, update checker) will not run"
120+
__log "ERROR" "pihole-docker" "Failed to install crontab - scheduled tasks (gravity, update checker) will not run"
90121
fi
91122

92123
# Run crond in foreground, prefix each line from STDIN/STDOUT with the current date/time/timezone and
@@ -99,33 +130,31 @@ install_logrotate() {
99130
# Install the logrotate config file - this is done already in Dockerfile
100131
# but if a user has mounted a volume over /etc/pihole, it will have been lost
101132
# pihole-FTL-prestart.sh will set the ownership of the file to root:root
102-
echo " [i] Ensuring logrotate script exists in /etc/pihole"
133+
__log "INFO" "pihole-docker" "Ensuring logrotate script exists in /etc/pihole"
103134
install -Dm644 -t /etc/pihole /etc/.pihole/advanced/Templates/logrotate
104-
echo ""
105135
}
106136

107137
migrate_gravity() {
108-
echo " [i] Gravity migration checks"
138+
__log "INFO" "pihole-docker" "Gravity migration checks"
109139
gravityDBfile=$(getFTLConfigValue files.gravity)
110140

111141
if [[ ! -f /etc/pihole/adlists.list ]]; then
112-
echo " [i] No adlist file found, creating one with a default blocklist"
142+
__log "INFO" "pihole-docker" "No adlist file found, creating one with a default blocklist"
113143
echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >/etc/pihole/adlists.list
114144
fi
115145

116146
if [ ! -f "${gravityDBfile}" ]; then
117-
echo " [i] ${gravityDBfile} does not exist (Likely due to a fresh volume). This is a required file for Pi-hole to operate."
118-
echo " [i] Gravity will now be run to create the database"
147+
__log "INFO" "pihole-docker" "${gravityDBfile} does not exist (Likely due to a fresh volume). This is a required file for Pi-hole to operate."
148+
__log "INFO" "pihole-docker" "Gravity will now be run to create the database"
119149
pihole -g
120150
else
121-
echo " [i] Existing gravity database found - schema will be upgraded if necessary"
151+
__log "INFO" "pihole-docker" "Existing gravity database found - schema will be upgraded if necessary"
122152
# source the migration script and run the upgrade function
123153
source /etc/.pihole/advanced/Scripts/database_migration/gravity-db.sh
124154
local upgradeOutput
125155
upgradeOutput=$(upgrade_gravityDB "${gravityDBfile}" "/etc/pihole")
126-
printf "%b" "${upgradeOutput}\\n" | sed 's/^/ /'
156+
__log "INFO" "pihole-docker" "Gravity DB migration output: ${upgradeOutput}"
127157
fi
128-
echo ""
129158
}
130159

131160
# shellcheck disable=SC2034
@@ -144,7 +173,7 @@ ftl_config() {
144173

145174
# If getFTLConfigValue "dns.upstreams" returns [], default to Google's DNS server
146175
if [[ $(getFTLConfigValue "dns.upstreams") == "[]" ]]; then
147-
echo " [i] No DNS upstream set in environment or config file, defaulting to Google DNS"
176+
__log "INFO" "pihole-docker" "No DNS upstream set in environment or config file, defaulting to Google DNS"
148177
setFTLConfigValue "dns.upstreams" "[\"8.8.8.8\", \"8.8.4.4\"]"
149178
fi
150179

@@ -156,7 +185,7 @@ migrate_v5_configs() {
156185
# During migration, their content is copied into the new single source of
157186
# truth file /etc/pihole/pihole.toml and the old files are moved away to
158187
# avoid conflicts with other services on this system
159-
echo " [i] Migrating dnsmasq configuration files"
188+
__log "INFO" "pihole-docker" "Migrating dnsmasq configuration files"
160189
V6_CONF_MIGRATION_DIR="/etc/pihole/migration_backup_v6"
161190
# Create target directory and make it owned by pihole:pihole
162191
mkdir -p "${V6_CONF_MIGRATION_DIR}"
@@ -171,7 +200,6 @@ migrate_v5_configs() {
171200

172201
mv /etc/dnsmasq.d/0{1,2,4,5}-pihole*.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
173202
mv /etc/dnsmasq.d/06-rfc6761.conf "${V6_CONF_MIGRATION_DIR}/" 2>/dev/null || true
174-
echo ""
175203

176204
# Finally, after everything is in place, we can create the new config file
177205
# /etc/pihole/pihole.toml
@@ -186,13 +214,11 @@ migrate_v5_configs() {
186214
# We suppress the message about environment variables as these will be set on FTL's first real start
187215
printf "%b" "${FTLoutput}\\n" | sed 's/^/ /' | sed 's/ Migrating config to Pi-hole v6.0 format/ [i] Migrating config to Pi-hole v6.0 format/' | sed 's/- 0 entries are forced through environment//'
188216

189-
# Print a blank line for separation
190-
echo ""
191217
}
192218

193219
setup_web_password() {
194220
if [ -z "${FTLCONF_webserver_api_password+x}" ] && [ -n "${WEBPASSWORD_FILE}" ] && [ -r "/run/secrets/${WEBPASSWORD_FILE}" ]; then
195-
echo " [i] Setting FTLCONF_webserver_api_password from file"
221+
__log "INFO" "pihole-docker" "Setting FTLCONF_webserver_api_password from file"
196222
FTLCONF_webserver_api_password=$(<"/run/secrets/${WEBPASSWORD_FILE}")
197223
export FTLCONF_webserver_api_password
198224
fi
@@ -201,13 +227,13 @@ setup_web_password() {
201227
if [ -z "${FTLCONF_webserver_api_password+x}" ]; then
202228
# Is this already set to something other than blank (default) in FTL's config file? (maybe in a volume mount)
203229
if [[ $(pihole-FTL --config webserver.api.pwhash) ]]; then
204-
echo " [i] Password already set in config file"
230+
__log "INFO" "pihole-docker" "Password already set in config file"
205231
return
206232
else
207233
# If we are here, the password is set in neither the environment nor the config file
208234
# We will generate a random password.
209235
RANDOMPASSWORD=$(tr -dc _A-Z-a-z-0-9 </dev/urandom | head -c 8)
210-
echo " [i] No password set in environment or config file, assigning random password: $RANDOMPASSWORD"
236+
__log "INFO" "pihole-docker" "No password set in environment or config file, assigning random password: $RANDOMPASSWORD"
211237

212238
# Explicitly turn off bash printing when working with secrets
213239
{ set +x; } 2>/dev/null
@@ -221,15 +247,15 @@ setup_web_password() {
221247
fi
222248
fi
223249
else
224-
echo " [i] Assigning password defined by Environment Variable"
250+
__log "INFO" "pihole-docker" "Assigning password defined by Environment Variable"
225251
fi
226252
}
227253

228254
fix_capabilities() {
229255
# Testing on Docker 20.10.14 with no caps set shows the following caps available to the container:
230256
# Current: cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap=ep
231257
# FTL can also use CAP_NET_ADMIN and CAP_SYS_NICE. If we try to set them when they haven't been explicitly enabled, FTL will not start. Test for them first:
232-
echo " [i] Setting capabilities on pihole-FTL where possible"
258+
__log "INFO" "pihole-docker" "Setting capabilities on pihole-FTL where possible"
233259
capsh --has-p=cap_chown 2>/dev/null && CAP_STR+=',CAP_CHOWN'
234260
capsh --has-p=cap_net_bind_service 2>/dev/null && CAP_STR+=',CAP_NET_BIND_SERVICE'
235261
capsh --has-p=cap_net_raw 2>/dev/null && CAP_STR+=',CAP_NET_RAW'
@@ -239,30 +265,28 @@ fix_capabilities() {
239265

240266
if [[ ${CAP_STR} ]]; then
241267
# We have the (some of) the above caps available to us - apply them to pihole-FTL
242-
echo " [i] Applying the following caps to pihole-FTL:"
243268
IFS=',' read -ra CAPS <<<"${CAP_STR:1}"
244-
for i in "${CAPS[@]}"; do
245-
echo " * ${i}"
246-
done
269+
# Build JSON array from capabilities
270+
CAPS_JSON=$(printf '%s\n' "${CAPS[@]}" | jq -R . | jq -s '{"applied_capabilities": .}')
271+
__log "INFO" "pihole-docker" "$CAPS_JSON"
247272

248273
setcap "${CAP_STR:1}"+ep "$(which pihole-FTL)" || ret=$?
249274

250275
if [[ $DHCP_READY == false ]] && [[ $FTLCONF_dhcp_active == true ]]; then
251276
# DHCP is requested but NET_ADMIN is not available.
252-
echo "ERROR: DHCP requested but NET_ADMIN is not available. DHCP will not be started."
253-
echo " Please add cap_net_admin to the container's capabilities or disable DHCP."
277+
__log "ERROR" "pihole-docker" "DHCP requested but NET_ADMIN is not available. DHCP will not be started."
278+
__log "INFO" "pihole-docker" " Please add cap_net_admin to the container's capabilities or disable DHCP."
254279
setFTLConfigValue dhcp.active false
255280
fi
256281

257282
if [[ $ret -ne 0 && "${DNSMASQ_USER:-pihole}" != "root" ]]; then
258-
echo " [!] ERROR: Unable to set capabilities for pihole-FTL. Cannot run as non-root."
259-
echo " If you are seeing this error, please set the environment variable 'DNSMASQ_USER' to the value 'root'"
283+
__log "ERROR" "pihole-docker" "Unable to set capabilities for pihole-FTL. Cannot run as non-root."
284+
__log "ERROR" "pihole-docker" "If you are seeing this error, please set the environment variable 'DNSMASQ_USER' to the value 'root'"
260285
exit 1
261286
fi
262287
else
263-
echo " [!] ERROR: Unable to set capabilities for pihole-FTL."
264-
echo " Please ensure that the container has the required capabilities."
288+
__log "ERROR" "pihole-docker" "Unable to set capabilities for pihole-FTL."
289+
__log "ERROR" "pihole-docker" "Please ensure that the container has the required capabilities."
265290
exit 1
266291
fi
267-
echo ""
268292
}

src/start.sh

Lines changed: 19 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ start() {
2323
# If the file /etc/pihole/setupVars.conf exists, but /etc/pihole/pihole.toml does not, then we are migrating v5->v6
2424
# FTL Will handle the migration of the config files
2525
if [[ -f /etc/pihole/setupVars.conf && ! -f /etc/pihole/pihole.toml ]]; then
26-
echo " [i] v5 files detected that have not yet been migrated to v6"
27-
echo ""
26+
__log "INFO" "pihole-docker" "v5 files detected that have not yet been migrated to v6"
2827
migrate_v5_configs
2928
fi
3029

@@ -36,7 +35,7 @@ start() {
3635
set_uid_gid
3736

3837
# Configure FTL with any environment variables if needed
39-
echo " [i] Starting FTL configuration"
38+
__log "INFO" "pihole-docker" "Starting FTL configuration"
4039
ftl_config
4140

4241
# Install additional packages inside the container if requested
@@ -51,7 +50,7 @@ start() {
5150
#migrate Gravity Database if needed:
5251
migrate_gravity
5352

54-
echo " [i] pihole-FTL pre-start checks"
53+
__log "INFO" "pihole-docker" "pihole-FTL pre-start checks"
5554
# Run the post stop script to cleanup any remaining artifacts from a previous run
5655
sh /opt/pihole/pihole-FTL-poststop.sh
5756

@@ -65,8 +64,7 @@ start() {
6564
local startFrom
6665
startFrom=$(stat -c%s "${FTLlogFile}")
6766

68-
echo " [i] Starting pihole-FTL ($FTL_CMD) as ${DNSMASQ_USER}"
69-
echo ""
67+
__log "INFO" "pihole-docker" "Starting pihole-FTL as user ${DNSMASQ_USER}"
7068

7169
capsh --user="${DNSMASQ_USER}" --keep=1 -- -c "/usr/bin/pihole-FTL $FTL_CMD >/dev/null" &
7270
# Notes on above:
@@ -79,22 +77,26 @@ start() {
7977

8078
# Wait for FTL to start by monitoring the FTL log file for the "FTL started" line
8179
if ! timeout 30 tail -F -c +$((startFrom + 1)) -- "${FTLlogFile}" | grep -q '########## FTL started'; then
82-
echo " [!] ERROR: Did not find 'FTL started' message in ${FTLlogFile} in 30 seconds, stopping container"
80+
__log "ERROR" "pihole-docker" "Did not find 'FTL started' message in ${FTLlogFile} in 30 seconds, stopping container"
8381
exit 1
8482
fi
8583

8684
pihole updatechecker
87-
local versionsOutput
88-
versionsOutput=$(pihole -v)
89-
echo " [i] Version info:"
90-
printf "%b" "${versionsOutput}\\n" | sed 's/^/ /'
91-
echo ""
85+
86+
# Get version information from API endpoint
87+
local versionJson
88+
versionJson=$(pihole api info/version | jq -c '{
89+
core: .version.core.local | {version, branch, hash},
90+
web: .version.web.local | {version, branch, hash},
91+
ftl: .version.ftl.local | {version, branch, hash, date}
92+
}')
93+
__log "INFO" "pihole-docker" "$versionJson"
9294

9395
if [ "${TAIL_FTL_LOG:-1}" -eq 1 ]; then
9496
# Start tailing the FTL log file from the EOF position we recorded on container start
9597
tail -F -c +$((startFrom + 1)) -- "${FTLlogFile}" &
9698
else
97-
echo " [i] FTL log output is disabled. Remove the Environment variable TAIL_FTL_LOG, or set it to 1 to enable FTL log output."
99+
__log "INFO" "pihole-docker" "FTL log output is disabled. Remove the Environment variable TAIL_FTL_LOG, or set it to 1 to enable FTL log output."
98100
fi
99101

100102
# Wait for the capsh process (which spawned FTL) to finish
@@ -116,8 +118,8 @@ stop() {
116118
if [ -z "${FTL_EXIT_CODE}" ]; then
117119
TRAP_TRIGGERED=1
118120
echo ""
119-
echo " [i] Container stop requested..."
120-
echo " [i] pihole-FTL is running - Attempting to shut it down cleanly"
121+
__log "INFO" "pihole-docker" "Container stop requested..."
122+
__log "INFO" "pihole-docker" "pihole-FTL is running - Attempting to shut it down cleanly"
121123
echo ""
122124
killall --signal 15 pihole-FTL
123125

@@ -135,12 +137,8 @@ stop() {
135137

136138
sh /opt/pihole/pihole-FTL-poststop.sh
137139

138-
echo ""
139-
echo " [i] pihole-FTL exited with status $FTL_EXIT_CODE"
140-
echo ""
141-
echo " [i] Container will now stop or restart depending on your restart policy"
142-
echo " https://docs.docker.com/engine/containers/start-containers-automatically/#use-a-restart-policy"
143-
echo ""
140+
__log "INFO" "pihole-docker" "pihole-FTL exited with status ${FTL_EXIT_CODE}"
141+
__log "INFO" "pihole-docker" "Container will now stop or restart depending on your restart policy - https://docs.docker.com/engine/containers/start-containers-automatically/#use-a-restart-policy"
144142

145143
exit "${FTL_EXIT_CODE}"
146144

0 commit comments

Comments
 (0)