[Proposal] LoggerInterface

382 views
Skip to first unread message

Jordi Boggiano

unread,
Nov 16, 2012, 6:05:25 AM11/16/12
to php...@googlegroups.com
Heya,

I would like to start discussing the creation of a LoggerInterface.

Logging is something that pretty much everything can make use of and
where having one unified interface that all libraries can type hint
against would be of tremendous value.

I looked at a few implementations, and have observed different types:

- One single point of entry like ->write() [1][2]. This is not ideal for
an interface since it does not make it easy to abstract the various log
levels.
- Static methods ::$logLevel() [3]. Again not very suitable for an
interface since injecting a classname to call statically is not really a
common practice.
- Separate instance methods ->$logLevel() [4][5]. This is the only model
really fitting IMO and the one I picked.

For compatibility's sake I picked short methods like emerg/warn/info. It
is not my preference - I think the addEmergency/addWarning/addInfo [6] I
used in Monolog are more explicit, but it's probably not worth arguing over.

The interface and a noop logger class can be found in the gist below,
please keep comments here though.

https://gist.github.com/4086282

The main logging implementations I know of are the one in ZF [5],
Monolog [6] (used by Symfony, soon Drupal, maybe others?), log4php [7].
Please correct me if I missed anything used widely outside of framework
specific implementations.

ZF and Monolog are compatible with the proposal, log4php supports only 5
of the 8 levels, but is otherwise compatible, so if they have interest
in this I guess they could easily add the 3 more levels.

Cheers

[1]
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Log/CakeLogInterface.php
[2]
http://git.typo3.org/FLOW3/Packages/TYPO3.FLOW3.git?a=blob;f=Classes/TYPO3/Flow/Log/LoggerInterface.php
[3] https://github.com/UnionOfRAD/lithium/blob/master/analysis/Logger.php
[4]
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Log/LoggerInterface.php
[5]
https://github.com/zendframework/zf2/blob/master/library/Zend/Log/LoggerInterface.php
[6] https://github.com/Seldaek/monolog/blob/master/src/Monolog/Logger.php
[7] https://github.com/apache/log4php/blob/trunk/src/main/php/Logger.php

--
Jordi Boggiano
@seldaek - http://nelm.io/jordi

Johannes Schmitt

unread,
Nov 16, 2012, 6:30:46 AM11/16/12
to php...@googlegroups.com
Is there a reason the message only "should" be a string instead of "must" be a string? Same for exception. If we do not enforce that, then we basically have no interface, and implementors will have to do type checks, and potentially throw exceptions if they don't like what they got as message. Obviously, this would come at a performance cost.

Apart from that it would be cool to have such an interface.

Cheers,
Johannes


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.



Jordi Boggiano

unread,
Nov 16, 2012, 6:33:47 AM11/16/12
to php...@googlegroups.com
On 16.11.2012 12:30, Johannes Schmitt wrote:
> Is there a reason the message only "should" be a string instead of
> "must" be a string? Same for exception. If we do not enforce that, then
> we basically have no interface, and implementors will have to do type
> checks, and potentially throw exceptions if they don't like what they
> got as message. Obviously, this would come at a performance cost.

Turned the string into a MUST, and I fixed the wording of the exception
thing to be what I actually meant, that if you pass an exception you
must put it in an "exception" key, but an exception key must not be an
exception instance IMO, because the context array might very well
contain arbitrary data in some cases that the developer does not control.

I hope this makes sense.

Cheers

Johannes Schmitt

unread,
Nov 16, 2012, 6:37:19 AM11/16/12
to php...@googlegroups.com
Thanks.

Another minor thing, you have not defined a return type for the methods. My guess is that the return type is not meant to be meaningful which would be good to make clear somehow (either @return void, @return null, or some text somewhere).

Cheers
Johannes


Jordi Boggiano

unread,
Nov 16, 2012, 6:45:42 AM11/16/12
to php...@googlegroups.com
Heya,

> Another minor thing, you have not defined a return type for the methods.
> My guess is that the return type is not meant to be meaningful which
> would be good to make clear somehow (either @return void, @return null,
> or some text somewhere).

Good point. I added @return null to all the methods. I think ZF returns
$this, but I honestly don't see much point in chain-ability of logging
calls.

Francois Zaninotto

unread,
Nov 16, 2012, 9:05:10 AM11/16/12
to php...@googlegroups.com
Hi Group,

Let me chime in on that subject. Log messages are mostly written for machines rather than humans, and machines suck at interpreting messages. I have seen several initiatives lately to provide structured logging through JSON objects - compatible with log mining softwares like Splunk. I think this goes into the right direction, so it would be a pity if the upcoming PHP LoggingInterface standard forbid this...

My 2c,

François


2012/11/16 Johannes Schmitt <schmi...@gmail.com>

Jordi Boggiano

unread,
Nov 16, 2012, 9:26:29 AM11/16/12
to php...@googlegroups.com
Heya,

> Let me chime in on that subject. Log messages are mostly written for
> machines rather than humans, and machines suck at interpreting messages.
> I have seen several
> <http://journal.paul.querna.org/articles/2011/12/26/log-for-machines-in-json/>
> initiatives
> <http://blog.nodejs.org/2012/03/28/service-logging-in-json-with-bunyan/>
> lately to provide structured logging through JSON objects - compatible
> with log mining softwares like Splunk. I think this goes into the right
> direction, so it would be a pity if the upcoming PHP LoggingInterface
> standard forbid this...

I agree, but I don't see how the current interface limits this. You have
a message and then you can pass an array. This array can easily be
serialized to json. For example the GelfHandler in monolog does that:

https://github.com/Seldaek/monolog/blob/master/src/Monolog/Formatter/GelfMessageFormatter.php

It appends the whole context array as Graylog "additional" data.

There is also a Logstash PR that adds a logstash-JSON format:
https://github.com/Seldaek/monolog/pull/124/files

Maybe we can do more, but then it'd be good if you can specify what you
are thinking about.

Cheers

Francois Zaninotto

unread,
Nov 16, 2012, 10:52:27 AM11/16/12
to php-fig
I was reacting to the guideline "the message MUST be a string". But maybe I'm misinterpreting the comment.


2012/11/16 Jordi Boggiano <j.bog...@seld.be>

Jordi Boggiano

unread,
Nov 16, 2012, 10:56:36 AM11/16/12
to php...@googlegroups.com
On 16.11.2012 16:52, Francois Zaninotto wrote:
> I was reacting to the guideline "the message MUST be a string". But
> maybe I'm misinterpreting the comment.

Well, I think usually you have an event that you log, with the message
as the short description of what this log record represents, and then an
array of data attached so that you can make sense of it all. Indeed
stashing everything in the message means you lose most parseability, but
I don't think dumping a bunch of data without a message attached is very
useful either.

Obviously nothing prevents you to call ->warn('', ['req' => $request])
though.

Matthew Lanigan

unread,
Nov 16, 2012, 10:56:30 AM11/16/12
to php...@googlegroups.com
I have to admit that I'm a bit confused as well. JSON *is* a string, so what's the problem?

Is the desire here to pass an object to the interface? There is no difference between a "JSON object" and any other object in PHP, and if objects are allowed to be passed it is not guaranteed that they will be meaningfully serializable. And in any case, the object can just be serialized before passing it to the interface.

Matthew

FGM at GMail

unread,
Nov 16, 2012, 11:04:42 AM11/16/12
to php...@googlegroups.com
One important (IMO) addition the Drupal <= 7.x logging interface (and
a few others) have is the notion that an event is actually not "a
string + some data", but "an event type + event-type-specific
parameters + some data"

In Drupal <= 7.x these event types are implemented as just event
message templates and the parameters as variables susbtitutions into
the template, but this is just one specific implementation which
needn't be limiting. It has however proven useful as a way to browse
data conveniently as in the mongodb_watchdog module, which uses these
templates (event types) as a grouping element.

Of course this can be mimicked by adding extra processing based on the
"some data" like the ones available in Monolog, but I think it is more
useful to have the mimicking done the other way round: defining an
interface with this slightly more evolved parameter specification,
which can very simply used in the degraded (string + data) mode, while
still providing to logs aggregators, which typically work in more
load-intensive scenarios, the more structured information without
having them do extra work to get it.

2012/11/16 Jordi Boggiano <j.bog...@seld.be>:

Jordi Boggiano

unread,
Nov 16, 2012, 11:18:07 AM11/16/12
to php...@googlegroups.com
Heya,

> In Drupal <= 7.x these event types are implemented as just event
> message templates and the parameters as variables susbtitutions into
> the template, but this is just one specific implementation which
> needn't be limiting. It has however proven useful as a way to browse
> data conveniently as in the mongodb_watchdog module, which uses these
> templates (event types) as a grouping element.
>
> Of course this can be mimicked by adding extra processing based on the
> "some data" like the ones available in Monolog, but I think it is more
> useful to have the mimicking done the other way round: defining an
> interface with this slightly more evolved parameter specification,
> which can very simply used in the degraded (string + data) mode, while
> still providing to logs aggregators, which typically work in more
> load-intensive scenarios, the more structured information without
> having them do extra work to get it.

I am not entirely sure I get your point, but I would say that's maybe
something that needs to be addressed in implementations (though it could
be documented in the interface to nudge people to use it), for example
monolog already allows you to use extra data in the message, but this
could be expanded to allow context data as well, like:

$logger->info('Created user %context.user%', ['user'=>$username]);

That way you can indeed do grouping on the message since it's a generic
"type" and yet you can rebuild a full message from the data + message
type for human consumption.

Now I am not sure I got it right, but I also am wondering whether this
is really better than a simpler:

$logger->info('Created user', ['user'=>$username]);

This won't allow you to get the full info, the human consumption would
look more like:

Created user {"user": "Bob"}

Assuming you json_encode the context to display anything easily to users.

Johannes Schmitt

unread,
Nov 16, 2012, 11:37:34 AM11/16/12
to php...@googlegroups.com
The problem if you leave this up to the implementation is that we don't need to define an interface because then implementations are not exchangable.


Jordi Boggiano

unread,
Nov 16, 2012, 11:43:09 AM11/16/12
to php...@googlegroups.com
On 16.11.2012 17:37, Johannes Schmitt wrote:
> The problem if you leave this up to the implementation is that we don't
> need to define an interface because then implementations are not
> exchangable.

Agreed, yet it is also hard to define this in an interface since it's
more usage kind of stuff. I am happy to keep discussing this and see if
anyone has a suggestion though.

The way I see it we need to enter simple, native php data types and then
the logger implementation has to process it one way or another depending
on the logging backend, because some stuff is meant for humans, some for
machines.

Francois Zaninotto

unread,
Nov 16, 2012, 11:51:25 AM11/16/12
to php...@googlegroups.com
IMHO, one of the log practices consists of logging what you call the 'context' with no 'message'. If the interface forces to pass an empty message, it feels somehow hackish to me.

You may answer that your proposal is message-oriented, and that other logging strategies require another interface. I'd agree, but in that case this should not be a "standard".

Anyway, feel free to ignore my 2c, you're more an expert in logging than I am ;)


2012/11/16 Johannes Schmitt <schmi...@gmail.com>

Jordi Boggiano

unread,
Nov 16, 2012, 12:16:59 PM11/16/12
to php...@googlegroups.com
> IMHO, one of the log practices consists of logging what you call the
> 'context' with no 'message'. If the interface forces to pass an empty
> message, it feels somehow hackish to me.
>
> You may answer that your proposal is message-oriented, and that other
> logging strategies require another interface. I'd agree, but in that
> case this should not be a "standard".

Obviously one possibility is to allow the message to be anything, and
then if you want to put structured data instead of a string you can. In
that case the context array would probably be irrelevant, since it's
main purpose is to provide more context around a given message.

If we look at the origin of the context, I would say it's just there to
make up for the lack of information and structure that the simple string
message can contain. So in a way I would say you're right and we should
just always pass an array, and if it has a message key that's cool we
can show a pretty message. After all if we build a new interface we can
throw away historical reasons.

But in practice I wonder if that's not going to be annoying to use. I
have the feeling that in most cases a simple string fits quite well.
Maybe that's just because the interface is at the moment set in a way
that it encourages you to use just a string.

> Anyway, feel free to ignore my 2c, you're more an expert in logging than
> I am ;)

Eh nobody is ignoring anyone :) I'm just used to do things one way, so
bear with me if it takes time to adjust.

Paul Jones

unread,
Nov 16, 2012, 12:22:49 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 11:16 AM, Jordi Boggiano wrote:

>> IMHO, one of the log practices consists of logging what you call the
>> 'context' with no 'message'. If the interface forces to pass an empty
>> message, it feels somehow hackish to me.
>>
>> You may answer that your proposal is message-oriented, and that other
>> logging strategies require another interface. I'd agree, but in that
>> case this should not be a "standard".
>
> Obviously one possibility is to allow the message to be anything, and
> then if you want to put structured data instead of a string you can. In
> that case the context array would probably be irrelevant, since it's
> main purpose is to provide more context around a given message.
>
> If we look at the origin of the context, I would say it's just there to
> make up for the lack of information and structure that the simple string
> message can contain. So in a way I would say you're right and we should
> just always pass an array, and if it has a message key that's cool we
> can show a pretty message. After all if we build a new interface we can
> throw away historical reasons.
>
> But in practice I wonder if that's not going to be annoying to use. I
> have the feeling that in most cases a simple string fits quite well.
> Maybe that's just because the interface is at the moment set in a way
> that it encourages you to use just a string.

