# Logging Practices Enforced in the Akash Console Project: A Complete Guide

> Explore the Akash Console project's enforced logging practices. Discover how it uses a centralized LoggerService wrapping pino, forbidding console.* methods for structured, context-aware logs.

- Repository: [Akash Network/console](https://github.com/akash-network/console)
- Tags: how-to-guide
- Published: 2026-02-24

---

**The Akash Console project mandates structured logging through a centralized `LoggerService` that wraps pino, explicitly prohibiting all native `console.*` methods in favor of configurable, context-aware log objects that require an `event` field.**

The `akash-network/console` repository enforces strict logging practices to ensure observability across its distributed deployment infrastructure. These practices are codified in repository guidelines and implemented through a custom logging package that standardizes output format, log levels, and contextual metadata injection. Understanding these enforcement mechanisms is essential for contributing to the codebase or deploying Console services in production environments.

## Centralized LoggerService Architecture

The foundation of Akash Console logging is the **`LoggerService`** class defined in [`packages/logging/src/services/logger/logger.service.ts`](https://github.com/akash-network/console/blob/main/packages/logging/src/services/logger/logger.service.ts). This service wraps a **pino** instance and acts as the single sanctioned abstraction for all logging operations across the monorepo.

### Pino-Based Implementation

`LoggerService` initializes its underlying pino logger by reading global configuration set during application bootstrap. The initialization logic (lines 75–77) retrieves the log level from the `LOG_LEVEL` environment variable, defaulting to `"info"`:

```typescript
private initPino(): pino.Logger {
  const options: pino.LoggerOptions = {
    level: LoggerService.config.LOG_LEVEL,
    mixin: LoggerService.mixin,
    // …
  };
  // …
}

```

The service exposes standard severity methods—`log()`, `info()`, `error()`, `warn()`, `debug()`, and `fatal()`—that accept either structured objects or `Error` instances. When receiving an error, the implementation automatically serializes it using the internal `logError` function (lines 176–197), enriching HTTP errors, SQL errors, and generic exceptions with stack traces and uniform metadata shapes.

### Context Binding with forContext()

Every log line can carry a **`context`** field identifying the subsystem or component generating the message. The static `forContext()` method creates a logger instance with pre-bound context:

```typescript
const dbLogger = LoggerService.forContext("Database");
dbLogger.debug({ event: "QUERY_EXECUTED", sql: "SELECT * FROM users" });

```

This implementation (lines 49–54 in [`logger.service.ts`](https://github.com/akash-network/console/blob/main/logger.service.ts)) attaches the context string to pino's bindings, ensuring every subsequent log entry includes the `"context": "Database"` field without manual repetition.

## Configuration and Environment Variables

Logging behavior is controlled via environment variables defined in [`packages/logging/src/config/env.config.ts`](https://github.com/akash-network/console/blob/main/packages/logging/src/config/env.config.ts). The schema validates two critical settings:

```typescript
LOG_LEVEL: z.enum(["fatal","error","warn","info","debug","trace"]).default("info"),
STD_OUT_LOG_FORMAT: z.enum(["json","pretty"]).default("json")

```

**`LOG_LEVEL`** determines the minimum severity emitted, while **`STD_OUT_LOG_FORMAT`** toggles between machine-readable JSON (ideal for aggregation pipelines) and human-readable pretty-printing. When `"pretty"` is selected in a Node.js environment, `LoggerService` attempts to dynamically import `pino-pretty` (lines 102–116), falling back silently to JSON if the module is unavailable.

## Structured Logging Requirements

Akash Console enforces that log messages be **structured objects** rather than primitive strings. While string messages are technically accepted, the repository guideline explicitly discourages them.

### The Event Field Convention

The definitive logging guideline located at [`.claude/instructions/use-logger-service-instead-of-console.md`](https://github.com/akash-network/console/blob/main/.claude/instructions/use-logger-service-instead-of-console.md) requires logs to include an **`event`** field. This field acts as a machine-readable identifier for filtering and alerting:

```typescript
// ✅ Correct usage
this.logger.info({ event: "DEPLOYMENT_CREATED", deploymentId: id });

// ❌ Incorrect usage
console.log("Deployment created", id);

```

The guideline marks raw `console.*` usage as **bad** (lines 28–38), while the `LoggerService` pattern with explicit event fields is marked as **good**.

### Error Serialization

When logging errors, the service normalizes different error types into a consistent structure. The `error()` method checks if the payload is an `Error` instance and wraps it accordingly:

```typescript
error(message: LogMessage): void {
  if (message && message instanceof Error) {
    this.pino.error({ err: message });
  } else {
    this.pino.error(message);
  }
}

```

This ensures that stack traces, error codes, and messages appear in predictable JSON paths regardless of whether the error originated from HTTP requests, database queries, or application logic.

## Dependency Injection Pattern

Services throughout the monorepo obtain loggers through constructor injection rather than instantiation. The provider files (e.g., [`apps/tx-signer/src/providers/logging.provider.ts`](https://github.com/akash-network/console/blob/main/apps/tx-signer/src/providers/logging.provider.ts)) re-export `LoggerService`, enabling the dependency injection container to manage singleton instances:

```typescript
@singleton()
export class DeploymentService {
  constructor(private readonly logger: LoggerService) {}

  async create() {
    try {
      // …deployment logic
      this.logger.info({ event: "DEPLOYMENT_CREATED", deploymentId: id });
    } catch (error) {
      this.logger.error({ event: "DEPLOYMENT_FAILED", error });
      throw error;
    }
  }
}

```

This pattern guarantees that the same configuration and mixins apply across all services within a request scope.

## Extensibility via Mixins

The logging architecture supports **mixins**—functions that inject additional fields into every log line. This enables OpenTelemetry trace correlation and Sentry integration without polluting application code.

### OTEL and Trace Data Injection

The `createOtelLogger` utility in [`packages/logging/src/utils/create-otel-logger/create-otel-logger.ts`](https://github.com/akash-network/console/blob/main/packages/logging/src/utils/create-otel-logger/create-otel-logger.ts) demonstrates mixing trace IDs into logs. Similarly, the web application logger in [`apps/stats-web/src/lib/createLogger/createLogger.ts`](https://github.com/akash-network/console/blob/main/apps/stats-web/src/lib/createLogger/createLogger.ts) merges Sentry baggage:

```typescript
return new LoggerService({
  ...options,
  mixin: () => {
    const traceData = getTraceData();
    return { traceId: traceData["sentry-trace"], baggage: traceData.baggage };
  }
});

```

When using this logger, every log line automatically carries distributed tracing context required for correlating logs across microservices.

## Enforcement Mechanisms

The prohibition against raw `console.*` methods is not merely conventional—it is codified in the repository's instruction set. The file [`.claude/instructions/use-logger-service-instead-of-console.md`](https://github.com/akash-network/console/blob/main/.claude/instructions/use-logger-service-instead-of-console.md) serves as a linting guideline and code review checklist, explicitly banning `console.log`, `console.warn`, `console.error`, and `console.info`.

Violations are caught during code review by searching for these patterns, and the guideline document provides concrete **good** and **bad** examples to clarify expectations. All new code must import `LoggerService` from either `@akashnetwork/logging` or `@src/core` depending on the package location within the monorepo.

## Summary

- **All logging must use `LoggerService`** from `@akashnetwork/logging` or `@src/core`; native `console.*` methods are explicitly prohibited per repository guidelines.
- **Logs must be structured objects** containing an `event` field for filtering, with optional `context`, `error`, and custom metadata fields.
- **Configuration is environment-driven** via `LOG_LEVEL` (severity threshold) and `STD_OUT_LOG_FORMAT` (JSON vs. pretty-print).
- **Contextual logging** is achieved through `LoggerService.forContext()` or constructor injection, automatically attaching component identifiers to every line.
- **Extensibility** comes through mixin functions that inject OpenTelemetry trace data or Sentry baggage without manual field passing.
- **Error handling** is standardized through the internal `logError` serializer, ensuring uniform error shapes across HTTP, SQL, and application exceptions.

## Frequently Asked Questions

### What happens if I use console.log in the Akash Console codebase?

Using `console.log`, `console.error`, or any native console method violates the repository guideline defined in [`.claude/instructions/use-logger-service-instead-of-console.md`](https://github.com/akash-network/console/blob/main/.claude/instructions/use-logger-service-instead-of-console.md). Code containing these calls will fail code review and must be refactored to use `LoggerService` with a structured object containing an `event` field.

### How do I configure the log level for different environments?

Set the `LOG_LEVEL` environment variable to one of the supported values: `"fatal"`, `"error"`, `"warn"`, `"info"`, `"debug"`, or `"trace"`. This is validated in [`packages/logging/src/config/env.config.ts`](https://github.com/akash-network/console/blob/main/packages/logging/src/config/env.config.ts) and applied when `LoggerService` initializes its pino instance. The default is `"info"` if the variable is unset.

### Can I add custom fields to every log line automatically?

Yes, supply a `mixin` function during `LoggerService` configuration or instantiation. This function should return an object containing the fields you want appended to all logs. The `createOtelLogger` and `createLogger` utilities demonstrate this pattern for injecting trace IDs and Sentry baggage.

### Is pretty-printed output available for local development?

Yes, set `STD_OUT_LOG_FORMAT=pretty` in your environment. When running in Node.js, `LoggerService` will attempt to load `pino-pretty` for human-readable output. If the module is not installed, it falls back to JSON format without crashing the application.