Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

Log Parsing and Alerting with awk, grep, journalctl

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

  1. Apache access and error logs: /var/log/apache2/access.log, error.log
  2. PHP-FPM: /var/log/php8.3-fpm.log or pool logs under /var/log/php/
  3. Laravel: /var/www/app/current/storage/logs/laravel.log on Deployer-style releases
  4. 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.

Flat-File Log Parsing PipelineLog FilesApache / PHP / Laraveltail / grepFilter patternsawkCount / fieldsMetricsCounts / top NExample: Laravel ERROR threshold checktail -n 2000 laravel.log | awk '/ ERROR / { c++ } END { print c+0 }'if count > 10 → alert script firesRuns every 5 minutes via cron on Ubuntu 22/24
Log parsing and alerting with awk and grep: tail recent lines, filter errors, count with awk, compare to a threshold.

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.

journalctl Query Flowsystemd Unitphp-fpm / mysqljournalctl-u -p --sincegrep / awkPattern filterAlertTime window--since "15 min ago"Priority filter-p err .. emergGotcha: rotated journals need persistent storageSet SystemMaxUse= in journald.conf — see log rotation guide
journalctl filters systemd journal entries by unit, time, and priority before grep or awk extract alert signals.

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.

Cron Log Alerting WorkflowcronEvery 5 minScriptgrep / awkThresholdcount > NNotifySlack / SMSExit 0 = healthy | Exit 1 = alert sentAvoid alert fatigueTune WINDOW and THRESHOLDLog to sysloglogger -t log-alert
Scheduled log parsing and alerting: cron runs threshold scripts that notify only when error counts exceed limits.

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.

ToolBest forWeak atTypical source
grepFast pattern match, pipe-friendly filteringField math, aggregationFlat files, journalctl pipes
awkColumn extraction, counts, simple reportsBinary logs, JSON without preprocessingApache combined, Laravel text logs
journalctlsystemd units, time windows, prioritiesApps 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/logs with grep for ERROR
  • HTTP error rates → awk on Apache or Nginx access logs
  • Disk or OOM events → journalctl -k plus 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.

CLI Alerts vs Full Observabilityawk + grep + journalctlCost: near zeroSetup: hoursBest: 1–3 VPS sitesSkills: shell + cronDeployer + GitLab CI friendlyStart hereLoki / ELK / DatadogCost: Rs 5k–50k+ / moSetup: days–weeksBest: multi-service fleetsSkills: Grafana / pipelinesSee Fluent Bit vs FluentdScale-up pathgrow
Log parsing and alerting with awk, grep, journalctl fits single-server Laravel stacks before you adopt Loki or commercial APM.

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 current symlink 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

It means filtering systemd journals and flat log files for error patterns, counting matches over a time window, and sending alerts via cron when thresholds are exceeded. On Ubuntu servers running Laravel, Apache, and PHP-FPM, grep finds patterns, awk extracts fields and counts, and journalctl queries systemd-managed services. No Loki or Datadog is required on day one.

Apache writes to /var/log/apache2/access.log and error.log. PHP-FPM logs land in /var/log/php8.3-fpm.log or pool-specific files under /var/log/php/. Laravel writes to storage/logs/laravel.log when LOG_CHANNEL is single or daily. On Deployer-style releases, point scripts at /var/www/app/current/storage/logs/laravel.log via the current symlink, not a stale release folder.

Start by locating the right file for each service. Use grep -E for extended regex pattern matches, piping to tail on busy files so you do not scan gigabytes every run. Add awk when you need field extraction, counts, or threshold logic. Laravel daily logs prefix timestamps; Apache combined logs put the HTTP status code in field 9. Verify field numbers with one sample line before automating.

For Laravel, grep lines matching ERROR after a timestamp prefix in the last 500 lines. For Apache 5xx responses, grep the combined access log for status codes 500–599 in field 9 using a pattern like quote-space-5xx. For PHP-FPM pool exhaustion, grep -i for server reached pm.max_children in the PHP-FPM log. Pipe all patterns through tail to limit scan size on production files.

awk shines when log lines have a fixed shape. Count Laravel ERROR entries by piping tail output through awk with a pattern match and incrementing a counter in END. For Apache 5xx counts, match field 9 against a regex starting with 5. To find top 404 URLs, print field 7 where field 9 equals 404, then sort and uniq -c. Field numbers change if you alter Apache LogFormat.

Use journalctl -u followed by the unit name, such as php8.3-fpm.service or a custom laravel-worker unit. Filter by priority with -p err, which includes error through emergency levels. Bound time windows with --since and --until for cron jobs. Add --no-pager for script use and -f to follow live output. PHP-FPM, MySQL, Redis, and queue workers often log here instead of flat files.

Pipe journalctl output like any text stream. Count PHP-FPM errors in the last 15 minutes by piping journalctl -u php8.3-fpm.service --since 15 min ago -p err through awk END print NR. Extract payment callback failures from a Laravel worker unit with grep -i PaymentCallbackFailed. Rate Apache restarts by grepping for Started The Apache HTTP Server over 24 hours and piping to wc -l.

The pattern is measure, compare, notify, exit non-zero if needed. Write a bash script that tails recent lines, counts matches with awk, compares against a THRESHOLD, logs via logger -t log-alert, and posts to a WEBHOOK_URL with curl. Store secrets in /etc/default/log-alerts, not the script body. Schedule with cron every 5–10 minutes. Many Nepal SMB sites on Rs 2,000–5,000/month VPS hosting run this way before adopting Prometheus or Loki.

They are complementary, not competing. grep is best for fast pattern matching on flat files or piped journalctl output. awk handles column extraction, counts, and simple reports on structured text like Apache combined logs. journalctl is the entry point for systemd units with time and priority filters. Decision rule: systemd-managed service starts with journalctl; Laravel exceptions use grep on storage/logs; HTTP error rates use awk on access logs.

Plain awk handles line-based text well. JSON needs jq or a preprocessing step. Laravel JSON log channels dump one object per line on Laravel 12 and 13 projects.

Retention depends on /etc/systemd/journald.conf settings like SystemMaxUse and disk size. Defaults often keep days to weeks, not months.

Grep 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.

Start with Laravel ERROR rate, Apache or 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. On systems like Adventure Third Pole Trek, queue worker logs in journalctl often reveal stuck jobs before the UI shows backlog. Separate alert scripts per concern rather than one mega-pattern that misses signals.

Confirm cron can read Laravel log files and that Deployer current symlink paths resolve after every release. Reduce noise by excluding documented benign errors like TokenMismatchException with grep -v, reviewing exclusions monthly. 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. Document every cron script and exclusion in your runbook so future ops do not reverse-engineer crontab alone.

Upgrade when cron alerts multiply and you outgrow a single VPS, not before. Prometheus and Loki excel at scale but are overkill for day-one monitoring on small teams. Ship logs off-box with Fluentd or Fluent Bit when traffic grows, migrating thresholds rather than tribal knowledge. Keep cron scripts as a local safety net even after adopting a heavier stack. Read alerting with Prometheus Alertmanager and log aggregation for small teams when you are ready to migrate.

Share this article

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.

Quick Contact Options
Choose how you want to connect me: