caddy_log_parser.sh
Raw
#!/bin/bash
###
### Caddy webserver JSON log parser
###
show_help() {
cat <<END
Usage: $0 [-c|--common] [-C|--combined] [-f|--files] [-h|--help] filename
Options:
-c, --common Apache Common Log Format (default)
-C, --combined Apache Combined Log Format
-f, --files Custom Log Format, focused on file transfers
-h, --help Show this help message and exit
END
}
# Function to output logs in common log format
output_common_log_format() {
jq -r '
. as $log |
$log.request.client_ip as $h |
"-" as $l |
($log.user_id // "-") as $u |
($log.ts | tostring | split(".") | .[1][:3]) as $ms |
($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t |
"\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r |
$log.status as $s |
($log.size // "-") as $b |
"\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b)"
' "${1}"
}
# Function to output logs in combined log format
output_combined_log_format() {
jq -r '
. as $log |
$log.request.client_ip as $h |
"-" as $l |
($log.user_id // "-") as $u |
($log.ts | tostring | split(".") | .[1][:3]) as $ms |
($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t |
"\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r |
$log.status as $s |
($log.size // "-") as $b |
($log.request.headers.Referer[0] // "-") as $referrer |
($log.request.headers["User-Agent"][0] // "-") as $user_agent |
"\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b) \"\($referrer)\" \"\($user_agent)\""
' "${1}"
}
output_file_log_format() {
jq -r '
. as $log |
$log.request.client_ip as $h |
"-" as $l |
($log.user_id // "-") as $u |
($log.ts | tostring | split(".") | .[1][:3]) as $ms |
($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t |
"\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r |
$log.status as $s |
($log.size // "-") as $b |
($log.request.headers.Referer[0] // "-") as $referrer |
($log.request.headers["User-Agent"][0] // "-") as $user_agent |
"\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b) \"\($referrer)\" \"\($user_agent)\""
' "${1}"
}
# Function to list served files
list_served_files() {
jq -r '
.request.client_ip as $client_ip |
(.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; " +0000")) as $datetime |
.status as $status_code |
(.size // "-") as $size |
.request.uri as $filename |
"\($client_ip) [\($datetime)] \($status_code) \($size) \"\($filename)\""
' "${1}"
}
# Default to common log format
format="common"
# Parse command line options
while [[ "$#" -gt 0 ]]; do
case $1 in
-c|--common)
format="common"
shift
;;
-C|--combined)
format="combined"
shift
;;
-f|--files)
format="files"
shift
;;
-h|--help)
show_help
exit 0
;;
*)
input="${1}"
shift
;;
esac
done
# Check if a filename is provided
if [ -z "${input}" ]; then
echo -e "Error: No input file provided. \n"
show_help
exit 1
fi
# Output the logs in the chosen format
if [ "$format" = "common" ]; then
output_common_log_format "${input}"
elif [ "$format" = "files" ]; then
list_served_files "${input}"
else
output_combined_log_format "${input}"
fi
# Apache Commong Log format:
# "%h %l %u %t \"%r\" %>s %b"
# Apache Combined LogFormat:
# "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\""
# %h is the remote host (client IP)
# %l is the remote logname (not used, so we'll use -)
# %u is the authenticated user
# %t is the time the request was received
# %r is the request line from the client ("method uri proto")
# %>s is the status code
# %b is the size of the object returned to the client
1 | #!/bin/bash |
2 | |
3 | ### |
4 | ### Caddy webserver JSON log parser |
5 | ### |
6 | |
7 | show_help() { |
8 | cat <<END |
9 | Usage: $0 [-c|--common] [-C|--combined] [-f|--files] [-h|--help] filename |
10 | |
11 | Options: |
12 | -c, --common Apache Common Log Format (default) |
13 | -C, --combined Apache Combined Log Format |
14 | -f, --files Custom Log Format, focused on file transfers |
15 | -h, --help Show this help message and exit |
16 | END |
17 | } |
18 | |
19 | # Function to output logs in common log format |
20 | output_common_log_format() { |
21 | jq -r ' |
22 | . as $log | |
23 | $log.request.client_ip as $h | |
24 | "-" as $l | |
25 | ($log.user_id // "-") as $u | |
26 | ($log.ts | tostring | split(".") | .[1][:3]) as $ms | |
27 | ($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t | |
28 | "\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r | |
29 | $log.status as $s | |
30 | ($log.size // "-") as $b | |
31 | "\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b)" |
32 | ' "${1}" |
33 | } |
34 | |
35 | # Function to output logs in combined log format |
36 | output_combined_log_format() { |
37 | jq -r ' |
38 | . as $log | |
39 | $log.request.client_ip as $h | |
40 | "-" as $l | |
41 | ($log.user_id // "-") as $u | |
42 | ($log.ts | tostring | split(".") | .[1][:3]) as $ms | |
43 | ($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t | |
44 | "\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r | |
45 | $log.status as $s | |
46 | ($log.size // "-") as $b | |
47 | ($log.request.headers.Referer[0] // "-") as $referrer | |
48 | ($log.request.headers["User-Agent"][0] // "-") as $user_agent | |
49 | "\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b) \"\($referrer)\" \"\($user_agent)\"" |
50 | ' "${1}" |
51 | } |
52 | |
53 | output_file_log_format() { |
54 | jq -r ' |
55 | . as $log | |
56 | $log.request.client_ip as $h | |
57 | "-" as $l | |
58 | ($log.user_id // "-") as $u | |
59 | ($log.ts | tostring | split(".") | .[1][:3]) as $ms | |
60 | ($log.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; "." + $ms + " +0000")) as $t | |
61 | "\($log.request.method) \($log.request.uri) \($log.request.proto)" as $r | |
62 | $log.status as $s | |
63 | ($log.size // "-") as $b | |
64 | ($log.request.headers.Referer[0] // "-") as $referrer | |
65 | ($log.request.headers["User-Agent"][0] // "-") as $user_agent | |
66 | "\($h) \($l) \($u)[\($t)] \"\($r)\" \($s) \($b) \"\($referrer)\" \"\($user_agent)\"" |
67 | ' "${1}" |
68 | } |
69 | |
70 | # Function to list served files |
71 | list_served_files() { |
72 | jq -r ' |
73 | .request.client_ip as $client_ip | |
74 | (.ts | floor | todateiso8601 | sub("T"; " ") | sub("Z"; " +0000")) as $datetime | |
75 | .status as $status_code | |
76 | (.size // "-") as $size | |
77 | .request.uri as $filename | |
78 | "\($client_ip) [\($datetime)] \($status_code) \($size) \"\($filename)\"" |
79 | ' "${1}" |
80 | } |
81 | |
82 | # Default to common log format |
83 | format="common" |
84 | |
85 | # Parse command line options |
86 | while [[ "$#" -gt 0 ]]; do |
87 | case $1 in |
88 | -c|--common) |
89 | format="common" |
90 | shift |
91 | ;; |
92 | -C|--combined) |
93 | format="combined" |
94 | shift |
95 | ;; |
96 | -f|--files) |
97 | format="files" |
98 | shift |
99 | ;; |
100 | -h|--help) |
101 | show_help |
102 | exit 0 |
103 | ;; |
104 | *) |
105 | input="${1}" |
106 | shift |
107 | ;; |
108 | esac |
109 | done |
110 | |
111 | # Check if a filename is provided |
112 | if [ -z "${input}" ]; then |
113 | echo -e "Error: No input file provided. \n" |
114 | show_help |
115 | exit 1 |
116 | fi |
117 | |
118 | |
119 | # Output the logs in the chosen format |
120 | if [ "$format" = "common" ]; then |
121 | output_common_log_format "${input}" |
122 | elif [ "$format" = "files" ]; then |
123 | list_served_files "${input}" |
124 | else |
125 | output_combined_log_format "${input}" |
126 | fi |
127 | |
128 | # Apache Commong Log format: |
129 | # "%h %l %u %t \"%r\" %>s %b" |
130 | |
131 | # Apache Combined LogFormat: |
132 | # "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-agent}i\"" |
133 | |
134 | # %h is the remote host (client IP) |
135 | # %l is the remote logname (not used, so we'll use -) |
136 | # %u is the authenticated user |
137 | # %t is the time the request was received |
138 | # %r is the request line from the client ("method uri proto") |
139 | # %>s is the status code |
140 | # %b is the size of the object returned to the client |