Tinylog

Lightweight and simple Java logging framework. Achieves high performance with minimal configuration, characterized by small footprint. Optimized for developers who want to avoid complex configuration and resource-constrained environments.

LoggingJavaLightweightHigh PerformanceZero Dependencies

GitHub Overview

tinylog-org/tinylog

tinylog is a lightweight logging framework for Java, Kotlin, Scala, and Android

Stars748
Watchers11
Forks81
Created:September 13, 2012
Language:Java
License:Apache License 2.0

Topics

androidjavajclkotlinkotlin-libraryloggerlogging-libraryscalaslf4jtinylog

Star History

tinylog-org/tinylog Star History
Data as of: 7/17/2025, 11:31 PM

Library

tinylog

Overview

tinylog is a lightweight logging framework for Java, Kotlin, Scala, and Android developed as a high-performance logging solution with zero external dependencies. Composed of just two JAR files (API and implementation) with a combined size of approximately 195KB, it achieves an ultra-lightweight design. The static logger class design eliminates the need to create logger instances in each class, providing a simple and intuitive API. Featuring exceptional performance for disabled log statements with minimal overhead, it is optimized for use in microservices, Android applications, and resource-constrained environments.

Details

tinylog 2025 edition maintains its solid position as a mature lightweight logging solution with over a decade of development experience. It supports operation in diverse environments including JVM, GraalVM, Android, native images, Java SE, Java EE, JPMS projects, and OSGi platforms. Provides comprehensive log output options through support for console, file, JSON, database, syslog server, and Android logcat output. Advanced features such as asynchronous logging, buffered output, and SLF4J bridges enable support for enterprise-level requirements.

Key Features

  • Ultra-lightweight Design: Minimal footprint of ~195KB with zero external dependencies
  • Static Logger API: Simple design requiring no logger instance creation
  • High-performance Architecture: Exceptional performance for disabled logs
  • Diverse Output Support: Console, file, JSON, database, syslog support
  • Cross-platform: JVM, Android, GraalVM, native image support
  • Asynchronous Logging: Asynchronous output support for high throughput

Pros and Cons

Pros

  • Deployment simplification through lightweight design with zero external dependencies
  • Intuitive and concise API experience through static logger design
  • Exceptional high performance and minimal resource usage
  • Flexible log management through rich output options
  • Optimization for microservices and Android applications
  • Easy integration into existing projects through SLF4J bridge

Cons

  • Limited feature set compared to Log4j and Logback
  • Lack of advanced configuration options for complex enterprise environments
  • Limited community size and third-party integrations
  • Constraints in structured logging and complex filtering features
  • Learning cost due to deviation from existing Java logging standards
  • Relatively insufficient debugging and troubleshooting information

Reference Pages

Code Examples

Installation and Setup

<!-- Maven - pom.xml -->
<dependencies>
    <!-- tinylog API -->
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>tinylog-api</artifactId>
        <version>2.6.2</version>
    </dependency>
    
    <!-- tinylog implementation -->
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>tinylog-impl</artifactId>
        <version>2.6.2</version>
    </dependency>
    
    <!-- SLF4J integration (optional) -->
    <dependency>
        <groupId>org.tinylog</groupId>
        <artifactId>slf4j-tinylog</artifactId>
        <version>2.6.2</version>
    </dependency>
</dependencies>
// Gradle - build.gradle
dependencies {
    // tinylog API
    implementation 'org.tinylog:tinylog-api:2.6.2'
    
    // tinylog implementation
    runtimeOnly 'org.tinylog:tinylog-impl:2.6.2'
    
    // SLF4J integration (optional)
    implementation 'org.tinylog:slf4j-tinylog:2.6.2'
}
// Basic operation verification
import org.tinylog.Logger;

public class TinylogExample {
    public static void main(String[] args) {
        Logger.info("tinylog is working correctly!");
        Logger.debug("Debug message test");
    }
}

Basic Logging Output

import org.tinylog.Logger;

public class BasicLoggingExample {
    
