Loguinho 🪵

Dart Flutter License: MIT GitHub Repository

Loguinho é um framework moderno, seguro, de alta performance e multiplataforma de logging estruturado para aplicações Dart e Flutter.

Ele resolve os maiores problemas de logging em aplicações reais: vazamento involuntário de dados sensíveis (PII) em logs do console, engasgos na interface do usuário (UI Thread drops de FPS) por escrita síncrona I/O e falta de rastreabilidade de sessões.

Loguinho Botanical Atlas Poster


✨ Principais Recursos

  • 🛡️ Sanitização Automática de PII: Mascara automaticamente CPFs, e-mails, números de cartão de crédito e senhas antes de registrar o log.
  • 🎨 Formatação Adaptativa: Layout limpo e colorido com ANSI no terminal em Desenvolvimento; JSON estruturado pronto para observabilidade em Produção.
  • Zero FPS Impact: Processamento assíncrono e lazy formatting que impede quedas de quadros na UI Thread do Flutter.
  • 🧭 Contexto de Sessão e W3C Trace: Suporte nativo a sessionId, userId, traceId e spanId para integração com OpenTelemetry e Grafana/Datadog.
  • 🚦 Configurações Prontas por Ambiente: Modos pré-configurados para development, stage e production.
  • 🧩 Arquitetura Extensível (SOLID): Suporte a múltiplos destinos (ConsoleOutput, MemoryBufferOutput) e formatadores customizados.

🚀 Como Adicionar ao Seu Projeto

Opção 1: Via pub.dev (Comando CLI)

Execute o comando no terminal na raiz do seu projeto:

# Para projetos Flutter:
flutter pub add loguinho

# Para projetos Dart:
dart pub add loguinho

Opção 2: Edição Manual no pubspec.yaml

Adicione o Loguinho com sua versão na seção dependencies do seu pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter

  # Instalação via pub.dev:
  loguinho: ^1.0.0

  # Ou instalação direta via repositório GitHub:
  # loguinho:
  #   git:
  #     url: https://github.com/flubit-dev/loguinho.git
  #     ref: main

Após editar o pubspec.yaml, execute flutter pub get (ou dart pub get).


⚡ Guia Rápido de Inicialização e Uso

1. Inicialização no main.dart

Configure o Loguinho no ponto de entrada da sua aplicação de acordo com o ambiente:

import 'package:flutter/material.dart';
import 'package:loguinho/loguinho.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();

  // Configura para ambiente de desenvolvimento (ANSI colorido no console)
  Loguinho.configure(LoggerConfig.development());

  // Em produção, basta alternar para:
  // Loguinho.configure(LoggerConfig.production());

  // (Opcional) Associe a sessão e usuário logado para correlação de logs
  Loguinho.setSessionId('sess_987654321');
  Loguinho.setUserId('usuario@empresa.com');

  runApp(const MyApp());
}

2. Registrando Logs na Aplicação

O Loguinho oferece métodos semânticos para cada tipo de evento:

import 'package:loguinho/loguinho.dart';

// Logs informativos e depuração
Loguinho.debug('Inicializando módulo de pagamentos...');
Loguinho.info('Navegador de checkout aberto');
Loguinho.success('Compra realizada com sucesso!');

// Logs com exceções e erros
try {
  // operação de rede
} catch (e, stackTrace) {
  Loguinho.error(
    'Falha ao conectar com o gateway de pagamento',
    category: 'PAYMENT',
    error: e,
    attributes: {'retryCount': 3},
  );
}

// Logs especializados
Loguinho.network('POST /v1/checkout 200 OK', attributes: {'responseTimeMs': 145});
Loguinho.security('Tentativa de acesso a recurso restrito', category: 'AUTH');
Loguinho.performance('Tempo de carregamento da tela', attributes: {'durationMs': 82});

🛡️ Mascaramento Automático de Dados Sensíveis (PII)

Por padrão no perfil de produção (ou ativando a opção no sanitizer), o Loguinho higieniza automaticamente mensagens e atributos:

Loguinho.info(
  'Autenticando cliente com CPF 123.456.789-00',
  attributes: {
    'email': 'cliente@empresa.com',
    'password': 'MinhaSenhaSecreta123',
    'cardNumber': '4532 1178 9012 3456',
  },
);

