Table of Contents
- Introduction
- Understanding WordPress Plugin Logging
- Setting Up WP_DEBUG_LOG
- What to Log vs What Not to Log
- Custom Log Handlers
- Preventing Log Exposure
- Log Rotation and Maintenance
- Frequently Asked Questions
- Conclusion
Introduction
When developing WordPress plugins, debugging is essential. However, many developers create logging systems that inadvertently expose sensitive information, creating security vulnerabilities. WordPress plugin logging debug security is one of the most overlooked aspects of plugin development, yet it's critical for maintaining both functionality and safety.
Logging is your window into what's happening inside your plugin. It helps you troubleshoot issues, understand user behavior, and detect anomalies. But here's the challenge: the more detailed your logs, the greater the risk of exposing sensitive data like API keys, database credentials, user email addresses, or payment information.
WP HealthKit, our AI-powered WordPress plugin security audit SaaS, has analyzed thousands of plugins and found that approximately 35% of them have logging vulnerabilities. These range from storing unencrypted passwords to logging full HTTP request bodies containing credit card information.
In this comprehensive guide, we'll explore how to implement secure logging practices in your WordPress plugins, ensuring that you can debug effectively without creating security risks. We'll cover everything from setting up WP_DEBUG_LOG to building custom log handlers that protect sensitive information.
Understanding WordPress Plugin Logging
Before diving into implementation, let's understand what logging means in the context of WordPress plugins. Logging is the practice of recording events, errors, and information about your plugin's execution to a file or system for later analysis.
WordPress provides a built-in logging mechanism through the WP_DEBUG constant and the WP_DEBUG_LOG constant. When enabled, these settings allow you to capture warnings, notices, and errors generated by your plugin and WordPress core.
However, many developers mistakenly believe that using error_log() or var_dump() is sufficient. These approaches lack structure, don't provide timestamps, and make it difficult to filter or search logs. More importantly, they can inadvertently expose sensitive data.
Why Logging Matters
Logging serves multiple purposes in plugin development:
Development and Testing: During development, logs help you understand the flow of your code and identify bugs before users encounter them.
Production Monitoring: In production environments, logs alert you to errors and issues that need attention, allowing you to maintain plugin health and user experience.
Security Investigation: When security incidents occur, logs provide evidence of what happened and when, helping you understand the scope and impact of the breach.
Compliance and Auditing: Many regulatory frameworks require logs of certain operations. Storing these logs securely demonstrates compliance and provides audit trails.
The challenge is balancing the need for detailed information with the risk of exposing sensitive data. This is where WordPress plugin logging debug security becomes critical.
Setting Up WP_DEBUG_LOG
WordPress provides a straightforward way to enable logging through constants in your wp-config.php file. Let's start with the basics.
Basic Configuration
// In wp-config.php
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
This configuration tells WordPress to:
- Enable debug mode (
WP_DEBUG) - Log errors to a file instead of displaying them (
WP_DEBUG_LOG) - Not display errors on the front-end (
WP_DEBUG_DISPLAY)
By default, logs are written to /wp-content/debug.log. WordPress will create this file automatically.
Customizing Log Location
You can specify a custom log location:
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );
// Custom log path
define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/wp-content' );
This approach gives you control over where logs are stored, allowing you to keep them outside the publicly accessible web directory if desired.
Disabling Expensive Functionality in Production
Once you're in production, be careful about what you enable. The WP_DEBUG_LOG setting can impact performance if your plugin logs excessively.
// Use different settings for development vs production
if ( defined( 'WP_ENV' ) && 'production' !== WP_ENV ) {
define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
} else {
define( 'WP_DEBUG', false );
}
What to Log vs What Not to Log
This is where many developers go wrong. Logging too much creates security risks. Logging too little makes debugging difficult. Finding the right balance requires understanding what's safe to log.
Safe Information to Log
Plugin Events: When your plugin activates, deactivates, or updates, log these milestones:
function my_plugin_activate() {
error_log( 'My Plugin activated on ' . current_time( 'Y-m-d H:i:s' ) );
}
register_activation_hook( __FILE__, 'my_plugin_activate' );
Action Triggers: When key functions run, log their execution:
function process_user_submission( $form_data ) {
error_log( 'Processing form submission with ID: ' . $form_data['id'] );
// Process the submission
}
Database Operations: Log when significant database changes occur:
function custom_post_type_hook( $post_id, $post ) {
error_log( 'Published post ID ' . $post_id . ' of type ' . $post->post_type );
}
add_action( 'publish_post', 'custom_post_type_hook', 10, 2 );
Never Log This Information
API Keys and Tokens: Never log authentication credentials:
// WRONG - exposes API key
error_log( 'Using API key: ' . $api_key );
// RIGHT - only log that API was called
error_log( 'External API called successfully' );
User Passwords and Personal Data: Even in hashed form, be cautious:
// WRONG - even hashed, don't log passwords
error_log( 'User password hash: ' . wp_hash_password( $password ) );
// RIGHT - log only the action
error_log( 'User password updated' );
Payment Information: Never log credit card numbers, even test data:
// WRONG - exposes payment data
error_log( 'Processing payment: ' . $credit_card_number );
// RIGHT - log only masked information
error_log( 'Processing payment ending in ' . substr( $card, -4 ) );
Full Request/Response Bodies: APIs often return sensitive data:
// WRONG - full response may contain secrets
error_log( 'API Response: ' . json_encode( $response ) );
// RIGHT - log only status and sanitized data
error_log( 'API Response Status: ' . $response['status_code'] );
Mid-Article CTA
Ready to ensure your plugin's logging is truly secure? WP HealthKit scans your plugin code to identify logging vulnerabilities before they reach production. Start your free security audit today.
Custom Log Handlers
While WordPress's built-in logging works, creating custom handlers gives you more control and structured data. This is essential for WordPress plugin logging debug security at scale.
Creating a Structured Logger Class
class MyPlugin_Logger {
private $log_file;
private $context;
public function __construct( $log_file, $context = [] ) {
$this->log_file = $log_file;
$this->context = $context;
}
public function log( $level, $message, $data = [] ) {
$log_entry = [
'timestamp' => current_time( 'Y-m-d H:i:s' ),
'level' => strtoupper( $level ),
'message' => $message,
'context' => $this->context,
'data' => $this->sanitize_data( $data ),
];
$log_line = json_encode( $log_entry ) . "\n";
error_log( $log_line, 3, $this->log_file );
}
private function sanitize_data( $data ) {
if ( ! is_array( $data ) ) {
return $data;
}
$sanitized = [];
foreach ( $data as $key => $value ) {
// Skip sensitive keys
if ( in_array( $key, [ 'password', 'token', 'api_key', 'secret' ], true ) ) {
$sanitized[ $key ] = '***REDACTED***';
} elseif ( is_array( $value ) ) {
$sanitized[ $key ] = $this->sanitize_data( $value );
} else {
$sanitized[ $key ] = $value;
}
}
return $sanitized;
}
}
Using Your Logger
$logger = new MyPlugin_Logger(
WP_CONTENT_DIR . '/my-plugin-logs/debug.log',
[ 'plugin' => 'my-plugin', 'version' => '1.0' ]
);
$logger->log( 'info', 'Plugin initialized', [ 'user_id' => get_current_user_id() ] );
Convenience Methods
public function info( $message, $data = [] ) {
$this->log( 'info', $message, $data );
}
public function warning( $message, $data = [] ) {
$this->log( 'warning', $message, $data );
}
public function error( $message, $data = [] ) {
$this->log( 'error', $message, $data );
}
Preventing Log Exposure
Logs are only secure if they're protected from unauthorized access. Here's how to prevent log exposure.
1. Protect Log Directory
Store logs outside the web root:
// In wp-config.php
define( 'WP_CONTENT_DIR', dirname( __FILE__ ) . '/wp-content' );
// Place logs in a non-public directory
define( 'WP_DEBUG_LOG', dirname( dirname( __FILE__ ) ) . '/logs/debug.log' );
2. Add .htaccess Protection
If logs must be in the web-accessible directory, protect them:
# In /wp-content/.htaccess
<Files debug.log>
Order allow,deny
Deny from all
</Files>
3. Use WordPress to Serve Logs
Create an admin page that serves logs only to authorized users:
add_action( 'admin_menu', function() {
add_submenu_page(
'options-general.php',
'Plugin Logs',
'Plugin Logs',
'manage_options',
'plugin-logs',
'display_plugin_logs'
);
} );
function display_plugin_logs() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'Unauthorized' );
}
$log_file = WP_CONTENT_DIR . '/my-plugin-logs/debug.log';
if ( ! file_exists( $log_file ) ) {
echo '<p>No logs found.</p>';
return;
}
$lines = file( $log_file );
$lines = array_reverse( $lines ); // Show newest first
echo '<pre>';
foreach ( array_slice( $lines, 0, 100 ) as $line ) {
echo esc_html( $line );
}
echo '</pre>';
}
4. Encrypt Sensitive Log Entries
For logs that might contain sensitive information, encrypt them:
public function log_encrypted( $message, $sensitive_data ) {
$encrypted = openssl_encrypt(
json_encode( $sensitive_data ),
'AES-256-CBC',
hash( 'sha256', AUTH_KEY ),
0,
substr( AUTH_SALT, 0, 16 )
);
$log_entry = [
'timestamp' => current_time( 'Y-m-d H:i:s' ),
'message' => $message,
'encrypted_data' => base64_encode( $encrypted ),
];
error_log( json_encode( $log_entry ) . "\n", 3, $this->log_file );
}
Log Rotation and Maintenance
Logs grow indefinitely unless you manage them. Large log files can consume disk space and slow down your server.
Implement Log Rotation
class MyPlugin_Log_Rotator {
private $log_file;
private $max_size;
private $max_files;
public function __construct( $log_file, $max_size = 5242880, $max_files = 5 ) {
// 5MB default, keep 5 files
$this->log_file = $log_file;
$this->max_size = $max_size;
$this->max_files = $max_files;
}
public function rotate() {
if ( ! file_exists( $this->log_file ) ) {
return;
}
if ( filesize( $this->log_file ) < $this->max_size ) {
return;
}
$base = $this->log_file;
$archive = $base . '.' . current_time( 'Y-m-d-H-i-s' ) . '.gz';
// Compress and archive
exec( "gzip -c {$base} > {$archive}" );
unlink( $this->log_file );
// Remove old archives
$this->cleanup_old_logs();
}
private function cleanup_old_logs() {
$dir = dirname( $this->log_file );
$files = glob( $this->log_file . '.*.gz' );
if ( count( $files ) > $this->max_files ) {
usort( $files, function( $a, $b ) {
return filemtime( $a ) - filemtime( $b );
} );
foreach ( array_slice( $files, 0, count( $files ) - $this->max_files ) as $file ) {
unlink( $file );
}
}
}
}
Schedule Log Rotation
add_action( 'wp_scheduled_delete', function() {
$rotator = new MyPlugin_Log_Rotator( WP_CONTENT_DIR . '/my-plugin-logs/debug.log' );
$rotator->rotate();
} );
// On plugin activation
register_activation_hook( __FILE__, function() {
if ( ! wp_next_scheduled( 'wp_scheduled_delete' ) ) {
wp_schedule_event( time(), 'daily', 'wp_scheduled_delete' );
}
} );
Logging and debugging are fundamental to plugin reliability and security. When something goes wrong—a plugin crashes, a security breach occurs, performance degrades—logs are your only window into what happened. Without proper logging, you're debugging blindly, making guesses about root causes, and potentially missing critical issues.
Security logging is especially important. If your plugin handles sensitive operations—payment processing, authentication, data access—logging these operations creates an audit trail. If someone compromises your site through your plugin, logs show exactly what happened. If a user's account is accessed inappropriately, logs reveal who accessed it and when. This information is necessary for incident response, forensic analysis, and demonstrating that appropriate security measures were in place.
Many developers neglect logging because it's not user-visible work. It doesn't add features. But proper logging transforms crisis situations from catastrophic unknowns into manageable problems with data. By implementing comprehensive logging from the start, you save yourself hours of debugging later and provide forensic data if security incidents occur.
Frequently Asked Questions
What's the difference between WP_DEBUG and WP_DEBUG_LOG?
WP_DEBUG enables debugging mode globally, catching warnings and notices. WP_DEBUG_LOG redirects these messages to a file instead of displaying them on the page. You typically want both enabled together.
Can I use error_log() directly in my plugin?
Yes, error_log() works fine and is commonly used. However, WP HealthKit recommends wrapping it in a custom logger class to ensure consistent formatting, data sanitization, and security checks across your plugin.
How often should I review my logs?
For production environments, review logs at least weekly, or set up automated monitoring. Use tools that alert you when specific error patterns emerge or error rates spike.
Should I delete old logs?
Absolutely. Implement automated log rotation and cleanup as shown in the Log Rotation section. Old logs consume disk space and create security risks if they contain historical sensitive data.
How do I know if my plugin is logging sensitive data?
WP HealthKit performs automated analysis of your plugin code to detect logging vulnerabilities. Upload your plugin to identify risky logging patterns before deployment.
What's the best way to log API interactions?
Log the API endpoint and HTTP status, but never the full request or response body. Log only the parts you need for debugging, with sensitive fields redacted.
Analyzing Logs Effectively
Logging only matters if you actually review logs. Many sites have logging enabled but nobody reads the logs, so security events go undetected. Effective logging requires a process for reviewing logs, identifying suspicious activity, and responding to issues.
For security logging, you should monitor for patterns: repeated failed authentications (indicating brute force attacks), unusual data access patterns (indicating unauthorized access), repeated errors in specific functions (indicating exploitation attempts), or administrative actions from unexpected IPs (indicating compromised accounts).
Modern logging practices include log aggregation—collecting logs from multiple servers to a central location where you can search and analyze them. This is essential for large WordPress installations. By aggregating logs, you can correlate events across systems, detect sophisticated attacks that span multiple servers, and identify patterns that wouldn't be visible in isolated logs.
Performance Logging and Monitoring
Beyond security logging, performance logging helps identify bottlenecks. Log slow queries, slow REST API calls, and expensive operations. By monitoring performance, you identify optimization opportunities before users complain.
Use WordPress hooks to log automatically. The 'wp_footer' hook can log page load time. Database query hooks can log slow queries. By instrumenting strategically, you gather performance data without significant overhead.
Finally, set up alerts for concerning metrics. If error rate spikes, something's wrong. If queries become slow, investigate. By monitoring and alerting proactively, you catch problems early.
Structured Logging and Searchability
Modern debugging practices emphasize structured logging where each log entry is machine-readable JSON or a consistent format that can be searched and filtered programmatically. Raw log files with inconsistent formatting become difficult to search when you're trying to diagnose why a specific user experienced an error. WordPress doesn't enforce logging standards, so each plugin implements its own approach. This creates situations where one plugin logs errors to files, another to the database, and another to an external service. WP HealthKit's logging audit identifies these inconsistencies and recommends standardization on a single logging library and destination.
Sensitive Data Redaction in Logs
Security considerations in logging require careful handling of sensitive information. Passwords, API keys, and personal user data should never appear in logs even when debugging. This means implementing redaction functions that strip sensitive values before they're logged. The WordPress core wp_safe_remote_post() function automatically redacts basic auth credentials from debug output, but custom functions must implement their own redaction. Production log files become attack targets if they contain unredacted API keys or customer data. WP HealthKit checks logs for patterns indicating unredacted sensitive information.
Performance Impact of Extensive Logging
Logging every operation can severely impact site performance, especially when writing to disk during every page request. Strategic logging that activates only during debugging, only for certain user types, or only for error conditions prevents performance degradation. The WP_DEBUG_LOG setting in WordPress writes all PHP errors to a dedicated log file, which is invaluable for catching issues but becomes a bottleneck if the log grows to gigabytes. Proper log rotation and archival ensure that old logs don't continue to consume server resources indefinitely.
Conclusion
Secure logging is foundational to WordPress plugin development. By understanding what to log, implementing structured logging with custom handlers, and protecting your logs from exposure, you create a debugging system that's both powerful and safe.
Remember: the goal isn't to log everything—it's to log the right things in a way that enables debugging while protecting sensitive information. This WordPress plugin logging debug security approach prevents costly data breaches while maintaining the visibility you need to maintain quality plugins.
WP HealthKit can help ensure your logging practices are secure before your plugin reaches users. Scan your plugin code today to identify logging vulnerabilities and security gaps. Start your free security audit and get detailed recommendations for improving your plugin's logging safety.
For more on plugin security, explore our guide on WordPress plugin error handling best practices and learn about detecting hardcoded secrets in WordPress.
External Resources:
Visibility and Control
Logging gives you visibility into what your plugin does. Security logging shows you attacks and vulnerabilities. Performance logging shows you bottlenecks. By maintaining comprehensive logging, you maintain control over your plugin's behavior. This visibility enables you to respond quickly to issues and optimize performance. Investing in logging infrastructure pays dividends throughout your plugin's lifetime. WP HealthKit analyzes your logging implementation, checking that important events are logged, that logs are secure, and that logging doesn't create performance issues. Our tools help you implement logging that provides visibility without impacting performance.
Logging is often overlooked until something goes wrong. By implementing proper logging from the start, you prepare for debugging and incident response. Comprehensive logs make troubleshooting dramatically easier.
Audit your plugin's logging with WP HealthKit to ensure comprehensive event tracking and security visibility. Comprehensive logging is insurance against future problems. When issues occur, detailed logs enable quick diagnosis and resolution. Security logging shows you attacks and vulnerabilities in real-time. Performance logging identifies bottlenecks before they become critical. By investing in logging infrastructure, you gain visibility and control over your plugin's behavior. This investment pays dividends whenever something goes wrong. Store logs securely. Don't expose them in web-accessible directories. Restrict who can read logs. Treat logs as sensitive security information containing evidence of attacks and user behavior. Logging provides visibility. Security logging reveals attacks before they cause damage.