TEC-DATA86-09 · Lesson 6 · 12 clock hours · lab
Log Parsing and Report Generation with grep, awk, and sed
Objective. Students will parse PostgreSQL and Oracle alert log files using grep, awk, and sed to extract error counts, slow-query events, and connection statistics, then format results into a plain-text summary report suitable for a data-governance audit.
⚠ Pending professional review
This lesson, its worked examples and its practice questions has not yet been verified by an industry-certified professional or by a certification or approval body. It is a study draft and may contain errors. Do not use it as instructions for work on a job site or on live equipment. Always follow your instructor, your supervising professional, and current official references and regulations.
VoTechWorks program approval and accreditation are still in progress. Credentials are issued only by the certifying bodies named in each program, after their own exams.
Why Log Parsing Matters for Database Administrators
Database logs are the primary evidence trail for performance problems, security incidents, and compliance audits. PostgreSQL writes messages to its server log with severity labels such as `LOG`, `WARNING`, `ERROR`, and `FATAL`. Oracle writes similar events to its alert log. A data-governance audit — the kind covered in the DAMA CDMP Body of Knowledge under Data Quality and Data Security — may require you to prove how many errors occurred in a given window, confirm that personally identifiable information (PII) was not exposed in log files, and show which queries are degrading system performance. Manual review of a 10,000-line log file is impractical. Shell tools — `grep`, `awk`, and `sed` — let you automate that work reliably and repeatably.
grep: Searching and Filtering
`grep` reads lines from a file or standard input and prints those that match a pattern. The `-E` flag enables extended regular expressions (ERE), which support alternation (`|`), quantifiers (`+`, `?`), and grouping without backslashes.
```bash
grep -E 'ERROR|FATAL' /var/log/postgresql/postgresql.log
```
This command prints every line containing either `ERROR` or `FATAL`. Useful flags:
- `-c` — print only the count of matching lines
- `-i` — case-insensitive match
- `-n` — prefix output with line numbers
- `-o` — print only the matched portion (not the whole line)
Piping `grep` output into another tool is a core pattern:
```bash
grep -E 'ERROR|FATAL' postgresql.log | wc -l
```
awk: Field-Based Processing and Counting
`awk` treats each input line as a record and splits it into fields by a delimiter (default: whitespace). Fields are accessed as `$1`, `$2`, and so on. `awk` programs follow the pattern `condition { action }`.
PostgreSQL log lines begin with a timestamp such as `2024-03-15 14:32:07.441 UTC`. The hour is the second colon-delimited segment of the time field. To count errors by hour:
```bash
grep -E 'ERROR|FATAL' postgresql.log | \
awk '{split($2, t, ":"); hour[t[1]]++} \
END {for (h in hour) print h, hour[h]}' | \
sort
```
Here `split($2, t, ":")` breaks the time field into an array `t`; `t[1]` is the hour. The associative array `hour` accumulates counts. The `END` block runs after all lines are processed.
For CSV input (such as a `pg_stat_statements` export), set the field separator with `-F','`:
```bash
awk -F',' '{sum[$1] += $5; count[$1]++} \
END {for (q in sum) print q, sum[q]/count[q]}' stats.csv
```
To produce a top-10 table, pipe the awk output to `sort -t' ' -k2 -rn | head -10`.
sed: Stream Editing and Redaction
`sed` applies editing commands to each line of input. The substitution command `s/pattern/replacement/flags` is most common. The `g` flag replaces all occurrences on a line.
To redact IPv4 addresses — a privacy-by-design measure aligned with DAMA data-governance principles — use:
```bash
sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/[REDACTED]/g' postgresql.log
```
Breaking down the regex:
- `[0-9]{1,3}` — one to three digits
- `\.` — a literal dot
- `{3}` — exactly three repetitions of the digit-dot group
- `[0-9]{1,3}` — final octet
Redirecting to a new file preserves the original:
```bash
sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/[REDACTED]/g' \
postgresql.log > postgresql_redacted.log
```
Never overwrite your only copy of a log file. Always redirect to a new file or use `sed -i` only after testing on a copy.
Automating with cron
`cron` schedules commands to run automatically. The user's crontab is edited with `crontab -e`. A weekly job that runs every Monday at 06:00 looks like:
```
0 6 1 /home/dba/scripts/slow_query_report.awk /var/log/pg_stats.csv >> /reports/weekly_slow.txt
```
Make the awk script executable (`chmod +x`) and include a shebang line (`#!/usr/bin/awk -f`) so the operating system knows how to run it directly.
Safety and Data-Handling Considerations
Log files frequently contain sensitive data: usernames, partial query text, and IP addresses. Before sharing any log excerpt outside your team:
1. Redact PII using the `sed` pattern above or an equivalent tool.
2. Restrict file permissions — log files should be readable only by the `postgres` OS user and authorized DBAs (`chmod 640`).
3. Store reports on encrypted volumes when they are retained for audit purposes.
4. Do not run parsing scripts as root unless required; use the principle of least privilege.
5. Validate scripts on a copy of production logs in a non-production environment before scheduling them as cron jobs.
These practices align with the DAMA CDMP domains of Data Security and Data Quality, and they also reflect controls expected by Oracle and Microsoft database certification exam content on auditing and access management.
Putting It Together: The Workflow
1. `grep` filters the raw log down to relevant lines.
2. `awk` aggregates and computes statistics.
3. `sed` sanitizes output before distribution.
4. Redirection (`>`, `>>`) or a cron job delivers the finished report.
This three-tool pipeline is portable across any Linux system where PostgreSQL or Oracle runs, which is why it appears in DBA job descriptions and aligns with the command-line skills tested in EnterpriseDB and Microsoft DP-300 exam objectives.
Key terms8
Extended Regular Expression (ERE)A pattern syntax enabled by grep -E (or egrep) that supports alternation (|), grouping with unescaped parentheses, and quantifiers such as + and ?, making complex log pattern matching concise.
Associative Array (awk)A key-value data structure built into awk that allows counts or sums to be accumulated under arbitrary string keys, such as hour labels extracted from a timestamp field.
sed Substitution CommandThe s/pattern/replacement/flags command in sed that replaces text matching a regular expression; the g flag applies the replacement to every match on a line rather than only the first.
Privacy by DesignA data-governance principle, recognized in the DAMA CDMP Body of Knowledge, that builds privacy protections such as automatic PII redaction directly into systems and workflows rather than treating them as an afterthought.
pg_stat_statementsA PostgreSQL extension that records execution statistics — including total calls, total execution time, and rows returned — for every distinct SQL statement the server has run, making it the primary source for slow-query analysis.
cronThe standard Unix/Linux job scheduler that runs commands at specified times or intervals; a user's scheduled jobs are stored in a crontab file edited with crontab -e.
Pipe (|)A shell operator that connects the standard output of one command directly to the standard input of the next, allowing grep, awk, and sed to be chained into a single processing pipeline without intermediate files.
Least PrivilegeA security principle requiring that processes and users are granted only the minimum permissions necessary for their task; applied in DBA work by running scripts under the database service account rather than root.
Worked examples4
1. A PostgreSQL log file contains the following three lines. How many ERROR or FATAL events occurred in hour 14, and what single grep-plus-awk pipeline would produce that count?
2024-03-15 14:05:11 UTC [1234]: ERROR: duplicate key value
2024-03-15 14:47:03 UTC [1235]: FATAL: role "app" does not exist
2024-03-15 15:02:44 UTC [1236]: ERROR: out of memory
- Step 1 — Filter for ERROR or FATAL: grep -E 'ERROR|FATAL' sample.log produces all three lines from the example.
- Step 2 — Extract the hour from field $2 (the time column, e.g., 14:05:11) using split($2, t, ":"); t[1] is the hour string.
- Step 3 — Accumulate counts in an associative array: hour[t[1]]++
- Step 4 — Print the count for hour 14 in the END block: for (h in hour) if (h == "14") print h, hour[h]
- Step 5 — Full pipeline: grep -E 'ERROR|FATAL' sample.log | awk '{split($2,t,":"); hour[t[1]]++} END {for (h in hour) print h, hour[h]}' | sort
- Step 6 — Evaluate: lines 1 and 2 have hour 14; line 3 has hour 15. Count for hour 14 = 2.
Answer: The pipeline prints '14 2' and '15 1'. The ERROR/FATAL count for hour 14 is 2.
2. A pg_stat_statements CSV export has columns: query_type, calls, total_exec_time_ms. Three rows are:
SELECT,200,40000
INSERT,50,5000
SELECT,100,30000
Using awk, compute the average execution time per call for each query_type.
- Step 1 — Set the field separator to comma: awk -F','
- Step 2 — Accumulate total time and call count per query type: {time[$1] += $3; calls[$1] += $2}
- Step 3 — In the END block, divide total time by total calls for each type: for (q in time) print q, time[q]/calls[q]
- Step 4 — Calculate SELECT: total_time = 40000 + 30000 = 70000; total_calls = 200 + 100 = 300; average = 70000 / 300 ≈ 233.33 ms per call.
- Step 5 — Calculate INSERT: total_time = 5000; total_calls = 50; average = 5000 / 50 = 100 ms per call.
Answer: SELECT: ≈ 233.33 ms per call. INSERT: 100.00 ms per call.
3. Write the sed one-liner that replaces every IPv4 address in postgresql.log with [REDACTED] and saves the result to postgresql_redacted.log. Explain each part of the regex.
- Step 1 — The command is: sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/[REDACTED]/g' postgresql.log > postgresql_redacted.log
- Step 2 — -E enables extended regex so curly-brace quantifiers need no backslash.
- Step 3 — ([0-9]{1,3}\.) matches one to three digits followed by a literal dot; {3} repeats this group exactly three times to cover the first three octets.
- Step 4 — [0-9]{1,3} at the end matches the fourth octet (no trailing dot).
- Step 5 — The g flag replaces every IP address on each line, not just the first.
- Step 6 — Redirecting with > writes to a new file, leaving the original log intact.
Answer: sed -E 's/([0-9]{1,3}\.){3}[0-9]{1,3}/[REDACTED]/g' postgresql.log > postgresql_redacted.log
4. You want a cron job to run /home/dba/scripts/slow_query_report.sh every Monday at 06:00, appending output to /reports/weekly_slow.txt. Write the correct crontab entry and identify each field.
- Step 1 — A crontab line has five time fields followed by the command: minute hour day-of-month month day-of-week command.
- Step 2 — Minute = 0 (top of the hour).
- Step 3 — Hour = 6 (06:00).
- Step 4 — Day-of-month = * (any day).
- Step 5 — Month = * (any month).
- Step 6 — Day-of-week = 1 (Monday; 0 and 7 both represent Sunday in standard cron).
- Step 7 — Use >> to append rather than overwrite the report file.
- Step 8 — Full entry: 0 6 * * 1 /home/dba/scripts/slow_query_report.sh >> /reports/weekly_slow.txt
Answer: 0 6 * * 1 /home/dba/scripts/slow_query_report.sh >> /reports/weekly_slow.txt
In class: Given a 10,000-line simulated PostgreSQL log file, students use grep with extended regex to extract all lines containing ERROR or FATAL, pipe to awk to count occurrences by hour, and redirect to a report file; they compare output with a provided answer key. · Students write a sed one-liner that redacts IP addresses in a log file (replacing each IPv4 address with [REDACTED]) to demonstrate privacy-by-design handling aligned with DAMA CDMP data-quality and governance exam domains. · Students build a 40-line awk script that parses pg_stat_statements output dumped to CSV, computes average execution time per query type, and prints a top-10 slowest-query table—then add this script as a weekly cron job.
Materials: Linux VM, sample PostgreSQL log file (instructor-provided), pg_stat_statements CSV export, awk manual (man awk), DAMA CDMP Body of Knowledge chapter on data quality
Check: Error-count report must match the answer key within ±2 counts; IP-redaction sed command must redact all 50 test IPs with zero false negatives; awk top-10 table must sort correctly; all three checked to pass.
Practice check8
Answer each question, then check it. Nothing is recorded; this is your own practice.
Record this lesson on my transcript. Sign in with your key and enrollment email to record lessons (enroll free if you have no key). This page stays open to read and practise.