Ver Mensaje Individual
  #36  
Antiguo 06-11-2015
Avatar de Delphius
[Delphius] Delphius is offline
Miembro Premium
 
Registrado: jul 2004
Ubicación: Salta, Argentina
Posts: 5.582
Reputación: 25
Delphius Va camino a la fama
Cita:
Empezado por Ñuño Martínez Ver Mensaje
En la FCL (que es la base que usan los componentes de Lazarus), la clase TCustomApplication (de la cual salen luego diferentes "TApplication" para proyectos específicos: GUI, servidor, etc.) tiene un método llamado "Log", el cual es el que he usado en el código que he puesto. Este es un método virtual que por defecto no hace nada. Diversos componentes y bibliotecas lo utilizan para ir dando cuenta de lo que hacen. El objetivo es que el programador extienda dicho componente y, recibiendo los parámetros, haga con ellos la bitácora adecuada: desde guardarlos en un archivo a enviarlos por correo o lo que sea. La verdad es que no sé si Delphi también tiene esto o algo parecido porque hace bastante tiempo que no lo manejo, y además la versión que dispongo es la 6.

En cuanto a la diferencia entre excepciones, tiene que ver con el compilador. Free Pascal y Delphi generan código diferente para manejar excepciones (y sé que el propio Free Pascal usa diferente código para diferentes sistemas operativos y procesadores, y supongo que las últimas versiones de Delphi también). Hay diferencias sutiles que, por lo que he visto, hacen referencia al funcionamiento interno. En general son iguales (la clase Exception, con sus constructores y sus propiedades), pero luego tienen algunas funciones internas en la RTL (system, sysutils, etc) donde hay alguna diferencia. Por ejemplo sé que Free Pascal/Lazarus incluye algunas propiedades en TObject y TComponent que no hacen nada, pero que están ahí por compatibilizar con Delphi, y añade otras que Delphi no tiene por compatibilizar con UNIX o MacOS, por ejemplo. Esto incluye algunas cosas en el manejo de RTTI para que las excepciones sepan de dónde vienen.

En definitiva, aunque en general son muy iguales, Lazarus/FreePascal y Delphi siguen siendo diferentes.
El método Log es medio nuevo para mi. Te comento que tienes un pequeño desliz: el método virtual y que puede sobreescribirse es el DoLog() y no Log()

Esta es la implementación de Log en TCustomApplication:

Código Delphi [-]
procedure TCustomApplication.Log(EventType: TEventType; const Msg: String);

begin
  If (FEventLogFilter=[]) or (EventType in FEventLogFilter) then
    DoLog(EventType,Msg);
end;

Que invoca al método virtual DoLog:

Código Delphi [-]
  Protected
    // ...
    Procedure DoLog(EventType : TEventType; const Msg : String);  virtual;

Cuya implementación está vacía:

Código Delphi [-]
procedure TCustomApplication.DoLog(EventType: TEventType; const Msg: String);

begin
  // Do nothing, override in descendants
end;

Tu código, aún intentando hacer un sistema de logging no tiene sentido que invoques a Log(), al menos en aplicación tradicionales, porque no hará nada: ¡la clase TApplication no lo sobre escribe!

Lo que comentas sobre las excepciones me parece por demás lógico que internamente, como en buena parte de la LCL, haga trabajo duro para adaptarse a lo multiplataforma. Yo más bien me preguntaba de cara al desarrollador, en lo que es público y/o publicado. Explorando hace un tiempo en TObject me pareció ver cosas como la que dices, que hay cosas que no hacen nada... Y vi cosas que D6 no tiene... como el ToString().

Es verdad que dices que ha pesar de ser similares, tienen también sus diferencias significativas.

Cita:
Empezado por mamcx Ver Mensaje
Código Delphi [-]ON Error: Exception DO BEGIN IF Config.Depurando THEN sysutils.ShowException (Error, ExceptAddr) ELSE Application.MessageBox ('No pudo realizarse la operación', 'Error'); Application.Log (etError, Format ('[%s] %s at %p', [Error.ClassName, Error.Message, ExceptAddr])); END


