Set up comprehensive PHP application monitoring using New Relic APM with custom metrics and error tracking. Configure structured logging with centralized collection and automated monitoring alerts.
Prerequisites
- PHP 8.2 or higher installed
- Apache or Nginx web server running
- Valid New Relic account with license key
- Root or sudo access to the server
What this solves
PHP applications require comprehensive monitoring to track performance, errors, and business metrics in production. This tutorial implements New Relic APM for detailed application performance monitoring and configures centralized logging with structured data collection. You'll set up custom metrics, error tracking, and automated alerts to maintain application health and respond quickly to issues.
Step-by-step installation
Update system packages
Start by updating your package manager to ensure you have the latest security patches and package information.
sudo apt update && sudo apt upgrade -y
Install required dependencies
Install the necessary packages for PHP development, log management, and system monitoring tools.
sudo apt install -y wget curl gnupg2 software-properties-common \
rsyslog logrotate php-dev php-cli php-fpm apache2-dev
Add New Relic repository
Add the official New Relic repository to install the PHP agent and monitoring tools.
curl -fsSL https://download.newrelic.com/548C16BF.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/newrelic.gpg
echo "deb [signed-by=/etc/apt/keyrings/newrelic.gpg] http://apt.newrelic.com/debian/ newrelic non-free" | sudo tee /etc/apt/sources.list.d/newrelic.list
sudo apt update
Install New Relic PHP agent
Install the New Relic PHP agent package which includes the daemon and PHP extension for application monitoring.
sudo apt install -y newrelic-php5
Configure New Relic PHP agent
Run the New Relic installation script and configure it with your license key. Replace YOUR_LICENSE_KEY with your actual New Relic license key from your account.
sudo newrelic-install install
Follow the prompts and enter your New Relic license key when prompted
Select your PHP version and web server configuration
Configure New Relic PHP settings
Create a comprehensive New Relic configuration with application naming, error collection, and performance settings.
extension = "newrelic.so"
; Application name as it appears in New Relic UI
newrelic.appname = "MyApp Production"
; License key
newrelic.license = "YOUR_LICENSE_KEY"
; Enable the agent
newrelic.enabled = true
; Set daemon address
newrelic.daemon.address = "/tmp/.newrelic.sock"
; High security mode
newrelic.high_security = false
; Transaction tracer settings
newrelic.transaction_tracer.enabled = true
newrelic.transaction_tracer.threshold = "apdex_f"
newrelic.transaction_tracer.detail = 1
newrelic.transaction_tracer.slow_sql = true
newrelic.transaction_tracer.stack_trace_threshold = 500
; Error collector settings
newrelic.error_collector.enabled = true
newrelic.error_collector.record_database_errors = true
; Browser monitoring
newrelic.browser_monitoring.auto_instrument = true
; Database monitoring
newrelic.datastore_tracer.database_name_reporting.enabled = true
newrelic.datastore_tracer.instance_reporting.enabled = true
; Custom event settings
newrelic.custom_events.enabled = true
Configure rsyslog for centralized logging
Set up rsyslog to collect PHP application logs with structured formatting and remote logging capabilities.
# PHP application log configuration
$ModLoad imfile
PHP error log
$InputFileName /var/log/php_errors.log
$InputFileTag php-error:
$InputFileStateFile stat-php-error
$InputFileSeverity error
$InputFileFacility local0
$InputRunFileMonitor
PHP application log
$InputFileName /var/log/myapp/application.log
$InputFileTag myapp:
$InputFileStateFile stat-myapp
$InputFileSeverity info
$InputFileFacility local1
$InputRunFileMonitor
Custom template for structured logging
$template AppLogFormat,"<%PRI%>%TIMESTAMP:::date-rfc3339% %HOSTNAME% %syslogtag% %STRUCTURED-DATA% %msg%\n"
Forward logs to central server (optional)
. @@logserver.example.com:514;AppLogFormat
Local file logging with rotation
local0.* /var/log/php_errors.log;AppLogFormat
local1.* /var/log/myapp/application.log;AppLogFormat
Set up log rotation
Configure logrotate to manage log file sizes and retention automatically.
/var/log/php_errors.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 www-data www-data
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
/var/log/myapp/*.log {
daily
missingok
rotate 30
compress
delaycompress
notifempty
create 644 www-data www-data
sharedscripts
postrotate
/usr/lib/rsyslog/rsyslog-rotate
endscript
}
Create application logging directories
Set up proper directory structure with correct ownership and permissions for application logs.
sudo mkdir -p /var/log/myapp
sudo chown www-data:www-data /var/log/myapp
sudo chmod 755 /var/log/myapp
sudo touch /var/log/myapp/application.log /var/log/myapp/error.log
sudo chown www-data:www-data /var/log/myapp/*.log
sudo chmod 644 /var/log/myapp/*.log
Configure PHP error logging
Update PHP configuration to enable comprehensive error logging with proper log levels and destinations.
; Error logging configuration
log_errors = On
error_log = /var/log/php_errors.log
; Log all errors except notices
error_reporting = E_ALL & ~E_NOTICE & ~E_STRICT & ~E_DEPRECATED
; Don't display errors to users
display_errors = Off
display_startup_errors = Off
; Log repeated errors
ignore_repeated_errors = Off
ignore_repeated_source = Off
; Maximum log file size
log_errors_max_len = 1024
; Enable syslog logging
openlog.ident = "php"
openlog.facility = LOG_USER
Create PHP monitoring class
Implement a custom PHP class for structured logging and New Relic custom metrics.
logFile = $logFile;
}
/**
* Log structured application events
*/
public function logEvent($level, $message, $context = []) {
$timestamp = date('c');
$logEntry = [
'timestamp' => $timestamp,
'level' => strtoupper($level),
'message' => $message,
'context' => $context,
'request_id' => $this->getRequestId(),
'user_id' => $this->getCurrentUserId()
];
file_put_contents($this->logFile, json_encode($logEntry) . "\n", FILE_APPEND | LOCK_EX);
// Send to syslog as well
syslog(LOG_INFO, json_encode($logEntry));
}
/**
* Record custom New Relic metrics
*/
public function recordMetric($name, $value) {
if (extension_loaded('newrelic')) {
newrelic_custom_metric($name, $value);
}
}
/**
* Track business events in New Relic
*/
public function trackEvent($eventType, $attributes = []) {
if (extension_loaded('newrelic')) {
newrelic_record_custom_event($eventType, $attributes);
}
}
/**
* Set transaction name for New Relic
*/
public function setTransactionName($name) {
if (extension_loaded('newrelic')) {
newrelic_name_transaction($name);
}
}
/**
* Add custom attributes to New Relic transaction
*/
public function addCustomAttribute($key, $value) {
if (extension_loaded('newrelic')) {
newrelic_add_custom_parameter($key, $value);
}
}
/**
* Notice error in New Relic
*/
public function noticeError($exception) {
if (extension_loaded('newrelic')) {
newrelic_notice_error($exception);
}
$this->logEvent('error', $exception->getMessage(), [
'file' => $exception->getFile(),
'line' => $exception->getLine(),
'trace' => $exception->getTraceAsString()
]);
}
private function getRequestId() {
return $_SERVER['HTTP_X_REQUEST_ID'] ?? uniqid('req_');
}
private function getCurrentUserId() {
// Implement your user identification logic
return $_SESSION['user_id'] ?? 'anonymous';
}
}
Create monitoring configuration
Set up monitoring alerts and thresholds for your application performance metrics.
[
'app_name' => 'MyApp Production',
'license_key' => getenv('NEWRELIC_LICENSE_KEY'),
'high_security' => false,
'transaction_tracer_threshold' => 0.5,
'error_collector' => true,
'browser_monitoring' => true,
],
'logging' => [
'level' => 'info',
'file' => '/var/log/myapp/application.log',
'max_size' => '100MB',
'rotation' => 30,
'structured' => true,
],
'alerts' => [
'error_rate_threshold' => 5.0, // percent
'response_time_threshold' => 2.0, // seconds
'throughput_threshold' => 100, // requests per minute
'memory_usage_threshold' => 80, // percent
],
'custom_metrics' => [
'business_metrics' => [
'user_registrations',
'order_completions',
'payment_failures',
'api_calls'
],
'performance_metrics' => [
'database_query_time',
'cache_hit_rate',
'external_api_response_time'
]
]
];
Implement application monitoring
Create an example application that demonstrates comprehensive monitoring integration.
setTransactionName('Home Page');
// Add custom attributes
$monitor->addCustomAttribute('page_type', 'home');
$monitor->addCustomAttribute('user_type', 'guest');
try {
// Start timing
$startTime = microtime(true);
// Log page view
$monitor->logEvent('info', 'Page viewed', [
'page' => 'home',
'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? 'unknown',
'ip_address' => $_SERVER['REMOTE_ADDR'] ?? 'unknown'
]);
// Simulate some business logic
$userData = getUserData();
$products = getProducts();
// Record custom metrics
$endTime = microtime(true);
$pageLoadTime = $endTime - $startTime;
$monitor->recordMetric('Custom/Page/LoadTime', $pageLoadTime);
$monitor->recordMetric('Custom/Products/Count', count($products));
// Track business event
$monitor->trackEvent('PageView', [
'page' => 'home',
'load_time' => $pageLoadTime,
'product_count' => count($products)
]);
echo "Welcome to MyApp
";
echo "Products loaded: " . count($products) . "
";
} catch (Exception $e) {
// Handle and report errors
$monitor->noticeError($e);
http_response_code(500);
echo "Error occurred
";
// Track error event
$monitor->trackEvent('Error', [
'error_message' => $e->getMessage(),
'error_file' => $e->getFile(),
'error_line' => $e->getLine()
]);
}
function getUserData() {
global $monitor;
$startTime = microtime(true);
// Simulate database call
usleep(50000); // 50ms
$endTime = microtime(true);
$queryTime = $endTime - $startTime;
$monitor->recordMetric('Custom/Database/UserQuery', $queryTime);
$monitor->logEvent('debug', 'User data retrieved', ['query_time' => $queryTime]);
return ['id' => 1, 'name' => 'Test User'];
}
function getProducts() {
global $monitor;
$startTime = microtime(true);
// Simulate cache miss and database query
$products = [
['id' => 1, 'name' => 'Product 1'],
['id' => 2, 'name' => 'Product 2'],
['id' => 3, 'name' => 'Product 3']
];
$endTime = microtime(true);
$queryTime = $endTime - $startTime;
$monitor->recordMetric('Custom/Database/ProductQuery', $queryTime);
$monitor->recordMetric('Custom/Cache/MissRate', 0.2);
return $products;
}
Start services and enable monitoring
Start the required services and verify that monitoring is working correctly.
sudo systemctl restart rsyslog
sudo systemctl restart php8.2-fpm
sudo systemctl restart apache2
sudo systemctl restart newrelic-daemon
Enable services to start on boot
sudo systemctl enable rsyslog php8.2-fpm apache2 newrelic-daemon
Verify your setup
Test that New Relic monitoring and logging are working correctly by checking service status and generating test data.
# Check New Relic daemon status
sudo systemctl status newrelic-daemon
Verify PHP extension is loaded
php -m | grep newrelic
Check log files
sudo tail -f /var/log/myapp/application.log
sudo tail -f /var/log/php_errors.log
Test application monitoring
curl -v http://localhost/
Check rsyslog is processing logs
sudo systemctl status rsyslog
sudo tail -f /var/log/syslog | grep myapp
Verify log rotation configuration
sudo logrotate -d /etc/logrotate.d/php-apps
Visit your New Relic dashboard to verify that application data is appearing. You should see transaction traces, error reports, and custom metrics within 5-10 minutes of generating traffic.
Configure monitoring alerts
Set up log monitoring script
Create a script to monitor log files for specific patterns and send alerts when issues are detected.
#!/bin/bash
LOG_FILE="/var/log/myapp/application.log"
ERROR_THRESHOLD=10
ALERT_EMAIL="admin@example.com"
TEMP_FILE="/tmp/log-monitor-state"
Create state file if it doesn't exist
if [[ ! -f "$TEMP_FILE" ]]; then
echo "0" > "$TEMP_FILE"
fi
LAST_COUNT=$(cat "$TEMP_FILE")
CURRENT_COUNT=$(grep -c '"level":"ERROR"' "$LOG_FILE" 2>/dev/null || echo "0")
NEW_ERRORS=$((CURRENT_COUNT - LAST_COUNT))
if [[ $NEW_ERRORS -gt $ERROR_THRESHOLD ]]; then
echo "High error rate detected: $NEW_ERRORS new errors" | \
mail -s "PHP Application Alert: High Error Rate" "$ALERT_EMAIL"
fi
Update state file
echo "$CURRENT_COUNT" > "$TEMP_FILE"
Check for specific critical errors
if grep -q "CRITICAL\|FATAL" "$LOG_FILE"; then
RECENT_CRITICAL=$(tail -n 100 "$LOG_FILE" | grep "CRITICAL\|FATAL")
echo "Critical errors detected:\n$RECENT_CRITICAL" | \
mail -s "PHP Application Alert: Critical Error" "$ALERT_EMAIL"
fi
Set up monitoring cron job
Configure automated monitoring to run every 5 minutes and send alerts when issues are detected.
sudo chmod +x /usr/local/bin/log-monitor.sh
sudo crontab -e
# Monitor application logs every 5 minutes
/5 * /usr/local/bin/log-monitor.sh
Rotate logs daily at 2 AM
0 2 * /usr/sbin/logrotate -f /etc/logrotate.d/php-apps
For more advanced monitoring setups, check out our Gunicorn performance monitoring tutorial and ELK Stack centralized logging guide.
Common issues
| Symptom | Cause | Fix |
|---|---|---|
| New Relic agent not reporting data | Invalid license key or daemon not running | sudo systemctl restart newrelic-daemon and verify license key |
| Permission denied writing to log files | Incorrect file ownership or permissions | sudo chown www-data:www-data /var/log/myapp/*.log |
| Log rotation not working | Logrotate configuration syntax error | sudo logrotate -d /etc/logrotate.d/php-apps to test config |
| High memory usage from logging | Excessive log verbosity or retention | Reduce log level in PHP config and adjust rotation frequency |
| Custom metrics not appearing in New Relic | New Relic extension not loaded properly | php -m | grep newrelic to verify, restart PHP-FPM if needed |
| Structured logs not parsing correctly | Invalid JSON format in log entries | Validate JSON output and check rsyslog template configuration |
Next steps
- Set up Prometheus exporters for additional metrics collection
- Configure PHP application caching with Redis and Memcached
- Implement PHP performance profiling with XHProf and Blackfire
- Set up advanced error tracking with Sentry integration
- Implement security monitoring with OWASP tools and threat detection
Automated install script
Run this to automate the entire setup
#!/usr/bin/env bash
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color
# Script variables
SCRIPT_NAME=$(basename "$0")
NEWRELIC_LICENSE_KEY=""
APP_NAME="MyApp Production"
# Usage function
usage() {
echo "Usage: $SCRIPT_NAME --license-key <key> [--app-name <name>]"
echo " --license-key New Relic license key (required)"
echo " --app-name Application name for New Relic (default: MyApp Production)"
echo " --help Show this help message"
exit 1
}
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
--license-key)
NEWRELIC_LICENSE_KEY="$2"
shift 2
;;
--app-name)
APP_NAME="$2"
shift 2
;;
--help)
usage
;;
*)
echo -e "${RED}Unknown option: $1${NC}"
usage
;;
esac
done
if [[ -z "$NEWRELIC_LICENSE_KEY" ]]; then
echo -e "${RED}Error: New Relic license key is required${NC}"
usage
fi
# Check if running as root or with sudo
if [[ $EUID -ne 0 ]]; then
echo -e "${RED}This script must be run as root or with sudo${NC}"
exit 1
fi
# Detect distribution
if [[ ! -f /etc/os-release ]]; then
echo -e "${RED}Cannot detect Linux distribution${NC}"
exit 1
fi
source /etc/os-release
case "$ID" in
ubuntu|debian)
PKG_MGR="apt"
PKG_UPDATE="apt update"
PKG_INSTALL="apt install -y"
PKG_UPGRADE="apt upgrade -y"
PHP_FPM_SERVICE="php-fpm"
WEB_SERVICE="apache2"
PHP_CONFIG_DIR="/etc/php"
LOG_DIR="/var/log"
;;
almalinux|rocky|centos|rhel|ol)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf update -y"
PHP_FPM_SERVICE="php-fpm"
WEB_SERVICE="httpd"
PHP_CONFIG_DIR="/etc/php.d"
LOG_DIR="/var/log"
;;
fedora)
PKG_MGR="dnf"
PKG_UPDATE="dnf check-update || true"
PKG_INSTALL="dnf install -y"
PKG_UPGRADE="dnf update -y"
PHP_FPM_SERVICE="php-fpm"
WEB_SERVICE="httpd"
PHP_CONFIG_DIR="/etc/php.d"
LOG_DIR="/var/log"
;;
amzn)
PKG_MGR="yum"
PKG_UPDATE="yum check-update || true"
PKG_INSTALL="yum install -y"
PKG_UPGRADE="yum update -y"
PHP_FPM_SERVICE="php-fpm"
WEB_SERVICE="httpd"
PHP_CONFIG_DIR="/etc/php.d"
LOG_DIR="/var/log"
;;
*)
echo -e "${RED}Unsupported distribution: $ID${NC}"
exit 1
;;
esac
# Cleanup function for rollback
cleanup() {
echo -e "${YELLOW}Cleaning up on error...${NC}"
systemctl stop newrelic-daemon 2>/dev/null || true
$PKG_MGR remove -y newrelic-php5 2>/dev/null || true
rm -f /etc/newrelic/newrelic.cfg 2>/dev/null || true
rm -f "$PHP_CONFIG_DIR/newrelic.ini" 2>/dev/null || true
rm -f /etc/rsyslog.d/php-monitoring.conf 2>/dev/null || true
}
trap cleanup ERR
echo -e "${GREEN}Starting PHP application monitoring setup with New Relic${NC}"
echo -e "${GREEN}[1/8] Updating system packages${NC}"
$PKG_UPDATE
$PKG_UPGRADE
echo -e "${GREEN}[2/8] Installing required dependencies${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
$PKG_INSTALL wget curl gnupg2 software-properties-common rsyslog logrotate php-dev php-cli php-fpm apache2-dev
else
$PKG_INSTALL wget curl gnupg2 rsyslog logrotate php-devel php-cli php-fpm httpd-devel
fi
echo -e "${GREEN}[3/8] Adding New Relic repository${NC}"
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
curl -fsSL https://download.newrelic.com/548C16BF.gpg | gpg --dearmor -o /etc/apt/keyrings/newrelic.gpg
echo "deb [signed-by=/etc/apt/keyrings/newrelic.gpg] http://apt.newrelic.com/debian/ newrelic non-free" > /etc/apt/sources.list.d/newrelic.list
$PKG_UPDATE
else
rpm -Uvh http://yum.newrelic.com/pub/newrelic/el5/x86_64/newrelic-repo-5-3.noarch.rpm || true
fi
echo -e "${GREEN}[4/8] Installing New Relic PHP agent${NC}"
$PKG_INSTALL newrelic-php5
echo -e "${GREEN}[5/8] Configuring New Relic PHP agent${NC}"
# Create New Relic directory if it doesn't exist
mkdir -p /etc/newrelic
chmod 755 /etc/newrelic
# Find PHP version and configuration directory
PHP_VERSION=$(php -r "echo PHP_MAJOR_VERSION.'.'.PHP_MINOR_VERSION;")
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
PHP_INI_DIR="/etc/php/$PHP_VERSION/mods-available"
mkdir -p "$PHP_INI_DIR"
NEWRELIC_INI="$PHP_INI_DIR/newrelic.ini"
else
PHP_INI_DIR="$PHP_CONFIG_DIR"
NEWRELIC_INI="$PHP_INI_DIR/newrelic.ini"
fi
# Create New Relic PHP configuration
cat > "$NEWRELIC_INI" << EOF
extension = "newrelic.so"
; Application name as it appears in New Relic UI
newrelic.appname = "$APP_NAME"
; License key
newrelic.license = "$NEWRELIC_LICENSE_KEY"
; Enable the agent
newrelic.enabled = true
; Set daemon address
newrelic.daemon.address = "/tmp/.newrelic.sock"
; High security mode
newrelic.high_security = false
; Transaction tracer settings
newrelic.transaction_tracer.enabled = true
newrelic.transaction_tracer.threshold = "apdex_f"
newrelic.transaction_tracer.detail = 1
newrelic.transaction_tracer.slow_sql = true
newrelic.transaction_tracer.stack_trace_threshold = 500
; Error collector settings
newrelic.error_collector.enabled = true
newrelic.error_collector.record_database_errors = true
; Browser monitoring
newrelic.browser_monitoring.auto_instrument = true
; Database monitoring
newrelic.datastore_tracer.database_name_reporting.enabled = true
newrelic.datastore_tracer.instance_reporting.enabled = true
; Custom event settings
newrelic.custom_events.enabled = true
EOF
chmod 644 "$NEWRELIC_INI"
# Enable the module on Debian/Ubuntu
if [[ "$ID" == "ubuntu" || "$ID" == "debian" ]]; then
phpenmod newrelic
fi
echo -e "${GREEN}[6/8] Setting up centralized logging${NC}"
mkdir -p "$LOG_DIR/myapp"
touch "$LOG_DIR/php_errors.log"
touch "$LOG_DIR/myapp/application.log"
chown www-data:www-data "$LOG_DIR/myapp" "$LOG_DIR/myapp/application.log" 2>/dev/null || \
chown apache:apache "$LOG_DIR/myapp" "$LOG_DIR/myapp/application.log" 2>/dev/null || true
chmod 755 "$LOG_DIR/myapp"
chmod 644 "$LOG_DIR/myapp/application.log"
# Configure rsyslog
cat > /etc/rsyslog.d/php-monitoring.conf << 'EOF'
# PHP application log configuration
$ModLoad imfile
# PHP error log
$InputFileName /var/log/php_errors.log
$InputFileTag php-error:
$InputFileStateFile stat-php-error
$InputFileSeverity error
$InputFileFacility local0
$InputRunFileMonitor
# PHP application log
$InputFileName /var/log/myapp/application.log
$InputFileTag myapp:
$InputFileStateFile stat-myapp
$InputFileSeverity info
$InputFileFacility local1
$InputRunFileMonitor
# Log rotation configuration
$WorkDirectory /var/lib/rsyslog
$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
EOF
chmod 644 /etc/rsyslog.d/php-monitoring.conf
echo -e "${GREEN}[7/8] Starting and enabling services${NC}"
systemctl enable rsyslog
systemctl restart rsyslog
systemctl enable newrelic-daemon
systemctl start newrelic-daemon
systemctl restart "$PHP_FPM_SERVICE"
systemctl restart "$WEB_SERVICE"
echo -e "${GREEN}[8/8] Verifying installation${NC}"
# Check if New Relic daemon is running
if systemctl is-active --quiet newrelic-daemon; then
echo -e "${GREEN}✓ New Relic daemon is running${NC}"
else
echo -e "${RED}✗ New Relic daemon is not running${NC}"
exit 1
fi
# Check if PHP extension is loaded
if php -m | grep -q newrelic; then
echo -e "${GREEN}✓ New Relic PHP extension is loaded${NC}"
else
echo -e "${RED}✗ New Relic PHP extension is not loaded${NC}"
exit 1
fi
# Check if rsyslog is running
if systemctl is-active --quiet rsyslog; then
echo -e "${GREEN}✓ Rsyslog service is running${NC}"
else
echo -e "${RED}✗ Rsyslog service is not running${NC}"
exit 1
fi
echo -e "${GREEN}PHP application monitoring setup completed successfully!${NC}"
echo -e "${YELLOW}Next steps:${NC}"
echo "1. Verify your application appears in New Relic dashboard"
echo "2. Configure log rotation for application logs"
echo "3. Set up New Relic alerts and notifications"
echo "4. Test error logging and monitoring"
Review the script before running. Execute with: bash install.sh