Become a member!

ExeWatch 1.30: SDK C++Builder, MSVC e Delphi pre-XE8, Tag Globali e Alert per Cliente

ExeWatch Logo
🌐
Questo articolo è disponibile anche in altre lingue:
🇬🇧 English  •  🇩🇪 Deutsch  •  🇪🇸 Español  •  🇧🇷 Português

TL;DR: ExeWatch 1.30 aggiunge il supporto nativo a C++Builder, Microsoft Visual C++ e alle versioni di Delphi dalla 4 alla XE7 tramite un nuovo SDK basato su DLL. Introduce i tag globali ereditati automaticamente su ogni log e il filtro alert per singolo cliente. Si aggiungono WaitForSending per attendere esplicitamente l’invio dei log in coda, timing thread-local, isolamento del file queue per applicazione e notifiche email all'80% e 100% della quota mensile eventi. Tutto incluso su ogni piano. Provalo su exewatch.com

Punti chiave della release

  • 🆕 Nuovo SDK basato su DLL per C++Builder, Microsoft Visual C++ (MSVC), MinGW, Clang e Delphi 4–XE7
  • 🏷️ Global Tags: coppie chiave/valore ereditate automaticamente da ogni log della sessione (Delphi nativo + JavaScript)
  • 🎯 Filtro alert per Customer ID: regole di alert log-level e timing scopabili a un singolo cliente
  • ⏱️ WaitForSending su tutti gli SDK: API per attendere lo svuotamento esplicito della coda di log
  • 🧵 Timing thread-local su Delphi, .NET e Python: ogni thread mantiene il proprio stack di timing
  • 📁 Isolamento del file queue per applicazione: sottocartella dedicata identificata dall’API key
  • 📧 Notifiche email su quota mensile eventi: warning all'80% e blocco al 100%
  • 🚀 DLL ABI portata alla versione 3 per riflettere WaitForSending

Una release che allarga il perimetro

Da quando ExeWatch è stato presentato, la risposta della comunità è stata superiore a ogni nostra previsione. Ogni settimana riceviamo richieste di integrazione, segnalazioni di scenari d’uso che non avevamo immaginato e, soprattutto, domande del tipo “il vostro SDK è disponibile anche per …?”. È la conferma più diretta che un prodotto come ExeWatch era necessario e richiesto dai programmatori di applicazioni che girano “fuori dal browser”: Delphi e C++Builder in primis, ma in generale chiunque sviluppi software desktop, servizi Windows, applicazioni industriali e strumenti tecnici, ovvero quel mondo dove i log di produzione sono storicamente difficili da ottenere e dove gli APM cloud-native pensati per i microservizi non si calano facilmente.

Le release di ExeWatch hanno seguito un percorso preciso: prima il consolidamento del core (logging, timing, breadcrumbs, device info), poi l’estensione a nuovi linguaggi (Delphi, poi .NET, JavaScript, Python), infine la maturazione delle funzionalità “di prodotto” (custom metrics, health monitoring, usage analytics).

Con la 1.30 facciamo un altro passo importante in due direzioni che si intrecciano:

  1. Più linguaggi e ambienti supportati: arriva il supporto a C++Builder e Microsoft Visual C++ tramite il nuovo SDK basato su DLL. Lo stesso meccanismo apre ExeWatch anche alle versioni di Delphi dalla 4 alla XE7 (l’SDK Delphi nativo ExeWatchSDKv1.pas richiede invece Delphi XE8 o successive)
  2. Più controllo fine sulle funzionalità esistenti: tag globali ereditati su ogni log, filtro alert per singolo cliente, gestione esplicita della chiusura, isolamento per applicazione

Sono novità che arrivano in larga parte da feedback di clienti enterprise che stanno usando ExeWatch in produzione su parchi macchina importanti. Una volta integrate, però, sono diventate disponibili su tutti i piani, dal gratuito Hobby al Business.

SDK basato su DLL: Delphi legacy, C++Builder e Microsoft Visual C++

In assoluto la richiesta più frequente che riceviamo da quando ExeWatch è stato presentato è “posso usarlo anche con versioni di Delphi precedenti a XE8?”. L’SDK nativo ExeWatchSDKv1.pas usa feature di linguaggio (generics, anonymous methods, namespaces con punti) che non sono disponibili sui compilatori più datati, e Delphi 4, 5, 6, 7, 2007, 2009, 2010 sono ancora oggi presenti in produzione su parchi macchina importanti, specialmente in ambito industriale, gestionale e scientifico.

