The logging system of translate5 is currently completely reworked.
The architecture is similar as the Zend Logger from Zend_Log. On investigation if Zend_Log can be used for Translate5 needs, it turned out that translate5s special needs to the logger was not implementable by just reusing / extending the Zend_Log facility. So a conceptional adoption was done.
Since not only errors are logged, instead of "Error" or "ErrorCodes" we are talking about "Event" and "EventCodes" in the context of logging.
In the context of Exception "error" can be used, since exceptions are errors.
The following levels are defined as class constants prefixed with LEVEL_ in ZfExtended_Logger class:
Level | Usage example |
---|---|
FATAL | FATAL PHP errors, other very fatal stuff, wrong PHP version or so |
ERROR | common unhandled exceptions |
WARN | User locked out due multiple wrong login, A user tries to edit a task property which he is not allowed to change |
INFO | user entered wrong password, wanted exceptions BadMethodCall on known non existent methods (HEAD), other known and wanted exceptions |
DEBUG | debugging on a level also useful for SysAdmins, for example user logins |
TRACE | tracing debugging on a level only useful for developers |
provides functions like fatal, error, warn, info, debug, trace for direct log output
// use the default logger instance: $logger = Zend_Registry::get('logger'); /* @var $logger ZfExtended_Logger */ $logger->info('E1234', 'This is the message for the log where {variable} can be used.', [ 'mydebugdata' => "FOOBAR can be an object or array too", 'variable' => "This text goes into the above variable with curly braces", ]); |
A ZfExtended_Logger can have multiple instances of writers. The writers are responsible for filtering (according to the configuration) and writing the log message to the specific backend.
So each writer can have a different filter configuration, one can configure one writer to listen on all import errors on error level debug and send them to one address, another writer can listen to all events on level > warn to log them as usual.
Abstract class for each specific writer.
Sends the event directly to the configured email address. No repetition recognition of events (spam protection) is done here.
Writes the events to the error_log configured in PHP.
Writes the events to the chrome developer toolbar JS log. The Addon "Chrome Logger" must be installed therefore in the browser.
Recommended not to be used in production environments!
translate5 specific writer which logs all events with a field "task" containing a editor_Models_Task instance in the extra data to a special task log table.
editor_Models_Logger_Task extending ZfExtended_Models_Entity_Abstract and editor_Models_Db_Logger_Task extending Zend_Db_Table_Abstract are used to save to and load from the task log table.
The configuraton of log writers can be either done in the application.ini / installation.ini or hardcoded in PHP.
resources.ZfExtended_Resource_Logger.writer.default.type = 'ErrorLog' resources.ZfExtended_Resource_Logger.writer.default.level = 4 ; → warn logs only till warning ; Test config: resources.ZfExtended_Resource_Logger.writer.mail.type = 'DirectMail' ; via the formatter define what the user gets: full debug content, or only notice and so on. resources.ZfExtended_Resource_Logger.writer.mail.level = 2 ; → warn logs only till error resources.ZfExtended_Resource_Logger.writer.mail.receiver[] = 'sysadmin@example.com' resources.ZfExtended_Resource_Logger.writer.mail.receiver[] = 'other-important-person@example.com' resources.ZfExtended_Resource_Logger.writer.mail.type = 'Database' ; via the formatter define what the user gets: full debug content, or only notice and so on. resources.ZfExtended_Resource_Logger.writer.mail.level = 16 ; → warn logs only till error |
resources.ZfExtended_Resource_Logger.writer.tasklog.type = 'editor_Logger_TaskWriter' resources.ZfExtended_Resource_Logger.writer.tasklog.level = 4 ; → warn logs only till warning ; ; The TaskWriter is a editor specific writer, it logs only events containing information about a task. The events are logged in a dedicated table, accessable via the frontend. Only warnings or events more severe as warnings are written to the task log. So no notices about tasks are additionally logged. ; |
// use the default logger instance: $logger = Zend_Registry::get('logger'); $logger->addWriter('name', $writerInstance); |
Currently there are two database tables receiving events:
Whats the difference?
Zf_errorlog receives basicly ALL events, limited by the configured log level of events to be received.
LEK_task_log receives:
-- instead of select * from Zf_errorlog; -- use for better readability: select * from Zf_errorlog\G |
In translate5 to less different exception types were used till now. Mostly just a ZfExtended_Exception was used.
To be more flexible in filtering on logging exceptions but also on internal exception handling with try catch more exceptions are needed.
editor_Models_Import_FileParser_Sdlxliff_Exception extends editor_Models_Import_FileParser_Exception extends ZfExtended_ErrorCodeException
In the Sdlxliff fileparser editor_Models_Import_FileParser_Sdlxliff_Exception are thrown. In general fileparser code editor_Models_Import_FileParser_Exception should be used.
Since in FileParsing many errors can happen, a fine granulated exception structure is needed. In other code places this is not the case. At least one own Exception per Plugin / Package.
Each exception contains the mapping between the used event code and the related error message string. Since we are in an exception we can talk here about errors and not generally about events.
class editor_Models_Import_FileParser_Sdlxliff_Exception extends editor_Models_Import_FileParser_Exception { /** * @var string */ protected $origin = 'import.fileparser.sdlxliff'; //the origin string for hierarchical filtering static protected $localErrorCodes = [ //the error (event) codes used by that exception 'E1000' => 'The file "{filename}" contains SDL comments which are currently not supported!', 'E1001' => 'The opening tag "{tagName}" contains the tagId "{tagId}" which is no valid SDLXLIFF!', 'E1003' => 'There are change Markers in the sdlxliff-file "{filename}"! Please clear them first and then try to check in the file again.', // [...] more mappings ]; } |
if ($shitHappened && $itWasMyFault) { //There are change Markers in the sdlxliff-file which are not supported! → there should be a brief comment to explain what is going wrong throw new editor_Models_Import_FileParser_Sdlxliff_Exception('E1003', [ // → The exception receives just the EventCode and an array with extra data 'task' => $this->task, 'filename' => $this->_fileName, ]); } |
For the handling / catching of such exceptions see the section "Examples for specific Exceptions" below.
Exception | HTTP Code | Description / Idea behind |
---|---|---|
Exception | PHP Basic Exception class should never be used directly. Can be thrown by underlying legacy code. | |
Zend_Exception | Zend Basic Exception class should never be used directly. This Exception an subclasses can be thrown by underlying legacy Zend code. | |
ZfExtended_Exception | ZfExtended Basic Exception class should not be used directly anymore. Should be replaced by specific exceptions. | |
ZfExtended_ErrorCodeException | Base class for specific exceptions, should not be thrown directly. | |
This exceptions are used to answer errors to the API in REST full way, by transporting semantic as HTTP response code.
This Exceptions are intended not to be caught, since they transport information to the Caller.
But of course they can be caught if needed and handled differently.
Exception | HTTP Code | Description / Idea behind |
---|---|---|
ZfExtended_BadMethodCallException | 405 Method Not Allowed | Means that the used HTTP Method is not allowed / not implemented. So a usage makes only sense on the controller level. |
ZfExtended_NotAuthenticatedException | 401 Unauthorized | Means the the user is not authorized, so in the request there was no information to identify the user. This error should redirect the user in the GUI to the login page. |
ZfExtended_NotFoundException | 404 Not Found | Is used in application routing, is thrown when the whole requested route is not found (invalid URL). |
ZfExtended_Models_Entity_NotFoundException | 404 Not Found | Is used if an entity can not be found (if it is loaded via an id or guid) |
ZfExtended_NoAccessException ZfExtended_Models_Entity_NoAccessException | 403 Forbidden | The user (authenticated or not) is not allowed to see / manipulate the requested resource. TODO: clear the difference between both exceptions. |
TODO new Unprocesseable Entity Exception (or reuse ValidateException?) ZfExtended_UnprocessableEntityException::createResponse | 422 Unprocessable Entity | Should be used PUT/POSTed content does prevent normal processing: Wrong parameters, missing parameters. |
ZfExtended_Models_Entity_Conflict | 409 Conflict | Should be used if the status of the entity does prevent normal processing: The entity is locked, the entity is used/referenced in other places. In other words: the entity it self is reasonable that the request can not be processed. |
ZfExtended_VersionConflictException | 409 Conflict | Must only be used if entity can not be saved due a changed entity version. The classical usage of the 409 HTTP error: the entity was changed in the meantime. |
ZfExtended_BadGateway TODO implement 504 Gateway Timeout | 502 Bad Gateway | Should be used if our request calls internally a third party service, and the third party service or the communication with it does not work. |
ZfExtended_ValidateException | 422 Unprocessable Entity | Use this exception if the given data in the request can not be validated or contains non processable data. |
ZfExtended_FileUploadException | 400 Bad Request | TODO, currently not used. Should be used in the context of errors with uploaded data |
ZfExtended_Models_MaintenanceException | 503 Service Unavailable | Only usable in the context of the maintenance mode |
ZfExtended_Models_Entity_Exceptions_IntegrityDuplicateKey | TODO → specific exception? No direct HTTP code | Is thrown on saving entities and the underlying DB call returns a "integrity constraint violation 1062 Duplicate entry" error: That means an entity with such key does already exist (entities with additional unique keys, customer number or user login). |
ZfExtended_Models_Entity_Exceptions_IntegrityConstraint | TODO → specific exception? No direct HTTP code | Is thrown on saving/deleting entities and the underlying DB call returns one of the following "integrity constraint violation" error:
|
One fundamental problem for development in translate5 is that the default GUI language of translate5 is german, but most of the error messages are in english.
At some places the error message is intended to be used directly as user feedback, therefore the error message must be also in german at that place and the message must go through the internal translation mechanism before send to the user.
That is indeed not the case for errors which should unlikely not happen, there a default english message is enough.
Therefore on exception creation we have to decide if:
This results in two different ways how semantic exceptions are produced:
This exceptions are used in the case of errors in the application.
Handling of such exceptions:
Exception | Description / Idea behind |
---|---|
editor_Models_Import_FileParser_Sdlxliff_Exception | Exception which is thrown on the usage of the SdlXliff fileparser. |
ZfExtended_Logger_Exception | Exception which is thrown in the context of error logging, so if in the logging is happening an error |
ZfExtended_ErrorCodeException | Base class for all ErrorCode based Exceptions |
The EventCodes used in exceptions and other logging usages are defined via the ErrorCodes listed and maintained in confluence..
The default logger instance is generally available in the registry:
$log = Zend_Registry::get('logger'); /* @var $log ZfExtendend_Logger */ $log->error("TODO"); // logs an error $log->logDev($data1, $data2, ...); // a convienent replacement for error_log(print_r($data, 1)); Only for development, should not be comitted! // the WorkflowLogger - dedicated to translate5 tasks and workflow stuff - must be instanced manually: // TODO |
Exception Definition / Usage:
- Before the refactoring we had only a few Exceptions
- Now for each domain (import / export / Plugin XY) one or more Exceptions should be defined.
- Each Exception has a different domain (example: import.fileparser.sdlxliff) for filtering.
- Also the Exception carries Semantik through its type, only if needed. Example:
Plugin_Demo_Exception > A general Exception in the Demo Plugin Scope
Plugin_Demo_NoConnectionException > On more specific errors (no connection established) specific exceptions can be created. Why that: If we can / want to handle that exceptions differently we can do that:
try {
$this->foo();
}
catch(Plugin_Demo_NoConnectionException $e) {
//handle the no connection error
logger::exceptionHandled($e); //logs the exception on level debug and marks the logged Exception as Handled.
}
// Plugin_Demo_Exception are handled via the final handler and stops the PHP request