> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Category Logging

> Log category system and hierarchy

LogTape structures logs with a category system. Sonamu automatically generates categories for Model, Frame, Workflow, and Agent classes.

## What is a Category?

A category represents the source of logs in a hierarchical structure.

**Format**: `["a", "b", "c"]` array

**Examples:**

* `["fastify"]` - Fastify framework
* `["sonamu", "model", "user-model"]` - UserModel class
* `["sonamu", "workflow", "email-send"]` - EmailSendWorkflow
* `["app", "payment", "processor"]` - Custom category

### Purpose of the Category System

Categories are needed to systematically manage logs.

**1. Log Source Identification**

```typescript theme={null}
// Where did the log originate?
["fastify"][("sonamu", "model")][("app", "payment")]; // HTTP request // Model layer // Payment logic
```

**2. Selective Logging**

```typescript theme={null}
// Log only specific parts
loggers: [
  {
    category: ["sonamu", "model"], // Only Model at debug level
    lowestLevel: "debug",
  },
  {
    category: ["fastify"], // Fastify at info level
    lowestLevel: "info",
  },
];
```

**3. Separate Log Storage**

```typescript theme={null}
// Different files for different parts
loggers: [
  {
    category: ["sonamu", "model"],
    sinks: ["modelLog"], // models.log
  },
  {
    category: ["app", "payment"],
    sinks: ["paymentLog"], // payment.log
  },
];
```

### Benefits of Hierarchical Structure

Why categories are hierarchical:

**1. Clear Structure**

```typescript theme={null}
["sonamu", "model", "user-model"];
//  ↓        ↓        ↓
//  framework  type    specific name
```

You can see at a glance where a log originated

**2. Flexible Filtering**

```typescript theme={null}
// All Sonamu logs
category: ["sonamu"];

// Only Model layer
category: ["sonamu", "model"];

// Only UserModel
category: ["sonamu", "model", "user-model"];
```

**3. Collision Prevention**

```typescript theme={null}
// Can distinguish "user" logs from different projects
["sonamu", "model", "user-model"][("app", "user", "service")][("external", "api", "user")]; // Sonamu's user // Application's user // External API's user
```

### Why Only Exact Matching?

LogTape only matches categories that **exactly match**.

```typescript theme={null}
// ❌ Partial matching not supported
category: ["sonamu"];
// → Logs from ["sonamu", "model", "user-model"] won't match

// ✅ Must match exactly
category: ["sonamu", "model", "user-model"];
// → Only matches ["sonamu", "model", "user-model"] logs
```

**Reasons:**

1. **Clarity**

```typescript theme={null}
// If partial matching was allowed?
category: ["sonamu"];
// → ["sonamu", "model", "user-model"]
// → ["sonamu", "workflow", "email-send"]
// → ["sonamu", "agent", "payment-agent"]
// → Too many logs would match!

// Exact matching
category: ["sonamu", "model", "user-model"];
// → Matches exactly the logs you want
```

2. **Performance**

```typescript theme={null}
// If wildcard matching was allowed?
category: ["sonamu", "*"]; // All sonamu subcategories
// → Pattern matching required every time (slow)

// Exact matching
category: ["sonamu", "model"];
// → Simple string comparison (fast)
```

3. **Predictability**

```typescript theme={null}
// Confusion with partial matching
loggers: [
  { category: ["sonamu"], lowestLevel: "info" },
  { category: ["sonamu", "model"], lowestLevel: "debug" },
];
// → Where would ["sonamu", "model", "user-model"] logs match?

// Clear with exact matching
loggers: [{ category: ["sonamu", "model"], lowestLevel: "debug" }];
// → Only matches ["sonamu", "model"], very clear!
```

## Category Structure Details

### Hierarchy Representation

Categories narrow down progressively.

```typescript theme={null}
["sonamu", "model", "user-model"];
//  ↓        ↓        ↓
//  namespace  type    specific name
```

**Configuration example:**