I think having $context (or perhaps $data, or some other name) for an array of additional stuff is a good idea.



-- pmj

Drak

unread,
Nov 16, 2012, 12:40:13 PM11/16/12
to php...@googlegroups.com
On 16 November 2012 17:16, Jordi Boggiano <j.bog...@seld.be> wrote:
But in practice I wonder if that's not going to be annoying to use. I

I think that woould be immensely annoying.
 
have the feeling that in most cases a simple string fits quite well.
Maybe that's just because the interface is at the moment set in a way
that it encourages you to use just a string.

Agreed. 

There might be no harm in having a second optional arg though for the rare use cases.
But, before we look at that as an option, I think we need to look closely at the potential use-cases. The interface should fit use-cases not some abstract concept no-one will use.

Regards,

Drak

Chuck Burgess

unread,
Nov 16, 2012, 12:42:02 PM11/16/12
to php...@googlegroups.com
On Fri, Nov 16, 2012 at 11:22 AM, Paul Jones <pmjo...@gmail.com> wrote:
I think having $context (or perhaps $data, or some other name) for an array of additional stuff is a good idea.


I'm with Paul here.  I think you can spec the interface to say "sure, you can pass in some arbitrary structure of extra info, BUT you must do so in an array".  Now, in order to help make implementations still be interchangeable, even though a particular $data format doesn't match other implementations, the standard should dictate that all implementations must allow for members inside $data that itself does not expect or use, and define what the standard expects the implementation to do with $data members like that.  Perhaps they should be ignored... but at least the standard will have defined such behavior.

Evert Pot

unread,
Nov 16, 2012, 1:03:19 PM11/16/12
to php...@googlegroups.com
The logical choice to not define it as an array, but as an interface.

One idea would be to define the $message argument as either:

* A string
* An object that implements an interface.

The interface could look like:

interface MessageInterface {

function __toString();

}


This satisfies the simple use-case Jordi wants to solve, and provides an extension framework for any project that may require additional features all while keeping things very simple..

If there's technical reasons against using the __toString() magic method, a different method is also possible:

interface MessageInterface {

function toLogMessage();

}

Evert

Paul Jones

unread,
Nov 16, 2012, 1:11:57 PM11/16/12
to php...@googlegroups.com
Maybe. However, I think in the interest of codifying what most people are already doing, defining things along those lines would be suboptimal. Nobody nowhere (that I know of ;-) passes a MessageObject implementing MessageInterface to a logger. Everybody everywhere (as far as I can tell ;-) passes a message string.

Let me know if I've misunderstood your argument.


-- pmj

justin

unread,
Nov 16, 2012, 1:11:45 PM11/16/12
to php...@googlegroups.com
Sounds like a great idea. Putting this into concrete terms, it
wouldn't look much different than current logging:


