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