La risposta della 1.30 è un nuovo SDK basato su DLL che disaccoppia completamente la compilazione del client dal compilatore del progetto utente. La logica vive dentro ExeWatchSDKv1DLL.dll (distribuita in due varianti, 32 e 64 bit) e il progetto utente ci si collega tramite una unit di import dedicata, ExeWatchSDKv1Imports.pas, che funziona da Delphi 5 in poi. La unit di import risolve internamente le differenze fra AnsiString e WideString, fra namespace antichi e moderni, fra IInterface e IUnknown.

// Delphi 5, 6, 7, 2007 e 2009: stesso codice, stessa API
uses
  ExeWatchSDKv1Imports;

procedure InitMyApp;
begin
  ew_Initialize('ew_win_your_key', 'ACME-Corp', '1.0.0');
  ew_Info('App started', 'startup');
end;

Una volta avuto in casa il meccanismo della DLL, esporlo a un secondo mondo è stato un passo naturale: C++Builder e Microsoft Visual C++ (MSVC). La DLL è un binario Windows standard, richiamabile da qualsiasi linguaggio in grado di chiamare funzioni C. Ad essa abbiamo accompagnato un header C utilizzabile sia in static link sia con caricamento dinamico via LoadLibrary, e un loader già pronto da includere nel progetto:

// ExeWatchSDKv1.h: static link oppure caricamento dinamico
#include "ExeWatchSDKv1.h"

int main(int argc, char** argv) {
    EWConfig cfg = {0};
    cfg.api_key      = "ew_win_your_key";
    cfg.customer_id  = "ACME-Corp";
    cfg.app_version  = "1.0.0";

    if (ew_Initialize(&cfg) != EW_OK) {
        fprintf(stderr, "ExeWatch init failed\n");
        return 1;
    }

    ew_Info("MSVC console starting", "startup");

    ew_StartTiming("invoice.generate", "pdf");
    // ... lavoro reale ...
    ew_EndTiming("invoice.generate");

    ew_WaitForSending(5);  // attendi flush prima di uscire (max 5s)
    ew_Shutdown();
    return 0;
}

Lo stesso header con lo stesso loader funziona indistintamente con C++Builder, MSVC, MinGW e Clang su Windows. Non servono build system speciali né dipendenze esterne: la DLL è autosufficiente e dal lato progetto basta un singolo file .c (l’header ExeWatchSDKv1.h e l’implementazione del loader, già pronta).

Sul piano operativo la DLL ABI è passata alla versione 3, allineata con la nuova API WaitForSending. Chi usava la 2 deve solo aggiornare DLL e unit di import insieme: lo schema delle chiamate esistenti non cambia.

Global Tags: dimensioni trasversali su ogni log

I tag classici di ExeWatch (tag del log, tag del timing) sono per definizione contestuali: descrivono il singolo evento. Ma molti scenari richiedono dimensioni trasversali che valgono per tutta la sessione di un’applicazione: il tenant per cui sta girando, la regione, il build flavor, l’ambiente, la variante prodotto.

Finora la soluzione standard era includere queste informazioni nel customer_id o nelle extra_data. Funzionava, ma era ripetitivo e, soprattutto, non si poteva filtrare in modo strutturato dalla dashboard.

Con la 1.30 arrivano i Global Tags, supportati sull’SDK Delphi nativo e sull’SDK JavaScript. Si dichiarano una volta in fase di inizializzazione e vengono automaticamente accodati a ogni log:

// Delphi
InitializeExeWatch(
  EXEWATCH_API_KEY,
  ACustomerId    := 'ACME-Corp',
  AGlobalTags    := [
    TPair<string, string>.Create('tenant',     'EU-North'),
    TPair<string, string>.Create('flavor',     'Pro-Edition'),
    TPair<string, string>.Create('deployment', 'kiosk')
  ]
);
// JavaScript
ExeWatch.init({
  apiKey: 'ew_web_your_key',
  customerId: 'ACME-Corp',
  globalTags: {
    tenant: 'EU-North',
    flavor: 'Pro-Edition',
    deployment: 'kiosk'
  }
});

