Today I want to present a new feature we will introduce with Stubbles 1.1.0: delayed logging. Stubbles already offers a simple to use but powerful logging API, so what can we achieve with this new feature and how to use it?
But first I want to take a look at how log data is generated and written. If you want to log something you first call $logger->createLogEntry('aTarget) which gives you an instance of LogEntry. Depending on the log entry factory which is actually used to create the instance this LogEntry instance might already be prefilled with some data, e.g. the session id, a timestamp and so on and so forth. After that you fill this instance with the data you actually want to log by using its addData() method, and finally calling log() to get it written to a logfile, a database or whatever appender you use.
Now, what happens if you want to log data but when some data utilized by the log entry factory is not available at this point? An example might be the startup phase of your application, where some of the log data depends on calculations done in a later step, but you have to log some data from the current step which in turn is a prerequisite for the later step. Until now this was only possible using ugly workarounds and hacks - and if you are using Stubbles one thing is for sure: you neither like workarounds nor hacks.
Stubbles 1.1.0 and delayed logging to the rescue: we extended the log entry class with a new logDelayed() method which will take care the logger knows about this log entry, but does not hand it over to the log appenders yet. Instead, the logger keeps the log entry in memory, and waits until someone calls it's processDelayedLogEntries() method, or (more likely) the logger instance gets destroyed. If this happens it asks the log entry factory to recreate the log entry in question before handing it to the log appenders, and this gives the log entry factory the chance to replace any earlier not available data with the current version before the log entry is finally written.
Sounds good? Well, as all great stuff this comes with a price. The log entry factory interface had to be extended, so if you upgrade your application you have to change your log entry factory implementation as well: either provide an own implementation of the new recreate() method or extend the abstract log entry factory class introduced with 1.1.0 which provides a default implemention.
Why this API break? We had two options: either provide a new interface or do the API break. Introducing a new interface would have saved us from the API break, but would be more effort to test and implement, harder to document and understand by (future) users and leading to more complications in the future as well. On the other hand, this is a minor release where API breaks are allowed. Keeping backward compatibility only to be friendly is nothing we should aim at as the API break would be have to be done somewhere in the future anyway, by deprecating the old interface. As users tend to delay API upgrades whenever they can it is better to force them to do it as early as possible.
Interceptors in Stubbles allow to intercept (yeah...) the request before the actual processor is called (PreInterceptor) and after the processor has done it's work to create the response (PostInterceptor) but before this response is sent back to the user agent. This is quite powerful and allows manipulating the request before any real work is done as well as manipulating the response before it is sent. Usage examples range from setting up the correct variant for the website depending on request parameters or cookies up to turning the response from a default 200 OK into a 304 Not Modified status.
Until now creation of interceptors and their dependencies was quite limited as they were configured via XJConf and not with Stubbles' own IoC framework which means there was no proper way to get an interceptors' dependencies injected. It was only possible by using the stubBinderRegistry as a kind of service locator, which means to actively look for things instead of just ask for them as explicit dependencies via constructor and/or setter method parameters. Fortunately, this is going to change with Stubbles 1.1.0.
Stubbles 1.1.0 introduces a new interceptor initializer based on a property file. The property file contains a list of both pre- and postinterceptor class names, and this new interceptor initializer will iterate through this list and create every interceptor instance using Stubbles IoC, allowing you to make full usage of Stubbles IoC features in your interceptor class. No more looking for things which makes your interceptors hard to test, but just explicitly asking for dependencies.
What about the old way? Stubbles 1.1.x will still support the old way using xml configuration files and XJConf without Stubbles IoC, but you have to activate it explicitly when upgrading. To do this, put a call to stubWebsiteBindingModule::usingInterceporInitializer(
'net::stubbles::ipo::interceptors::stubInterceptorXJConfInitializer') in your index.php file (please note that this is not a static method call, but a method call on an instance of stubWebsiteBindingModule). However, we encourage to upgrade to the new behaviour, as it is more powerful and much cleaner. The old way becomes deprecated with 1.1.0 and will be removed with 1.2.0 or 2.0.0 (whichever comes first, we don't know yet).
Today I gave a short talk about Stubbles' website cache in our weekly team meeting. The talk explains what the website cache is good for, how it basically works, and what a developer should take into account when developing new page elements. You can find the slides of the talk in the manual for the website cache, see attachments.