    public static void main(String[] args) {
        // Basic log output at each level
        Logger.trace("Detailed trace information");
        Logger.debug("Debug information");
        Logger.info("General information message");
        Logger.warn("Warning: potential issue");
        Logger.error("Error: something went wrong");
        
        // Parameterized logging (recommended)
        String userName = "John Doe";
        int userId = 12345;
        String action = "login";
        
        Logger.info("User {} (ID: {}) performed {}", userName, userId, action);
        Logger.debug("Processing time: {}ms", System.currentTimeMillis());
        
        // Exception logging
        try {
            int result = 10 / 0;
        } catch (ArithmeticException ex) {
            Logger.error(ex, "Calculation error occurred");
            Logger.error("Error details: {}", ex.getMessage());
        }
        
        // Conditional logging (performance-focused)
        if (Logger.isDebugEnabled()) {
            String expensiveDebugInfo = generateExpensiveDebugInfo();
            Logger.debug("Debug info: {}", expensiveDebugInfo);
        }
        
        // Multiple parameter formatting
        String product = "Laptop";
        double price = 1299.99;
        int quantity = 2;
        double total = price * quantity;
        
        Logger.info("Order details: {} × {} = ${}", product, quantity, total);
        
        // Object information logging
        User user = new User("Jane Smith", "[email protected]");
        Logger.info("User created: {}", user);
        
        // Array/collection logging
        String[] categories = {"Electronics", "Books", "Clothing"};
        Logger.debug("Available categories: {}", (Object) categories);
        
        java.util.List<String> items = java.util.Arrays.asList("item1", "item2", "item3");
        Logger.info("Processing items: {}", items);
    }
    
    private static String generateExpensiveDebugInfo() {
        // Simulate expensive computation
        return "Detailed system state information";
    }
    
    static class User {
        private String name;
        private String email;
        
        public User(String name, String email) {
            this.name = name;
            this.email = email;
        }
        
        @Override
        public String toString() {
            return String.format("User{name='%s', email='%s'}", name, email);
        }
    }
}

Log Level Configuration

# tinylog.properties - Basic configuration
# Root level setting
level = info