Da quel momento, ogni log inviato dall’applicazione porta con sé i tre tag. Nella pagina Logs della dashboard troverai un filtro Global Tags che ti permette di vedere, per esempio, solo i log del tenant EU-North con flavor Pro-Edition:

Filtro Global Tags nella pagina Logs di ExeWatch: il dropdown mostra le coppie chiave/valore raccolte dalle applicazioni (environment: abi-test, environment: staging, feature_flag: new_checkout) pronte per essere usate come filtro

Il dropdown "Global Tags" nella pagina Logs di ExeWatch elenca tutte le coppie chiave/valore ricevute dall'SDK e permette di filtrare la timeline a colpo d'occhio.

È esattamente la dimensione mancante per chi gestisce installazioni multi-tenant o release differenziate.

Alert con filtro per Customer ID

Sempre nel solco del “controllo fine”, abbiamo aggiunto un filtro opzionale per Customer ID sia agli alert di tipo log-level sia a quelli di tipo timing.

Lo scenario è familiare: hai una flotta di 500 installazioni, una di queste (magari un cliente nuovo con un setup particolare) genera errori ricorrenti. Se abbassi la soglia globale ricevi 20 notifiche al giorno dal resto del parco. Se la lasci alta, ti perdi il problema sul cliente specifico.

Con il filtro per customer puoi avere entrambe le cose: una regola “globale” tollerante e una regola specifica per quel cliente con soglia bassa e cooldown breve. Quando crei o modifichi un alert, vedrai un nuovo campo opzionale Filter by Customer: lasciato sull’opzione di default All Customers la regola vale per tutta la flotta, mentre scegliendo un Customer ID specifico la regola si attiva solo per quel cliente.

Dialog di creazione di un Log Level Alert in ExeWatch: il campo opzionale Filter by Customer mostra un elenco a discesa con tutti i Customer ID ricevuti dall'applicazione, fra cui SampleCustomer, MsvcSmokeText, BreadcrumbsSample, madExceptSample, Sample C++ Customer e DEMO-CUSTOMER

Il nuovo campo "Filter by Customer" nel dialog di creazione di un Log Level Alert. Lo stesso campo è disponibile anche per i Timing Alert, con lo stesso comportamento.

Il filtro è disponibile sia sui Log Level Alert sia sui Timing Alert, con la stessa logica: se popolato con un Customer ID, la regola si attiva soltanto quando l’evento (errore o timing lento) proviene da quel cliente; se lasciato sull’opzione di default All Customers, la regola vale per tutta la flotta. È utile anche per il pattern opposto: ricevere notifiche solo per i clienti pilota durante una fase di rollout graduale di una nuova versione.

WaitForSending: attesa esplicita del flush

Il log shipping di ExeWatch è asincrono per progettazione: il logging non blocca mai il chiamante, e un thread dedicato si occupa di inviare i batch al server in background. Per le applicazioni VCL, FMX, WPF, WinForms o per i servizi a lunga esecuzione questa scelta è ideale: lo svuotamento finale della coda viene gestito automaticamente in Shutdown quando il main loop termina in modo controllato.

Esistono però scenari in cui il chiamante vuole sapere che la coda è stata smaltita prima di proseguire: script CLI che terminano subito dopo aver loggato un errore, job batch nightly, sessioni di test E2E che devono propagare i log prima di chiudere il browser, finalizzazione di un Windows Service in risposta a uno stop request. In tutti questi casi la 1.30 introduce un’API uniforme su tutti gli SDK che si chiama WaitForSending:

// Delphi, alla fine di una console app
EW.Fatal('Unrecoverable error: ' + E.Message, 'startup');
EW.WaitForSending(5);  // attendi al massimo 5 secondi
ExitCode := 1;
// C#, alla fine di un Windows Service
EW.Fatal("Service stopping due to fatal error", "lifecycle");
EW.WaitForSending(5);
ExeWatchSdk.Shutdown();
// JS, prima di chiudere/ricaricare la pagina
ew.fatal("Unrecoverable client error", "ui");
await ew.waitForSending(5);
# Python, fine script CLI
ew.fatal("Job failed", "batch")
ew.wait_for_sending(5)

Il valore di ritorno è il numero di item ancora in coda quando il timeout è scaduto: zero significa “tutto inviato correttamente”.

