
September 11, 2026
12 min read
By Kokil Thapa | Last reviewed: September 2026
Production failures leave traces in logs long before users complain. Log parsing and alerting with awk, grep, journalctl is the fastest path for small teams on Ubuntu servers that run Laravel, Apache, and PHP-FPM. You do not need Loki or Datadog on day one. A Linux system administration baseline plus three CLI tools covers most incidents. This guide shows copy-paste filters, threshold alerts, and cron jobs I use on real deployments.
How do you parse application logs with grep and awk on Linux?
Flat log files still dominate PHP and Laravel hosting. Apache writes to /var/log/apache2/. PHP-FPM logs land in pool-specific files. Laravel writes to storage/logs/laravel.log when LOG_CHANNEL=single or daily. Your first job is knowing which file holds the signal.
Start with grep for literal or regex matches. Add awk when you need field extraction, counts, or time-window logic. Together they replace many one-off dashboard queries.
Locate the right log files
- Apache access and error logs:
/var/log/apache2/access.log,error.log - PHP-FPM:
/var/log/php8.3-fpm.logor pool logs under/var/log/php/ - Laravel:
/var/www/app/current/storage/logs/laravel.logon Deployer-style releases - MySQL slow query log when enabled:
/var/log/mysql/slow.log
On shared EC2 boxes I maintain, several sister sites share one host. Path discipline matters. Point scripts at the symlinked current release, not a stale release folder. That mistake causes silent alert gaps after deploy.
grep patterns that catch real failures
# Last 500 Laravel ERROR lines
grep -E '^\[20[0-9]{2}-' /var/www/app/current/storage/logs/laravel.log \
| grep ' ERROR ' | tail -n 500
# Apache 5xx in combined log (status field 9)
grep -E '" (5[0-9]{2}) ' /var/log/apache2/access.log | tail -n 100
# PHP-FPM pool exhaustion
grep -i 'server reached pm.max_children' /var/log/php8.3-fpm.log Use grep -E for extended regex. Pipe to tail on busy files so you do not scan gigabytes on every run. For regex experiments, the site regex tester saves time before you commit patterns to cron.
awk for counts, fields, and thresholds
awk shines when the log line has a fixed shape. Laravel daily logs prefix timestamps. Apache combined logs put the status code in field 9.
# Count Laravel ERROR entries in the last 1000 lines
tail -n 1000 /var/www/app/current/storage/logs/laravel.log \
| awk '/ ERROR / { c++ } END { print c+0 }'
# Apache 5xx count from last 5000 access lines
tail -n 5000 /var/log/apache2/access.log \
| awk '$9 ~ /^5/ { c++ } END { print c+0 }'
# Top requested URLs returning 404 (field 7 = request path)
tail -n 10000 /var/log/apache2/access.log \
| awk '$9 == "404" { print $7 }' \
| sort | uniq -c | sort -rn | head -n 20 Field numbers differ if you change Apache LogFormat. Verify with one line and awk '{ for(i=1;i<=NF;i++) print i, $i }' before you automate.
For Laravel-specific debugging, pair file parsing with application-level tooling described in debug Laravel in production safely with logs and Telescope. CLI parsing catches what you forgot to instrument.
What is the best way to query systemd logs with journalctl?
Modern Ubuntu ships systemd. Many services never write traditional flat files. PHP-FPM, MySQL, Redis, and queue workers often log to the journal. journalctl is your entry point for log parsing and alerting with awk, grep, journalctl on those units.
The official journalctl documentation covers the full flag set. These patterns cover daily ops.
Essential journalctl queries
# PHP-FPM errors since boot
journalctl -u php8.3-fpm.service -p err --no-pager
# Laravel queue worker unit (custom systemd unit)
journalctl -u laravel-worker@1.service --since "1 hour ago" --no-pager
# MySQL errors today
journalctl -u mysql.service --since today -p warning --no-pager
# Kernel OOM kills (memory pressure)
journalctl -k --since "24 hours ago" | grep -i 'out of memory'
# Follow live (Ctrl+C to stop)
journalctl -u apache2.service -f Priority flags map to syslog levels. -p err includes error, critical, alert, and emergency. Use --since and --until to bound cron windows.
Combine journalctl with grep and awk
journalctl output is text. Pipe it like any log stream.
# Count PHP-FPM errors in the last 15 minutes
journalctl -u php8.3-fpm.service --since "15 min ago" -p err --no-pager \
| awk 'END { print NR }'
# Extract payment callback failures from a Laravel unit log
journalctl -u laravel-worker@1.service --since "1 hour ago" --no-pager \
| grep -i 'PaymentCallbackFailed' \
| awk '{ print $1, $2, $3 }'
# Rate of Apache restarts (indicator of config or OOM issues)
journalctl -u apache2.service --since "24 hours ago" --no-pager \
| grep -i 'Started The Apache HTTP Server' \
| wc -l On booking systems like Adventure Third Pole Trek, queue worker logs in journalctl often reveal stuck jobs before the UI shows backlog. Check workers first when cron alerts spike.
Journal disk usage is a common production surprise. Read log rotation and disk space management on Linux before you rely on week-old journal history.
How do you build log alerting scripts without a full observability stack?
Prometheus and Loki are excellent at scale. Many Nepal SMB sites run on a single VPS with Rs 2,000–5,000/month hosting (~USD 15–37). A shell script plus cron is enough to page on payment failures, 5xx spikes, or disk-full warnings.
The pattern is simple: measure, compare, notify, exit non-zero for CI hooks if needed.
A reusable alert script skeleton
#!/usr/bin/env bash
# /usr/local/bin/check-laravel-errors.sh
set -euo pipefail
LOG="/var/www/app/current/storage/logs/laravel.log"
THRESHOLD="${THRESHOLD:-10}"
WINDOW="${WINDOW:-2000}"
WEBHOOK_URL="${WEBHOOK_URL:-}"
count=$(tail -n "$WINDOW" "$LOG" | awk '/ ERROR / { c++ } END { print c+0 }')
if [ "$count" -gt "$THRESHOLD" ]; then
msg="Laravel ERROR count ${count} exceeds ${THRESHOLD} (last ${WINDOW} lines)"
logger -t log-alert "$msg"
if [ -n "$WEBHOOK_URL" ]; then
curl -sS -X POST -H 'Content-Type: application/json' \
-d "{\"text\":\"${msg}\"}" "$WEBHOOK_URL"
fi
exit 1
fi
exit 0 Make it executable: chmod 750 /usr/local/bin/check-laravel-errors.sh. Store secrets in /etc/default/log-alerts, not in the script body.
Cron schedule examples
# /etc/cron.d/app-log-alerts
SHELL=/bin/bash
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
*/5 * * * * root /usr/local/bin/check-laravel-errors.sh
*/5 * * * * root /usr/local/bin/check-apache-5xx.sh
*/10 * * * * root /usr/local/bin/check-php-fpm-journal.sh A companion journal-based check:
#!/usr/bin/env bash
# check-php-fpm-journal.sh
count=$(journalctl -u php8.3-fpm.service --since "10 min ago" -p err --no-pager | awk 'END { print NR }')
THRESHOLD=5
[ "$count" -gt "$THRESHOLD" ] && logger -t log-alert "PHP-FPM errors: $count" && exit 1
exit 0 Wire the webhook to Slack, Discord, or an SMS gateway you already use. Keep messages short. Include hostname and a link to your runbook.
When you outgrow cron, read alerting with Prometheus Alertmanager and log aggregation for small teams. Migrate thresholds—not tribal knowledge.
Which log parsing tool should you use: grep, awk, or journalctl?
They are complementary, not competing products. Pick based on log source and the question you need answered.
| Tool | Best for | Weak at | Typical source |
|---|---|---|---|
grep | Fast pattern match, pipe-friendly filtering | Field math, aggregation | Flat files, journalctl pipes |
awk | Column extraction, counts, simple reports | Binary logs, JSON without preprocessing | Apache combined, Laravel text logs |
journalctl | systemd units, time windows, priorities | Apps that only file-log to storage/ | PHP-FPM, MySQL, custom units |
For JSON structured logs, consider jq alongside awk. Laravel LOG_STACK JSON channels are growing in popularity on Laravel 12 and 13 projects. Plain awk still handles most legacy stacks I maintain.
Broader context lives in observability vs monitoring: logs, metrics, and traces. Logs answer "what happened on this request." Metrics tell you how often. You need both eventually.
Decision rules I apply on client servers
- Service managed by systemd → start with
journalctl -u … - Laravel application exceptions → tail
storage/logswith grep forERROR - HTTP error rates → awk on Apache or Nginx access logs
- Disk or OOM events →
journalctl -kplus grep - SEO crawl anomalies → combine with SEO log file analysis for technical wins
Legal-tech portals such as Notary Nepal generate document upload errors and payment callback noise. Separate alert scripts per concern. One mega-pattern causes missed signals.
How do you harden log parsing and alerting for production Laravel apps?
Scripts fail silently when paths rot, permissions block reads, or time zones confuse --since windows. Hardening is boring. It saves Sunday nights.
Permissions and paths
Cron runs as root or a dedicated logcheck user. Laravel log files must be readable. After Deployer symlink swaps, confirm current/storage/logs resolves correctly. I reload PHP-FPM after deploy partly for opcache and partly so log handles stay sane.
Reduce noise before you alert
Exclude known benign lines:
tail -n 3000 /var/www/app/current/storage/logs/laravel.log \
| grep ' ERROR ' \
| grep -v 'TokenMismatchException' \
| grep -v 'ThrottleRequestsException' \
| awk 'END { print NR }' Review exclusions monthly. A suppressed pattern can hide a real regression. Document each exclusion in your internal runbook.
Escalation without 3 a.m. fatigue
Follow SLO-driven alerting that does not page at 3am. Use two tiers: webhook for warnings, SMS only when counts stay high for three consecutive checks. Payment and auth failures on eCommerce sites deserve the stricter tier.
For ongoing ops, support and maintenance contracts should list which scripts run on each server. Future you should not reverse-engineer cron from crontab -l alone.
When traffic grows, ship logs off-box with guidance from Fluentd vs Fluent Bit for log shipping. Keep the cron scripts as a local safety net.
PHP and MySQL versions on current stacks
Most Laravel 12 apps I deploy run PHP 8.3 or 8.4 on Ubuntu 24.04. Laravel 13 expects PHP 8.3+. Match unit names to your installed version: php8.3-fpm.service, not a stale 8.1 unit. MySQL 8.4 LTS and MySQL 9.7 both expose errors via journal when systemd manages the daemon.
Application performance ties to log health. Slow queries logged today become tomorrow's outage. Cross-check with testing and optimization when error rates climb without obvious deploys.
Key Takeaways
- Map each service to its log source—flat file or systemd journal—before you write filters.
- Use grep for patterns, awk for counts and field extraction, journalctl for PHP-FPM, MySQL, and queue units.
- Wrap threshold checks in cron scripts with webhook notifications and syslog via
logger. - Tune WINDOW and THRESHOLD to avoid alert fatigue; exclude only documented benign errors.
- Upgrade to Loki or Prometheus when cron alerts multiply—not before you outgrow one VPS.
- After every Deployer release, verify log paths on the
currentsymlink still resolve.
People Also Ask
Can awk parse JSON logs?
Plain awk handles line-based text well. JSON needs jq or a preprocessing step. Laravel JSON log channels dump one object per line. Pipe through jq -r '.level' for level counts. Mixed formats on one server are common during migrations.
How long does journalctl keep logs?
Retention depends on /etc/systemd/journald.conf settings like SystemMaxUse and disk size. Defaults often keep days to weeks—not months. Archive critical units to flat files if compliance requires longer history.
Is grep enough for production alerting?
Grep alone finds matches but does not count well across large files or time windows. Pair it with awk or wc -l for thresholds. For systemd sources, journalctl should come first—it applies time filters cheaply.
What should I alert on first for a Laravel site?
Start with Laravel ERROR rate, Apache/Nginx 5xx count, PHP-FPM pm.max_children warnings, and disk usage above 85%. Payment callback and queue failure patterns come next on commerce and booking apps.
Ship reliable alerts on the stack you already run
Log parsing and alerting with awk, grep, journalctl keeps small teams responsive without a six-figure observability bill. You already have the tools on every Ubuntu box that runs your Laravel or WordPress site. Start with three cron checks this week. Promote the noisiest patterns into documented runbooks.
If you want alerting wired into your deploy pipeline and monitored long-term, see Linux system administration or web development services. For a production legal-tech or eCommerce reference, browse the portfolio. Need help tuning regex before you commit alerts? Use the regex tester and contact us with your stack details.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