# Output format setting
writer = console
writer.format = {date: yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {level}: {message}

# Package-specific level settings
[email protected] = debug
[email protected] = warn
[email protected] = error

# Multiple output destinations
writer1 = console
writer1.level = info
writer1.format = {date: HH:mm:ss} {level}: {message}

writer2 = file
writer2.level = debug
writer2.file = logs/application.log
writer2.format = {date: yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {level} {class}.{method}() - {message}

# File rotation settings
writer3 = rolling file
writer3.level = info
writer3.file = logs/app-{date: yyyy-MM-dd}.log
writer3.policies = startup, daily: 00:00
writer3.format = {date: yyyy-MM-dd HH:mm:ss.SSS} {level}: {message}
// Programmatic level configuration
import org.tinylog.configuration.Configuration;

public class DynamicConfiguration {
    
    public static void configureLogging() {
        // Dynamically change log level
        Configuration.set("level", "debug");
        
        // Set levels for specific classes
        Configuration.set("[email protected]", "trace");
        Configuration.set("[email protected]", "warn");
        
        // Dynamic output destination configuration
        Configuration.set("writer", "console");
        Configuration.set("writer.format", "{date: HH:mm:ss} {level}: {message}");
        
        Logger.info("Log configuration updated dynamically");
    }
    
    public static void setupProductionLogging() {
        // Production environment configuration
        Configuration.set("level", "info");
        Configuration.set("writer1", "file");
        Configuration.set("writer1.file", "/var/log/myapp/application.log");
        Configuration.set("writer1.format", 
            "{date: yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {level} {class} - {message}");
        
        // Separate error logging
        Configuration.set("writer2", "file");
        Configuration.set("writer2.file", "/var/log/myapp/error.log");
        Configuration.set("writer2.level", "error");
        Configuration.set("writer2.format", 
            "{date: yyyy-MM-dd HH:mm:ss.SSS} {level}: {message}{exception}");
        
        Logger.info("Production log configuration completed");
    }
}

Java-specific Features

import org.tinylog.Logger;
import org.tinylog.TaggedLogger;

public class JavaSpecificFeatures {
    
    // Tagged logger usage
    private static final TaggedLogger SECURITY_LOGGER = Logger.tag("SECURITY");
    private static final TaggedLogger PERFORMANCE_LOGGER = Logger.tag("PERFORMANCE");
    private static final TaggedLogger DATABASE_LOGGER = Logger.tag("DATABASE");
    
    public static void demonstrateTaggedLogging() {
        // Security-related logging
        SECURITY_LOGGER.warn("Unauthorized login attempt from IP: {}", "192.168.1.100");
        SECURITY_LOGGER.info("User authentication successful: {}", "user123");
        
        // Performance-related logging
        long startTime = System.currentTimeMillis();
        performSomeOperation();
        long duration = System.currentTimeMillis() - startTime;
        PERFORMANCE_LOGGER.info("Operation completed in: {}ms", duration);
        
        // Database-related logging
        DATABASE_LOGGER.debug("SQL execution: SELECT * FROM users WHERE id = ?", 123);
        DATABASE_LOGGER.error("Database connection error");
    }
    
    // Java 8+ stream operations with logging
    public static void streamLoggingExample() {
        java.util.List<String> items = java.util.Arrays.asList(
            "item1", "item2", "item3", "item4"
        );
        
        Logger.info("Processing started: {} items", items.size());
        
        java.util.List<String> processed = items.stream()
            .peek(item -> Logger.debug("Processing: {}", item))
            .map(String::toLowerCase)
            .peek(item -> Logger.trace("Transformed: {}", item))
            .filter(item -> item.length() > 2)
            .peek(item -> Logger.debug("Filter passed: {}", item))
            .collect(java.util.stream.Collectors.toList());
        
        Logger.info("Processing completed: {} results", processed.size());
    }
    
    // CompletableFuture and asynchronous logging
    public static void asyncLoggingExample() {
        Logger.info("Async processing started");
        
        java.util.concurrent.CompletableFuture<String> future = 
            java.util.concurrent.CompletableFuture.supplyAsync(() -> {
                Logger.debug("Async task executing...");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Logger.warn("Sleep interrupted", e);
                    Thread.currentThread().interrupt();
                }
                return "async result";
            });
        
        future.thenAccept(result -> {
            Logger.info("Async processing completed: {}", result);
        }).exceptionally(throwable -> {
            Logger.error(throwable, "Async processing error");
            return null;
        });
    }
    
    // Resource management with logging
    public static void resourceManagementLogging() {
        Logger.debug("Resource acquisition started");
        
        try (java.io.FileInputStream fis = new java.io.FileInputStream("example.txt")) {
            Logger.info("File opened successfully");
            // File processing
            Logger.debug("File processing completed");
        } catch (java.io.IOException e) {
            Logger.error(e, "File processing error: {}", e.getMessage());
        } finally {
            Logger.debug("Resource cleanup completed");
        }
    }
    
    // Custom formatting with logging
    public static void customFormattingExample() {
        // JSON-style format
        Logger.info("{{\"event\": \"user_login\", \"user_id\": {}, \"timestamp\": {}}}", 
                   12345, System.currentTimeMillis());
        
        // CSV-style format
        Logger.info("USER_ACTION,{},{},{}", 
                   "user123", "CREATE_POST", java.time.LocalDateTime.now());
        
        // Structured information logging
        logStructuredEvent("ORDER_CREATED", 
                          "order_id", "ORD-001",
                          "customer_id", "CUST-456", 
                          "amount", 1299.99);
    }
    
    private static void logStructuredEvent(String event, Object... keyValues) {
        StringBuilder sb = new StringBuilder();
        sb.append("EVENT:").append(event);
        
        for (int i = 0; i < keyValues.length; i += 2) {
            if (i + 1 < keyValues.length) {
                sb.append(" ").append(keyValues[i]).append("=").append(keyValues[i + 1]);
            }
        }
        
        Logger.info(sb.toString());
    }
    
    private static void performSomeOperation() {
        // Simulate some operation
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}

Configuration File Examples

# tinylog.properties - Complete configuration example

# Global level setting
level = info

# === Console output configuration ===
writer1 = console
writer1.level = debug
writer1.stream = out
writer1.format = {date: HH:mm:ss.SSS} [{thread}] {level}: {class-name}.{method}() - {message}

# === Main file output configuration ===
writer2 = file
writer2.level = info
writer2.file = logs/application.log
writer2.charset = UTF-8
writer2.buffered = true
writer2.format = {date: yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {level} {class} - {message}{exception}

# === Error file separation configuration ===
writer3 = file
writer3.level = error
writer3.file = logs/error.log
writer3.format = {date: yyyy-MM-dd HH:mm:ss.SSS} {level}: {message}{exception: |}

# === Rolling file configuration ===
writer4 = rolling file
writer4.level = debug
writer4.file = logs/debug-{date: yyyy-MM-dd}.log
writer4.policies = startup, daily: 00:00, size: 10MB
writer4.backups = 7
writer4.format = {date: HH:mm:ss.SSS} {level} {class}.{method}():{line} - {message}

# === JSON output configuration ===
writer5 = json file
writer5.level = info
writer5.file = logs/application.json
writer5.field.timestamp = {date: yyyy-MM-dd'T'HH:mm:ss.SSSXXX}
writer5.field.level = {level}
writer5.field.thread = {thread}
writer5.field.logger = {class}
writer5.field.message = {message}
writer5.field.exception = {exception}

# === Package-specific level configuration ===
[email protected] = debug
[email protected] = info
[email protected] = warn

# Third-party libraries
[email protected] = warn
[email protected] = error
[email protected] = info

# === Tag-specific configuration ===
tag@SECURITY = SECURITY
level@tag:SECURITY = info
writer@tag:SECURITY = file
writer@tag:SECURITY.file = logs/security.log
writer@tag:SECURITY.format = {date: yyyy-MM-dd HH:mm:ss} SECURITY {message}

tag@PERFORMANCE = PERFORMANCE  
level@tag:PERFORMANCE = debug
writer@tag:PERFORMANCE = file
writer@tag:PERFORMANCE.file = logs/performance.log
# tinylog.yaml - YAML configuration example
level: info

writers:
  - type: console
    level: debug
    format: "{date: HH:mm:ss.SSS} [{thread}] {level}: {message}"
    
  - type: file
    level: info
    file: "logs/application.log"
    charset: "UTF-8"
    buffered: true
    format: "{date: yyyy-MM-dd HH:mm:ss.SSS} [{thread}] {level} {class} - {message}{exception}"
    
  - type: rolling file
    level: debug
    file: "logs/app-{date: yyyy-MM-dd}.log"
    policies:
      - type: startup
      - type: daily
        time: "00:00"
      - type: size
        size: "50MB"
    backups: 30
    format: "{date: yyyy-MM-dd HH:mm:ss.SSS} {level} {class}.{method}():{line} - {message}"

# Package level configuration
levels:
  "com.example.service": debug
  "com.example.database": warn
  "org.springframework": error

Performance Optimization

import org.tinylog.Logger;
import org.tinylog.configuration.Configuration;

public class PerformanceOptimization {
    
    // Batch processing for high-frequency logging
    public static void batchLoggingExample() {
        // Enable asynchronous writing
        Configuration.set("writingthread", "true");
        Configuration.set("writingthread.priority", "1");  // Lowest priority
        
        Logger.info("Asynchronous log writing started");
        
        // Mass log output test
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            Logger.debug("Batch processing #{}: Processing data...", i);
        }
        long duration = System.currentTimeMillis() - startTime;
        
        Logger.info("10,000 log output completed: {}ms", duration);
    }
    
    // Buffering optimization
    public static void bufferingOptimization() {
        // Buffer size configuration
        Configuration.set("writer", "file");
        Configuration.set("writer.file", "logs/buffered.log");
        Configuration.set("writer.buffered", "true");
        Configuration.set("writer.buffering.size", "65536");  // 64KB buffer
        
        Logger.info("Buffering optimization configuration completed");
        
        // Buffer effect test
        long startTime = System.nanoTime();
        for (int i = 0; i < 1000; i++) {
            Logger.info("Buffering test #{}: {}", i, generateTestData());
        }
        long duration = (System.nanoTime() - startTime) / 1_000_000;
        
        Logger.info("Buffering test completed: {}ms", duration);
    }
    
    // Conditional logging optimization
    public static void conditionalLoggingOptimization() {
        // Level check optimization
        if (Logger.isTraceEnabled()) {
            String expensiveTrace = performExpensiveCalculation();
            Logger.trace("Detailed trace: {}", expensiveTrace);
        }
        
        if (Logger.isDebugEnabled()) {
            java.util.Map<String, Object> debugInfo = gatherDebugInformation();
            Logger.debug("Debug information: {}", debugInfo);
        }
        
        // Optimization without level check (tinylog auto-optimizes)
        Logger.trace("Auto-optimization: {}", performExpensiveCalculation());
        Logger.debug("Auto-optimization debug: {}", gatherDebugInformation());
    }
    
    // Memory efficiency optimization
    public static void memoryOptimization() {
        // Avoid string concatenation to reduce garbage collection load
        Logger.info("User info: ID={}, name={}, email={}", 
                   getUserId(), getUserName(), getUserEmail());
        
        // Avoid StringBuilder usage (use parameters)
        int items = 100;
        double total = 1560.50;
        Logger.info("Order summary: {} items, total ${}", items, total);
        
        // Minimize object creation
        logUserAction("LOGIN", System.currentTimeMillis(), true);
    }
    
    // Asynchronous logging configuration
    public static void asyncLoggingSetup() {
        // Asynchronous writing thread configuration
        Configuration.set("writingthread", "true");
        Configuration.set("writingthread.priority", "3");  // Lower than normal priority
        Configuration.set("writingthread.observe", "5s");   // Check every 5 seconds
        
        Logger.info("Asynchronous log configuration completed");
        
        // High throughput test
        java.util.concurrent.ExecutorService executor = 
            java.util.concurrent.Executors.newFixedThreadPool(10);
        
        for (int i = 0; i < 10; i++) {
            final int threadId = i;
            executor.submit(() -> {
                for (int j = 0; j < 1000; j++) {
                    Logger.info("Thread {} - Message {}", threadId, j);
                }
            });
        }
        
        executor.shutdown();
        try {
            if (executor.awaitTermination(30, java.util.concurrent.TimeUnit.SECONDS)) {
                Logger.info("All threads completed");
            } else {
                Logger.warn("Timeout: Some threads incomplete");
            }
        } catch (InterruptedException e) {
            Logger.error("Wait interrupted", e);
            Thread.currentThread().interrupt();
        }
    }
    
    // Custom writer optimization
    public static void customWriterOptimization() {
        // Multiple writer efficiency
        Configuration.set("writer1", "console");
        Configuration.set("writer1.level", "warn");
        Configuration.set("writer1.format", "{level}: {message}");
        
        Configuration.set("writer2", "file");
        Configuration.set("writer2.level", "debug");
        Configuration.set("writer2.file", "logs/detailed.log");
        Configuration.set("writer2.buffered", "true");
        Configuration.set("writer2.format", 
            "{date: yyyy-MM-dd HH:mm:ss.SSS} {level} {class}.{method}() - {message}");
        
        Configuration.set("writer3", "rolling file");
        Configuration.set("writer3.level", "info");
        Configuration.set("writer3.file", "logs/production-{date: yyyy-MM-dd}.log");
        Configuration.set("writer3.policies", "daily: 00:00, size: 100MB");
        Configuration.set("writer3.backups", "30");
        
        Logger.info("Multi-writer configuration completed");
    }
    
    // Helper methods
    private static String performExpensiveCalculation() {
        // Simulate expensive computation
        return "Calculation result: " + Math.random();
    }
    
    private static java.util.Map<String, Object> gatherDebugInformation() {
        java.util.Map<String, Object> info = new java.util.HashMap<>();
        info.put("memory", Runtime.getRuntime().totalMemory());
        info.put("threads", Thread.activeCount());
        info.put("timestamp", System.currentTimeMillis());
        return info;
    }
    
    private static String generateTestData() {
        return "TestData_" + System.nanoTime();
    }
    
    private static void logUserAction(String action, long timestamp, boolean success) {
        Logger.info("User action: {} at {} (success: {})", action, timestamp, success);
    }
    
    private static int getUserId() { return 12345; }
    private static String getUserName() { return "John Doe"; }
    private static String getUserEmail() { return "[email protected]"; }
}