Nota el comentario: "Lo que veo en el ejemplo de Ñuño es un intento de replicar...". No quise decir que estaba haciendo un sistema de Log, sino que replica lo que hace uno de estos, osea, un sistema de log permite hacer llamadas a "info", "debug", "warning", y potencialmente llegar a lo que se esta mostrando: Si quiero, cuando sea "debug" mandalo a consola op archivo y si es "warning", mostrar un mensaje o lo que sea.

O dicho de otra forma, es una implementacion de lo que hace Ñuño!

Re-leyendo veo que me falto aclarar porque rayos menciono el punto, y es que hablaste de usar las directivas de compilacion con relacion a esto. Y cuando preguntas:



Es algo que es obvio cuando uno esta acostumbrado a usar una libreria de LOG, pero no tanto en el ejemplo propuesto. Osea: Porque usar una condicional en vez de usar la directiva de compilacion? Porque los errores/mensajes no son todos iguales y tienen diversos niveles: Asi como hay "warnings" y "error" a la hora de compilar, una app puede lanzar una excepcion, pero estas son "warnings" otras son "errors" otras son "catastrophic errors" otras son "ignorables/infos", etc, y es el programador y no el compilador que determina a que nivel asignar cada cosa. Lo mismo decir que una excepcion es parte de la logica de negocio, o interna, o que es algo pal' usuario o solo para el desarrollador.

Solo que en el ejemplo de codigo, se esta asumiendo (que ademas es muy normal) que en modo DEBUG solo existe el desarrollador. Y es muy practico en tiempo de ejecucion, sin usar IDEs, setear la app a modo "debug" para diagnosticarla cuando esta corriendo donde el cliente: Cosa que es lo que se hace con una libreria de log robusta Y esa era la conexion que pobremente intente hacer!



Sorry. La intención de mostrar como se hace en otros entornos es tratar de dilucidar otras formas de hacer las cosas... Tambien es que asi como cuando programo a veces me veo intentando escribir un lenguaje cuando estoy en otro, se me cruzan los cables cuando hablo de ellos. Pienso que existe un lenguaje, que es superior a todos, pero que solo esta de forma parcial en cada uno.. y me pregunto como hacer que lo mejor de unos llega a los otros que uso...
Es cierto, lo intenta... pero como ya he dicho: no lleva a nada el Log().

Ahora que lo aclaras y mencionas lo de "clasificar" los tipos de errores, tiene bastante sentido y su lógica el emplear alguna forma de condicional para jugar con los niveles de advertencias. Más a lo que apuntaba yo es que tan necesario es recurrir a "inventos" como éste y quizá habría una forma de indicar cuando se está en modo debug y/o empleando el IDE y cuando se está en "modo release" mediante alguna directiva de compilación y no tener que estar alterando el código fuente demasiado.

La otra pata que se ha debatido en el hilo y también lleva a confusión y mezclado con otros temas discutidos es que esto:

Código Delphi [-]
  TRY     ... { Código aquí... }     IF IntentoBorrarYNoDebe THEN       RAISE Exception.Create ('¡No puede eliminar eso!');     ... { Código allá... }   EXCEPT     ON Error: Exception DO Application.MessageBox (Error.Message, 'Error')   END;
No debería hacerse así. Si el contexto del negocio ya sugiere que hay elementos no deben ser borrables, sugerir una excepción para lidiarlo no es apropiada. Es un poco más limpio la propuesta de AgustinOrtu:

Código Delphi [-]
procedure Borrar(const IdRegistro: Int64); begin    if IsSystemProtectedRec(IdRegistro, ATableName) then     raise ESystemProtectedRec.CreateFmt('Cannot delete the record %d of the table %s because is system protected', [IdRegistro, ATableName]);  { codigo "normal" de borrado de registros normales } end;

Pero igual tiene el problema que bien a señalado roman. Mezclar y abusar excepciones con el contexto de negocio. ¡Aquí la excepción sobra!:

Código Delphi [-]
procedure DeleteRec(const IdRegistro: int64);
begin
  if NOT IsSystemProtectRec(IdRegistro, ATableName)
    then DoDelete(idRegistro, ATableName)
    else ShowMessage('Por razones de seguridad el registro seleccionado no está permitido eliminar. Verifique que esté todo en orden o elija otro);
end;

Saludos,
__________________
Delphius
[Guia de estilo][Buscar]
Responder Con Cita