npm package: @schafevormfenster/logging
Description: Foundational layer for observability and diagnostics - structured logging, error normalization, and content truncation
Latest version: 0.1.8

# Logging

Core logging utilities for structured logging with Pino.

## Purpose

Minimal, focused logging package providing:
- Structured logging via Pino
- Error normalization for consistent logging
- Content truncation utilities

## Installation

```bash
pnpm add @schafevormfenster/logging
```

### TypeScript Sources (Optional)

For faster development with Vite, use TypeScript sources directly:

```typescript
// vite.config.ts - Requires tsconfig "target": "ES2022"+
export default defineConfig({
  resolve: { conditions: ['source', 'import', 'default'] }
});
```

Benefits: Faster HMR, direct debugging, better tree-shaking.

## API

### getLogger(name: string): AppLogger

Creates a Pino logger instance with the given name.

```typescript
import { getLogger } from "@schafevormfenster/logging";

const logger = getLogger("my-service");

logger.info({ userId: "123" }, "User logged in");
logger.error({ error: new Error("Failed") }, "Operation failed");
logger.security({ action: "login", userId: "123" }, "Login attempt");
```

**Configuration:**
- `PINO_LOG_LEVEL` - Log level (default: "info")
  - Options: "trace", "debug", "info", "warn", "error", "fatal"

### normalizeError(error: unknown): NormalizedError

Normalizes any error value into a consistent structure for logging.

```typescript
import { normalizeError } from "@schafevormfenster/logging";

try {
  await riskyOperation();
} catch (error) {
  const normalized = normalizeError(error);
  logger.error(normalized, "Operation failed");
}
```

**Features:**
- Handles Error instances, error-like objects, strings, and primitives
- Preserves error properties (name, code, statusCode, stack)
- Traverses error cause chains (ES2022 Error.cause)
- Prevents infinite recursion in circular cause chains
- Type-safe with `NormalizedError` interface

### truncateContent(content: string, options?: TruncateOptions): string

Truncates strings to a maximum length with customizable behavior.

```typescript
import { truncateContent } from "@schafevormfenster/logging";

// Basic truncation
const short = truncateContent("Very long message...", { maxLength: 20 });
// => "Very long message..."

// Truncate in middle
const middle = truncateContent("path/to/very/long/file.txt", {
  maxLength: 20,
  position: "middle"
});
// => "path/to...file.txt"

// Custom ellipsis
const custom = truncateContent("Long text", {
  maxLength: 10,
  ellipsis: " [more]"
});
// => "Long [more]"
```

**Options:**
- `maxLength` - Maximum length (default: 100)
- `ellipsis` - Truncation indicator (default: "...")
- `breakOnWord` - Break at word boundaries (default: true)
- `position` - Where to truncate: "end", "middle", "start" (default: "end")

## Examples

### Complete logging workflow

```typescript
import { getLogger, normalizeError, truncateContent } from "@schafevormfenster/logging";

const logger = getLogger("api.users");

async function createUser(userData: unknown) {
  try {
    // Log incoming data (truncated for security)
    const preview = truncateContent(JSON.stringify(userData), { maxLength: 100 });
    logger.debug({ data: preview }, "Creating user");

    const user = await db.users.create(userData);
    logger.info({ userId: user.id }, "User created successfully");
    
    return user;
  } catch (error) {
    // Normalize error for consistent logging
    const normalized = normalizeError(error);
    logger.error(normalized, "Failed to create user");
    throw error;
  }
}
```

## Migration from Previous Versions

**Removed:**
- `measureTime()` - Use timing utilities directly in your app
- `logPerformance()` - Use timing utilities directly in your app  
- `logLevelConfig` - Use `PINO_LOG_LEVEL` environment variable
- All redaction utilities - Moved to `@schafevormfenster/security`

**Changed:**
- Package now exports from `logger.ts` instead of `index.ts`
- Imports remain the same: `import { ... } from "@schafevormfenster/logging"`