$logger->err('Something awful has happened!');
$logger->err(new StringPlusContextMessage('Something awful has
happened!', $someArbitraryData));
$logger->err(new JustContextMessage($someArbitraryData));



... with message implementations like this:


class JustContextMessage implements MessageInterface
{
private $context = array();

public function __construct(array $context)
{
$this->context = $context;
}

function __toString()
{
return json_encode($this->context);
}
}

class StringPlusContextMessage implements MessageInterface
{
private $message;
private $context = array();

public function __construct($message, array $context)
{
$this->message;
$this->context = $context;
}

function __toString()
{
return sprintf('%s: %s', $this->message, json_encode($this->context));
}
}


$logger->err('Something awful has happened!');
$logger->err(new StringPlusContextMessage('Something awful has
happened!', $someArbitraryData));
$logger->err(new JustContextMessage($someArbitraryData));
> --
> You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com.
> To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.
>
>



--
http://justinhileman.com

justin

unread,
Nov 16, 2012, 1:17:48 PM11/16/12
to php...@googlegroups.com
Or, even better,


$logger->err(MissingFileMessage($filename));

class MissingFileMessage
{
public $filename;

public function __construct($filename)
{
$this->filename = $filename;
}

public function __toString()
{
return 'Missing file: ' . $this->filename;
}
}


Because then message for common loggable cases can be encapsulated in
message classes, rather than strings floating around your codebase.
--
http://justinhileman.com

Evert Pot

unread,
Nov 16, 2012, 1:19:38 PM11/16/12
to php...@googlegroups.com
On Nov 16, 2012, at 7:11 PM, justin <jus...@justinhileman.info> wrote:

> Sounds like a great idea. Putting this into concrete terms, it
> wouldn't look much different than current logging:
>
>
> $logger->err('Something awful has happened!');
> $logger->err(new StringPlusContextMessage('Something awful has
> happened!', $someArbitraryData));
> $logger->err(new JustContextMessage($someArbitraryData));
>
>
>
> ... with message implementations like this:
>
>
> class JustContextMessage implements MessageInterface
> {
> private $context = array();
>
> public function __construct(array $context)
> {
> $this->context = $context;
> }

Well my goal is avoid something like $context. Almost always will the receiver of the log message, and the emitter will have to be in agreement about the meaning of the message; or more specifically the properties.

Still using $context to pass data with arbitrary rules would defeat the purpose.

Evert

Evert Pot

unread,
Nov 16, 2012, 1:25:21 PM11/16/12
to php...@googlegroups.com

>
> Maybe. However, I think in the interest of codifying what most people are already doing, defining things along those lines would be suboptimal. Nobody nowhere (that I know of ;-) passes a MessageObject implementing MessageInterface to a logger. Everybody everywhere (as far as I can tell ;-) passes a message string.
>
> Let me know if I've misunderstood your argument.

I would definitely prefer the most simple solution, and the lowest common denominator.

I was trying to abstract away the $context; It's kinda ugly.

Evert

justin

unread,
Nov 16, 2012, 1:28:19 PM11/16/12
to php...@googlegroups.com
On Fri, Nov 16, 2012 at 1:19 PM, Evert Pot <ever...@gmail.com> wrote:
> Well my goal is avoid something like $context. Almost always will the receiver of the log message, and the emitter will have to be in agreement about the meaning of the message; or more specifically the properties.
>
> Still using $context to pass data with arbitrary rules would defeat the purpose.


Yeah, that's what I'm saying. If you want something like that, it's
the message's responsibility to deal with it, not the logger's.



--
http://justinhileman.com

Drak

unread,
Nov 16, 2012, 1:30:41 PM11/16/12
to php...@googlegroups.com
So for the spec, $message could just be string|object?

Drak


justin

unread,
Nov 16, 2012, 1:33:22 PM11/16/12
to php...@googlegroups.com
string|MessageInterface
--
http://justinhileman.com

Johannes Schmitt

unread,
Nov 16, 2012, 1:34:12 PM11/16/12
to php...@googlegroups.com
Personally, I don't really see a point in adding a MessageInterface with just one method, you could also just pass the result of calling that method directly.

I'm beginning to wonder whether there is really a benefit in adding such an interface because as was mentioned, the code creating the message (however it may look like), and the code processing that message are unfortunately highly coupled. It would probably be easier to just provide adapters between common implementations, and let consuming libraries choose one implementation.


Paul Jones

unread,
Nov 16, 2012, 1:36:40 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 12:30 PM, Drak wrote:

> So for the spec, $message could just be string|object?

I'd be OK with that, with the stipulation that implementations would cast the object to a string:

$message = (string) $message; // invokes __toString() without forcing a method call

Then $context goes away. I'd really like to keep $context/$data/$whatever as an array, but I understand why some find it ugly.



-- pmj

Tim Otten

unread,
Nov 16, 2012, 2:00:37 PM11/16/12
to php...@googlegroups.com
Most logging APIs provide a way to categorize log messages based on where they come from. For example, in log4j, it's conventional to categorize based on the class, and this information is provided when constructing the logger:

private final static Logger log = Logger.getLogger(MyClass.class);

Other systems instead require passing the category name when sending each log message -- eg Drupal's watchdog() expects $type (aka the module-name), and the Typo3 example[2] passes in $packageKey.

How would the log category be established for portable libraries that wish to adopt the standardized LoggerInterface?
> --
> Jordi Boggiano
> @seldaek - http://nelm.io/jordi
>

Paul Jones

unread,
Nov 16, 2012, 2:16:32 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 1:00 PM, Tim Otten wrote:

> Most logging APIs provide a way to categorize log messages based on where they come from. For example, in log4j, it's conventional to categorize based on the class, and this information is provided when constructing the logger:
>
> private final static Logger log = Logger.getLogger(MyClass.class);
>
> Other systems instead require passing the category name when sending each log message -- eg Drupal's watchdog() expects $type (aka the module-name), and the Typo3 example[2] passes in $packageKey.
>
> How would the log category be established for portable libraries that wish to adopt the standardized LoggerInterface?

I've been thinking about this for Aura and I believe that would be something handled by the factory that creates the Logger instance (which then gets injected into the object that will use the Logger).

By way of example, three classes (factory, implementation, and the object that needs to use logging):

<?php
namespace Vendor\Package;

use PSR\LoggerInterface;

class LoggerFactory
{
public function __construct(LoggerManager $manager)
{
$this->manager = $manager;
}

public function newInstance($class)
{
return new Logger($this->manager, $class);
}
}

class Logger implement LoggerInterface
{
protected $manager;

protected $class;

public function __construct(LoggerManager $manager, $class)
{
$this->manager = $manager;
$this->class = $class;
}

public function err($message)
{
$this->manager->log('err', $this->class);
}
}

class NeedsLogger
{
protected $log;

public function __construct(LoggerInterface $log)
{
$this->log = $log;
}

public function foo()
{
$this->log->err('oops');
}
}

Then when using DI to construct objects that need a logger, one might see something like this:

$di->params['Vendor\Package\NeedsLogger']['log'] = $di->lazy(function() use $di {
$logger_factory = $di->get('logger_factory');
return $logger_factory->newInstance('Vendor\Package\NeedsLogger');
});

That's one way of doing it.


-- pmj

Evert Pot

unread,
Nov 16, 2012, 2:43:52 PM11/16/12
to php...@googlegroups.com
On Nov 16, 2012, at 7:36 PM, Paul Jones <pmjo...@gmail.com> wrote:

>
> On Nov 16, 2012, at 12:30 PM, Drak wrote:
>
>> So for the spec, $message could just be string|object?
>
> I'd be OK with that, with the stipulation that implementations would cast the object to a string:
>
> $message = (string) $message; // invokes __toString() without forcing a method call

One argument I would give for not using __toString(), but something like getMessage() instead, is that it would be consistent with the behaviour of PHP's base Exception class.

In the Exception class:

* The meaning of getMessage() is a human readable string.
* The meaning of __toString() is debugging information
* The class and/or implemented interfaces provide a way for a machine to determine the nature of the exception.

By 'claiming' __toString(), you prevent an implementor to use this for a different purpose.

But perhaps I'm over-thinking this; I just wanted to provide it as a thought.

>
> Then $context goes away. I'd really like to keep $context/$data/$whatever as an array, but I understand why some find it ugly.

Yea and keep in mind that for a given framework (like Aura), you can totally pick a pattern that works better for you.
As long as you decorate the standard interface, or even create a simple adapter. You don't really lose any interoperability benefits that way.

I also think with the caching proposal, people are a bit too stuck on the idea that in order to be compliant to the PSR, it means their end-user API must change, while in reality the implementation can totally be hidden away from the end-user.

Evert

Larry Garfield

unread,
Nov 16, 2012, 3:51:18 PM11/16/12
to php...@googlegroups.com
That assumes that tossing context onto the end of the string is
sufficient. In Drupal's case, we introduced this capability for
translation purposes. To wit:

watchdog($type, "User $username deleted $num nodes.");

In English, the syntactically correct order there is as above. In many
other languages, though, the proper ordering of words would be something
more akin to "$num nodes by $username deleted". (I'm not a linguist so
I couldn't tell you which one off hand.) Our translation system already
handled that fairly well with placeholders (very akin to SQL prepared
statements), so we adopted that here, too.

watchdog($type, "User %username deleted %num nodes", array(
'%username' => $username,
'%num' => $num_deleted,
));

Then the translation system can easily handle the message string itself,
and translate on read. When you're running a multi-lingual site, making
sure that log messages are displayed in the language of the person
reading the log, not the language of the person that triggered the error
or wrote a given module, is very important.

Simply saying that we should display "Nodes deleted: {user: 'Larry',
num: '5'}" may simplify the interface, but doesn't resolve the above
translation problem. It's also a sub-optimal message, especially if you
have non-trivial placeholders/context/whatever.


In general I completely agree that logging is a problem space that
benefits from a PSR standard, and that an LCD approach is (generally)
good. However, as with caching I also want to make sure we end up with
a lowest common denominator that doesn't make known additional needs
(types/categories, translation, non-trivial context, etc.) unnecessarily
fugly or people will simply not use the base interface.

So far I'm liking the "string or MessageInterface" approach, and it
parallels some of the discussion from caching earlier this year.

I'm not sure about making the "type" something configured on the logging
object. That results in potentially a large number of active logging
objects; whether or not that's a problem is a matter of opinion based on
the particular system, I suppose. (I could see a typical Silex app
having 2-3, while Drupal would have 16, only a few of which get used at
any given time. That makes me uneasy on performance grounds.)

It also presupposes that we know the type of error in advance. Imagine:

public function import($url) {
try {
$data = $this->httpClient->fetch($url);
$data = $this->process($data);
$this->db->query(...); // Save the data we imported
}
catch (HttpException $e) {
$this->logger("HTTP go boom");
}
catch (PDOException $e) {
$this->logger("Database go boom");
}
}

In this case, we're logging different types of error (http failed vs. db
failed) to the same logger object. We need to know at runtime which
type of error we're recording. I wouldn't want to have to inject 2
different logger objects (and therefore have instantiated 2 different
logger objects) just in case either exception happened. The logger is
already a wasted object 99% of the time, unless your database is
particularly unstable.

--Larry Garfield

Drak

unread,
Nov 16, 2012, 3:58:32 PM11/16/12
to php...@googlegroups.com
Larry, 

Translation should be done before being sent to the logger, for example:

    log(trans('%foo% broke', $foo)); // pseudocode

Leaving the complexity of translation to the translation helpers. I'm not sure the context being discussed here is anything to do with the translation contexts of which I am quite familiar.

Regards,

Drak


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+unsubscribe@googlegroups.com.

FGM at GMail

unread,
Nov 16, 2012, 4:02:55 PM11/16/12
to php...@googlegroups.com
Translation must be performed before being sent to the logger, for the
reason Larry explained: we want messages to be displayed in the
language of the reader of the log, not the writer of the log.

2012/11/16 Drak <dr...@zikula.org>:
>> php-fig+u...@googlegroups.com.
>> For more options, visit https://groups.google.com/groups/opt_out.
>>
>>
>
> --
> You received this message because you are subscribed to the Google Groups
> "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com.
> To unsubscribe from this group, send email to
> php-fig+u...@googlegroups.com.

Drak

unread,
Nov 16, 2012, 4:25:07 PM11/16/12
to php...@googlegroups.com
Yes, that's what I said, isn't it? Or am I missing something?

Larry Garfield

unread,
Nov 16, 2012, 4:36:32 PM11/16/12
to php...@googlegroups.com
1) Module developer Alice, speaks English, writes code.

2) User Bob, speaks German, generates an error:

$this->log(localize('%foo failed in exciting ways, 'the system'));

Resulting string saved to the log:
das System nicht auf spannende Weise

3) Administrator Carl, speaks French, goes to the /admin/logs page.
What he sees:

das System nicht auf spannende Weise

What he wants to see:

le syst�me a �chou� de fa�on passionnante

What he does as a result:

http://www.flickr.com/photos/7591336@N07/3928133658/

Localization of strings must happen on display, not on save, because its
contextually dependent on the display-time context, not the save-time
context.

--Larry Garfield

On 11/16/12 3:25 PM, Drak wrote:
> Yes, that's what I said, isn't it? Or am I missing something?
>
>
> On 16 November 2012 21:02, FGM at GMail <fgma...@gmail.com
> <mailto:fgma...@gmail.com>> wrote:
>
> Translation must be performed before being sent to the logger, for the
> reason Larry explained: we want messages to be displayed in the
> language of the reader of the log, not the writer of the log.
>
> 2012/11/16 Drak <dr...@zikula.org <mailto:dr...@zikula.org>>:
> > Larry,
> >
> > Translation should be done before being sent to the logger, for
> example:
> >
> > log(trans('%foo% broke', $foo)); // pseudocode
> >
> > Leaving the complexity of translation to the translation helpers.
> I'm not
> > sure the context being discussed here is anything to do with the
> translation
> > contexts of which I am quite familiar.
> >
> > Regards,
> >
> > Drak
> >
> >
> > On 16 November 2012 20:51, Larry Garfield <la...@garfieldtech.com
> <mailto:php...@googlegroups.com>.
> >> To unsubscribe from this group, send email to
> >> php-fig+u...@googlegroups.com
> <mailto:php-fig%2Bunsu...@googlegroups.com>.
> >> For more options, visit https://groups.google.com/groups/opt_out.
> >>
> >>
> >
> > --
> > You received this message because you are subscribed to the
> Google Groups
> > "PHP Framework Interoperability Group" group.
> > To post to this group, send email to php...@googlegroups.com
> <mailto:php...@googlegroups.com>.
> > To unsubscribe from this group, send email to
> > php-fig+u...@googlegroups.com
> <mailto:php-fig%2Bunsu...@googlegroups.com>.
> > For more options, visit https://groups.google.com/groups/opt_out.
> >
> >
>
> --
> You received this message because you are subscribed to the Google
> Groups "PHP Framework Interoperability Group" group.
> To post to this group, send email to php...@googlegroups.com
> <mailto:php...@googlegroups.com>.
> To unsubscribe from this group, send email to
> php-fig+u...@googlegroups.com
> <mailto:php-fig%2Bunsu...@googlegroups.com>.

Paul Jones

unread,
Nov 16, 2012, 4:49:27 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 3:36 PM, Larry Garfield wrote:

> 1) Module developer Alice, speaks English, writes code.
>
> 2) User Bob, speaks German, generates an error:
>
> $this->log(localize('%foo failed in exciting ways, 'the system'));
>
> Resulting string saved to the log:
> das System nicht auf spannende Weise
>
> 3) Administrator Carl, speaks French, goes to the /admin/logs page. What he sees:
>
> das System nicht auf spannende Weise
>
> What he wants to see:
>
> le système a échoué de façon passionnante
>
> What he does as a result:
>
> http://www.flickr.com/photos/7591336@N07/3928133658/
>
> Localization of strings must happen on display, not on save, because its contextually dependent on the display-time context, not the save-time context.

I'd argue that's a problem with the translation system, not with the save-time/display-time issue. An intercessory message key would seem to do the trick here:

// where $this->translator is injected, and 'ERR_FAILURE' gets converted to the
// appropriate language-specific string with placeholders. (cf Aura.Intl)
$message = $this->translator->translate('ERR_FAILURE', array('foo' => 'the system'));
$this->logger->err($message);

Might not help in the Drupal situation though.


-- pmj

Tim Otten

unread,
Nov 16, 2012, 5:08:02 PM11/16/12
to php...@googlegroups.com
In Larry's use-case, that code would still result in Carl (the French reader) seeing a message localized to German.

If that doesn't suffice, consider the case of a multinational project where:
* Alice is an IT support staffer at HQ in New York; she speaks English
* Bob is an IT support staffer at the branch in Berlin; he speaks German

When a system-fault arises, Bob does the initial triage. He speaks German, so he wants to inspect the log in German. He can usually figure out the problem, but sometimes he needs to escalate to Alice. Alice speaks English, so she wants to read the log in English.

The only way to support this is to store a canonical representation of the error message and then translate depending on the reader. (In some systems, the canonical representation might be an abstract code like ERR_FAILURE, or it might be the English.)

thom...@gmail.com

unread,
Nov 16, 2012, 5:10:47 PM11/16/12
to php...@googlegroups.com
Mark is steaming
Sent from my BlackBerry® wireless device

Lukas Kahwe Smith

unread,
Nov 16, 2012, 5:12:55 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 23:08 , Tim Otten <t...@onebitwise.com> wrote:

> In Larry's use-case, that code would still result in Carl (the French reader) seeing a message localized to German.
>
> If that doesn't suffice, consider the case of a multinational project where:
> * Alice is an IT support staffer at HQ in New York; she speaks English
> * Bob is an IT support staffer at the branch in Berlin; he speaks German
>
> When a system-fault arises, Bob does the initial triage. He speaks German, so he wants to inspect the log in German. He can usually figure out the problem, but sometimes he needs to escalate to Alice. Alice speaks English, so she wants to read the log in English.
>
> The only way to support this is to store a canonical representation of the error message and then translate depending on the reader. (In some systems, the canonical representation might be an abstract code like ERR_FAILURE, or it might be the English.)


right .. but i dont quite see how i18n relates to this API. we already established that it will somehow need to be possible to set a data structure as the message (not sure yet if there was an agreement on who is responsible for serialization in that case). so if you want your messages to be translatable after retrieval, then its your responsibility to figure out a message data structure that will enable this.

regards,
Lukas Kahwe Smith
m...@pooteeweet.org



Paul Jones

unread,
Nov 16, 2012, 5:50:18 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 4:08 PM, Tim Otten wrote:

> If that doesn't suffice, consider the case of a multinational project where:

> * Alice is an IT support staffer at HQ in New York; she speaks English
> * Bob is an IT support staffer at the branch in Berlin; he speaks German
>
> When a system-fault arises, Bob does the initial triage. He speaks German, so he wants to inspect the log in German. He can usually figure out the problem, but sometimes he needs to escalate to Alice. Alice speaks English, so she wants to read the log in English.
>
> The only way to support this is to store a canonical representation of the error message and then translate depending on the reader. (In some systems, the canonical representation might be an abstract code like ERR_FAILURE, or it might be the English.)

Aha. I misunderstood your meanings of "display time" and "save time." Thanks for clarifying that.

With that in mind, I'd argue (1) that the more useful thing to do is, as you say, save the canonical/abstract form, and (2) that the use case, while valid, occurs so infrequently among the developer audience as to warrant special attention primarily from implementors, not from the standard (although if we can make allowances that make it easier for implementors we should do so).



-- pmj

Paul Jones

unread,
Nov 16, 2012, 5:51:31 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 4:12 PM, Lukas Kahwe Smith wrote:

> i dont quite see how i18n relates to this API. we already established that it will somehow need to be possible to set a data structure as the message (not sure yet if there was an agreement on who is responsible for serialization in that case). so if you want your messages to be translatable after retrieval, then its your responsibility to figure out a message data structure that will enable this.

I concur with this sentiment.


-- pmj

Larry Garfield

unread,
Nov 16, 2012, 7:12:48 PM11/16/12
to php...@googlegroups.com
Then by implication, that means we would have to do the
string-or-MessageInterface approach, and a system like Drupal that is
inherently multilingual would have to *always* use the MessageInterface
approach. To wit:

try {
// ...
}
catch (Exception $e) {
$message = new DrupalMessage('%foo failed in exciting ways');
$message->placeholders(array('%foo' => 'the system'));
$this->logger->err($message);
}

class DrupalMessage implements MessageInterface {
// ...
public function toLogMessage() {
return json_encode($this);
// And then on the receiving end, json_decode() and run
// through localization and whatever else you want.
}

}

I'm not necessarily against that, but I want to make clear the
implications of that decision because I don't believe Drupal is the only
system that cares about multi-lingual logs, or that localization is the
only concern that would trigger a "must always use an object, sorry"
situation. (Remember, by implication if you find yourself in that
situation then you likely need a proprietary driver and cannot reuse
someone else's; they could use yours, though, in string-mode.)

If we are going to push the esoteria to an object, then we need to
decide what we consider esoteria and what we don't. Specifically:

- Level: This seems like it has to be baked into the API.
- The message: Mandatory, of course.
- The category/type/tag: I would argue this is of sufficiently
widespread usefulness that it should be baked into the API, as an
optional parameter. "You want to organize your logs? Sorry, you need an
object always then" doesn't seem very friendly to me.
- Other?

--Larry Garfield

Evert Pot

unread,
Nov 16, 2012, 8:05:47 PM11/16/12
to php...@googlegroups.com
>
> Then by implication, that means we would have to do the string-or-MessageInterface approach, and a system like Drupal that is inherently multilingual would have to *always* use the MessageInterface approach. To wit:

This could just be a hidden implementation detail though. Anything using a Drupal logging api does not have to interact with this interface directly.

>
> try {
> // ...
> }
> catch (Exception $e) {
> $message = new DrupalMessage('%foo failed in exciting ways');
> $message->placeholders(array('%foo' => 'the system'));
> $this->logger->err($message);
> }
>
> class DrupalMessage implements MessageInterface {
> // ...
> public function toLogMessage() {
> return json_encode($this);
> // And then on the receiving end, json_decode() and run
> // through localization and whatever else you want.
> }
>
> }

It doesn't make any sense to serialize / deserialize this using json...

Why not add extra methods to DrupalMessage that carry this information as normal PHP values?
The receiving end (in your example) is already making assumptions about the format of a message (now a json-encoded-string), it can instead make the API assumption based on the fact that this is an instance of DrupalMessage, and not something else.

Imagine doing the same with exceptions, instead of subclassing exceptions, you'd be encoding a json string in the $message argument.

Evert

Paul Jones

unread,
Nov 16, 2012, 9:59:51 PM11/16/12
to php...@googlegroups.com

On Nov 16, 2012, at 6:12 PM, Larry Garfield wrote:

> Then by implication, that means we would have to do the string-or-MessageInterface approach, and a system like Drupal that is inherently multilingual would have to *always* use the MessageInterface approach.

/me nods

I stated earlier, and I'll reiterate, that I'm perfectly fine with a string-or-object approach (note this is subtly different from the string-or-MessageInterface approach) with the caveat that implementors should cast the incoming message to a string (thus invoking __toString() automatically if the message happens to be an object).


> If we are going to push the esoteria to an object, then we need to decide what we consider esoteria and what we don't. Specifically:
>
> - Level: This seems like it has to be baked into the API.
> - The message: Mandatory, of course.
> - The category/type/tag: I would argue this is of sufficiently widespread usefulness that it should be baked into the API, as an optional parameter. "You want to organize your logs? Sorry, you need an object always then" doesn't seem very friendly to me.

I am a fan of organizing by category/type/tag; hell, that exactly how Solar_Log works. But I believe that's something that can be handled by injecting a Logger (or even multiple loggers) with the type(s) already set on them, not something that would have to be specified at method-call time.



-- pmj

Larry Garfield

unread,
Nov 16, 2012, 10:05:08 PM11/16/12
to php...@googlegroups.com
On 11/16/2012 08:59 PM, Paul Jones wrote:
> On Nov 16, 2012, at 6:12 PM, Larry Garfield wrote:
>
>> Then by implication, that means we would have to do the string-or-MessageInterface approach, and a system like Drupal that is inherently multilingual would have to *always* use the MessageInterface approach.
> /me nods
>
> I stated earlier, and I'll reiterate, that I'm perfectly fine with a string-or-object approach (note this is subtly different from the string-or-MessageInterface approach) with the caveat that implementors should cast the incoming message to a string (thus invoking __toString() automatically if the message happens to be an object).

No, that can't work for the reasons I stated previously. You lose
necessary information for properly displaying the message to the user in
the log viewer, whatever it is.


>
>> If we are going to push the esoteria to an object, then we need to decide what we consider esoteria and what we don't. Specifically:
>>
>> - Level: This seems like it has to be baked into the API.
>> - The message: Mandatory, of course.
>> - The category/type/tag: I would argue this is of sufficiently widespread usefulness that it should be baked into the API, as an optional parameter. "You want to organize your logs? Sorry, you need an object always then" doesn't seem very friendly to me.
> I am a fan of organizing by category/type/tag; hell, that exactly how Solar_Log works. But I believe that's something that can be handled by injecting a Logger (or even multiple loggers) with the type(s) already set on them, not something that would have to be specified at method-call time.
>
>
>
> -- pmj

See my first comment in this thread why I don't believe that's a viable
approach for larger systems. Having to inject multiple logger objects
to one client object is a no-go.

--Larry Garfield

Jordi Boggiano

unread,
Nov 17, 2012, 5:36:44 AM11/17/12
to php...@googlegroups.com
I'll try to condense all answers in one, albeit long, mail. I think
Larry's summary makes a good starting point. Please skip to the end if
you don't feel like reading it all.

>>> If we are going to push the esoteria to an object, then we need to
>>> decide what we consider esoteria and what we don't. Specifically:
>>>
>>> - Level: This seems like it has to be baked into the API.
>>> - The message: Mandatory, of course.
>>> - The category/type/tag: I would argue this is of sufficiently
>>> widespread usefulness that it should be baked into the API, as an
>>> optional parameter. "You want to organize your logs? Sorry, you need
>>> an object always then" doesn't seem very friendly to me.

The level/message are taken care of. For the category (I'll call it
channel because I'm used to that and I'll probably slip if I try to call
it anything else) I see 3 ways of doing it:

- Pass it in the logger's constructor. That's what monolog/symfony does
and it works quite well IMO. The problem you described with having
multiple loggers is in my experience quite rare, because the DB code
will already log the failure of the query, so when you catch the
exception you don't need to log a db failure. If anything you need to
log to your own channel that you aborted doing something because of a db
failure, but you don't want to log an additional record to the DB
channel I would say.

- Pass it as third optional argument, like ->info($msg, $context,
$channel). It's possible and not really harmful to have it there if it
helps, but I don't think it's necessary.

- Allow it to be passed in context options. Say if you send a
"channel"=>"foo" it would override the default one of the logger.

This last point raises another question. Whether we want to codify more
specialized usages for the context data.

>> I stated earlier, and I'll reiterate, that I'm perfectly fine with a
>> string-or-object approach (note this is subtly different from the
>> string-or-MessageInterface approach) with the caveat that implementors
>> should cast the incoming message to a string (thus invoking
>> __toString() automatically if the message happens to be an object).
>
> No, that can't work for the reasons I stated previously. You lose
> necessary information for properly displaying the message to the user in
> the log viewer, whatever it is.

I feel quite strongly against this MessageInterface. It seems overly
complex for no benefit (or very little) really. The context array is
enough for the translation use case. The only thing to keep in mind
regarding the translation is that it will require a custom log
handler/backend. Because it needs to give you access to all the
structured data at display time, so you don't want any loss of information.

The way it's done in Drupal as I understand is that you log in the DB or
something the fully structured records, with message + variables. But if
(assuming you are using monolog as it seems to be the plan) you also log
to a flat file, most likely you would want to dump the english messages
there to allow people to read the logs when ssh'd into a machine. `cat`
or `tail` is not going to support your translation scheme, so you'd want
a normal log handler there. With the MessageInterface approach, it seems
others suggest that Drupal should just have a __toString() that does
json_encode. That would allow them to display translated messages, but
it wouldn't allow for readable plain text logs anymore. If the
json_encode is done at the handler level then you keep this functionality.

# Conclusion (TLDR for people skipping from above)

To me it comes down to a similar thing to "filter input, escape output".
Logs should come in in raw form into the LoggerInterface, and then
internally be processed and the various handlers can dump them in
various ways.

The benefit of the LoggerInterface is not really to frameworks.
Frameworks will most likely keep some abstraction on top anyway to keep
BC. The real benefit I see is to libraries. At the moment libraries like
Doctrine provide their own interface so you can add a logger to them but
you have to make a wrapper. It might be ok for Doctrine which is
generally a key part of the app, but what if every library did that?
Since this would not be acceptable what they generally do is they offer
no logging at all. If every library could just type hint LoggerInterface
and know that it can send strings in there, and maybe a small amount of
codified context data that gives us a good basis for having more logging
information available.

Most likely those lib messages would not be translatable in the drupal
UI, but then again it's better to have info in english only than no info
at all.

So I believe the message should remain a string. We can allow an object
with __toString() because that's anyway the equivalent of a string, but
I don't even see the point of that. If people have real use cases I
would be happy to discuss and see how they can be implemented with the
current interface, or why they would be better served by an object or
MessageInterface.

And I believe the context is enough because it can enable you to add
placeholders in the messages that take data from the context, it can
allow you to just pass more data in any case, and if you must then you
add a custom handler that will handle this stuff when it's there, but it
should not be a requirement that it is there, otherwise the libraries
can't use this logger interface as a generic tool anymore, and we are
back to square one.

Cheers

Lukas Kahwe Smith

unread,
Nov 17, 2012, 5:40:40 AM11/17/12
to php...@googlegroups.com

On Nov 17, 2012, at 11:36 , Jordi Boggiano <j.bog...@seld.be> wrote:

> I'll try to condense all answers in one, albeit long, mail. I think
> Larry's summary makes a good starting point. Please skip to the end if
> you don't feel like reading it all.


can you maintain a discussion summary somewhere? f.e. on the FIG github wiki?

FGM at GMail

unread,
Nov 17, 2012, 6:02:31 AM11/17/12
to php...@googlegroups.com
Just a clarification regarding channel use. In Drupal's case, what we
currently pass as the "module" and looks like it would naturally
translate to a channel doesn't actually have a 1:1 mapping.

- This "module" parameter is mostly about classifying the source of
the event (in our case mostly our modules)
- The channel notion as in Monolog is rather about a specific
configuration of logging tools (handlers, processors), which can be
associated (or not) with an event source

This partly covers Larry's worries about the multiplication of logger
objects: there should be only as many logger objects as needed per
channel configuration, meaning typically just 1 per system, as long as
1 channel logs keeps the different event sources information.

Which lets us find frameworks are likely to have these two different needs:

- Identifying the event source
- Specifying a logging configuration (channel)

Maybe it would make sense to include a way to specify the event source
in the interface, either as a predefined (optional ?) key in the
context/extra/data/whatever array/object or as an explicit part of the
interface. I would favor the latter.


2012/11/17 Jordi Boggiano <j.bog...@seld.be>:

Tim Otten

unread,
Nov 17, 2012, 3:16:19 PM11/17/12
to php...@googlegroups.com
On Nov 16, 2012, at 11:16 AM, Paul Jones wrote:

> Then when using DI to construct objects that need a logger, one might see something like this:
>
> $di->params['Vendor\Package\NeedsLogger']['log'] = $di->lazy(function() use $di {
> $logger_factory = $di->get('logger_factory');
> return $logger_factory->newInstance('Vendor\Package\NeedsLogger');
> });

My feelings are very mixed about relying on the application-developer to use DIC to specify log metadata. On one hand, it puts a lot of power and flexibility in the hands of the application-developer. On the other hand, if we assume that an application is composed of many small, loosely-coupled libraries, then that responsibility could quickly become onerous. It could be mitigated if either:

(a) Libraries included default config files for use with common DICs
(b) Common DIC's allowed the logger to be constructed very dynamically. For example, this gets part of the way there:

http://symfony.com/doc/master/components/dependency_injection/factories.html#passing-arguments-to-the-factory-method

If one could pass an argument to the factory method which indicated the "Vendor\Package\NeedsLogger" dynamically (based on the identity of the requestor), then that would suffice for auto-wiring.

Maybe "a" or "b" is workable -- I'm not familiar enough with the various frameworks to say. But there is the alternative to ask that developers write log statements like this:

$log->info(__CLASS__, "Authentication failed for user '%username%'", array(
'username' => 'fred',
));

It's not quite as pretty, but it's still legible, and it can consistently provide the "source" of a log-record in differing application frameworks.

Paul Jones

unread,
Nov 18, 2012, 10:16:45 AM11/18/12
to php...@googlegroups.com

On Nov 17, 2012, at 2:16 PM, Tim Otten wrote:

> On Nov 16, 2012, at 11:16 AM, Paul Jones wrote:
>
>> Then when using DI to construct objects that need a logger, one might see something like this:
>>
>> $di->params['Vendor\Package\NeedsLogger']['log'] = $di->lazy(function() use $di {
>> $logger_factory = $di->get('logger_factory');
>> return $logger_factory->newInstance('Vendor\Package\NeedsLogger');
>> });
>
> My feelings are very mixed about relying on the application-developer to use DIC to specify log metadata.

To be clear, that's just one approach. One could do it just as well with a service locator, or by hand when setting up a logging mechanism in the constructor of the class that needs the logging. The point is that the Logger methods don't necessarily require the class/module/origin parameter, when it can be fed in externally.


> But there is the alternative to ask that developers write log statements like this:
>
> $log->info(__CLASS__, "Authentication failed for user '%username%'", array(
> 'username' => 'fred',
> ));
>
> It's not quite as pretty, but it's still legible, and it can consistently provide the "source" of a log-record in differing application frameworks.

This is exactly how Solar_Log <http://solarphp.com/apidoc/class.Solar_Log.Overview> works. While I am partial to it, I think that signature is relatively uncommon in PHP land. However, if others (particularly Jordi) are willing to entertain the idea, I would support it.


-- pmj

Jordi Boggiano

unread,
Nov 18, 2012, 10:22:18 AM11/18/12
to php...@googlegroups.com
Heya,

>> But there is the alternative to ask that developers write log statements like this:
>>
>> $log->info(__CLASS__, "Authentication failed for user '%username%'", array(
>> 'username' => 'fred',
>> ));
>>
>> It's not quite as pretty, but it's still legible, and it can consistently provide the "source" of a log-record in differing application frameworks.
>
> This is exactly how Solar_Log
> <http://solarphp.com/apidoc/class.Solar_Log.Overview> works. While I
> am partial to it, I think that signature is relatively uncommon in
> PHP land. However, if others (particularly Jordi) are willing to
> entertain the idea, I would support it.

If people see a need for it as I said before I could see it being added
as third, optional $channel argument. Now either you pass __CLASS__ in
there, or something more meaningful to allow grouping of records somehow.

Cheers

Paul Jones

unread,
Nov 18, 2012, 10:44:27 PM11/18/12
to php...@googlegroups.com
To be clear: are you saying `$log->info($message, $context, $channel)` would be acceptable, even if not your first preference?

I think if the $channel is present, it makes the most sense as a *first* param. But I'm OK with it either the third param or with passing it as a constructor argument. (The constructor argument makes the most sense to me after having used all the variations myself, but this is likely to be an exercise in compromise. ;-)

Does anyone else (Drupal folks in particular) have strong feelings on this, one way or the other?



-- pmj

Larry Garfield

unread,
Nov 18, 2012, 11:34:18 PM11/18/12
to php...@googlegroups.com
Drupal's current logging API uses ($channel, $message, $variables
[subset of context], $severity, and $link [more context]); $severity
moving to the method name is a-OK by me. $variables and $link merging
into $context I can live with. The three remaining values ($channel,
message, $context), eh, I don't much care what their order is. If it's
a 3rd, optional parameter then we need to explicitly define the correct
handling when it's not specified, though.

To Jordi's previous point, I want to clarify: You're suggesting, then,
that Drupal, CakePHP, Symfony full stack, Joomla, etc. would *not*
actually use this API themselves, but use it as a way to let Symfony
Components, Zend Components, or Guzzle plug into whatever their API
actually was? That's somewhat backward of how I was envisioning it, but
would considerably change the thrust of the conversation.

I'm not sure which direction I prefer at the moment. I need to ruminate
further.

--Larry Garfield

FGM at GMail

unread,
Nov 19, 2012, 3:03:27 AM11/19/12
to php...@googlegroups.com
If I may insist a bit, Drupal current API mixes "channel" and "event
source" because we use a shared logging configuration. One of the
interesting points this interface (as well as the existing patch on
d.o.) brings us is the ability to completely decouple
- the channel (logger chain and configuration) used to route log
events to possibly multiple configurations
- from the event source, used when reading the logs afterwards

For instance, a boot sequence can use a logging channel available very
early before a DB connection exists, possibly with a handler capturing
to DB later in the page cycle à la FingersCrossed if some event went
over some severity level, while all event sources in code later in the
page cycle can log normally to a single channel writing to the DB,
tagged with this event source info. And of course, pieces of code with
specific needs can open another channel and log in a different
configuration.

Which in the end will typically mean a small number of logger objects
around, not one per event source.

2012/11/19 Larry Garfield <la...@garfieldtech.com>:

Paul Jones

unread,
Nov 19, 2012, 11:32:22 AM11/19/12
to php...@googlegroups.com

On Nov 19, 2012, at 2:03 AM, FGM at GMail wrote:

> If I may insist a bit, Drupal current API mixes "channel" and "event
> source" because we use a shared logging configuration. One of the
> interesting points this interface (as well as the existing patch on
> d.o.) brings us is the ability to completely decouple
> - the channel (logger chain and configuration) used to route log
> events to possibly multiple configurations
> - from the event source, used when reading the logs afterwards
>
> For instance, a boot sequence can use a logging channel available very
> early before a DB connection exists, possibly with a handler capturing
> to DB later in the page cycle à la FingersCrossed if some event went
> over some severity level, while all event sources in code later in the
> page cycle can log normally to a single channel writing to the DB,
> tagged with this event source info. And of course, pieces of code with
> specific needs can open another channel and log in a different
> configuration.
>
> Which in the end will typically mean a small number of logger objects
> around, not one per event source.

This leads me to believe that the proposal from Jordi is fine as it stands; logger objects can be created (perhaps from a factory or builder or locator) so that the channel is set at construction time, with no need for the third parameter in the method call. Does that sound fair?


-- pmj

Evert Pot

unread,
Nov 19, 2012, 11:35:31 AM11/19/12
to php...@googlegroups.com
> This leads me to believe that the proposal from Jordi is fine as it stands; logger objects can be created (perhaps from a factory or builder or locator) so that the channel is set at construction time, with no need for the third parameter in the method call. Does that sound fair?

I strongly feel the same way. All of this can be done through extension.

Due to the nature of the Log and Message interface this can be extremely easily done, and this implementation can also be easily hidden from a user through various means; Helper functions, factories, etc..

Evert

FGM at GMail

unread,
Nov 19, 2012, 1:07:19 PM11/19/12
to php...@googlegroups.com
If we indeed assume that channel (as a logging configuration) is
really controlled by having a few logger items, it needn't indeed be
part of the logging calls, since it is included in the logger instance
itself. It does not remove the need for a "channel" as en event
source, though.

By doing that choice of a small number of logger objects, one per
logging configuration, (which makes sense to me) we simply ignore the
ability to use a single logger object instance called with the channel
as a parameter, which is another possible approach. I think the 1
logger-per-channel-and-pass-the-event-source-in-calls is probably
better, but are there no arguments to be made for the alternative:
singleton-logger-with-both-channel-and-event-source-in-calls approach
? Or yet other approaches ?

2012/11/19 Evert Pot <ever...@gmail.com>:

Jordi Boggiano

unread,
Nov 19, 2012, 1:28:36 PM11/19/12
to php...@googlegroups.com
On 19.11.2012 19:07, FGM at GMail wrote:
> If we indeed assume that channel (as a logging configuration) is
> really controlled by having a few logger items, it needn't indeed be
> part of the logging calls, since it is included in the logger instance
> itself. It does not remove the need for a "channel" as en event
> source, though.
>
> By doing that choice of a small number of logger objects, one per
> logging configuration, (which makes sense to me) we simply ignore the
> ability to use a single logger object instance called with the channel
> as a parameter, which is another possible approach. I think the 1
> logger-per-channel-and-pass-the-event-source-in-calls is probably
> better, but are there no arguments to be made for the alternative:
> singleton-logger-with-both-channel-and-event-source-in-calls approach
> ? Or yet other approaches ?

As I said, I think having it as a third, optional argument for *when you
want* to provide an alternative channel is a good compromise. That way
people configuring multiple objects have no problem, and people that
want a singleton used with explicit channels also have no problem.

For use in libraries though I would say we should discourage the use of
that third argument as it should just use the channel of the object it
received.

Alternatively we could make this third param something else than
channel, so it doesn't override the channel of the instance, but rather
adds to it.

Evert Pot

unread,
Nov 19, 2012, 1:41:09 PM11/19/12
to php...@googlegroups.com
On Nov 19, 2012, at 7:28 PM, Jordi Boggiano <j.bog...@seld.be> wrote:

> On 19.11.2012 19:07, FGM at GMail wrote:
>> If we indeed assume that channel (as a logging configuration) is
>> really controlled by having a few logger items, it needn't indeed be
>> part of the logging calls, since it is included in the logger instance
>> itself. It does not remove the need for a "channel" as en event
>> source, though.
>>
>> By doing that choice of a small number of logger objects, one per
>> logging configuration, (which makes sense to me) we simply ignore the
>> ability to use a single logger object instance called with the channel
>> as a parameter, which is another possible approach. I think the 1
>> logger-per-channel-and-pass-the-event-source-in-calls is probably
>> better, but are there no arguments to be made for the alternative:
>> singleton-logger-with-both-channel-and-event-source-in-calls approach
>> ? Or yet other approaches ?
>
> As I said, I think having it as a third, optional argument for *when you
> want* to provide an alternative channel is a good compromise. That way
> people configuring multiple objects have no problem, and people that
> want a singleton used with explicit channels also have no problem.

If you're adding an extra argument, with no explicit meaning (may be ignored, doesn't have to be specified) to me makes as little sense as a completely 'free for all' context argument.

If an implementor requires a way to specify a channel; They should do so by creating an extension.

The extension can be both on the logger object, or on the message (or both).

With both your solution (an arbitrary extra argument) and in interface, the implementor of the Logger object will have to provide a fallback when the channel information is not provided by the caller.

But it is explicit this way.

I would love for someone to explain to me why another argument (and perhaps also the $context argument) would be a desirable thing over an interface. Is this solely because of a worry that there's more code involved logging anything?

Evert

Evert Pot

unread,
Nov 19, 2012, 1:52:05 PM11/19/12
to php...@googlegroups.com
> If you're adding an extra argument, with no explicit meaning (may be ignored, doesn't have to be specified) to me makes as little sense as a completely 'free for all' context argument.
>
> If an implementor requires a way to specify a channel; They should do so by creating an extension.
>
> The extension can be both on the logger object, or on the message (or both).
>
> With both your solution (an arbitrary extra argument) and in interface, the implementor of the Logger object will have to provide a fallback when the channel information is not provided by the caller.
>
> But it is explicit this way.
>
> I would love for someone to explain to me why another argument (and perhaps also the $context argument) would be a desirable thing over an interface. Is this solely because of a worry that there's more code involved logging anything?
>
> Evert

Just to add to that..

Imagine if Symfony attaches a specific meaning to the $context argument, a certain key that will be interpreted and send to the log.
Drupal does this too, but attaches a different meaning to the same key name.

Does this mean that either framework should check for this key, and make sure it's in the format they expect?
Or should keys in $context get vendor-prefixes?
Third option: every $context array should get a 'vendor' key, describing how to interpret the $context

In all cases this is a weaker check, and a slight re-implementation of a feature we have in PHP : interfaces.

Arbitrary arguments open the door for pseudo-standards and conflicts; but an Interface clearly does not. We have a very clear definition of what to expect when a $message implements a certain interface, the rules are rock-solid and it's a solid contract, if you will.

I feel (but may be wrong here, and would love to hear it, if so) that the need for these extra arguments solely comes from the assumption that if vendors were to implement this interface, that this is also the API they need to expose to the user. Is that wrong?

Evert

Jordi Boggiano

unread,
Nov 19, 2012, 2:16:57 PM11/19/12
to php...@googlegroups.com
Heya,

>> I would love for someone to explain to me why another argument (and perhaps also the $context argument) would be a desirable thing over an interface. Is this solely because of a worry that there's more code involved logging anything?

Yes, I think having to create a new Message instance sucks hard for just
passing a string and an array. I don't agree that we need objects
everywhere. Sometimes the "core" data structures are good enough.

The point is simply that most of the time a string is enough. Sometimes
you want more details and giving a context array that can be serialized
and shown later in its full form makes more sense than pre-serializing
it in the string. The latter means that it can't be shown in full again
since the log backend does not know how you serialized it.

As for the third argument, I personally do not see a need for it.

> Imagine if Symfony attaches a specific meaning to the $context argument, a certain key that will be interpreted and send to the log.
> Drupal does this too, but attaches a different meaning to the same key name.

That's why I want the spec to define a few special context keys that
should be used to convey some meaning, but everything else should be
done loosely and not throw any exception for "invalid data in context".

I can give you a few examples because we have a few such uses in
monolog. Some handlers will treat line/file keys in the context array as
special (because some backends provide special ways to attach a log
record to a line/file). Some handlers will treat "exception" as an
exception if it is one, and again throw extra data like the stack trace
at the backend. If the exception key happens to hold a string, things
should work anyway, but simply not send a stack trace.

> In all cases this is a weaker check, and a slight re-implementation of a feature we have in PHP : interfaces.

Agreed, interfaces could provide that, but what if you have 10 different
meanings for special keys, each have an interface, and then you need a
100 message classes that each implement a different combination of
interfaces depending on which you want to expose? I just don't think
that's practical at all.

Evert Pot

unread,
Nov 19, 2012, 2:36:49 PM11/19/12
to php...@googlegroups.com
On Nov 19, 2012, at 8:16 PM, Jordi Boggiano <j.bog...@seld.be> wrote:

> Heya,
>
>>> I would love for someone to explain to me why another argument (and perhaps also the $context argument) would be a desirable thing over an interface. Is this solely because of a worry that there's more code involved logging anything?
>
> Yes, I think having to create a new Message instance sucks hard for just
> passing a string and an array. I don't agree that we need objects
> everywhere. Sometimes the "core" data structures are good enough.

A pain for writing this every time, or a pain for in terms of performance?

Why would (coming from Monolog) you not add a simple API, exposing exactly the features you want.
Mapping them to a MonologMessage would only have to happen in one place.

>
> The point is simply that most of the time a string is enough. Sometimes
> you want more details and giving a context array that can be serialized
> and shown later in its full form makes more sense than pre-serializing
> it in the string. The latter means that it can't be shown in full again
> since the log backend does not know how you serialized it.
>
> As for the third argument, I personally do not see a need for it.
>
>> Imagine if Symfony attaches a specific meaning to the $context argument, a certain key that will be interpreted and send to the log.
>> Drupal does this too, but attaches a different meaning to the same key name.
>
> That's why I want the spec to define a few special context keys that
> should be used to convey some meaning, but everything else should be
> done loosely and not throw any exception for "invalid data in context".
>
> I can give you a few examples because we have a few such uses in
> monolog. Some handlers will treat line/file keys in the context array as
> special (because some backends provide special ways to attach a log
> record to a line/file). Some handlers will treat "exception" as an
> exception if it is one, and again throw extra data like the stack trace
> at the backend. If the exception key happens to hold a string, things
> should work anyway, but simply not send a stack trace.

I feel that if anything like this would be part of the specification, it would still make more sense to add that explicitly to an interface. An interface will allow you to define the meaning of $line, $file and $exception; and also document that these are optional.

>
>> In all cases this is a weaker check, and a slight re-implementation of a feature we have in PHP : interfaces.
>
> Agreed, interfaces could provide that, but what if you have 10 different
> meanings for special keys, each have an interface, and then you need a
> 100 message classes that each implement a different combination of
> interfaces depending on which you want to expose? I just don't think
> that's practical at all.

In the case of monolog, it sounds like you already have a pretty good set of properties and meanings.
I'm not opting for a 100 message classes, but:

* If Monolog as an implementation can use a few extra properties and attach a standard meaning to them, define one MonologMessage that encapsulates that.
* If some of those should instead be generalized, try to encapsulate them in the PSR within the interface.

I definitely agree that it doesn't make sense for each property to get it's own interface, but I do think it's logical that each vendor does; if they have a need to extend the base spec.

Regardless, I think I made my point (hopefully) clear. Beyond this, it's more of a matter of opinion. So if mine is an unpopular one, I'll happily recede as I don't wish to get in the way of progress. Any interface is better than no interface :).

Evert


Tim Otten

unread,
Nov 19, 2012, 3:35:42 PM11/19/12
to php...@googlegroups.com
I softly agree with Evert's emails about PHP interfaces being preferable over freeform arrays -- but disagree on a more basic point.

I feel that log API extensions (whether they are "freeform $context arrays" or "subclasses") have been used as catchalls for two different things:

1. Adding log metadata that's generically useful for logging -- perhaps data which was omitted from the interface. This would cover things like "level", "category", "source", "exception", etc. Larry's previous email did a good job of pointing these out.

2. Adding error-specific context data -- for example, an "authentication failure" would log the submitted user name. An "access denied" error would log the ID of the requested resource or log details of the active access-control rule.

Case #1 seems dangerous to me -- if each framework adopts its own extensions to the logging layer, then that will create barriers for interoperability. For example, suppose a library uses the "MonologMessage" class but is installed on a non-Monolog system. The library will need to declare a dependency on Monolog -- even though the application doesn't use Monolog. If you stitch together four libraries from four different frameworks, you'll wind up with four logging dependencies -- one of those logging dependencies will be loaded and used faithfully at runtime, and three will be used in a reductive manner (eg falling back to "toString()" behavior). It would be best if we eliminate the need for framework-specific extensions by defining a proper interface.

Case #2 does make sense to me. I'm not sure it's worth the overhead to require a new class for each possible combination of context-specific log data, but there is a genuine need for different log consumers to supply different data. By analogy, it's quite reasonable for libraries to create new "Exception" subclasses.

Evert Pot

unread,
Nov 19, 2012, 4:18:16 PM11/19/12
to php...@googlegroups.com
> Case #1 seems dangerous to me -- if each framework adopts its own extensions to the logging layer, then that will create barriers for interoperability. For example, suppose a library uses the "MonologMessage" class but is installed on a non-Monolog system. The library will need to declare a dependency on Monolog -- even though the application doesn't use Monolog. If you stitch together four libraries from four different frameworks, you'll wind up with four logging dependencies -- one of those logging dependencies will be loaded and used faithfully at runtime, and three will be used in a reductive manner (eg falling back to "toString()" behavior). It would be best if we eliminate the need for framework-specific extensions by defining a proper interface.

I would want to point out as a small retort, that I feel that it's essential that __toString(), or the base interface will always provide a solid, useful outcome; something you can actually use in a log.

If an external logger receives a MonologMessage, they should not have to depend on Monolog to handle that message. If that were the case, a PSR would be pointless as it fails in the one thing it tries to solve.

What needs to be decided upon is a lowest common denominator, while providing a extension framework that ensures that people are not limited by it.

Evert

Larry Garfield

unread,
Nov 19, 2012, 5:28:42 PM11/19/12
to php...@googlegroups.com
On 11/19/12 3:18 PM, Evert Pot wrote:

> What needs to be decided upon is a lowest common denominator, while providing a extension framework that ensures that people are not limited by it.
>
> Evert

Yes. The tricky part is, as always, determining just how low that
lowest common denominator can get before it ceases to be useful, and
ensuring that the extension framework doesn't require all sorts of
painful contortions to use. Either of those would render any standard
here impotent. (That's what makes this fun. :-) )

I don't believe anyone responded to my last inquiry (directed mostly at
Jordi but relevant to all), about who we expect the "consumer" of this
interface to be.

1) Is the intent that Drupal, CakePHP, Symfony, and PHPBB can all share
the same logging interface, so a logging backend can be interchanged
between them? That is, we only need to write one log-to-redis
implementation, and all of the above can share it.

2) Is the intent that Symfony2 Components, Zend Components, Guzzle, and
Assetic can share the same logging interface, so that
Drupal/Symfony/CakePHP only have to bridge one "foreign" logging client
interface to whatever their proprietary logger is? (Some may still use
a common lib like Monolog, but that would be a separate question.)