Timing thread-local

Le misurazioni di timing (StartTiming / EndTiming) sono fra le funzionalità più usate dell’SDK perché permettono di profilare codice in produzione con costo zero. Fino alla 1.30, in applicazioni fortemente multi-thread, due worker che usavano la stessa timing id su thread diversi condividevano la stessa “slot” interna. In casi limite questo poteva produrre misurazioni meno precise di quanto avresti voluto.

Con questa release l’SDK gestisce uno stack di timing per thread. Ogni thread ha la propria mappa indipendente dei timer pendenti, e timer con la stessa id su thread diversi sono completamente isolati. Cambia il comportamento interno; l’API non cambia. La modifica è disponibile su Delphi, .NET e Python (il JavaScript-browser è single-thread di natura, e i Web Worker hanno già un contesto isolato):

// Delphi: adesso ogni thread mantiene il proprio stack di timing
// Nota: avvolgere sempre StartTiming/EndTiming in un try..finally,
// così il timer viene chiuso anche se DoHeavyWork solleva un'eccezione.
TThread.CreateAnonymousThread(
  procedure
  begin
    EW.StartTiming('worker.process', 'background');
    try
      DoHeavyWork;
    finally
      EW.EndTiming('worker.process');  // chiude il timer di QUESTO thread
    end;
  end).Start;

TThread.CreateAnonymousThread(
  procedure
  begin
    EW.StartTiming('worker.process', 'background');  // OK, non collide
    try
      DoHeavyWork;
    finally
      EW.EndTiming('worker.process');
    end;
  end).Start;

L’id logico del timing rimane identico: in dashboard worker.process continua ad aggregare correttamente le esecuzioni di entrambi i thread. Ma internamente ogni thread tiene la propria “slot” e i timer non si pestano i piedi.

JavaScript SDK: cattura completa degli errori HTTP

Sull’SDK JavaScript abbiamo due aggiunte che cambiano la qualità delle informazioni sugli errori in produzione.

La prima: ogni volta che un fetch o un XMLHttpRequest torna uno stato >= 400, l’SDK cattura automaticamente il body della risposta e lo allega come extra_data. Significa che insieme allo status code ti arriva anche il messaggio di errore restituito dal backend, che è quasi sempre la chiave per capire cosa è successo.

La seconda: una nuova callback httpErrorExtractor che ti permette di personalizzare cosa viene allegato:

ExeWatch.init({
  apiKey: 'ew_web_your_key',
  httpErrorExtractor: (body, contentType, status, url) => {
    if (contentType?.includes('json')) {
      try {
        const parsed = JSON.parse(body);
        return {
          message: parsed.detail || parsed.error || `HTTP ${status}`,
          extra: { trace_id: parsed.trace_id }
        };
      } catch (_) { /* fall through */ }
    }
    return null;  // usa il body raw come default
  }
});

Utile se il tuo backend espone un payload di errore strutturato (es. FastAPI con {"detail": "...", "trace_id": "..."}) e vuoi che ExeWatch lo loggi nello stesso formato che useresti nella dashboard di osservabilità lato server.

Isolamento del file queue per applicazione

Dettaglio invisibile, conseguenza importante. Su una macchina dove girano più applicazioni ExeWatch (tipicamente un’applicazione utente principale più un servizio Windows ausiliario, o due varianti di prodotto sulla stessa workstation), finora la directory di storage offline era unica per default e l’isolamento richiedeva di passare path differenti manualmente nella configurazione di ogni SDK.

Dalla 1.30 ogni applicazione che inizializza l’SDK crea automaticamente una sottocartella dedicata identificata dall’API key. Più applicazioni possono coesistere sulla stessa directory di storage in completa sicurezza, senza richiedere configurazione aggiuntiva.

Notifiche email su quota eventi

Il piano gratuito Hobby include 5.000 eventi al mese, il Pro 100.000, il Business 1.000.000. Da 1.30, l’owner e i membri del team ricevono un’email al raggiungimento di due soglie:

  • 80% della quota mensile (warning): c’è tempo per agire prima del blocco
  • 100% della quota mensile (limit reached): gli ulteriori log non vengono accettati fino al reset mensile

