Become a member!

Integrar ExeWatch en Proyectos Delphi que Ya Usan madExcept

ExeWatch Logo
🌐
Este artículo también está disponible en otros idiomas:
🇮🇹 Italiano  •  🇬🇧 English  •  🇩🇪 Deutsch

TL;DR: No tienes que elegir. Si ya usas madExcept, consérvalo. Añade la unidad puente a tu proyecto y cada excepción que madExcept intercepte llegará también al dashboard de ExeWatch — con el stack trace ya resuelto por madExcept. Sin actualizar la SDK y sin cambiar la configuración existente de madExcept.


El Temor, Contado por un Cliente Real

Hace unos días un cliente nuestro — desarrollador Delphi de larga trayectoria, usuario de madExcept desde principios de los 2010 — nos hizo esta pregunta:

“Llevo años usando madExcept. El diálogo local, el email automático, el zip del bug report — son parte de cómo mi aplicación reporta problemas. Si añado ExeWatch, ¿tengo que tirar todo eso?”

La respuesta corta es no. La respuesta larga es el tema de este artículo.

Alguien podría pensar que ExeWatch y madExcept son alternativas, pero no lo son. Resuelven problemas que se solapan en la superficie pero llegan desde extremos opuestos, y cuando los pones juntos obtienes algo mejor que cada uno por separado. Este post explica exactamente por qué, y muestra la integración en la práctica.


Qué Hace Bien Cada Herramienta

Antes de hablar de integración vale la pena ser precisos sobre en qué destaca cada herramienta. La confusión alrededor del “ExeWatch contra madExcept” suele nacer al asumir que hacen el mismo trabajo — y no es así.

madExcept: la última línea de defensa en la máquina del usuario

madExcept vive en profundidad dentro del proceso. Cuando se lanza una excepción, madExcept es capaz de:

  • Resolver el stack trace con nombres de unidad y números de línea, usando la información de depuración embebida en tiempo de enlace (así no necesitas distribuir los ficheros .map junto al ejecutable)
  • Mostrar un diálogo local al usuario, permitiéndole añadir un comentario antes de enviar el reporte
  • Guardar en disco un bug report completo — con lista de módulos, dump de registros, lista de hilos, captura de pantalla — y conservarlo incluso cuando la red está caída
  • Enviar el reporte por email o subirlo a un endpoint FTP/HTTP de forma automática
  • Detectar congelamientos del hilo principal y fugas de memoria

madExcept es una herramienta de referencia en el ecosistema Delphi desde hace más de dos décadas, adoptada por miles de desarrolladores en otras tantas aplicaciones de escritorio en producción. Es sólida, madura y una de esas librerías que hacen tan bien lo que hacen, y desde hace tanto tiempo, que es difícil imaginar construir una aplicación Delphi de escritorio sin ella. Si tu workflow depende de recibir crash reports por email, o de dejar que los usuarios finales aporten contexto a través de un diálogo, madExcept hace un trabajo que ninguna herramienta APM cloud-first está diseñada para reemplazar.

ExeWatch: qué pasa después de que el crash report se envía

ExeWatch comienza donde madExcept termina. Una vez que ha ocurrido un error, las preguntas interesantes ya no son “¿cuál era el stack?” sino:

  • ¿Es un bug nuevo o uno conocido? ¿Ha pasado en otras instalaciones, en otras versiones de la app, en las últimas 24 horas?
  • ¿Qué estaba haciendo el usuario justo antes? Breadcrumbs — clics en botones, navegaciones, peticiones HTTP — capturados automáticamente por la SDK.
  • ¿Con qué frecuencia se produce en producción? Entre clientes, entre versiones, en el tiempo.
  • ¿Está afectando a un solo cliente o a muchos? ExeWatch te muestra dispositivos, clientes, sesiones.
  • ¿Vemos una tendencia? Health scores, alertas, detección de anomalías.
  • ¿Qué estaba pasando alrededor? Timing/profiling de operaciones lentas, métricas personalizadas, logs de info que precedieron al error.

ExeWatch agrega logs, timings, métricas y salud de todas las instalaciones de tu aplicación y te da un dashboard para buscar y correlacionar. Responde a la pregunta “¿mi app está funcionando bien ahí fuera?”, no a la pregunta “¿qué acaba de crashear en la máquina de este usuario?”.