Those are *very* different things, and we need to be explicitly on the
same page as to which we're discussing.

--Larry Garfield

Hari K T

unread,
Nov 16, 2012, 1:02:19 PM11/16/12
to php...@googlegroups.com
I am not an expert in it.
But one question is :

May be its with the naming convention with zend, monolog etc ,

Why like err , emerg, crit . Can't we make it full critical , emergency , error . 

Just an unwanted thought .

Hari K T
M: +91-9388758821 | W: http://harikt.com/blog



On Fri, Nov 16, 2012 at 11:12 PM, Chuck Burgess <demon...@gmail.com> wrote:
On Fri, Nov 16, 2012 at 11:22 AM, Paul Jones <pmjo...@gmail.com> wrote:
I think having $context (or perhaps $data, or some other name) for an array of additional stuff is a good idea.


I'm with Paul here.  I think you can spec the interface to say "sure, you can pass in some arbitrary structure of extra info, BUT you must do so in an array".  Now, in order to help make implementations still be interchangeable, even though a particular $data format doesn't match other implementations, the standard should dictate that all implementations must allow for members inside $data that itself does not expect or use, and define what the standard expects the implementation to do with $data members like that.  Perhaps they should be ignored... but at least the standard will have defined such behavior.

Johannes Schmitt

unread,
Nov 19, 2012, 12:08:14 PM11/19/12
to php...@googlegroups.com
Could someone elaborate why a MessageInterface is superior to the context array that Jordi proposes?

