alepha@docs:~/docs/guides/observability$
cat 1-logging.md
2 min read
Last commit:

#Logging

console.log is fine for debugging. It's not fine for production.

Alepha has a structured logging system that knows where logs come from, supports log levels, and outputs pretty colors in dev or JSON in production.

#Basic Logging

Use $logger() in any service:

typescript
 1import { $logger } from "alepha/logger"; 2  3class PaymentService { 4  log = $logger(); 5  6  async processPayment(orderId: string, amount: number) { 7    this.log.info("Processing payment", { orderId, amount }); 8  9    try {10      await this.stripe.charge(amount);11      this.log.info("Payment successful", { orderId });12    } catch (error) {13      this.log.error("Payment failed", { orderId, error });14      throw error;15    }16  }17}

Output in development (pretty):

bash
[14:32:05.123] INFO <app.PaymentService> Processing payment { orderId: "ord_123", amount: 99.99 }
[14:32:05.456] INFO <app.PaymentService> Payment successful { orderId: "ord_123" }

Output in production (JSON, one line):

json
1{"level":"info","time":"2024-01-15T14:32:05.123Z","module":"app.PaymentService","msg":"Processing payment","orderId":"ord_123","amount":99.99}

JSON logs are parseable by Datadog, Grafana Loki, CloudWatch, etc.

#Log Destination

By default, logs go to stdout and are synchronous. This means every log.info() call blocks until the message is written.

For development, this is fine. You want logs immediately, and the performance hit is negligible.

For high-throughput production systems, synchronous logging can become a bottleneck. An async destination with buffering is planned for a future release. For now, if you're logging thousands of messages per second, consider:

  • Reducing log verbosity (LOG_LEVEL=warn)
  • Logging to a file and tailing it
  • Using an external log shipper (Fluentd, Vector)

#Log Levels

Alepha supports six levels (from quietest to noisiest):

typescript
1// SILENT - nothing logged2this.log.error("Something broke");           // ERROR - red3this.log.warn("Something unexpected");       // WARN - yellow4this.log.info("Normal operations");          // INFO - default visible5this.log.debug("Detailed debugging info");   // DEBUG - hidden by default6this.log.trace("Very detailed tracing");     // TRACE - extremely verbose

#Configuring Log Level

Set via LOG_LEVEL environment variable:

bash
LOG_LEVEL=debug npx alepha dev    # see debug and above
LOG_LEVEL=warn npx alepha start   # quiet production, only warnings+
LOG_LEVEL=trace npx alepha dev    # see absolutely everything
LOG_LEVEL=silent npx alepha start # complete silence

#Per-Module Log Levels

This is where Alepha shines. You can set different levels for different modules:

bash
# global info, but debug for payments module
LOG_LEVEL="info,app.payments:debug" npx alepha dev

# multiple module configs
LOG_LEVEL="alepha.core:trace,app.users:debug,warn" npx alepha dev

# use equals or colon - both work
LOG_LEVEL="app.payments=debug,info" npx alepha dev

# semicolon separator also works
LOG_LEVEL="alepha:trace;app:debug;info" npx alepha dev

#Wildcard Patterns

Need to debug an entire namespace? Use wildcards:

bash
# all alepha.* modules at debug level
LOG_LEVEL="alepha.*:debug,info" npx alepha dev

# all modules ending in .test at silent
LOG_LEVEL="*.test:silent,info" npx alepha dev

#How Matching Works

The logger uses first match wins with prefix matching:

typescript
1// config: "alepha:debug,alepha.core:trace,info"2 3// "alepha.core" matches "alepha" first → DEBUG (not TRACE!)4// order matters: put specific patterns first5 6// better: "alepha.core:trace,alepha:debug,info"7// now "alepha.core" matches first → TRACE

This is huge. You can turn on trace logging for just the one service causing problems without drowning in noise from the rest of the app.

#Context-Aware Logging

The logger automatically includes the module name. If you're using $module, logs show the full path:

typescript
1const PaymentsModule = $module({2  name: "app.payments",3  services: [PaymentService, RefundService],4});5 6// logs from PaymentService show: <app.payments.PaymentService>

This makes it trivial to filter logs in production:

bash
grep "app.payments" /var/log/myapp.log

#Request Logging

Want to log every HTTP request? Alepha does it automatically when you enable the server module. You'll see:

bash
[14:32:05.100] INFO <alepha.server> --> GET /api/users
[14:32:05.150] INFO <alepha.server> <-- GET /api/users 200 50ms

#Comparison: Winston vs Pino vs Alepha

Winston:

typescript
 1// lots of configuration 2const logger = winston.createLogger({ 3  level: 'info', 4  format: winston.format.combine( 5    winston.format.timestamp(), 6    winston.format.json() 7  ), 8  transports: [new winston.transports.Console()], 9});10 11logger.info("message", { orderId: "123" });12// doesn't know which service it came from

Pino:

typescript
1// fast, but manual setup2const logger = pino({ level: 'info' });3const childLogger = logger.child({ module: 'PaymentService' });4childLogger.info({ orderId: "123" }, "message");

Alepha:

typescript
1// zero config, automatic context2class PaymentService {3  log = $logger();  // knows it's PaymentService4 5  doThing() {6    this.log.info("message", { orderId: "123" });7    // output includes module name automatically8  }9}

#Best Practices

#Log at Boundaries

typescript
1// log when entering/exiting important operations2async processOrder(order: Order) {3  this.log.info("Processing order", { orderId: order.id });4 5  // ... lots of internal work ...6 7  this.log.info("Order processed", { orderId: order.id, duration: elapsed });8}

#Include Context

typescript
 1// bad 2this.log.error("Payment failed"); 3  4// good 5this.log.error("Payment failed", { 6  orderId, 7  amount, 8  customerId, 9  errorCode: error.code,10});

#Don't Log Secrets

typescript
1// bad - password in logs2this.log.info("User login", { email, password });3 4// good5this.log.info("User login", { email });

#Use Appropriate Levels

typescript
1this.log.trace("Entering function with args", { args });  // extreme detail2this.log.debug("Cache hit for key xyz");                  // debugging info3this.log.info("User created");                            // normal operations4this.log.warn("Rate limit approaching");                  // concerning but not broken5this.log.error("Database connection lost");               // something broke

#Summary

Need Solution
Basic logging $logger()
JSON logs for production LOG_FORMAT=json (default in prod)
Debug specific module LOG_LEVEL="module.name:debug,info"
Debug with wildcards LOG_LEVEL="app.*:debug,info"

Logging is unglamorous but essential. Alepha makes it structured, filterable, and production-ready out of the box.

On This Page
No headings found...
ready
mainTypeScript
UTF-8guides_observability_logging.md