---
title: "Implementare un logger categorizzabile"
excerpt: "Scopri come implementare un logger categorizzabile in Flutter per migliorare la gestione dei log."
author: francesco
date: "25 aprile 2025"
category: "Sviluppo frontend"
tags:
  - Flutter
  - Frontend
  - Log
coverImage: "/assets/posts/logger/head.png?height=600&width=1200"
---

Quando si pubblica un'applicazione, spesso si ha bisogno di un modo per monitorare
le performance e il comportamento dell'app. Un modo comune per farlo è utilizzare
un logger, che può registrare eventi, errori e altre informazioni utili. In questo
articolo, esploreremo come implementare un logger categorizzabile in Flutter,
che ci permetterà di organizzare i log in categorie specifiche e facilitare la
loro gestione.

## Perché un logger categorizzabile?

Molti progetti utilizzano Firebase Crashlytics o Sentry per monitorare gli errori e le performance.
Tuttavia, questi strumenti richiedono che l'applicazione sia costantemente in
contatto con il servizio per inviare i log. In alcuni casi, potrebbe non essere
necessario avere un flusso continuo di log, ma può bastare registrare gli eventi
localmente e inviarli in un secondo momento, ad esempio quando l'app è online o
quando l'utente lo richiede.
È proprio qui che entra in gioco il logger categorizzabile.

Un logger categorizzabile consente di organizzare i log in categorie specifiche,
rendendo più facile la loro gestione e analisi. Possiamo ad esempio avere categorie
per gli errori, le performance, le azioni degli utenti e così via. In questo modo,
possiamo filtrare i log in base alla categoria e concentrarci solo su quelli che
ci interessano in un determinato momento.

## Creazione del logger

Iniziamo definendo i tipi di eventi di cui vogliamo tenere traccia:

```dart
enum LogLevel {
  debug(0),
  info(1),
  warning(2),
  error(3),
  fatal(99);

  const LogLevel(this.value);

  final int value;
}
```

In questo esempio, abbiamo definito cinque livelli di log: `debug`, `info`, `warning`,
`error` e `fatal`. Ogni livello ha un valore associato che ci permette di
classificarli in ordine di gravità. È qui che possiamo decidere di aggiungere
categorie come `performance`, `userAction`, `network` e così via.

Passiamo ora alla classe Logger. Questa classe sarà responsabile della
registrazione dei log e della loro gestione. Ecco un esempio di implementazione:

```dart
typedef LogTransformer = void Function(LogEntry entry);

class LogEntry {
  final LogLevel level;
  final String message;
  final String? category;
  final DateTime timestamp;

  // ...
}

class Logger {
  static final Logger _instance = Logger._internal();
  factory Logger() => _instance;
  Logger._internal();

  final List<LogTransformer> _transformers = [];

  void addTransformer(LogTransformer transformer) {
    _transformers.add(transformer);
  }

  void log(LogLevel level, String message, {String? category}) {
    final entry = LogEntry(
      level: level,
      message: message,
      category: category,
      timestamp: DateTime.now(),
    );

    for (final transformer in _transformers) {
      // Invia il messaggio trasformato a un servizio di logging
      // oppure a un repository locale.
      transformer(entry);
    }
  }
}
```

In questo esempio, abbiamo creato una classe `Logger` che utilizza il
[singleton pattern](https://refactoring.guru/design-patterns/singleton)
per garantire che ci sia solo un'istanza del logger in tutta
l'applicazione (ovviamente in un'app in production useremmo qualcosa di simile
alla [dependency injection](https://stackify.com/dependency-injection/) per non
compromettere la testabilità e la modularità del codice).
La classe ha un metodo `log` che accetta un livello di log,
un messaggio e una categoria opzionale. Quando viene chiamato il metodo `log`,
viene creato un oggetto `LogEntry` che contiene tutte le informazioni sul log.

Il logger accetta anche una lista di trasformatori (`LogTransformer`) che sono
responsabili della gestione dei log. Questi trasformatori possono essere
utilizzati per inviare i log a un servizio esterno, salvarli in un file locale
o eseguire qualsiasi altra operazione necessaria, come salvare i log in una
`List` per una successiva visione degli stessi.

Il modo in cui abbiamo definito l'enum `LogLevel` e la classe `LogEntry` ci
permette di filtrare molto facilmente i log in base alla loro categoria:

```dart
// Assumiamo di avere una lista di log del tipo
List<LogEntry> _logs = [];

// Filtriamo i log in base alla categoria
LogLevel category = LogLevel.warning;
List<LogEntry> filteredLogs = _logs
    .where((log) => log.category.value >= category.value)
    .toList();
```

Così facendo, ignoriamo tutti i log di tipo `debug` e `info`. Ovviamente, possiamo
anche prendere solo i log di una categoria specifica, ad esempio `performance` o `userAction`,
a seconda delle nostre esigenze.

## Conclusione

In questo articolo, abbiamo visto come implementare un logger categorizzabile
in Flutter. Questo approccio ci consente di organizzare i log in categorie
specifiche, facilitando la loro gestione e analisi. Abbiamo anche visto come
creare una classe `Logger` che utilizza il singleton pattern e accetta
trasformatori per gestire i log in modo flessibile.

Abbiamo implementato un logger molto semplice ma estremamente potente. A
[_TECHTONIC FAULT_](/), crediamo che la semplicità sia la chiave per un buon
sviluppo software, senza compromettere la potenza e la flessibilità. Con questo
logger, possiamo monitorare le performance e il comportamento delle nostre
applicazioni in modo efficace e senza complicazioni inutili.