I think we could also add the channel to that context array; why an extra optional argument?



Evert

Johannes Schmitt

unread,
Nov 16, 2012, 9:20:50 AM11/16/12
to php...@googlegroups.com
This is where the $data parameter comes in I believe.

However, I'm not sure what the value of this is in practice as we basically have not defined any structure for it nor defined which values are allowed, what the behavior should be when unsupported data is encountered, etc. So, I could imagine that the $data will pretty much be tied to a specific implementor, and might not easily work for another implementor.

That kind of defeats the purpose of the interface as you will need adapters to make two implementations compatible.




On Fri, Nov 16, 2012 at 3:05 PM, Francois Zaninotto <fzani...@gmail.com> wrote:
Hi Group,

Let me chime in on that subject. Log messages are mostly written for machines rather than humans, and machines suck at interpreting messages. I have seen several initiatives lately to provide structured logging through JSON objects - compatible with log mining softwares like Splunk. I think this goes into the right direction, so it would be a pity if the upcoming PHP LoggingInterface standard forbid this...

My 2c,

François


2012/11/16 Johannes Schmitt <schmi...@gmail.com>
Is there a reason the message only "should" be a string instead of "must" be a string? Same for exception. If we do not enforce that, then we basically have no interface, and implementors will have to do type checks, and potentially throw exceptions if they don't like what they got as message. Obviously, this would come at a performance cost.