Ovviamente la quota stessa rimane visibile in dashboard, ma l’email è il segnale che molti utenti aspettavano: ti raggiunge dove guardi tutti i giorni e ti permette di intervenire (abbassando il livello minimo di log dell’SDK, applicando filtri o pianificando l’upgrade) prima che il problema diventi un blocco.

Pricing e On Premise

La pagina pricing si è arricchita di un quarto piano accanto a Hobby, Pro e Business: On Premise. Non è un piano self-service, e non lo sarà mai: ogni installazione on-premise di ExeWatch ha esigenze specifiche di sizing, backup, retention e supporto. Per questo abbiamo una conversazione diretta con chi ne ha bisogno.

Le casistiche più comuni sono quelle prevedibili: aziende che per policy interna non possono inviare dati telemetrici a un servizio gestito, contesti regolatori (GxP, healthcare, defense), oppure semplici esigenze di latenza quando l’applicazione è geograficamente lontana dal nostro datacenter europeo.

L’offerta on-premise include tutte le funzionalità del Business (incluso l’AI Engine Full) più applicazioni, eventi, retention, alert e team members illimitati, integrazioni personalizzate, onboarding dedicato e SLA negoziato. Per i dettagli: exewatch@bittime.it.

Video e webinar

Se vuoi vedere ExeWatch “in movimento” prima di provarlo, sul canale YouTube trovi tre video utili a seconda della lingua e dello stack di interesse:

Per il pubblico ispanofono è già fissato il webinar ExeWatch in spagnolo di mercoledì 20 maggio 2026, organizzato in collaborazione con Delphi Studio. Per iscriversi basta compilare questo form di registrazione.

Perché ExeWatch continua a crescere così

Una cosa che mi colpisce sempre, ogni volta che esce una release con tante feature: nessuna di queste novità è nata “in solitaria” davanti a un foglio bianco. Ognuna è il risultato di una conversazione concreta con chi sta usando ExeWatch in produzione.

Il supporto a C++Builder e MSVC viene da clienti del mondo industriale che hanno codice C++ legacy critico in produzione. I tag globali vengono da un cliente con installazioni multi-tenant. Il filtro alert per customer è arrivato dopo una telefonata in cui un cliente mi diceva “ho un grosso utente che mi inonda di alert ma non posso alzare la soglia per gli altri”. WaitForSending è un’esigenza emersa parlando con chi gestisce job batch nightly su CLI.

Questo è anche il motivo per cui tutte queste funzionalità (incluse quelle “sponsorizzate” da clienti enterprise) sono disponibili anche sul piano gratuito Hobby. I pattern che emergono a scala arrivano prima o poi anche su progetti più piccoli: meglio averli pronti quando servono.

Cosa c’è dopo

Vale la pena ricordare che l’SDK Delphi nativo compila e funziona già su Delphi per Windows, Delphi per Linux e Delphi per Android: chi sviluppa applicazioni FireMonkey per il mondo mobile ha quindi ExeWatch a disposizione fin da ora, senza dover aspettare un SDK separato. Il prossimo passo in questo senso è chiudere il cerchio con SDK nativi indipendenti dal toolchain Delphi per chi sviluppa mobile con stack diversi (Kotlin/Java per Android nativo, Swift per iOS).

Sul backend stiamo lavorando per estendere l’AI Engine a un terzo livello di analisi: non solo errori e timing, ma anche pattern di uso (sessioni, ondate di traffico, picchi di utilizzo). L’obiettivo è che la dashboard ti dica “qualcosa è cambiato” anche quando il cambiamento non è un errore, ma una variazione anomala nel modo in cui i tuoi clienti usano il prodotto.

Se usi già ExeWatch, tutte le novità della 1.30 sono già attive sul tuo account: non devi fare niente di lato server. Per beneficiare di WaitForSending, dei Global Tags e del nuovo SDK C/C++ ti basta aggiornare l’SDK lato applicazione dalla pagina Integration della dashboard.

Se non l’hai ancora provato, il piano Hobby è gratuito e non richiede carta di credito: bastano cinque minuti per la prima integrazione e in genere i primi log arrivano in dashboard prima ancora di finire di leggere la guida.



ExeWatch: Application Performance Monitoring per applicazioni server, desktop e web, con Artificial Intelligence Engine integrato. Creato da bit Time Professionals.

Comments

comments powered by Disqus