itrace 0.1.1

Structured, columnar tracing for Rust applications
Documentation
# itrace design

Qui voglio descrivere un library crate chiamato itrace il cui scopo è quello di fare tracing
in progetti Rust. La prima particolarità è che voglio strutturare le righe del trace
in colonne. Di queste colonne, le prime due sono standard e definite dal crate itrace.
Tali colonne sono:
    - Level: un solo carattere indicante il livello di tracciamento. In dettaglio:
        * T per trace.
        * D per debug.
        * I per info.
        * W per warning.
        * E per error.
      Questa colonna avrà quindi una larghezza di un solo carattere e lo sfondo di un colore
      diverso in funzione del livello. I colori saranno indicati da un file di configurazione
      di cui parleremo in seguito.
    - DateTime: riporta data ed ora in un formato specificato dalla configurazione, stesso file
      di cui ho accennato poco fa.
Le altre colonne sono definibili dal utente del crate nelle modalità definite di seguito. Infine
sarà presente l'ultima colonna della riga che riporta il messaggio di tracing vero e proprio.

Tutte le colonne devono essere configurabili attraverso un opportuno file che chiamiamo tracer,
che sarà presente nella directory di configurazione tipica del sistema operativo usato. Ad 
esempio, per linux tale folder sarà nella home dell'utente in .config/itrace. Tale directrory
può contenere un qualsiasi numero di file tracer creati dall'utente e/o dalle applicazioni
utilizzatrici del crate itrace. Sarà comunque presente anche un file di default avente nome:
default.toml, dove toml è l'estensione dei file di configurazione in formato TOML che avranno i
file tracer. In dettaglio, ogni file tracer potrà contenere:
    - le propietà della colonna Level,
    - le propietà della colonna DateTime,
    - le proprietà di ogni singola colonna definita dallo user del crate itrace,
    - il livello minimo da tracciare per ogni modulo dell'applicazione (così come definiti nel
      crate env_logger) oltre che al livello minimo di default.
In particolare le proprietà delle colonne saranno:
    - Level: colore di background e foreground,
    - DateTime: formato della data e dell'ora,
    - Custom column: per ogni colonna specifica sarà possibile definire i colori di background e
      foreground, la larghezza della colonna in numero di caratteri e l'allineamento del testo
      all'interno della colonna. Sarà possibile anche definire le proprietà di una colonna 
      generica che saranno usate come fall back nel caso non ci fosse la definizione per una delle
      colonne definite nel crate utilizzatore.
Inoltre, il file tracer dovrà permettere di configurare anche la possibilità di non usare i colori
nel tracciamento, il che risulta utile per evitare di riempire il tracciamento di caratteri speciali,
essenzialmente i caratteri di escape usati nei terminali per attivare i colori, nelle situazioni per
le quali i terminali non supportino i colori. 

Infine, dovrebbe essere possibile configurare il path del file di destinazione del tracciamento. Se tale path non fosse definito, il tracciamento verrebbe fatto su stderr.

Il crate itrace fornirà anche un'API aggiuntiva compatibile con il crate tracing di tokio. Dopo le 
prime iterazioni mi sono reso conto che **tracing-subscriber** fornisce un trait che permette di definire il proprio formatter. Oltre al trait FormatEvent, c'è modo di usare anche il meccanismo di filtering. Quindi la scelta migliore è di sfruttare **tracing** che è un crate ampiamente usato e che solitamente preferisco usare nei miei progetti. 

## Struttura crate

Il crate sarà strutturato come un workspace con due crate al suo interno: il crate itrace stesso e 
un crate itrace-derive se fosse utile. Sarà inoltre presente un folder examples, tipico dei progetti Rust per gli esempi di utilizzo dei crate lib. Infine sarà presente anche un directory tests per i
test di integrazione.

```
itrace/
├── Cargo.toml              (workspace)
├── itrace/
│   ├── Cargo.toml
│   └── src/lib.rs
├── itrace-derive/
│   ├── Cargo.toml
│   └── src/lib.rs
├── examples/
│   └── basic.rs
└── tests/
    └── integration.rs
```

## itrace e itrace-derive