Apart from that it would be cool to have such an interface.

Cheers,
Johannes


On Fri, Nov 16, 2012 at 12:05 PM, Jordi Boggiano <j.bog...@seld.be> wrote:
Heya,

I would like to start discussing the creation of a LoggerInterface.

Logging is something that pretty much everything can make use of and
where having one unified interface that all libraries can type hint
against would be of tremendous value.

I looked at a few implementations, and have observed different types:

- One single point of entry like ->write() [1][2]. This is not ideal for
an interface since it does not make it easy to abstract the various log
levels.
- Static methods ::$logLevel() [3]. Again not very suitable for an
interface since injecting a classname to call statically is not really a
common practice.
- Separate instance methods ->$logLevel() [4][5]. This is the only model
really fitting IMO and the one I picked.

For compatibility's sake I picked short methods like emerg/warn/info. It
is not my preference - I think the addEmergency/addWarning/addInfo [6] I
used in Monolog are more explicit, but it's probably not worth arguing over.

The interface and a noop logger class can be found in the gist below,
please keep comments here though.

https://gist.github.com/4086282

The main logging implementations I know of are the one in ZF [5],
Monolog [6] (used by Symfony, soon Drupal, maybe others?), log4php [7].
Please correct me if I missed anything used widely outside of framework
specific implementations.

ZF and Monolog are compatible with the proposal, log4php supports only 5
of the 8 levels, but is otherwise compatible, so if they have interest
in this I guess they could easily add the 3 more levels.

Cheers

[1]
https://github.com/cakephp/cakephp/blob/master/lib/Cake/Log/CakeLogInterface.php
[2]
http://git.typo3.org/FLOW3/Packages/TYPO3.FLOW3.git?a=blob;f=Classes/TYPO3/Flow/Log/LoggerInterface.php
[3] https://github.com/UnionOfRAD/lithium/blob/master/analysis/Logger.php
[4]
https://github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpKernel/Log/LoggerInterface.php
[5]
https://github.com/zendframework/zf2/blob/master/library/Zend/Log/LoggerInterface.php
[6] https://github.com/Seldaek/monolog/blob/master/src/Monolog/Logger.php
[7] https://github.com/apache/log4php/blob/trunk/src/main/php/Logger.php

--
Jordi Boggiano
@seldaek - http://nelm.io/jordi
--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


--
You received this message because you are subscribed to the Google Groups "PHP Framework Interoperability Group" group.
To post to this group, send email to php...@googlegroups.com.
To unsubscribe from this group, send email to php-fig+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Carlos Campderrós

unread,
Nov 20, 2012, 3:41:19 AM11/20/12
to php...@googlegroups.com
Hi,

On Fri, Nov 16, 2012 at 7:02 PM, Hari K T <ktha...@gmail.com> wrote:

Why like err , emerg, crit . Can't we make it full critical , emergency , error . 

These are the names of the severity levels in linux's syslog, and also are on the same order.



--
Si no puedes deslumbrar con tu sabiduría,
desconcierta con tus gilipolleces

Jordi Boggiano

unread,
Nov 20, 2012, 4:56:31 AM11/20/12
to php...@googlegroups.com
On 20.11.2012 09:41, Carlos Campderrós wrote:
> Hi,
>
> On Fri, Nov 16, 2012 at 7:02 PM, Hari K T <ktha...@gmail.com
> <mailto:ktha...@gmail.com>> wrote:
>
>
> Why like err , emerg, crit . Can't we make it full critical ,
> emergency , error .
>
>
> These are the names of the severity levels in linux's syslog, and also
> are on the same order.

Indeed that's the reason. While I think it's not great to make
everything shorter instead of explicit, I left the proposal as is
because that's compatible with the interfaces of most existing loggers.
If the majority prefers to break things, I'm happy to use the full names
for priorities instead.

Cheers

Jordi Boggiano

unread,
Nov 20, 2012, 5:08:48 AM11/20/12
to php...@googlegroups.com
Heya,

> I don't believe anyone responded to my last inquiry (directed mostly at
> Jordi but relevant to all), about who we expect the "consumer" of this
> interface to be.
>
> 1) Is the intent that Drupal, CakePHP, Symfony, and PHPBB can all share
> the same logging interface, so a logging backend can be interchanged
> between them? That is, we only need to write one log-to-redis
> implementation, and all of the above can share it.

Just to clarify log-to-redis or log-to-anything should be of no concerns
to users of the logger. You get a LoggerInterface and you log messages
to it. Now within the scope of a framework I don't think this PSR will
have a large effect (at least not in the short term) because you
typically already have a logging solution in place. So now let's say
there is this new PSR interface, and what you can do is make your
existing logging solution adopt it. That way your framework code can be
adapter to it slowly, and users of the framework can adapt their
application code too, but if they don't they still rely on your own
logging thing which is maybe home-grown or an adapter over an external
lib, doesn't matter much.

> 2) Is the intent that Symfony2 Components, Zend Components, Guzzle, and
> Assetic can share the same logging interface, so that
> Drupal/Symfony/CakePHP only have to bridge one "foreign" logging client
> interface to whatever their proprietary logger is? (Some may still use
> a common lib like Monolog, but that would be a separate question.)

Now for libs as I said I think the impact is larger, because right now
there is no way for a library to plug into your framework's log
facilities. If the framework is adopting the interface (even if just as
an adapter), then you can inject that framework-logger-object into the
libraries and they are brought closer to your application/framework,
using the same logging system.

> Those are *very* different things, and we need to be explicitly on the
> same page as to which we're discussing.

It is in fact one and the same. It's just that the impact on frameworks
is lower I believe because most likely you won't want to break BC for
all your users. But you can *adopt* the interface without breaking BC,
and then allow all the libraries which at the moment have no means of
logging to suddenly log stuff. There the impact is greater, but both are
connected and part of a whole.

I hope this makes sense.

Jordi Boggiano

unread,
Nov 20, 2012, 5:25:50 AM11/20/12
to php...@googlegroups.com
Heya,

>> Yes, I think having to create a new Message instance sucks hard for just
>> passing a string and an array. I don't agree that we need objects
>> everywhere. Sometimes the "core" data structures are good enough.
>
> A pain for writing this every time, or a pain for in terms of performance?

Both, but I was thinking more in terms of writing.

> Why would (coming from Monolog) you not add a simple API, exposing exactly the features you want.
> Mapping them to a MonologMessage would only have to happen in one place.

I don't see why you keep saying this. Can you show code samples? Because
the way I understand it it is not the case. If the LoggerInterface needs
a MessageInterface and you build a wrapper in your framework that uses
monolog and converts stuff to MonologMessages, how do you do this
conversion? How does an external library knowing nothing of your
framework or monolog can log an error and give an exception object as
context data? With the array it works, yes it depends on this shady
thing called documentation in the spec, and it's not enforced by
interfaces, but it works. With the interface as I said you would need
too many of them for it to make any sense IMO.

As for comparing this to custom exception classes, IMO they are a
different thing. When you throw a custom exception, you send a message
out there with a custom type you create that allows users of your code
to receive your messages alone and ignoring the rest. That means the
receivers must know about you, and that's fine because they chose to use
your code.

With the log messages, it's the reverse. You have a receiver expecting a
custom type, and the sender has to know about it, but it can't since
it's a generic interface. The only way this works is if all message
types are part of the PSR, and I don't think that's practical at all.
Having an array of arbitrary data and some structures that are
encouraged/documented but never enforced by the LoggerInterface
implementors is more practical I believe.

>> Agreed, interfaces could provide that, but what if you have 10 different
>> meanings for special keys, each have an interface, and then you need a
>> 100 message classes that each implement a different combination of
>> interfaces depending on which you want to expose? I just don't think
>> that's practical at all.
>
> In the case of monolog, it sounds like you already have a pretty good set of properties and meanings.
> I'm not opting for a 100 message classes, but:
>
> * If Monolog as an implementation can use a few extra properties and attach a standard meaning to them, define one MonologMessage that encapsulates that.
> * If some of those should instead be generalized, try to encapsulate them in the PSR within the interface.
>
> I definitely agree that it doesn't make sense for each property to get it's own interface, but I do think it's logical that each vendor does; if they have a need to extend the base spec.