```typescript theme={null}
export default defineConfig({
  logging: {
    loggers: [
      // All logs starting with "sonamu"
      {
        category: ["sonamu"],
        sinks: ["console"],
        lowestLevel: "info",
      },

      // Model only
      {
        category: ["sonamu", "model"],
        sinks: ["modelLog"],
        lowestLevel: "debug",
      },

      // UserModel only
      {
        category: ["sonamu", "model", "user-model"],
        sinks: ["userLog"],
        lowestLevel: "debug",
      },
    ],
  },

  server: {
    // ...
  },
});
```

### Category Matching

LogTape only matches categories that **exactly match**.

```typescript theme={null}
// ❌ Partial matching not supported
category: ["sonamu"];
// → Logs from ["sonamu", "model", "user-model"] won't match

// ✅ Must match exactly
category: ["sonamu", "model", "user-model"];
// → Only matches ["sonamu", "model", "user-model"] logs
```

## Automatic Category Generation

Sonamu automatically generates categories for specific classes.

### Model Classes

```typescript theme={null}
// UserModelClass → ["sonamu", "model", "user-model"]
// PostModelClass → ["sonamu", "model", "post-model"]
// OrderItemModelClass → ["sonamu", "model", "order-item-model"]
```

**Conversion rules:**

1. Remove `ModelClass` from class name
2. Convert PascalCase → snake\_case
3. Convert snake\_case → kebab-case

**Examples:**

```
UserModelClass
→ User (remove ModelClass)
→ user (snake_case)
→ ["sonamu", "model", "user"] (kebab-case, add prefix)

OrderItemModelClass
→ OrderItem
→ order_item
→ ["sonamu", "model", "order-item"]
```

### Frame Classes

```typescript theme={null}
// UserFrameClass → ["sonamu", "frame", "user-frame"]
// AdminUserFrameClass → ["sonamu", "frame", "admin-user-frame"]
```

**Conversion rules**: Same as Model but removes `FrameClass`

### Workflow

```typescript theme={null}
// EmailSendWorkflow → ["sonamu", "workflow", "email-send"]
// OrderProcessWorkflow → ["sonamu", "workflow", "order-process"]
```

**Conversion rules:**

1. Remove `Workflow` from class name (suffix only)
2. Convert PascalCase → snake\_case
3. Convert snake\_case → kebab-case

**Examples:**

```
EmailSendWorkflow
→ EmailSend (remove Workflow)
→ email_send (snake_case)
→ ["sonamu", "workflow", "email-send"] (kebab-case, add prefix)
```

### Agent Classes

```typescript theme={null}
// PaymentAgentClass → ["sonamu", "agent", "payment-agent"]
// AdminNotificationAgentClass → ["sonamu", "agent", "admin-notification-agent"]
```

**Conversion rules**: Same as Model but removes `AgentClass`

## Naite Categories

Naite test keys follow special category conversion rules.

```typescript theme={null}
// "user.findOne" → ["user", "findOne"]
// "user:list" → ["user", "list"]
// "admin.company:detail" → ["admin", "company", "detail"]
```

**Conversion rules:**

1. Split by `.`
2. Split each part by `:`
3. Flatten

**Examples:**

```
"user.findOne"
→ ["user", "findOne"]

"admin.company:detail"
→ split by "." → ["admin", "company:detail"]
→ split by ":" → [["admin"], ["company", "detail"]]
→ flatten → ["admin", "company", "detail"]
```

## Category-Based Logging Configuration

### Separate Logs by Model

```typescript theme={null}
import { defineConfig } from "sonamu";
import { getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      userLog: getFileSink("logs/user-model.log"),
      postLog: getFileSink("logs/post-model.log"),
    },

    loggers: [
      {
        category: ["sonamu", "model", "user-model"],
        sinks: ["userLog"],
        lowestLevel: "debug",
      },
      {
        category: ["sonamu", "model", "post-model"],
        sinks: ["postLog"],
        lowestLevel: "debug",
      },
    ],
  },

  server: {
    // ...
  },
});
```

### Adjust Log Levels by Type

```typescript theme={null}
export default defineConfig({
  logging: {
    loggers: [
      // Model: debug level
      {
        category: ["sonamu", "model"],
        sinks: ["console"],
        lowestLevel: "debug",
      },

      // Workflow: info level
      {
        category: ["sonamu", "workflow"],
        sinks: ["console"],
        lowestLevel: "info",
      },

      // Agent: warning level
      {
        category: ["sonamu", "agent"],
        sinks: ["console"],
        lowestLevel: "warning",
      },
    ],
  },

  server: {
    // ...
  },
});
```