Y aquí llega un punto que merece subrayarse: una aplicación que nunca crashea puede tener igualmente clientes descontentos. Una operación que el usuario percibe como lenta sin llegar a reportarla, una importación que falla con un mensaje genérico, una llamada HTTP que expira solo en redes de cierto país, una tendencia de errores que crece silenciosamente a lo largo de semanas, un flujo de checkout que se ralentiza cuando la base de datos está bajo carga — nada de esto aparece en un crash report, y ninguna integración “solo-crash” podrá verlo nunca; sin embargo, son precisamente los detalles que erosionan día tras día la percepción de calidad de tu software. ExeWatch está pensado para estos escenarios al menos tanto como para los crashes.

Bajo esa luz, madExcept aporta una pieza muy detallada del puzzle — la pieza cuando hay un crash. ExeWatch toma esa pieza y la coloca dentro de una imagen mucho más amplia, compuesta también por timing, métricas, tendencias, breadcrumbs y health. Una aplicación puede ser “estable” — cero crashes en seis meses — y al mismo tiempo dejar a los usuarios frustrados por ralentizaciones, errores silenciosos o regresiones de rendimiento que ningún handler de excepción verá jamás.


Por Qué a Primera Vista Parece Que Se Pisan

Cuando un usuario añade ExeWatch por primera vez a una app que ya tiene madExcept, suele notar algo raro: las excepciones no manejadas nunca llegan al dashboard de ExeWatch, aunque el resto de logs sí.

La causa es una cuestión de orden. La auto-captura de excepciones de GUI de ExeWatch se engancha al Application.OnException de la VCL. El hook propio de madExcept está instalado a un nivel más bajo — esencialmente entre el sistema operativo y la RTL — y se dispara antes de que el handler VCL corra. Una vez que madExcept ha gestionado la excepción (mostrando un diálogo, enviando un email o reiniciando la aplicación), el handler VCL no llega a ejecutarse. En consecuencia, el hook de ExeWatch nunca ve la excepción.

No es un bug de ninguna de las dos. Es como madExcept está diseñada para funcionar, y lo mismo pasa si la encadenas con cualquier otra librería que también quiera ver las excepciones no manejadas (Sentry, EurekaLog, frameworks de logging personalizados). Quien engancha primero gana.

La respuesta no es eliminar uno de los hooks — ambos cumplen propósitos reales. La respuesta es dejar que madExcept capture la excepción primero, y luego hacer que pase explícitamente una copia a ExeWatch desde dentro de su propio callback. Este es un punto de extensión documentado en madExcept llamado RegisterExceptionHandler.


El Puente: Una Sola Unidad

Este es el puente completo. Copia este fichero a tu proyecto, inclúyelo en la cláusula uses del .dpr, y listo — la sección initialization instala el callback automáticamente:

unit ExeWatchMadExceptBridgeU;

interface

implementation

uses
  System.SysUtils,
  System.JSON,
  madExcept,
  ExeWatchSDKv1;

procedure ExeWatchMadExceptHandler(const ExceptIntf: IMEException;
  var Handled: Boolean);
var
  ExtraData: TJSONObject;
  ExClass, ExMsg: string;
  E: TObject;
begin
  if (ExceptIntf = nil) or (not ExeWatchIsInitialized) then
    Exit;

  E := ExceptIntf.ExceptObject;
  if Assigned(E) and (E is Exception) then
  begin
    ExClass := E.ClassName;
    ExMsg   := Exception(E).Message;
  end
  else
  begin
    ExClass := ExceptIntf.ExceptClass;
    ExMsg   := ExceptIntf.ExceptMessage;
  end;

  ExtraData := TJSONObject.Create;
  ExtraData.AddPair('exception_class',   ExClass);
  ExtraData.AddPair('exception_message', ExMsg);
  ExtraData.AddPair('stack_trace',       ExceptIntf.BugReport);

  EW.Log(llError, ExMsg, 'exception', ExtraData);

  // Handled se deja deliberadamente sin tocar: diálogo, bug report
  // y email de madExcept siguen funcionando exactamente como antes.
end;

initialization
  RegisterExceptionHandler(ExeWatchMadExceptHandler, stDontSync);

finalization
  UnregisterExceptionHandler(ExeWatchMadExceptHandler);

end.

