bookmark_borderHorde/Log Rewrite goes PSR-3

I have rewritten Horde/Log based on the PSR-3 Logging standard published by PHP-FIG.

Why?

It had to been done at some point. The current wave of Corona pandemic has cancelled some joyful other activities planned for this weekend and I had been looking into PSR-3 loggers for quite some time. Most importantly, I wanted to do something else which needed a separeate logging facility and I was not ready to invest time into the various pitfalls of the old logger design. Just look at the Logger Factory – it is much too complex. Currently, there is no good balance between having too few logs in general or being flooded with mostly useless details of all the different aspects of horde. Filtering is essential, but the data cannot easily be divided into the contexts that are relevant to different problems and tasks.

Goals

My main goal was simple: Consuming code should be able to give the Logger more context about the messages it sends. This can be helpful to sort out what is interesting and what is just distracting noise. For example, a logger-aware library or application may send markers along with the actual message. All messages go to the same logger, but different log handlers may be set up to only care about certain aspects. Want to write all caldav sync errors for a specific user to a separate file? Want to keep a separate log of failed login attempts? Want to forward your time tracking application’s “project closed” log events to an external json-consuming system? Even though the old logger had all the necessary parts, it made these tasks too difficult.

As a product, the new logger is not very interesting outside the Horde context. While it can be used for logging in PSR-3 aware libraries, it still has too many dependencies on other parts of the Horde ecosystem. To reduce this, I may factor out the Constraints log filter into a separate library. The opposite case is more interesting: If PSR-3 replaces tight coupling to a custom logger, projects may use their existing logger. They have one less alien dependency to deal with. This might make some libraries more attractive, like Horde/Activesync or Horde/Imap.

From a code quality perspective, I also wanted to make the code more transparent to readers and tools. The old implementation relied on a __call magic method without really needing it, multiple parts relied on tersely documented array structures. The new implementation passes PHPStan Level 8. Coverage with parameter and return type hints is very high, with native property and return types following where possible. The current implementation is based on version 1.1.4 of the standard. When moving to a PHP 8 minimum requirement, this can be upgraded to the more strictly typed version 3.0 standard.

However, I am still missing unit tests against the new code. As it is substantially different from the H5 implementation, I could not easily adapt the existing test cases. This will require more work.

Also, integration of the new Logger into the core system is a separate task. Old and new logger infrastructure will have to coexist for some time. There is simply too much code that needs to be touched.

Architecture

The logger is architected as a modular system. The consuming code only has to deal with the Horde\Log\Logger. It implements the Psr\Log\LoggerInterface. The logger can support custom log levels not covered by RFC 5424. Log Levels are implemented as objects with a string name and a criticality number value.

The PSR-3 standard mandates log messages may be strings or any object that can be turned into a string. Internally, we convert them into LogMessage objects containing the string message, a reference to the LogLevel object and a hash of context attributes.

LogFilters are gatekeepers which look into a LogMessage and decide if it may be logged. They can be used as global LogFilters to suppress a log message altogether or as local filters which only affect a certain log handler. The Logger may include many different LogHandlers. These implement the actual processing of logs, writing them to a file, to a local syslog program or sending them over the network. Log messages may further be formatted for different needs. One handler may want to send XML documents to another server, another handler may store plaintext in a structured file. PSR-3 proposes a templating format where the logger can fill placeholders in the message with data from the context array. In Horde/Log, this job is done by a series of LogFormatters. Depending on configuration, a LogHandler can have zero, one or many such LogFormatters. Not all combinations make sense.

PHP 8 readiness

The new code is ready to run on PHP 7.4 and PHP 8. However, the horde/constraint and horde/thrift dependencies have not yet been upgraded for PHP 8, limiting usefulness. This will be done as time permits, with many other topics having higher priority.

References

bookmark_borderOctober Review: TOTP in Horde

I have been working on multiple things recently.

Kronolith Web UI: Appointment Cancellation Bug

Fix an annoying bug where internal user attendees get cancellation mails when an appointment is updated by the owner. This only seems to happen from the Web UI, not from CalDAV. I already analysed how this is happening. The fix is going to be a little bigger as I do not want to invest in the legacy infrastructure (socalled “Imples”) and use the opportunity to use a more modern approach. Work is in progress.

New Material UI based frontend for passwd.

I have worked with the team on a Material Design based UI. It uses ReactJs and Typescript and the new horde/http_server library and it is very different from existing Horde UIs. Do not expect it to blend well with the existing horde look&feel. The whole thing is a proof of concept and is an alien as the DIMP UI was back in Horde 3. This proof of concept still lives in a public feature branch and if you want to try it, you need to enable a new setting in the Preferences Screen.

Two-Factor support in Horde Base and a TOTP library

More and more online services start using two-factor authentication for improved security. Along with a password, users have to enter some passcode they read from a keychain fob device or from an app on their phones (like Google Authenticator).

I have started a new library horde/otp which implements TOTP and other styles of passcodes used as a secondary authentication factor. The library needs some additional glue code in horde/core and horde/base which still has to be built. I would have liked to finish this in October but there is only so much time.

Improved horde-installer-plugin

The composer plugin for Horde has received some refactoring and enhancements. The current feature branch offers a custom command in the composer CLI . This custom command rebuilds the relevant configuration files when you move your Horde installation after running the install/update commands. There are also some minor changes to the way configurations are written. End users should not notice.

DNS library

B1 Systems have finally opensourced a DNS library for the Horde ecosystem. It has been used internally for some years. The library can serve as the DNS building block of an IPAM system, but it also has an adapter to apply changes to the Amazon Route 53 service.

PHPStan support

Beginning this month, libraries and apps will gradually introduce the static analyzer tool phpstan. The tool will run as part of the CI pipeline and detect various types of code imperfections which potentially can mean hard-to-detect bugs. The findings will be addressed as time permits.