### Management by Namespace

```typescript theme={null}
export default defineConfig({
  logging: {
    sinks: {
      sonamuLog: getFileSink("logs/sonamu.log"),
      appLog: getFileSink("logs/app.log"),
    },

    loggers: [
      // Sonamu internal
      {
        category: ["sonamu"],
        sinks: ["sonamuLog"],
        lowestLevel: "info",
      },

      // Application
      {
        category: ["app"],
        sinks: ["appLog"],
        lowestLevel: "debug",
      },
    ],
  },

  server: {
    // ...
  },
});
```

## Custom Categories

You can use custom categories in application code.

```typescript theme={null}
import { getLogger } from "@logtape/logtape";

// Create logger
const logger = getLogger(["app", "payment", "processor"]);

// Logging
logger.info("Processing payment", { orderId: 123 });
logger.error("Payment failed", { error: "timeout" });
```

**Configuration:**

```typescript theme={null}
export default defineConfig({
  logging: {
    sinks: {
      paymentLog: getFileSink("logs/payment.log"),
    },

    loggers: [
      {
        category: ["app", "payment", "processor"],
        sinks: ["paymentLog"],
        lowestLevel: "info",
      },
    ],
  },

  server: {
    // ...
  },
});
```

## Practical Examples

### Development Environment Detailed Logging

```typescript theme={null}
import { defineConfig } from "sonamu";
import { getConsoleSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
    },

    loggers: [
      // Fastify: info
      {
        category: ["fastify"],
        sinks: ["console"],
        lowestLevel: "info",
      },

      // All Sonamu internal: debug
      {
        category: ["sonamu"],
        sinks: ["console"],
        lowestLevel: "debug",
      },

      // App logic: debug
      {
        category: ["app"],
        sinks: ["console"],
        lowestLevel: "debug",
      },
    ],
  },

  server: {
    listen: { port: 34900 },
  },
});
```

### Production Selective Logging

```typescript theme={null}
import { defineConfig } from "sonamu";
import { getConsoleSink, getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      console: getConsoleSink(),
      workflowLog: getFileSink("logs/workflow.log"),
      errorLog: getFileSink("logs/error.log"),
    },

    loggers: [
      // Fastify: warning and above
      {
        category: ["fastify"],
        sinks: ["console"],
        lowestLevel: "warning",
      },

      // Workflow: full recording
      {
        category: ["sonamu", "workflow"],
        sinks: ["workflowLog"],
        lowestLevel: "info",
      },

      // Errors only to separate file
      {
        category: ["sonamu"],
        sinks: ["errorLog"],
        lowestLevel: "error",
      },
    ],
  },

  server: {
    listen: { port: 34900 },
  },
});
```

### Log Separation by Domain

```typescript theme={null}
import { defineConfig } from "sonamu";
import { getFileSink } from "@logtape/logtape";

export default defineConfig({
  logging: {
    sinks: {
      userDomain: getFileSink("logs/user-domain.log"),
      orderDomain: getFileSink("logs/order-domain.log"),
      paymentDomain: getFileSink("logs/payment-domain.log"),
    },

    loggers: [
      // User related
      {
        category: ["sonamu", "model", "user-model"],
        sinks: ["userDomain"],
        lowestLevel: "debug",
      },
      {
        category: ["app", "user"],
        sinks: ["userDomain"],
        lowestLevel: "debug",
      },

      // Order related
      {
        category: ["sonamu", "model", "order-model"],
        sinks: ["orderDomain"],
        lowestLevel: "debug",
      },
      {
        category: ["app", "order"],
        sinks: ["orderDomain"],
        lowestLevel: "debug",
      },

      // Payment related
      {
        category: ["app", "payment"],
        sinks: ["paymentDomain"],
        lowestLevel: "debug",
      },
    ],
  },

  server: {
    listen: { port: 34900 },
  },
});
```

## Internal Operation Principles

