Logging & Caching
Getting Started
Introduction InstallationUsage
Usage Connections Session-manager Logging-caching SecurityReference
Testing Examples Auto-publishLogging & Caching
PSR-3 Logging
Every OPC UA client created by OpcuaManager automatically receives a Symfony Monolog logger. The logger captures connection events, retries, errors, and protocol details.
Automatic Injection
The PhpOpcuaSymfonyOpcuaBundle resolves a LoggerInterface from the Symfony container and passes it to OpcuaManager. Every client created through the manager gets this logger via setLogger().
By default, the bundle uses the logger service. When session_manager.log_channel is set to a channel name (e.g. opcua), the bundle resolves monolog.logger.opcua instead:
# config/packages/php_opcua_symfony_opcua.yaml
php_opcua_symfony_opcua:
session_manager:
log_channel: opcuaThis injects the monolog.logger.opcua service into both the OpcuaManager and the opcua:session console command.
Override Per-Client
use Psr\Log\NullLogger;
$client = $this->opcua->connect();
$client->setLogger(new NullLogger()); // disable logging for this clientOverride Per-Connection Config
Pass a logger explicitly in the connection config array (e.g. via connectTo()):
$client = $this->opcua->connectTo('opc.tcp://...', [
'logger' => new NullLogger(),
]);An explicit logger in the config takes precedence over the default Monolog logger.
What Gets Logged
| Level | Events |
|---|---|
DEBUG |
Protocol details, handshake steps |
INFO |
Connections, batch splits, type discovery |
WARNING |
Retry attempts |
ERROR |
Failures, exceptions |
Daemon Logging
The opcua:session command uses the same Monolog channel configured via log_channel. If not specified, the default Symfony logger is used.
Configure a dedicated Monolog channel for the daemon:
# config/packages/monolog.yaml
monolog:
channels:
- opcua
handlers:
opcua:
type: rotating_file
path: '%kernel.logs_dir%/opcua.log'
level: debug
channels: [opcua]# config/packages/php_opcua_symfony_opcua.yaml
php_opcua_symfony_opcua:
session_manager:
log_channel: opcuaPSR-14 Event System
The client supports PSR-14 event dispatching. When an event dispatcher is attached, the client fires events for every significant operation -- 47 event types in total covering connections, reads, writes, subscriptions, security, and more.
Automatic Injection
The bundle automatically injects Symfony's EventDispatcherInterface (when available in the container) into OpcuaManager. Every client created through the manager receives the event dispatcher.
Event Categories
| Category | Example Events | Description |
|---|---|---|
| Connection | Connected, Disconnected, Reconnecting |
Lifecycle of the TCP/secure channel |
| Read | BeforeRead, AfterRead, ReadFailed |
Single and multi-read operations |
| Write | BeforeWrite, AfterWrite, WriteFailed |
Single and multi-write operations |
| Browse | BeforeBrowse, AfterBrowse |
Address space navigation |
| Subscription | SubscriptionCreated, DataChangeNotification |
Pub/sub monitoring |
| Security | CertificateTrusted, CertificateRejected, UntrustedCertificate |
Trust store decisions |
| Method | BeforeCall, AfterCall |
Method invocations |
| Discovery | EndpointsDiscovered, DataTypesDiscovered |
Endpoint and type discovery |
Listening for Events
Use Symfony's #[AsEventListener] attribute to listen for OPC UA events:
use PhpOpcua\Client\Event\DataChangeReceived;
use Symfony\Component\EventDispatcher\Attribute\AsEventListener;
#[AsEventListener]
class OpcuaDataChangeListener
{
public function __invoke(DataChangeReceived $event): void
{
// Handle data change notification
}
}Or register listeners via services.yaml:
# config/services.yaml
services:
App\EventListener\OpcuaReadListener:
tags:
- { name: kernel.event_listener, event: PhpOpcua\Client\Event\AfterRead }Or use an event subscriber:
use PhpOpcua\Client\Event\Connected;
use PhpOpcua\Client\Event\Disconnected;
use PhpOpcua\Client\Event\AfterRead;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class OpcuaEventSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
Connected::class => 'onConnected',
Disconnected::class => 'onDisconnected',
AfterRead::class => 'onAfterRead',
];
}
public function onConnected(Connected $event): void
{
// Log connection
}
public function onDisconnected(Disconnected $event): void
{
// Clean up
}
public function onAfterRead(AfterRead $event): void
{
// Process read result
}
}Per-Client Override
$client = $this->opcua->connect();
$client->setEventDispatcher(null); // disable events for this clientPSR-16 Caching
Every OPC UA client created by OpcuaManager automatically receives a PSR-16 cache. The cache stores browse results, endpoint discovery, and type discovery data.
Automatic Injection
The bundle wraps the configured Symfony cache pool (PSR-6) with Symfony\Component\Cache\Psr16Cache to produce a PSR-16 CacheInterface. This adapter is injected into OpcuaManager and passed to every client via setCache().
# config/packages/php_opcua_symfony_opcua.yaml
php_opcua_symfony_opcua:
session_manager:
cache_pool: cache.app # any PSR-6 cache pool service IDThe default is cache.app. You can use any Symfony cache pool:
# config/packages/cache.yaml
framework:
cache:
pools:
cache.opcua:
adapter: cache.adapter.redis
default_lifetime: 3600# config/packages/php_opcua_symfony_opcua.yaml
php_opcua_symfony_opcua:
session_manager:
cache_pool: cache.opcuaPer-Call Cache Control
Many browse operations accept a useCache parameter:
// Use cache (default)
$refs = $client->browse('i=85', useCache: true);
// Skip cache for this call
$refs = $client->browse('i=85', useCache: false);
// Resolve with cache
$nodeId = $client->resolveNodeId('/Objects/Server', useCache: true);Methods supporting useCache:
browse()browseAll()getEndpoints()resolveNodeId()discoverDataTypes()
Cache Invalidation
// Invalidate a specific node
$client->invalidateCache('i=85');
// Flush all cached data
$client->flushCache();Override Per-Client
use PhpOpcua\Client\Cache\InMemoryCache;
$client = $this->opcua->connect();
$client->setCache(new InMemoryCache(300)); // 300s TTLDisable Caching
$client->setCache(null);Available Cache Drivers
The underlying client provides two built-in PSR-16 implementations:
| Driver | Class | Use case |
|---|---|---|
| In-Memory | InMemoryCache |
Default -- per-process, lost on request end |
| File | FileCache |
Persists across requests, good for CLI workers |
In a Symfony context, you will typically rely on the automatically injected Symfony cache pool (Redis, Memcached, filesystem, etc.) rather than these built-in drivers.
Daemon Caching
The opcua:session command receives the same PSR-16 cache adapter configured via cache_pool. The daemon injects this cache into each OPC UA client it creates.
php_opcua_symfony_opcua:
session_manager:
cache_pool: cache.opcua # Redis-backed pool for the daemonRead Metadata Cache
When read_metadata_cache is enabled, the client caches non-Value attribute reads (DisplayName, DataType, Description, etc.). These attributes rarely change, so caching them avoids redundant round-trips.
Configuration
# config/packages/php_opcua_symfony_opcua.yaml
php_opcua_symfony_opcua:
connections:
default:
endpoint: '%env(OPCUA_ENDPOINT)%'
read_metadata_cache: trueRefresh Parameter
The read() method accepts a refresh parameter to bypass the metadata cache for a specific call:
// Normal read -- uses metadata cache if enabled
$dv = $client->read('ns=2;i=1001');
// Force a fresh read from the server, ignoring any cached metadata
$dv = $client->read('ns=2;i=1001', refresh: true);This is useful when you know the server has changed metadata (e.g. after reconfiguration) and you want to pick up the latest values.
Write Type Auto-Detection Cache
When auto_detect_write_type is enabled (the default), the client discovers the OPC UA data type of a node before writing, so you can omit the type parameter on write(). The discovered types are cached for subsequent writes to the same node.
php_opcua_symfony_opcua:
connections:
default:
endpoint: '%env(OPCUA_ENDPOINT)%'
auto_detect_write_type: true # defaultThis caching works alongside PSR-16 caching. The type discovery results are stored in the same cache backend configured for the client.