La possibilità di definizione di colonne custom, mi spinge a progettare per itrace un crate di derive
che usa le proc-macro in Rust. In particolare, verrà definito un proc-macro attribute #[itracer] che
potrà specificare il nome del tracer da utilizzare, e dove trovare, come descritto sopra, tutte le 
informazioni di configurazione specifiche.

Tale macro, se utile, potrebbe implementare il trait di tracing_subscriber:
  * fmt::FormatEvent - per la formatattazione
Ed usare, eventualmente cambiandolo con il meccanismo di hot reload descritto in seguito il tipo:
  * filter::EnvFilter - per i filtri con i livelli per modulo.


## Output format

L'esempio del paragrafo precedente produrrebbe l'output con le colonne come dettagliato di
seguito, a meno dei colori ovviamente:

```
 I  14:49:55.743834Z     myapp      eu-west-1    node-01    Server started
 ^  └──────────────┘  └─────────┘  └─────────┘  └───────┘  └────────────┘
 │      timestamp       col app      col zone    col node      message
 │
 └─ level badge (1 char)
```

Come descritto:

- Il **level badge** e **timestamp** sono sempre presenti.
- Tutte le altre colonne sono user-defined.
- Colonne il cui valore non è fornito vengono renderizzate come spazi,
  presenvando l'allineamento delle colonne che seguono.

## Tracer config file

I file tracer sono in formato toml. Il file avrà le sezioni:
    - general: contiene le chiavi di carattere generale come colors che permette di
      disattivare i colori, o il path del eventuale file generato dal tracing.
    - logging: contiene righe nel formato <target> = <level> con le stesse logiche e
      formato dell'analogo usato nel crate env_logger.
    - level.<nome level>: contiene chiavi di tipo dotted keys che specificano il 
      colore o in formato RGB o Ansi per background e/o foreground. In dettaglio le
      chiavi possibili sono:
        + bg.rgb
        + bg.ansi
        + fg.rgb
        + fg.ansi
      Ovviamente sarà accettata una sola chiave per bg e una sola per fg. <nome level>
      è il nome del livello: trace, debug, info, warn o error. I valori per le chiavi
      rgb sono degli array di tre interi u8 rispettivamente per red, green e blue.
      Invece, i valori per le chiavi ansi sono interi che rappresentano gli indici
      delle tabelle dei colori ansi.
    - datetime: contiene una chiave format che, come stringa, esprime il format della
      colonna temporale. Il format dipende dalla libreria che gestisce il tempo. 
      Legherei tale libreria ad una feature-gate che può lasciare l'utente libero di
      decidere quale crate usare tra chrono, jiff o altre eventualmente da aggiungere.
    - <custom-column>: contiene chiavi dotted come la sezione level per stabilire i 
      colori di background e forground, sia rgb che ansi, ed una chiave bare width che
      specifica la larghezza in caratteri della colonna <custom-column>. Una bare key
      che specifica l'allineamento del testo nella colonna. Esiste anche 
      un valore di <custom-column> di default e cioè default-column per la configurazione
      di fall-back.

## Hot reload tracer

La funzionalità di configurazione del tracciamento introdotta dal crate env_logger è utile,
ma ha lo svantaggio che viene letta solo allo start dell'applicazione. In itrace voglio che
sia possibile avere la possibilità, usando l'attivazione di una feature, di eseguire il
reload a caldo del tracer. Questo implica l'aggiunta di complessità nella gestione della 
memoria nella quale sono presenti i dettagli di formato e colori da usare per le singole
righe di tracing. Ed è anche necessario specificare il meccanismo di trigger del reload.

Voglio che tale funzionalità, rispettando la filosofia Rust, non implichi un costo aggiunto
quando non è usata. Quindi va progettata bene, e qui ancora non ho le idee chiare.

Per quanto riguarda il meccanismo di trigger preferisco l'uso di un socket, possibilmente 
di tipo unix (una pipe in windows), o al più con binding sul loop localhost. In MacOS non
ho conoscenze specifiche per suggerire un meccanismo equivalente.

Il risultato del hot reload dovrebbe creare un nuovo oggetto con i dettagli utili al trace
e che dovrebbe andare a sostituire quello usato fino al momento del trigger di hot reload.
Ancora non ho i dettagli della modalità operativa in cui tale sostituzione possa avvenire,
inizierò con ArcSwap che però devo approfondire.