As said above I don't think that's good since if a vendor
(implementation of the logger interface right?) extends the spec, then
the logger interface becomes useless. Your only way as a lonely library
author to talk to the vendor specific interface is being aware of it,
requiring it in your package, and making all users of your lib download
that package too even though they might not use or want that particular
implementation.

By using a string+array we allow users to use the LoggerInterface very
easily. I think we can both at least agree on the fact it's both
conceptually easier and shorter to write. The only pain point it creates
vs interfaces is that implementation of the interfaces might have to do
a bit more work/validation, and be more flexible. They can't just expect
strict things. The thing is.. there are (figuratively) 5 persons on the
planet that will write implementations, and a gazillion that will use
them. So I think it's best to push for ease of use rather than ease of
implementation, and I say that as one of the 5 victims :)

> Regardless, I think I made my point (hopefully) clear. Beyond this, it's more of a matter of opinion. So if mine is an unpopular one, I'll happily recede as I don't wish to get in the way of progress. Any interface is better than no interface :).

Agreed, I just keep pushing a bit because I feel if you don't agree
maybe we just aren't on the same page.

FGM at GMail

unread,
Nov 20, 2012, 5:32:12 AM11/20/12
to php...@googlegroups.com
Maybe it would be best to have aliases ? These cost almost nothing and
are trivial to implement anyway. That way the interface could have
"common usage" methods with the shorter names, and "clean", complete
names.

- That's more or less what Monolog::Logger does with method pairs like
Logger::crit() and Logger::addCritical()

Also, some logging packages have variants on the shorter names, or
don't align on syslog
- KLogger has: logDebug(), logInfo(), logNotice(), logWarn(),
logError(), logCrit(), logAlert(), logEmerg(),
- log4php has: trace(), debug(), info(), warn(), error(), fatal(),
(not Syslog-compatible)
- Monolog has: add(Debug|Info|Notice|Warning|Error|Alert|Emergency),
as well as debug(), info(), notice(), warn(), err(), crit(), alert(),
emerg()
- PEAR/Log only has the shorter names: debug(), info(), notice(),
warning(), err(), crit(), alert(), emerg()
- ZF2 Zend_Log has: debug(), info(), notice(), warn(), err(), crit(),
alert(), emerg()

And most packages also have a generic log() (or equivalent) method for
which the shorter or longer names are just wrappers. This methods also
often allows custom severity levels: do want to allow/recommend/forbid
either practice in PSR-Logger ?
- having a generic method taking the severity (and probably other
params, see other discussions)
- defining the availability and some possibly limits on custom
severity levels (like limiting them to be integers)

2012/11/20 Jordi Boggiano <j.bog...@seld.be>:

Carlos Campderrós

unread,
Nov 20, 2012, 5:41:36 AM11/20/12
to php...@googlegroups.com
Hi,

On Tue, Nov 20, 2012 at 11:32 AM, FGM at GMail <fgma...@gmail.com> wrote:
Maybe it would be best to have aliases ? These cost almost nothing and
are trivial to implement anyway. That way the interface could have
"common usage" methods with the shorter names, and "clean", complete
names.

- That's more or less what Monolog::Logger does with method pairs like
Logger::crit() and Logger::addCritical()


In my opinion the interface just should have one version, and vendors may add the aliases if they want although doing this will probably break interoperability.
 
Also, some logging packages have variants on the shorter names, or
don't align on syslog

By the sample you showed here, it seems that there's some preference in the logger ecosystem for the syslog shorter names, so I would stick to this.
 

And most packages also have a generic log() (or equivalent) method for
which the shorter or longer names are just wrappers. This methods also
often allows custom severity levels: do want to allow/recommend/forbid
either practice in PSR-Logger ?

I think having the generic log() is up to the vendors, and should not be in the interface.
 
- having a generic method taking the severity (and probably other
params, see other discussions)

same as above.
 
- defining the availability and some possibly limits on custom
severity levels (like limiting them to be integers)

same as above.
 

Larry Garfield

unread,
Nov 20, 2012, 11:23:26 AM11/20/12
to php...@googlegroups.com
On 11/20/12 3:56 AM, Jordi Boggiano wrote:
> On 20.11.2012 09:41, Carlos Campderr�s wrote:
>> Hi,
>>
>> On Fri, Nov 16, 2012 at 7:02 PM, Hari K T <ktha...@gmail.com
>> <mailto:ktha...@gmail.com>> wrote:
>>
>>
>> Why like err , emerg, crit . Can't we make it full critical ,
>> emergency , error .
>>
>>
>> These are the names of the severity levels in linux's syslog, and also
>> are on the same order.
>
> Indeed that's the reason. While I think it's not great to make
> everything shorter instead of explicit, I left the proposal as is
> because that's compatible with the interfaces of most existing loggers.
> If the majority prefers to break things, I'm happy to use the full names
> for priorities instead.
>
> Cheers


I'd rather use the full names. Partial word method names annoy me, and
remind me of early days of Unix when words had to be short because
characters were expensive. :-) I'd much rather type critical() than
crit(). It's more readable and as a touch typist I cannot measure the
extra time it takes to type.

If we really want, I'd be OK with offering an extension of the interface
that allows the older syslog-style short-names.

ShortLoggerInterface extends LoggerInterface {

/**
* Alias of LoggerInterface::critical().
*/
function crit();

}

--Larry Garfield

Larry Garfield

unread,
Nov 20, 2012, 11:32:11 AM11/20/12
to php...@googlegroups.com
I think you answered my question, albeit indirectly. :-)

Then the primary target of this interface would be that Solarium (Solr
PHP client lib) can write its log data against LoggerInterface. Then
when Drupal uses Solarium, it has to provide only one
LoggerInterface-implementing object that internally calls its watchdog()
system (our current logger). In that case, I'm fine with there being no
"channel" parameter or just an optional one, and translation handling is
legitimately out of scope (because I don't expect Solarium to know or
care about Drupal's translation system, nor should it; translating logs
coming out of Solarium is then Drupal's problem, not FIG's). That
renders MessageInterface mostly unneeded.

If the primary target is writing a single log-to-redis implementation
that conforms to an interface that Drupal and PHPBB and Cake can all
use, so that Drupal code and Cake code can write to the same logging
backend, then having a more robust API with channel and a clear way to
implement translations and such is very important, because otherwise
everyone will be extending the interface to such an extent that it's not
actually useful. (Drupal *will* need channel, translation, and metadata
support for its logger, so a logging interface that doesn't support
those would not get much if any support.)

It sounds like you're talking about the former, which reduces the impact
of the existing-Drupal-support concerns that FGM and I have.

--Larry Garfield

Tim Otten

unread,
Nov 20, 2012, 8:01:13 PM11/20/12
to php...@googlegroups.com
I think this may have been indirectly addressed by others, but just in case...

On Nov 19, 2012, at 3:18 PM, Evert Pot wrote:

>> Case #1 seems dangerous to me -- if each framework adopts its own extensions to the logging layer, then that will create barriers for interoperability. For example, suppose a library uses the "MonologMessage" class but is installed on a non-Monolog system. The library will need to declare a dependency on Monolog -- even though the application doesn't use Monolog. If you stitch together four libraries from four different frameworks, you'll wind up with four logging dependencies -- one of those logging dependencies will be loaded and used faithfully at runtime, and three will be used in a reductive manner (eg falling back to "toString()" behavior). It would be best if we eliminate the need for framework-specific extensions by defining a proper interface.
>
> I would want to point out as a small retort, that I feel that it's essential that __toString(), or the base interface will always provide a solid, useful outcome; something you can actually use in a log.
>
> If an external logger receives a MonologMessage, they should not have to depend on Monolog to handle that message. If that were the case, a PSR would be pointless as it fails in the one thing it tries to solve.

Yes but no.

Situation: Suppose we have a library that wishes to log things. The library author (aka log-consumer) decides that the basic log message isn't good enough, so -- following the recommendation of your previous email -- he decides to use "MonologMessage". Now, instead of calling "$log->info('some text')", the library will call "$log->info(new MonologMessage(...))"

If you take this situation and look at the runtime (eg use xdebug to monitor callpaths and see how often functions get called), then your statement would appear correct: passing an instance of "MonologMessage" (which implements MessageInterface) to a different logger would not cause Monolog to do much work. Monolog would not be responsible for writing or routing log messages. All that work would be done by a different logger, and that other logger would accept instances of "MonologMessage."