<Note>
  This section explains how Sonamu's category system works internally. The functions shown here are
  used automatically by Sonamu and cannot be called directly by users.
</Note>

### isSameCategory()

An internal function that checks if two categories match exactly.

```typescript theme={null}
// Sonamu internal function (not directly usable)
isSameCategory(["sonamu", "model"], ["sonamu", "model"]);
// → true

isSameCategory(["sonamu", "model"], ["sonamu", "workflow"]);
// → false

isSameCategory(["sonamu"], ["sonamu", "model"]);
// → false (different lengths)
```

**Internal use:** Used when matching categories in LogTape's logger configuration.

### convertDomainToCategory()

An internal function that converts class names to categories.

```typescript theme={null}
// Sonamu internal function (not directly usable)
convertDomainToCategory("UserModelClass", "model");
// → ["sonamu", "model", "user-model"]

convertDomainToCategory("EmailSendWorkflow", "workflow");
// → ["sonamu", "workflow", "email-send"]

convertDomainToCategory("PaymentAgentClass", "agent");
// → ["sonamu", "agent", "payment-agent"]
```

**Internal use:** Automatically assigns categories when Model, Frame, Workflow, and Agent classes are created.

**Conversion logic:**

1. Remove suffix from class name (`ModelClass`, `FrameClass`, `AgentClass`, `Workflow`)
2. Convert PascalCase → snake\_case
3. Convert snake\*case → kebab-case (`*`→`-`)
4. Compose as `["sonamu", type, name]` format

### convertNaiteKeyToCategory()

An internal function that converts Naite test keys to categories.

```typescript theme={null}
// Sonamu internal function (not directly usable)
convertNaiteKeyToCategory("user.findOne");
// → ["user", "findOne"]

convertNaiteKeyToCategory("admin.company:detail");
// → ["admin", "company", "detail"]
```

**Internal use:** Automatically generates log categories when running Naite tests.

**Conversion logic:**

1. Split by `.`
2. Split each part by `:`
3. Flatten

## Important Notes

### 1. Only Exact Matching Supported

```typescript theme={null}
// ❌ No wildcards
category: ["sonamu", "*"]; // Won't work

// ❌ No partial matching
category: ["sonamu"]; // Won't match ["sonamu", "model"]

// ✅ Exact categories only
category: ["sonamu", "model", "user-model"];
```

### 2. Categories are readonly Arrays

```typescript theme={null}
// ✅ Use readonly arrays
const category: readonly string[] = ["app", "payment"];

// ❌ Regular arrays may cause type errors
const category: string[] = ["app", "payment"];
```

### 3. Class Naming Conventions

```typescript theme={null}
// ✅ Correct names
class UserModelClass {} // → ["sonamu", "model", "user-model"]
class OrderItemModelClass {} // → ["sonamu", "model", "order-item-model"]
class EmailSendWorkflow {} // → ["sonamu", "workflow", "email-send"]

// ❌ Convention violations (won't auto-convert)
class User {} // Not a Model
class UserModel {} // Missing "Class" suffix
class WorkflowEmailSend {} // "Workflow" as prefix (should be suffix)
```

### 4. Fastify Category Duplication

```typescript theme={null}
// ❌ Conflict with Fastify category
fastifyCategory: ["sonamu", "model"];

loggers: [
  {
    category: ["sonamu", "model"], // Mixed with Fastify logs!
    // ...
  },
];

// ✅ Separate categories
fastifyCategory: ["fastify"];

loggers: [
  {
    category: ["sonamu", "model"],
    // ...
  },
];
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Fastify Logging" icon="bolt" href="/en/configuration/logging/fastify-logging">
    Customize HTTP request/response logging
  </Card>

  <Card title="Sinks & Filters" icon="filter" href="/en/configuration/logging/sinks-and-filters">
    Control log output with custom Sinks and Filters
  </Card>

  <Card title="LogTape Setup" icon="gear" href="/en/configuration/logging/logtape-setup">
    Learn basic logging configuration and concepts
  </Card>

  <Card title="LogTape Official Documentation" icon="book-open" href="https://github.com/dahlia/logtape">
    Check LogTape's advanced features and APIs
  </Card>
</CardGroup>