Eso es todo. Tres observaciones que merece la pena destacar:

1. El callback no “consume” la excepción. Handled se deja sin modificar, por lo que el pipeline normal de madExcept — diálogo, email, guardado del bug report — sigue corriendo exactamente igual que antes de que el puente existiera. El usuario final ve la misma experiencia.

2. stDontSync permite que el callback corra en cualquier hilo. La API de logging de ExeWatch es thread-safe, así que no necesitamos que madExcept haga marshalling al hilo principal. Importa en servicios o en apps con muchos worker threads, donde una sincronización forzada podría causar bloqueos.

3. El stack trace enviado a ExeWatch es el de madExcept, no el de la SDK. Este es el detalle que casi todo el mundo pasa por alto. ExceptIntf.BugReport contiene el stack completamente resuelto — nombres de unidad, números de línea, lista de módulos, registros — gracias a la información de depuración que madExcept embebió en tiempo de enlace. Pasamos esa cadena a ExeWatch a través del campo extra_data.stack_trace. El método Log de la SDK respeta un stack_trace suministrado por el llamador y no lo sobrescribe, así que el dashboard muestra los frames resueltos por madExcept en lugar de direcciones crudas.


Qué Obtienes de Verdad

Después de instalar el puente, el recorrido de una excepción es así:

  1. Una excepción salta en algún punto de tu código
  2. madExcept la intercepta — exactamente como antes
  3. Nuestro callback corre mientras madExcept sigue procesando
  4. Enviamos a ExeWatch un log de nivel ERROR con el stack de madExcept
  5. El flujo normal de madExcept continúa sin modificaciones: diálogo, bug report, email al equipo de desarrollo, reinicio opcional

El resultado neto es que para cada excepción ahora tienes ambas cosas:

  • En la máquina local: todo lo que madExcept siempre ha hecho — el diálogo, el fichero de reporte, el email
  • En el dashboard cloud de ExeWatch: la misma excepción, con el stack resuelto por madExcept, junto a breadcrumbs, session ID, info del dispositivo, customer ID, versión de la app y el resto de logs que la SDK capturó alrededor

Visualmente, el contraste es este.

En el PC del usuario: el diálogo de madExcept tras un access violation, con los botones send bug report, continue, restart, close.

En el PC del usuario final: madExcept hace lo de siempre — diálogo de error local con los familiares botones send bug report, continue application, restart, close. La experiencia del usuario final no cambia en absoluto.

En el dashboard de ExeWatch: el mismo access violation con el stack trace resuelto por madExcept, nombres de unidad y números de línea legibles.

En la pantalla del desarrollador, en el dashboard de ExeWatch: la misma excepción — aquí un EAccessViolation — llega agregada, con el stack trace ya resuelto por madExcept (nombres de unidad, números de línea, offsets de módulos) y todo el contexto capturado por la SDK alrededor (sesión, hilo, dispositivo, breadcrumbs, timing).

El valor de lo segundo no es un duplicado de lo primero. madExcept te cuenta un crash. ExeWatch te dice:

  • ¿Está pasando solo en la v2.3.1?
  • ¿Los usuarios de Windows 10 lo ven más que los de Windows 11?
  • ¿Empezó después del último deploy?
  • ¿Qué cliente lo vio primero?
  • ¿Qué cliqueó el usuario justo antes?
  • ¿Los dispositivos afectados tienen en común un antivirus concreto?

Son preguntas de correlación y tendencia para las que hace falta un nivel de agregación distinto — precisamente el que ExeWatch aporta. No es una limitación de madExcept: son planos diferentes del mismo problema, y madExcept sigue haciendo su trabajo con excelencia en su plano.


Un Ejemplo Concreto

Imagina una aplicación que lleva años usando madExcept, con su workflow de email-report bien consolidado. Cada excepción genera un diálogo, el usuario pulsa “Enviar”, el equipo de desarrollo recibe un zip rico y detallado — excelente para entender cada caso concreto. A medida que la base instalada crece, se vuelve útil también una segunda capa de lectura, complementaria a la primera: agrupación por versión, conteos por cliente, tendencias en el tiempo.

Añades ExeWatch a su lado. Instalas el puente. Despliegas.

Desde el día siguiente:

  • El flujo de email no cambia — el equipo sigue recibiendo los bug reports exactamente como antes.
  • El dashboard de ExeWatch ahora muestra cada excepción, agrupada por huella (clase + stack), con conteos por versión y por cliente.
  • Configuras una alerta: “si los errores superan 3 en 10 minutos, envíalos a Discord”. La primera regresión real tras un deploy se detecta en minutos, antes de que los usuarios empiecen a llamar.
  • Una regla de detección de anomalías identifica un incremento gradual de una excepción concreta a lo largo de la última semana — una fuga de memoria lenta que solo se manifiesta tras varias horas de uso — que emerge gracias a la agregación temporal sobre la base instalada.

madExcept sigue haciendo su trabajo. ExeWatch hace un trabajo diferente. Ninguno sustituye al otro.


Requisitos y Dónde Encontrar el Código

El sample completo, con una form de demostración que lanza a propósito excepciones normales, access violations y logs de info de base, vive en el repositorio de ejemplos de ExeWatch:

github.com/danieleteti/ExeWatchSamples › SpecificScenarios/madExceptIntegration

El sample incluye:

  • ExeWatchMadExceptBridgeU.pas — el único fichero que copias en tu propio proyecto
  • EWMadExceptIntegration.dproj — un proyecto demo Delphi 12+ ejecutable (Win32 + Win64)
  • MainFormU.pas / .dfm — tres botones para disparar una Exception normal, un access violation y un log de info de comparación
  • EWMadExceptIntegration.mes — el fichero de settings de madExcept, para que al abrir el proyecto madExcept se enlace automáticamente
  • README.md — instrucciones paso a paso más variaciones (enviar solo el call stack en lugar del bug report entero, filtrar excepciones no-crash, equivalente EurekaLog)

Requisitos:

  • madExcept instalado en el IDE — disponible en madshi.net
  • Delphi XE8 o superior — el puente usa solo features estándar de la RTL (System.JSON, scoped unit names, try/except con Exception tipada)
  • SDK de ExeWatch v0.21.0 o superior — cualquier versión reciente vale; no hace falta una build especial
  • Una cuenta ExeWatch — el plan Hobby es gratis, sin tarjeta de crédito

El mismo patrón se extiende a EurekaLog mediante su callback RegisterEventExceptionNotify. La lógica es idéntica: extraer el stack resuelto de la información de excepción de EurekaLog, construir el mismo payload extra_data, llamar a EW.Log. Añadiremos un sample dedicado a EurekaLog al repositorio en las próximas semanas.

La sección completa de integración, con un ancla directa que puedes compartir, también está documentada en exewatch.com/ui/docs#coexisting-madexcept.


¿Y Sin madExcept?

Una pregunta natural tras todo esto: ¿puedo obtener un stack trace preciso también sin madExcept?

Sí. La SDK Delphi de ExeWatch captura stack traces de forma nativa en cada llamada a EW.Error, EW.Fatal y EW.ErrorWithException. El único requisito operativo, por ahora, es que para ver nombres de unidad y números de línea legibles en lugar de direcciones de memoria crudas hace falta distribuir el fichero .map junto al ejecutable — generado activando “Detailed” como Map file en las Linking options del proyecto. Es un paso adicional a la hora de liberar, pero resuelve el problema.

madExcept traslada ese paso al link time, embebiendo la información de depuración dentro del propio ejecutable: por eso, cuando está presente, nos apoyamos en su stack ya resuelto. Estamos estudiando formas de eliminar la dependencia del .map también sin madExcept — para dudas, feedback o solicitudes escribe a exewatch@bittime.it.


No Elijas — Integra

Si estás evaluando ExeWatch y temes que signifique abandonar una herramienta en la que llevas años confiando, no te preocupes. madExcept y ExeWatch no son duplicados; son capas distintas de la misma historia de observabilidad. La capa local — diálogos, reportes, dumps — es lo que madExcept sabe hacer mejor. La capa cloud — agregación, búsqueda, alertas, tendencias — es lo que ExeWatch sabe hacer mejor.

Una única unidad puente es todo lo que hace falta para que trabajen juntas, y cuando lo hacen obtienes una vista sobre la salud de tu aplicación que ninguna de las dos herramientas por sí sola puede ofrecerte.


Enlaces y Recursos


ExeWatch — Monitorización para aplicaciones server, desktop y web. Creado por bit Time Professionals.

Comments

comments powered by Disqus