Pino

High-performance structured logging with Pino

Logixlysia can integrate with Pino, a high-performance JSON-first logging library, as an optional logging backend.

Why Pino?

  • High Performance - One of the fastest Node.js loggers available
  • Structured Logging - JSON-first approach for better log parsing
  • Production Ready - Battle-tested in production environments
  • Flexible - Extensive configuration options and transport support

Basic Usage

In Elysia Routes

Access Pino through the store in your route handlers:

app.get('/users/:id', ({ store, params }) => {
  const { pino } = store
  
  pino.info({
    userId: params.id,
    action: 'view_profile'
  }, 'User profile accessed')
  
  return { user: 'data' }
})

Outside Elysia Routes (Standalone)

Use Pino in services, background jobs, or any code outside of HTTP request context:

// logger.ts
import logixlysia from 'logixlysia'

export const logixlysiaIns = logixlysia({
  config: {
    logFilePath: './logs/app.log',
    pino: {
      level: 'info',
      base: { 
        service: 'my-api',
        version: '1.0.0'
      }
    }
  }
})

// Export Pino instance for standalone use
export const logger = logixlysiaIns.store.pino
// services/order.service.ts
import { logger } from '../logger'

export class OrderService {
  async processOrder(orderId: string) {
    logger.info({ orderId }, 'Processing order')
    
    try {
      // ... business logic
      logger.info({ orderId }, 'Order processed successfully')
    } catch (error) {
      logger.error({ orderId, error }, 'Order processing failed')
      throw error
    }
  }
}
// index.ts
import { Elysia } from 'elysia'
import { logixlysiaIns } from './logger'
import { OrderService } from './services/order.service'

const orderService = new OrderService()

new Elysia()
  .use(logixlysiaIns)
  .post('/orders/:id/process', async ({ params }) => {
    await orderService.processOrder(params.id)
    return { success: true }
  })
  .listen(3000)

Note: When using Pino outside of routes, you won't have access to HTTP context (method, pathname, IP, etc.). For HTTP request logging, use store.logger inside route handlers.

Configuration

Log Level

logixlysia({
  config: {
    pino: {
      level: 'debug' // 'fatal', 'error', 'warn', 'info', 'debug', 'trace'
    }
  }
})

Pretty Printing

logixlysia({
  config: {
    pino: {
      prettyPrint: true
    }
  }
})

Or with custom options:

logixlysia({
  config: {
    pino: {
      prettyPrint: {
        colorize: true,
        translateTime: 'HH:MM:ss Z',
        ignore: 'pid,hostname'
      }
    }
  }
})

Redacting Sensitive Data

logixlysia({
  config: {
    pino: {
      redact: ['password', 'token', 'apiKey']
    }
  }
})

Or with paths:

logixlysia({
  config: {
    pino: {
      redact: {
        paths: ['user.password', 'req.headers.authorization'],
        remove: true
      }
    }
  }
})

Base Fields

logixlysia({
  config: {
    pino: {
      base: {
        service: 'my-api',
        version: '1.0.0',
        environment: process.env.NODE_ENV
      }
    }
  }
})

Pino Transports

Pino supports its own transport system for advanced logging destinations. You can configure Pino transports using the transport option:

logixlysia({
  config: {
    pino: {
      transport: {
        target: 'pino/file',
        options: {
          destination: './logs/pino-example.log'
        }
      }
    }
  }
})

Important: Pino transports are separate from Logixlysia's transport system:

  • Pino transports (pino.transport) - Only affect logs written through store.pino.info(), store.pino.error(), etc.
  • Logixlysia transports (config.transports) - Only affect Logixlysia's internal HTTP request logging

For example:

logixlysia({
  config: {
    // Pino transport - only for store.pino.* logs
    pino: {
      transport: {
        target: 'pino/file',
        options: {
          destination: './logs/pino-example.log'
        }
      }
    },
    // Logixlysia transports - only for HTTP request logs
    transports: [customTransport]
  }
})

app.get('/example', ({ store }) => {
  // This log goes to pino/file transport
  store.pino.info({ feature: 'pino' }, 'pino log example')
  
  // HTTP request logs go to Logixlysia transports (if configured)
  // or to console/file based on logixlysia config
})

This separation allows you to:

  • Use Pino transports for structured application logs (store.pino.*)
  • Use Logixlysia transports for HTTP request/response logs
  • Configure different destinations for different log types

Advanced Usage

Structured Logging

app.get('/users/:id', ({ store, params }) => {
  const { pino } = store
  
  pino.info({
    userId: params.id,
    action: 'view_profile',
    timestamp: Date.now()
  }, 'User profile viewed')
  
  return user
})

Child Loggers

Create child loggers with context:

app.get('/api/orders/:id', ({ store, params }) => {
  const { pino } = store
  
  const orderLogger = pino.child({ 
    orderId: params.id,
    module: 'order-service' 
  })
  
  orderLogger.info('Fetching order details')
  orderLogger.debug({ query: 'SELECT * FROM orders WHERE id = ?' })
  
  return order
})

Performance Logging

app.post('/api/process', async ({ store, body }) => {
  const { pino } = store
  const startTime = Date.now()
  
  const result = await processData(body)
  
  pino.info({
    operation: 'process_data',
    duration: Date.now() - startTime,
    itemsProcessed: result.count,
    success: true
  }, 'Data processing completed')
  
  return result
})

Error Logging

app.get('/api/risky', ({ store }) => {
  const { pino } = store
  
  try {
    performRiskyOperation()
  } catch (error) {
    pino.error({
      err: error,
      operation: 'risky_operation'
    }, 'Operation failed')
    
    throw error
  }
})

Environment-based Configuration

const isDev = process.env.NODE_ENV === 'development'

logixlysia({
  config: {
    pino: {
      level: isDev ? 'debug' : 'info',
      prettyPrint: isDev,
      redact: isDev ? [] : ['password', 'token', 'apiKey'],
      base: {
        service: 'my-api',
        environment: process.env.NODE_ENV
      }
    }
  }
})

Best Practices

  1. Use Structured Logging - Pass objects instead of string interpolation

    // ✅ Good
    pino.info({ userId: 123, action: 'login' }, 'User logged in')
    
    // ❌ Avoid
    pino.info(`User ${userId} logged in`)
  2. Create Child Loggers - For scoped logging with context

    const requestLogger = pino.child({ requestId: generateId() })
  3. Log Performance Metrics - Track important metrics

    pino.info({ duration: 150, operation: 'db_query' }, 'Query completed')
  4. Redact Sensitive Data - Always redact sensitive information

    logixlysia({
      config: {
        pino: {
          redact: ['password', 'token', 'apiKey']
        }
      }
    })
  5. Use Appropriate Log Levels - Follow log level guidelines

    • fatal - Application is about to crash
    • error - Error that needs attention
    • warn - Warning but application continues
    • info - General information (default)
    • debug - Detailed debugging information
    • trace - Very detailed debugging
  6. Environment-based Configuration - Configure differently per environment

    const isDev = process.env.NODE_ENV === 'development'
    
    logixlysia({
      config: {
        pino: {
          level: isDev ? 'debug' : 'info',
          prettyPrint: isDev
        }
      }
    })
  7. Export Pino for Standalone Use - Use in services, jobs, and utilities

    // logger.ts
    export const logger = logixlysiaIns.store.pino
    
    // services/user.service.ts
    import { logger } from '../logger'
    logger.info({ userId }, 'User updated')

Additional Resources

On this page