Enhancing Application Logging with Tenant Context

Introduction

Effective logging is crucial for monitoring and debugging applications, especially in multi-tenant environments. This post explores how to enhance application logging by incorporating tenant-specific context, enabling better isolation and analysis of log data.

The Importance of Tenant-Aware Logging

In a multi-tenant system, logs from different tenants are often interleaved, making it difficult to isolate issues specific to a single tenant. By adding tenant context to log entries, developers can easily filter and analyze logs for individual tenants, improving troubleshooting and performance monitoring.

Implementing TenantLogger

Instead of using a generic logging facade, a TenantLogger can be implemented to automatically include tenant-specific information in each log entry. This can be achieved through a wrapper around the underlying logging library.

Consider this conceptual example in a PHP-like syntax:

class TenantLogger {
    private $logger;
    private $tenantId;
    private $userId;

    public function __construct($logger, $tenantId = null, $userId = null) {
        $this->logger = $logger;
        $this->tenantId = $tenantId;
        $this->userId = $userId;
    }

    public function info($message, array $context = []) {
        $context = array_merge($context, [
            'tenant_id' => $this->tenantId,
            'user_id' => $this->userId,
        ]);
        $this->logger->info($message, $context);
    }

    // Implement other log levels (warning, error, etc.) similarly
}

// Usage example:
// Assuming $logger is an instance of a standard logger (e.g., Monolog)
$tenantLogger = new TenantLogger($logger, $tenantId, $userId);
$tenantLogger->info('User performed action', ['action' => 'data_export']);

Handling Expected Conditions

It's common to encounter expected conditions that might initially seem like errors but are actually normal in certain contexts. For instance, in a multi-tenant environment, older tenants might not have specific database tables or columns present in newer schemas. Instead of treating these as errors, downgrade them to debug-level logs.

try {
    // Attempt to access a profile setting
    $profileSetting = $this->profileSettingsRepository->getSetting($settingName);
} catch (TableNotFoundException $e) {
    // Downgrade to debug log since older tenants might not have this table
    $this->logger->debug('Profile table missing for tenant', [
        'tenant_id' => $this->tenantId,
        'exception' => $e->getMessage(),
    ]);
    $profileSetting = null; // Or provide a default value
}

Adding Context to Existing Log Entries

When retrofitting an application with tenant-aware logging, ensure that existing log entries are updated to include relevant context (e.g., tenant ID, user ID). This provides a more complete picture when analyzing logs.

$this->logger->info('Processing data', [
    'tenant_id' => $this->tenantId,
    'user_id' => $this->userId,
    'data_size' => $data->getSize(),
]);

Conclusion

By implementing a TenantLogger and incorporating tenant-specific context into log entries, you can significantly improve the effectiveness of application logging in multi-tenant environments. Remember to handle expected conditions gracefully by downgrading them to debug-level logs and to add relevant context to existing log entries. This will lead to better troubleshooting, performance monitoring, and overall application health.

Gerardo Ruiz

Gerardo Ruiz

Author

Share: