Roba este código: monitorización ExeWatch para Delphi, .NET, C++ y más
🇬🇧 English • 🇮🇹 Italiano • 🇩🇪 Deutsch • 🇧🇷 Português
ExeWatch: monitorización de aplicaciones en tiempo real para Delphi, C++Builder, .NET, JavaScript y Python, con motor de IA integrado y plan Hobby gratuito.
TL;DR: El repositorio ExeWatch Samples es un conjunto de proyectos listos para ejecutar que muestran cómo integrar ExeWatch en aplicaciones reales: Delphi (VCL, FMX, Android, WebBroker, DMVCFramework, runtime packages, webhooks), .NET (Console, Windows Forms, Windows Service), JavaScript en el navegador, y C++Builder / MSVC / Python a través de la DLL nativa. Cada ejemplo ejercita una parte del SDK, así que copias el patrón que encaja con tu app. Una API key, plan Hobby gratuito, y tus eventos aparecen en el panel en pocos segundos.
ExeWatch crece, y la comunidad construye sobre él
ExeWatch sigue creciendo, rápido. En apenas unos meses ha alcanzado cientos de desarrolladores registrados que ahora monitorizan en producción software escrito en Delphi, C++Builder, .NET, JavaScript y Python: desde apps de escritorio y servicios de Windows hasta APIs REST y aplicaciones web completas.
Lo que usan primero dice mucho sobre por qué luego se quedan:
- Captura automática de excepciones - los errores no gestionados se interceptan y reportan, con clase, mensaje y stack.
- Trails de breadcrumbs - la secuencia exacta de acciones que llevó a un error, adjuntada automáticamente al fallo.
- Timings de rendimiento con Avg / Min / Max / P95 - mide cualquier operación y observa la tendencia en el panel.
- Nested timing traces - waterfalls estilo profiler y un árbol agregado Calling-Context-Tree, para ver dónde se va realmente el tiempo.
- Contadores y gauges - métricas de negocio y de runtime junto a tus logs.
- Inteligencia de hardware y dispositivo - CPU, RAM, disco, SO, monitores, detalles de dispositivos móviles.
- Seguimiento multi-cliente - filtra cada log por id de cliente.
- Alertas por email y de timing más webhooks - recibe avisos, o dispara tu propia automatización, cuando algo va mal o se ralentiza.
- Un motor de IA integrado que ayuda a dar sentido a todo esto.
Una funcionalidad merece una mención especial: los Nested Timing Traces, una de las incorporaciones más solicitadas y la que más comentarios positivos recibe. Convierte una lista plana de timings en un waterfall estilo profiler que muestra exactamente dónde se va el tiempo, con los bucles fusionados y avg/min/max/p95 por nodo. Cada SDK del repositorio incluye una demo de nested trace lista para ejecutar (GenerateInvoiceReport), así que puedes producir un árbol como este desde tu propia app en pocos minutos:
Si quieres la historia completa de cómo se construyen y visualizan las trazas, lee el artículo dedicado: ExeWatch 1.35: Nested Timing Traces.
La forma más rápida de entender cómo se traduce todo esto en tu propio código es el repositorio de ejemplos. Vamos a recorrerlo.
Por qué importan estos ejemplos: versiones reales, lenguajes reales
Este repositorio no es solo un escaparate de marketing. Existe porque las preguntas son reales. Solo en las últimas semanas, un cliente me pidió añadir ExeWatch a una aplicación Delphi 6, otro a un proyecto C++Builder 10, y otro quería pilotarlo desde un lenguaje que no está en absoluto en la lista habitual.
Cada una de esas peticiones tiene respuesta aquí dentro, y ese es precisamente el punto: los ejemplos son la forma más rápida de ver qué SDK usar para qué entorno, y cómo.
- Delphi moderno (XE8 y posteriores) usa el SDK Pascal nativo (
ExeWatchSDKv1.pas+ el hook VCL o FMX): los ejemplos VCL, FMX, Android, WebBroker y DMVCFramework siguen todos este camino. - Delphi legacy (hasta Delphi 6/7) y cualquier versión anterior a XE8 no pueden compilar la unit moderna, así que hablan con la DLL nativa a través de la unit de importación Pascal
ExeWatchSDKv1Imports.pasenDLLSDKCommons/. Mismas llamadas, mismo panel, solo enlazadas a la DLL en lugar de compiladas dentro. - C++Builder (antiguo y nuevo) carga la misma DLL dinámicamente: el ejemplo
CPPBuilderWithDLLSDK/compila sin cambios bajo bcc32, bcc64 y bcc64x, así que un proyecto C++Builder 10 y uno 13 usan código idéntico. - Otros lenguajes consumen la DLL a través de su ABI de Windows estándar: los ejemplos MSVC y Python ctypes demuestran el patrón, que luego se traslada a Rust, Go, MinGW, Clang y más allá.
Así que cuando alguien pregunta “¿puedo poner ExeWatch en este compilador antiguo / ese lenguaje inusual?”, la respuesta honesta suele ser “sí, y aquí tienes el ejemplo que te muestra cómo”.
Antes de empezar: una API key, sin tarjeta de crédito
Cada ejemplo necesita una API key válida. Regístrate en exewatch.com, crea una aplicación en el panel y copia su key (el prefijo depende de la plataforma de destino: ew_win_ Windows, ew_lin_ Linux, ew_mac_ macOS, ew_and_ Android, ew_ios_ iOS, ew_web_ navegador). El plan Hobby gratuito no pide tarjeta de crédito y te da una aplicación, 10.000 eventos al mes, 7 días de retención y dos alertas: más que suficiente para ejecutarlo todo.
Después el ciclo es siempre el mismo: abre el proyecto, pega tu key donde está el placeholder, compila, ejecuta y observa los eventos llegar al panel en tiempo real.
Clientes de escritorio y móvil
Son los ejemplos “cliente en la máquina de otra persona”: apps con interfaz gráfica donde la captura de excepciones y los breadcrumbs importan más, porque normalmente no puedes reproducir el fallo en local.
Delphi VCL (DelphiVCL/) es el punto de partida canónico. Un formulario de escritorio Windows con un botón por funcionalidad: los cinco niveles de log, timing, breadcrumbs-luego-error, identidad de usuario, tags, contadores y gauges, además de la captura automática de excepciones VCL vía ExeWatchSDKv1.VCL. Los logs se persisten en disco antes del envío, así que no se pierde nada si la app falla. Úsalo cuando desarrolles software de escritorio Windows clásico en Delphi y quieras el camino más corto de cero a datos.
uses ExeWatchSDKv1, ExeWatchSDKv1.VCL; // hook VCL = captura automática de excepciones GUI
InitializeExeWatch(EXEWATCH_API_KEY, 'SampleCustomer');
EW.Info('User logged in', 'auth');
Delphi FMX (DelphiFMX/) refleja el ejemplo VCL funcionalidad por funcionalidad, pero sobre FireMonkey, así que puede apuntar a Windows, macOS, Linux, iOS y Android. La única diferencia real es la cláusula uses: ExeWatchSDKv1.FMX en lugar del hook VCL. Úsalo cuando distribuyas una app FireMonkey multiplataforma.
uses ExeWatchSDKv1, ExeWatchSDKv1.FMX; // hook FMX en lugar del de VCL
InitializeExeWatch(EXEWATCH_API_KEY, 'SampleCustomer');
EW.Info('User logged in', 'auth');
Delphi Android (DelphiAndroid/) es el SDK FMX apuntado directamente a un teléfono. Una UI estrecha y desplazable construida por código ejercita cada punto de entrada, incluido el uso desde un hilo en segundo plano y el flush, y se centra en aspectos específicos de móvil: device info en versión Android, captura de una excepción no gestionada antes de que el proceso sea destruido, y prueba de thread-safety fuera del hilo de UI. Úsalo cuando desarrolles para Android y quieras probar toda la API en un dispositivo real o emulador.
uses ExeWatchSDKv1, ExeWatchSDKv1.FMX;
InitializeExeWatch(EXEWATCH_API_KEY, 'AndroidSample'); // key Android: ew_and_
EW.Info('Info message from Android', 'test');
.NET Windows Forms (DotNetWindowsForms/) es la contraparte C#: una GUI con pestañas donde pegas la API key en tiempo de ejecución (sin recompilar) y exploras logging, timings anidados y paralelos, device info, métricas y actualizaciones de versión simuladas. Instala ExeWatch.WinForms para capturar Application.ThreadException automáticamente. Úsalo cuando tu app de escritorio sea WinForms.
ExeWatchWinForms.Install(); // captura Application.ThreadException, antes de Application.Run()
ExeWatchSdk.Initialize(new ExeWatchConfig(apiKey, customerId));
EW.Info("User logged in", "auth");
Servidores, servicios y apps web
Código de larga ejecución, headless o de servidor, donde timings, contadores y contexto estructurado de errores se convierten en visibilidad operativa.
Delphi WebBroker (DelphiWebBroker/) es un pequeño servidor REST que envuelve cada petición en un único helper HandleRequest: incrementa un contador de peticiones, añade un breadcrumb, inicia un timing, ejecuta el handler y luego registra éxito o fallo (y en caso de error lo cuenta, registra la excepción y devuelve HTTP 500). Seis endpoints de demo, incluido un delay ?ms= para generar respuestas lentas. Úsalo cuando tengas un servicio WebBroker y quieras monitorización a nivel de petición con un solo wrapper.
InitializeExeWatch(TExeWatchConfig.Create(EXEWATCH_API_KEY, 'demo_customer'));
EW.SetTag('app', 'WebBrokerSample');
// dentro del wrapper por petición:
EW.IncrementCounter('http.requests', 1);
EW.StartTiming(Endpoint);
try
Handler;
EW.EndTiming(Endpoint, nil, True); // éxito
except
on E: Exception do
EW.EndTiming(Endpoint, nil, False); // fallo
end;
Delphi DMVCFramework (DelphiDMVCFramework/) es el ejemplo de servidor más completo: una app web real con TemplatePro y HTMX, CRUD de personas con búsqueda en vivo e importación por lotes, informes pesados con timings anidados, servicios externos simulados con modos de fallo realistas (timeouts de pago, rate limit 429, errores 502), extra data estructurada, breadcrumbs, contadores y gauges periódicos. Lo destacable es lo poco que cuesta: tres líneas en el .dpr y ningún cambio en los controllers. Enrutas la salida de LoggerPro de DMVCFramework hacia ExeWatch con un appender de callback, llamas a InitializeExeWatch y apuntas al logger el Profiler integrado del framework. A partir de ahí cada petición se registra y cronometra gratis. Úsalo cuando desarrolles APIs o apps web con DMVCFramework y quieras visibilidad profunda con casi cero código.
// 1. enruta la salida de LoggerPro de DMVCFramework hacia ExeWatch
SetDefaultLogger(CreateLogBuilderWithDefaultConfiguration.WriteToCallback
.WithCallback(procedure(const ALogItem: TLogItem; const AFormattedMessage: string)
begin
if ExeWatchIsInitialized and (ALogItem.LogType > TLogType.Debug) then
EW.Log(TEWLogLevel(Ord(ALogItem.LogType)), ALogItem.LogMessage,
ALogItem.LogTag, ALogItem.TimeStamp, ALogItem.ThreadID);
end).Done.Build);
// 2. inicializa, luego 3. apunta el profiler integrado al logger
InitializeExeWatch(TExeWatchConfig.Create(EXEWATCH_API_KEY, 'dmvc_sample_customer'));
Profiler.ProfileLogger := Log; // cada action cronometrada, sin tocar los controllers
Profiler.WarningThreshold := 500; // avisa si una action supera los 500 ms
Receptor de WebHook Delphi (DelphiWebHookSample/) invierte la relación: en lugar de enviar datos a ExeWatch, recibe los webhooks de alerta de ExeWatch. Un controller DMVCFramework valida el token del webhook y procesa el payload de la alerta, y un endpoint trace-demo complementario emite un nested timing trace bajo demanda. Úsalo cuando quieras que las alertas disparen tu propia automatización (notificaciones en chat, ticketing, auto-remediación) y no solo el email.
// valida el secreto compartido, luego actúa sobre el payload de la alerta
if Context.Request.Headers['X-ExeWatch-Token'] <> EXEWATCH_WEBHOOK_TOKEN then
Exit(UnauthorizedResponse('Invalid token'));
AlertType := Payload.S['alert_type']; // 'log_level', 'timing', 'health_critical'...
LogI('ExeWatch [%s] %s: %s', [AlertType, Payload.S['app_name'], Payload.S['message']]);
Result := OKResponse('Webhook received');
.NET Windows Service (DotNetWindowsService/) es un Worker Service con un ciclo de procesamiento cada 10 segundos. Muestra el patrón recomendado para servicios de larga ejecución: inicializa en StartAsync, envuelve cada ciclo en un timing externo con sub-timings anidados, usa la convención del timing con try/catch para que los ciclos fallidos se marquen como fallidos sin tumbar el servicio, emite contadores y gauges, y hace flush en StopAsync. Puede ejecutarse como app de consola o instalarse con sc create. Úsalo cuando gestiones servicios en segundo plano o daemons.
// StartAsync - una sola vez al arrancar el servicio
ExeWatchSdk.Initialize(new ExeWatchConfig(ApiKey, CustomerId) { AppVersion = "1.0.0-service-demo" });
EW.SetTag("service_name", "ExeWatchDemoService");
EW.Info("Service started", "lifecycle");
// cada ciclo - cronometrado, los fallos marcados sin tumbar el servicio
EW.StartTiming("process_cycle", "worker");
try { /* ... trabajo ... */ EW.EndTiming("process_cycle"); }
catch (Exception ex) {
EW.EndTiming("process_cycle", new Dictionary<string, object> { ["error"] = ex.Message }, false);
}
.NET Console (DotNetConsole/) recorre toda la superficie del SDK de forma secuencial, incluidas 20 iteraciones cronometradas con fallos aleatorios para que el panel muestre de inmediato Avg / Min / Max / P95 y una tasa de éxito realistas. Úsalo cuando quieras un smoke test de 60 segundos del SDK .NET, o un job CLI/batch que instrumentar.
var config = new ExeWatchConfig(ApiKey, CustomerId) { AppVersion = "1.0.0-console-demo" };
ExeWatchSdk.Initialize(config);
EW.SetTag("environment", "development");
EW.Info("Configuration loaded successfully", "config");
En el navegador
JavaScript (JS/) es un único index.html: sin npm, sin paso de build. Configura window.ewConfig con tu key ew_web_, carga el SDK desde el CDN, y pulsa los botones de logging, timing, breadcrumbs-luego-error, identidad de usuario, tags y métricas. Úsalo cuando quieras seguimiento de errores y rendimiento de front-end en una página web simple o dentro de un sitio existente.
<script>
window.ewConfig = { apiKey: 'ew_web_...', customerId: 'SampleCustomer', appVersion: '1.0.0' };
</script>
<script src="https://exewatch.com/static/js/exewatch.v1.min.js"></script>
<script>
ew.info('User signed in', 'auth'); // el global 'ew' está listo tras cargar el SDK
</script>
C, C++ y cualquier lenguaje capaz de llamar a una DLL
ExeWatch incluye una DLL nativa que expone una ABI de Windows deliberadamente aburrida: extern "C" + __stdcall + wchar_t* + #pragma pack(1). Eso la hace consumible desde casi cualquier cosa. Todos estos ejemplos la cargan dinámicamente con LoadLibrary + GetProcAddress, lo que evita el clásico desajuste entre import library .lib y .a y permite que la app arranque limpia incluso cuando falta la DLL.
C++Builder (CPPBuilderWithDLLSDK/) es una app VCL que pilota la DLL a través del par compartido ExeWatchSDKv1.h + ExeWatchSDKv1.dynload.c. Un solo #define EW_DYNAMIC_LOAD convierte cada ew_* en un puntero a función, y el mismo código compila bajo bcc32, bcc64 y bcc64x. Úsalo cuando trabajes en C++Builder y quieras evitar peleas con el linker.
#define EW_DYNAMIC_LOAD // cada ew_* pasa a ser un puntero a función
#include "ExeWatchSDKv1.h"
if (ew_LoadSDK() != EW_OK) { /* DLL ausente: muestra un mensaje claro */ }
ew_Initialize(EXEWATCH_API_KEY, L"Sample C++ Customer", L"");
ew_Info(L"This is an INFO message", L"sample");
Microsoft Visual C++ (MSVCWithDLLSDK/) demuestra que el DLL SDK no tiene límites ni dependencias y puede usarse en cualquier sitio y por cualquiera, sin nada del ecosistema Embarcadero a la vista: una app de consola en cl.exe puro, con un run_msvc.cmd que localiza vcvars64.bat, compila y ejecuta. Recorre la superficie común (init, usuario, tags, breadcrumbs, timing, contador, gauge, error con breadcrumbs auto-adjuntos, flush, shutdown). Úsalo cuando tu base de código sea MSVC, o simplemente quieras un quickstart autónomo.
#define EW_DYNAMIC_LOAD
#include "ExeWatchSDKv1.h"
ew_LoadSDK();
ew_Initialize(EXEWATCH_API_KEY, CUSTOMER_ID, APP_VERSION);
ew_SetTag(L"environment", L"dev");
ew_Info(L"Sample app started", L"startup");
La DLL desde otros lenguajes (SpecificScenarios/DllFromOtherLanguages/) es el rincón de la portabilidad demostrada. Un smoke test en Python ctypes puro valida los cinco ejes de la ABI (convención de llamada, nombres sin decorar, marshalling UTF-16, empaquetado de structs y ABI de callbacks) sin ninguna huella de Embarcadero. El README es explícito: para una app Python real deberías usar el módulo del SDK Python nativo, que descargas desde la página Integration del panel exactamente igual que descargas las units de Delphi (todavía no está en PyPI, así que no hay pip install); este ejemplo existe como referencia de portado para Rust, Go, MinGW, Clang y otros runtimes capaces de FFI. Úsalo cuando necesites llamar a ExeWatch desde un toolchain inusual y quieras una plantilla fiable.
dll = ctypes.WinDLL(dll_path) # ABI stdcall, nombres sin decorar
cfg = TEWDLLConfig(); cfg.StructSize = sizeof(TEWDLLConfig)
cfg.ApiKey = EXEWATCH_API_KEY; cfg.CustomerId = CUSTOMER_ID; cfg.SampleRate = 1.0
dll.ew_InitializeEx(byref(cfg))
dll.ew_Info('Hello from Python ctypes', 'smoke')
Arquitectura y escenarios how-to concretos
El último grupo responde preguntas específicas que surgen una vez que lo básico funciona.
Runtime packages (DelphiVCLWithPackages/) demuestra que ExeWatch funciona con BPLs de Delphi cargados dinámicamente vía LoadPackage. El truco es compilar el SDK en su propio package (ExeWatchSDKPkg.bpl) para que la instancia global EW, los breadcrumbs, los tags y la identidad de usuario se compartan de verdad entre el EXE anfitrión y cada BPL de módulo. Dos módulos de demo (Customers, Orders) registran, cronometran y capturan errores usando exactamente la misma API que una app normal. Úsalo cuando tengas una aplicación Delphi modular, de estilo plugin.
// HostApp: inicializa una vez; el SDK vive en ExeWatchSDKPkg.bpl, compartido con cada módulo
InitializeExeWatch(TExeWatchConfig.Create(APP_API_KEY, APP_CUSTOMER_ID));
EW.SetTag('app_type', 'runtime_packages');
// dentro de un BPL de módulo cargado dinámicamente - mismo EW global, sin setup extra
EW.Info('Customer added: ' + CustomerName, 'customers');
Uso de breadcrumbs (SpecificScenarios/BreadcrumbsUsage/) zanja en un solo proyecto las tres preguntas más comunes sobre breadcrumbs: repártelos donde ocurren las acciones (no los agrupes), las excepciones no gestionadas los adjuntan automáticamente mientras que las capturadas requieren tu llamada a EW.Error, y solo los logs Error y Fatal llevan breadcrumbs (Info / Warning / Debug no). Cuatro botones hacen visible cada regla en el panel. Úsalo cuando quieras acertar con los breadcrumbs a la primera.
EW.AddBreadcrumb(btClick, 'ui', 'Clicked "Save" button');
try
FakeSaveToDatabase; // lanza una excepción
except
on E: Exception do
EW.ErrorWithException(E, 'settings'); // los breadcrumbs se adjuntan a este Error
end;
Device info personalizada inicial (SpecificScenarios/InitialCustomDeviceInfo/) responde a un problema sutil de arranque: ¿cómo adjuntas tags y metadatos de entorno (sesión RDP, Terminal Server, modo escritorio) al primer evento, el que se emite durante la inicialización? La respuesta son dos campos de configuración, GlobalTags para los datos por los que filtras los logs e InitialCustomDeviceInfo para los detalles que describen el dispositivo. Úsalo cuando necesites el contexto presente desde la primera línea de log.
Config := TExeWatchConfig.Create(EXEWATCH_API_KEY, 'SampleCustomer');
Config.GlobalTags := [TPair<string, string>.Create('session_type', SessionType)];
Config.InitialCustomDeviceInfo := [
TPair<string, string>.Create('screen_res', DetectScreenResolution),
TPair<string, string>.Create('color_depth', DetectColorDepth)];
InitializeExeWatch(Config); // los tags presentes ya desde el primer evento
Integración con madExcept (SpecificScenarios/madExceptIntegration/) es para quienes ya usan madExcept. Una única unit puente registra un callback de madExcept y reenvía cada excepción a ExeWatch con el stack ya simbolizado de madExcept (nombres de unit y números de línea) en lugar de las direcciones en crudo que el SDK capturaría en una build de release. También muestra el equivalente para EurekaLog. Úsalo cuando quieras stacks legibles y buscables en el panel y ya pagues la simbolización en tiempo de enlace.
procedure ExeWatchMadExceptHandler(const ExceptIntf: IMEException; var Handled: Boolean);
var ExtraData: TJSONObject;
begin
ExtraData := TJSONObject.Create;
ExtraData.AddPair('stack_trace', ExceptIntf.BugReport); // stack resuelto por madExcept
EW.Log(llError, ExceptIntf.ExceptMessage, 'exception', ExtraData);
end;
initialization
RegisterExceptionHandler(ExeWatchMadExceptHandler, stDontSync);
Una sola API, muchos runtimes
La razón por la que un recorrido como este se mantiene corto es que la API es intencionadamente la misma en todas partes. Initialize, Info / Warning / Error, AddBreadcrumb, StartTiming / EndTiming, SetUser, SetTag, IncrementCounter / RecordGauge, y la captura automática de excepciones: la grafía cambia un poco entre Delphi, C#, JavaScript y la DLL C, pero los conceptos y el panel son idénticos. Apréndela en un ejemplo y la conoces en todos.
Clona el repo, elige el proyecto más cercano a tu stack, mete tu key, y tendrás datos reales delante en un par de minutos.
Enlaces y recursos
- Repositorio de ejemplos: github.com/danieleteti/ExeWatchSamples
- Sitio oficial: exewatch.com
- Documentación: exewatch.com/ui/docs
- Precios & On Premise: exewatch.com/ui/pricing
- Changelog: exewatch.com/ui/changelog
- Contacto On Premise: exewatch@bittime.it
- Artículo relacionado: ExeWatch 1.35: Nested Timing Traces, profiling jerárquico de tus operaciones
ExeWatch: Application Performance Monitoring para aplicaciones de servidor, escritorio y web, con un motor de Inteligencia Artificial integrado. Creado por bit Time Professionals.
Comments
comments powered by Disqus