However, if you look at the code statically, you would see that the library now depends on "MonologMessage." It cannot do any logging unless that symbol is well-defined. That symbol would be defined by the "monolog" package, so the library author would add "monolog" to the list of dependencies in "composer.json." Now anyone who uses the library must have a copy of Monolog (even if it's mostly dormant).

Of course, implementing MessageInterface isn't always bad. If the library author (aka log-consumer) implements the MessageInterface, then that's OK -- the library won't be introducing any funny dependencies. But if the framework or log-service implements MessageInterface, then it undermines interoperability.

justin

unread,
Nov 20, 2012, 11:17:15 PM11/20/12
to php...@googlegroups.com
On Tue, Nov 20, 2012 at 5:01 PM, Tim Otten <t...@onebitwise.com> wrote:
> Situation: Suppose we have a library that wishes to log things. The library author (aka log-consumer) decides that the basic log message isn't good enough, so -- following the recommendation of your previous email -- he decides to use "MonologMessage". Now, instead of calling "$log->info('some text')", the library will call "$log->info(new MonologMessage(...))"
>
> If you take this situation and look at the runtime (eg use xdebug to monitor callpaths and see how often functions get called), then your statement would appear correct: passing an instance of "MonologMessage" (which implements MessageInterface) to a different logger would not cause Monolog to do much work. Monolog would not be responsible for writing or routing log messages. All that work would be done by a different logger, and that other logger would accept instances of "MonologMessage."
>
> However, if you look at the code statically, you would see that the library now depends on "MonologMessage." It cannot do any logging unless that symbol is well-defined. That symbol would be defined by the "monolog" package, so the library author would add "monolog" to the list of dependencies in "composer.json." Now anyone who uses the library must have a copy of Monolog (even if it's mostly dormant).
>
> Of course, implementing MessageInterface isn't always bad. If the library author (aka log-consumer) implements the MessageInterface, then that's OK -- the library won't be introducing any funny dependencies. But if the framework or log-service implements MessageInterface, then it undermines interoperability.


Given this scenario, it looks like there's room for a simple
MessageInterface implementation (or set of implementations) to be
packaged as a standalone library. I'd see it much like the SPL
Exception subclasses. Maybe this could be a Monolog component package,
like `monolog/message` or something. That way library authors (and
even frameworks) could benefit from a lightweight set of advanced
message functionality, and it could even be used internally by
`monolog/monolog`.

-- justin

Paul Jones

unread,
Nov 21, 2012, 10:55:25 AM11/21/12
to php...@googlegroups.com
So where are we at this point?


-- pmj

Jordi Boggiano

unread,
Nov 21, 2012, 11:09:01 AM11/21/12
to php...@googlegroups.com
On 20.11.2012 17:32, Larry Garfield wrote:
> I think you answered my question, albeit indirectly. :-)
>
> Then the primary target of this interface would be that Solarium (Solr
> PHP client lib) can write its log data against LoggerInterface. Then
> when Drupal uses Solarium, it has to provide only one
> LoggerInterface-implementing object that internally calls its watchdog()
> system (our current logger). In that case, I'm fine with there being no
> "channel" parameter or just an optional one, and translation handling is
> legitimately out of scope (because I don't expect Solarium to know or
> care about Drupal's translation system, nor should it; translating logs
> coming out of Solarium is then Drupal's problem, not FIG's). That
> renders MessageInterface mostly unneeded.

Agreed.

> If the primary target is writing a single log-to-redis implementation
> that conforms to an interface that Drupal and PHPBB and Cake can all
> use, so that Drupal code and Cake code can write to the same logging
> backend, then having a more robust API with channel and a clear way to
> implement translations and such is very important, because otherwise
> everyone will be extending the interface to such an extent that it's not
> actually useful. (Drupal *will* need channel, translation, and metadata
> support for its logger, so a logging interface that doesn't support
> those would not get much if any support.)

Glad if you think it's a non-issue, but I anyway think the current
interface can also work for that purpose. And I believe FGM's work on
replacing drupal's watchdog() by monolog kind of proves it. You need
translations but those are applied at display time. You need metadata
which is $context, and you need a channel which can be passed in the
constructor and/or given in the context data. Works fine. The thing is
just that in Drupal, and similarly in other frameworks IMO, you don't
need to change the interface. If you want extra features you might need
to extend or create new handlers so that your logs are stored
differently and can be displayed a bit differently, but the actual
*collection* of logs can be done just fine with that interface.

Jordi Boggiano

unread,
Nov 21, 2012, 11:14:47 AM11/21/12
to php...@googlegroups.com
Heya,
I don't quite know what to do with this interface discussion. I said
what I had to say. I still don't see much value in having that, not sure
if I managed to slightly change the opinions of Evert or Justin (who I
think are the only proponents of the MessageInterface?).

On another note I updated the gist to match the fully qualified level
names: https://gist.github.com/4086282

I guess one next step would to write a proposal around the interface,
that would detailing some things (sort of summarizing the discussion).

Paul Jones

unread,
Nov 21, 2012, 11:32:49 AM11/21/12
to php...@googlegroups.com

On Nov 21, 2012, at 10:14 AM, Jordi Boggiano wrote:

>> So where are we at this point?
>
> I don't quite know what to do with this interface discussion. I said
> what I had to say. I still don't see much value in having that, not sure
> if I managed to slightly change the opinions of Evert or Justin (who I
> think are the only proponents of the MessageInterface?).
>
> On another note I updated the gist to match the fully qualified level
> names: https://gist.github.com/4086282
>
> I guess one next step would to write a proposal around the interface,
> that would detailing some things (sort of summarizing the discussion).

Perhaps the thing that needs an interface is the *context* and not the message string. That way, folks who need simple string messages are not encumbered, and those who need context can do something useful with it.


-- pmj

Jordi Boggiano

unread,
Nov 21, 2012, 11:44:05 AM11/21/12
to php...@googlegroups.com
Heya,

> Perhaps the thing that needs an interface is the *context* and not the message string. That way, folks who need simple string messages are not encumbered, and those who need context can do something useful with it.

To me the main value of the context is that I can throw arbitrary data
at it, and know it will appear in the logs somehow. In fancy backends
you will get actual additional fields on the record that are usable for
filtering etc, in dumber text files you might just get a json encoded
blob at the end of the line. But you get something through, with very
low barriers.

If you need to wrap it up in a nicely packaged present, chances are most
people will not bother, and you end up with less valuable information in
the logs. This goes against the goal of the PSR as far as I am concerned.

Evert Pot

unread,
Nov 21, 2012, 12:01:22 PM11/21/12
to php...@googlegroups.com
> I don't quite know what to do with this interface discussion. I said
> what I had to say. I still don't see much value in having that, not sure
> if I managed to slightly change the opinions of Evert or Justin (who I
> think are the only proponents of the MessageInterface?).

I definitely understood your argument, but for me personally they did not outweigh the drawbacks.

But, just like you.. I think what needed to be said about it has been said, and I will happily go along with the majority; whatever that may be.

Cheers,
Evert

Paul Jones

unread,
Nov 21, 2012, 12:15:17 PM11/21/12
to php...@googlegroups.com
Agreed. I'd rather see a POPA (plain old PHP array) myself, but wanted to raise it as an option.



-- pmj

Lukas Kahwe Smith

unread,
Nov 21, 2012, 12:44:00 PM11/21/12
to php...@googlegroups.com, php...@googlegroups.com
i made this suggestion already in the cache discussion. we could provide suggested keys for such arrays as constants to ease compatibility and help with preventing typos.

regards,
Lukas

Hari K T

unread,
Nov 21, 2012, 1:00:40 PM11/21/12
to php...@googlegroups.com

> Agreed. I'd rather see a POPA (plain old PHP array) myself, but wanted to raise it as an option.

i made this suggestion already in the cache discussion. we could provide suggested keys for such arrays as constants to ease compatibility and help with preventing typos.

then an array is fine I guess.
 

regards,
Lukas

Paul Jones

unread,
Nov 21, 2012, 1:00:57 PM11/21/12
to php...@googlegroups.com
Hi all,

We seem to have reached the end of the initial discussion on LoggerInterface. Involvement has been kind of low, so I say we ping some of the other project reps and get them to take a look. Meanwhile, Jordi, do you feel like writing up a formal draft proposal?


-- pmj

Jordi Boggiano

unread,
Nov 21, 2012, 1:14:26 PM11/21/12
to php...@googlegroups.com
Heya,

> Meanwhile, Jordi, do you feel like writing up a formal draft
> proposal?

Of course, I will try to and do that when I find some time.

Larry Garfield

unread,
Nov 21, 2012, 3:20:58 PM11/21/12
to php...@googlegroups.com
On 11/21/12 11:44 AM, Lukas Kahwe Smith wrote:

>>> To me the main value of the context is that I can throw arbitrary data
>>> at it, and know it will appear in the logs somehow. In fancy backends
>>> you will get actual additional fields on the record that are usable for
>>> filtering etc, in dumber text files you might just get a json encoded
>>> blob at the end of the line. But you get something through, with very
>>> low barriers.
>>>
>>> If you need to wrap it up in a nicely packaged present, chances are most
>>> people will not bother, and you end up with less valuable information in
>>> the logs. This goes against the goal of the PSR as far as I am concerned.
>>
>> Agreed. I'd rather see a POPA (plain old PHP array) myself, but wanted to raise it as an option.
>
> i made this suggestion already in the cache discussion. we could provide suggested keys for such arrays as constants to ease compatibility and help with preventing typos.
>
> regards,
> Lukas

I think that's a good compromise. +1 here.

--Larry Garfield

Evert Pot

unread,
Nov 21, 2012, 3:28:02 PM11/21/12
to php...@googlegroups.com
I agree too; it's a bit more strict that way.

Patrick Mulvey

unread,
Nov 21, 2012, 9:14:37 PM11/21/12
to php...@googlegroups.com
I'm know I'm not a voting member, but I do have my own closed-source framework that I develop myself. I've been following this discussion and I agree with the conclusions and consensus at this point. It'd be good if we could get the results of this discussion summarised to facilitate further involvement.

Tim Otten

unread,
Nov 24, 2012, 3:09:29 PM11/24/12
to php...@googlegroups.com
The suggestion about using "%" notation to reference data is a good one -- it makes the logging code more readable, and it helps with internationalization, but it doesn't show up in the gist. Perhaps something like these docblock comments would do?


Note that this prescribes only the interface for submitting messages -- it doesn't specify how implementers should evaluate the expressions. For example, a compliant implementation could reasonably:

1) Interpolate the variable values immediately -- and write the resulting string to a log file
2) Ignore the %var% notation -- write the raw message to a log file (along with the pretty-printed $context)
3) Save the $message and $context to a database -- and attempt translation+interpolate at display time. (Note: A full i18n/l10n solution for logging would require more standards on i18n/l10n generally, and that's out-of-scope for the logging standard. But this keeps the door open for properly localizing logs.)


On Nov 16, 2012, at 10:18 AM, Jordi Boggiano wrote:

Heya,

In Drupal <= 7.x these event types are implemented as just event
message templates and the parameters as variables susbtitutions into
the template, but this is just one specific implementation which
needn't be limiting. It has however proven useful as a way to browse
data conveniently as in the mongodb_watchdog module, which uses these
templates (event types) as a grouping element.

Of course this can be mimicked by adding extra processing based on the
"some data" like the ones available in Monolog, but I think it is more
useful to have the mimicking done the other way round: defining an
interface with this slightly more evolved parameter specification,
which can very simply used in the degraded (string + data) mode, while
still providing to logs aggregators, which typically work in more
load-intensive scenarios, the more structured information without
having them do extra work to get it.

I am not entirely sure I get your point, but I would say that's maybe
something that needs to be addressed in implementations (though it could
be documented in the interface to nudge people to use it), for example
monolog already allows you to use extra data in the message, but this
could be expanded to allow context data as well, like:

   $logger->info('Created user %context.user%', ['user'=>$username]);

That way you can indeed do grouping on the message since it's a generic
"type" and yet you can rebuild a full message from the data + message
type for human consumption.

Now I am not sure I got it right, but I also am wondering whether this
is really better than a simpler:

   $logger->info('Created user', ['user'=>$username]);

This won't allow you to get the full info, the human consumption would
look more like:

   Created user {"user": "Bob"}

Assuming you json_encode the context to display anything easily to users.


Cheers

--
Jordi Boggiano
@seldaek - http://nelm.io/jordi

Paul Jones

unread,
Nov 24, 2012, 3:27:21 PM11/24/12
to php...@googlegroups.com

On Nov 24, 2012, at 2:09 PM, Tim Otten wrote:

> The suggestion about using "%" notation to reference data is a good one -- it makes the logging code more readable, and it helps with internationalization, but it doesn't show up in the gist. Perhaps something like these docblock comments would do?
>
> https://gist.github.com/4141164
>
> Note that this prescribes only the interface for submitting messages -- it doesn't specify how implementers should evaluate the expressions. For example, a compliant implementation could reasonably:
>
> 1) Interpolate the variable values immediately -- and write the resulting string to a log file
> 2) Ignore the %var% notation -- write the raw message to a log file (along with the pretty-printed $context)
> 3) Save the $message and $context to a database -- and attempt translation+interpolate at display time. (Note: A full i18n/l10n solution for logging would require more standards on i18n/l10n generally, and that's out-of-scope for the logging standard. But this keeps the door open for properly localizing logs.)

I don't think we should specify formats regarding placeholders for interpolation. Some systems use ":foo", others "{{foo}}", others "{:foo}", others "%foo%", etc. I opine that it is up to the implementation to figure out what to do with the $message and corresponding $context.


-- pmj

Tim Otten

unread,
Nov 24, 2012, 4:52:35 PM11/24/12
to php...@googlegroups.com
I agree that the general flow for processing $message and $context should be up to the log-implementation and the application, but the variable-expression notation is part of the contract between the log-consumer and log-implementer -- if we punt on standardizing that, then the result will be one of these:

- Incompatibility/bugginess -- If we say that variable-expressions are allowed but leave that as an implementation-detail, then some libraries and implementations will choose one (%var%) while some choose another ({{var}}), and the various libraries cannot be freely mixed with the various implementations. If appdevs mix them anyway, then the resulting log output will be buggy. (If a library uses one set of escape codes while an implementation uses another, the messages will be evaluated wrongly -- sometimes failing to evaluate an expression, and sometimes accidentally evaluating an expression.)

- Complexity -- To allow one log-implementation to work with libraries that expect different notations, that implementation would need to support *all* notations -- and it would need *wiring* to determine when to use each notation

- Inflexibility -- If library developers realize that app-devs are faced with the above choices, then they'll likely "work-around" it by ignoring the string-interpolation feature in the log-implementation and instead do their own string manipulation; but that prevents the log-implementers and app-devs from doing their own clever things with the logging data flows (like translation, like color-coding or escaping variable output, like elegant display of oversized data strings, like exact-matching on log messages instead of probablistic-matching with regexes).

From a software-engineering perspective, the resulting system will be simpler, more reliable, and more flexible if we standardize than if we don't. What's the downside? Will it offend someone's aesthetic sensibility if their favorite notation isn't the "winner"? (Personally, I don't care what the notation is -- as long as it's interoperable.)

Larry Garfield

unread,
Nov 25, 2012, 3:49:49 AM11/25/12
to php...@googlegroups.com
I'm inclined to agree. Placeholders have use beyond just translation,
but are a prerequisite for translation. They're useful for security,
too, as you can effectively use them the same way as prepared
statements. We've already established that a PSR can go beyond simply
defining a language interface (none of our 3 PSRs yet have even done
that!), so it's definitely in scope to define a basic string syntax for
compatibility.

If we provide some standard context keys as Lukas suggested, then we can
make one of them "placeholders", which are just an array of values to
swap into the string with your favorite string replacement function. A
given implementation can then decide if it wants to do that at save-time
or display-time. (Of course, that then begs the question of whether we
leave that in context or decide it's important enough to be its own
parameter. I think there's good arguments both ways.)

As for the syntax, meh. Drupal actually has 3 placeholder forms: %var,
@var, and !var, which indicate different levels of sanitization. (%var
means "you have to sanitize this, I haven't", !var means "trust me, it's
safe", and @var means "sanitize this and throw em tags around it. We can
definitely ignore that one.) I'm fine if we want to adopt % and ! a la
Drupal, but if not we can translate any parsable placeholder format into
what we need easily enough.

If we don't want to use the Drupal syntax, I'd say Twig-style ( {{var}}
) is the next obvious format to consider. It's a format that a lot of
people are familiar with from one of a number of template languages, and
fits the use case rather naturally. The downside is that it doesn't
offer a way to differentiate between "I've sanitized this" and "you need
to sanitize this". Whether or not we want to offer that distinction in
the API is, I think, open to debate.

--Larry Garfield

Jordi Boggiano

unread,
Nov 25, 2012, 5:46:36 AM11/25/12
to php...@googlegroups.com
> If we don't want to use the Drupal syntax, I'd say Twig-style ( {{var}}
> ) is the next obvious format to consider. It's a format that a lot of
> people are familiar with from one of a number of template languages, and
> fits the use case rather naturally. The downside is that it doesn't
> offer a way to differentiate between "I've sanitized this" and "you need
> to sanitize this". Whether or not we want to offer that distinction in
> the API is, I think, open to debate.

## Just FYI

Here is how it works in monolog at the moment (when you use the
LineFormatter for flat files, no other log backend uses placeholders I
believe):

All the log record properties (datetime, channel, level_name, message,
context and extra - the latter being stuff added by processors which can
do things like automatically adding the REMOTE_ADDR to every log record)
are accessible as: %datetime%, %channel%, .. and for context/extra which
are arrays either you use %context% which prints a json_encoded version,
or you can use %context.foo% which is a variable inside it.

So this is used to create a line in the log file, but it's not used for
interpolating stuff inside the message itself at all.

As such, I guess adding support for %foo% in the message that
interpolates from $context['foo'] directly is fairly harmless (in terms
of BC).

## Now for the actual spec..

I think adding this makes sense if only to nudge people towards using
the same conventions, and because that way there is a chance that libs
could start using placeholders too, ultimately enabling drupal (or other
obviously) to provide translations for the logs of the libs they rely upon.

For the format I would prefer %foo% or {{foo}} over simply %foo because
I think having delimiters on both sides is best.

Not sure we need sanitation at all.. Larry can you explain a bit more
what this does and what the point is? Could you also grep to see if the
"I sanitized this already" syntax is used much?
It is loading more messages.
0 new messages