Saída Estruturada Sanitizada:

{
  "timestamp": "2026-07-31T12:00:00.000Z",
  "level": "INFO",
  "message": "Autenticando cliente com CPF ***.***.***-**",
  "attributes": {
    "email": "c***e@empresa.com",
    "password": "[MASCARADO_SEGURANCA]",
    "cardNumber": "**** **** **** 3456"
  },
  "sessionId": "sess_987654321"
}

🏗️ Boas Práticas: Clean Architecture e Inversão de Dependência (DIP)

Para não acoplar seu código de negócio diretamente à biblioteca de log e facilitar testes unitários, recomendamos criar uma abstração da camada de infraestrutura usando o Adapter Pattern:

1. Crie uma Interface no seu Projeto (domain/services/i_logger.dart)

abstract class ILogger {
  void debug(String message, {String? category, Map<String, dynamic>? attributes});
  void info(String message, {String? category, Map<String, dynamic>? attributes});
  void error(String message, {String? category, Map<String, dynamic>? attributes, Object? error});
}

2. Crie o Adapter Concreto (infrastructure/logging/loguinho_logger_adapter.dart)

import 'package:loguinho/loguinho.dart';
import 'package:meu_app/domain/services/i_logger.dart';

class LoguinhoLoggerAdapter implements ILogger {
  @override
  void debug(String message, {String? category, Map<String, dynamic>? attributes}) {
    Loguinho.debug(message, category: category, attributes: attributes);
  }

  @override
  void info(String message, {String? category, Map<String, dynamic>? attributes}) {
    Loguinho.info(message, category: category, attributes: attributes);
  }

  @override
  void error(String message, {String? category, Map<String, dynamic>? attributes, Object? error}) {
    Loguinho.error(message, category: category, attributes: attributes, error: error);
  }
}

3. Injete o Logger na sua Classe de Negócio (Bloc/Controller/Service)

class PaymentController {
  final ILogger _logger;

  PaymentController(this._logger);

  Future<void> processPayment(double amount) async {
    _logger.info('Iniciando processamento', attributes: {'amount': amount});
    // lógica de negócio...
  }
}

🧪 Como Testar Seu Código e o Pacote

1. Testando Classes de Negócio do Seu App (Com Mock ou Fake Logger)

Como a sua classe de negócio depende da interface ILogger, nos testes unitários do seu app você pode passar uma implementação Fake:

import 'package:flutter_test/flutter_test.dart';
import 'package:meu_app/domain/services/i_logger.dart';
import 'package:meu_app/controllers/payment_controller.dart';

class FakeLogger implements ILogger {
  final List<String> logs = [];

  @override
  void debug(String message, {String? category, Map<String, dynamic>? attributes}) => logs.add(message);

  @override
  void info(String message, {String? category, Map<String, dynamic>? attributes}) => logs.add(message);

  @override
  void error(String message, {String? category, Map<String, dynamic>? attributes, Object? error}) => logs.add(message);
}

void main() {
  test('Deve registrar log ao processar pagamento', () async {
    final fakeLogger = FakeLogger();
    final controller = PaymentController(fakeLogger);

    await controller.processPayment(150.0);

    expect(fakeLogger.logs, contains('Iniciando processamento'));
  });
}

2. Testando com o Buffer em Memória do próprio Loguinho

Se preferir assertar os eventos gravados no buffer do Loguinho durante um teste de integração:

test('Deve validar buffer de memória do Loguinho', () {
  Loguinho.configure(LoggerConfig.development());
  
  Loguinho.info('Evento de Teste');

  final bufferedLogs = Loguinho.memoryBuffer.getEvents();
  expect(bufferedLogs.any((e) => e.message == 'Evento de Teste'), isTrue);
});

3. Rodando os Testes da própria Biblioteca Loguinho

Caso queira contribuir ou validar a biblioteca localmente:

# Clone o repositório:
git clone https://github.com/flubit-dev/loguinho.git
cd loguinho

# Execute a suíte de testes unitários:
dart test

📄 Documentação Técnica Detalhada (doc/)

Para aprofundamento arquitetural e especificações técnicas completas, consulte o diretório doc/:


📜 Licença

Este repositório está sob a licença MIT. Veja o arquivo LICENSE para mais detalhes.