---
name: logging-helper
description: This skill should be used when working with Angular components, directives, services, pipes, guards, or TypeScript classes. Logging is MANDATORY in all Angular files. Implements @isa/core/logging with logger() factory pattern, appropriate log levels, lazy evaluation for performance, error handling, and avoids console.log and common mistakes.
---

# Logging Helper Skill

Ensures consistent and efficient logging using `@isa/core/logging` library.

## When to Use

- Adding logging to new components/services
- Refactoring existing logging code
- Reviewing code for proper logging patterns
- Debugging logging issues

## Core Principles

### 1. Always Use Factory Pattern

```typescript
import { logger } from '@isa/core/logging';

// ✅ DO
#logger = logger();

// ❌ DON'T
constructor(private loggingService: LoggingService) {}
```

### 2. Choose Appropriate Log Levels

- **Trace**: Fine-grained debugging (method entry/exit)
- **Debug**: Development debugging (variable states)
- **Info**: Runtime information (user actions, events)
- **Warn**: Potentially harmful situations
- **Error**: Errors affecting functionality

### 3. Context Patterns

**Static Context** (component level):
```typescript
#logger = logger({ component: 'UserProfileComponent' });
```

**Dynamic Context** (instance level):
```typescript
#logger = logger(() => ({
  userId: this.authService.currentUserId,
  storeId: this.config.storeId
}));
```

**Message Context** (use functions for performance):
```typescript
// ✅ Recommended - lazy evaluation
this.#logger.info('Order processed', () => ({
  orderId: order.id,
  total: order.total,
  timestamp: Date.now()
}));

// ✅ Acceptable - static values
this.#logger.info('Order processed', {
  orderId: order.id,
  status: 'completed'
});
```

## Essential Patterns

### Component Logging
```typescript
@Component({
  selector: 'app-product-list',
  standalone: true,
})
export class ProductListComponent {
  #logger = logger({ component: 'ProductListComponent' });

  ngOnInit(): void {
    this.#logger.info('Component initialized');
  }

  onAction(id: string): void {
    this.#logger.debug('Action triggered', { id });
  }
}
```

### Service Logging
```typescript
@Injectable({ providedIn: 'root' })
export class DataService {
  #logger = logger({ service: 'DataService' });

  fetchData(endpoint: string): Observable<Data> {
    this.#logger.debug('Fetching data', { endpoint });

    return this.http.get<Data>(endpoint).pipe(
      tap((data) => this.#logger.info('Data fetched', () => ({
        endpoint,
        size: data.length
      }))),
      catchError((error) => {
        this.#logger.error('Fetch failed', error, () => ({
          endpoint,
          status: error.status
        }));
        return throwError(() => error);
      })
    );
  }
}
```

### Error Handling
```typescript
try {
  await this.processOrder(orderId);
} catch (error) {
  this.#logger.error('Order processing failed', error as Error, () => ({
    orderId,
    step: this.currentStep,
    attemptNumber: this.retryCount
  }));
  throw error;
}
```

### Hierarchical Context
```typescript
@Component({
  providers: [
    provideLoggerContext({ feature: 'checkout', module: 'sales' })
  ]
})
export class CheckoutComponent {
  #logger = logger(() => ({ userId: this.userService.currentUserId }));

  // Logs include: feature, module, userId + message context
}
```

## Common Mistakes to Avoid

```typescript
// ❌ Don't use console.log
console.log('User logged in');
// ✅ Use logger
this.#logger.info('User logged in');

// ❌ Don't create expensive context eagerly
this.#logger.debug('Processing', {
  data: this.computeExpensive() // Always executes
});
// ✅ Use function for lazy evaluation
this.#logger.debug('Processing', () => ({
  data: this.computeExpensive() // Only if debug enabled
}));

// ❌ Don't log in tight loops
for (const item of items) {
  this.#logger.debug('Item', { item });
}
// ✅ Log aggregates
this.#logger.debug('Batch processed', () => ({
  count: items.length
}));

// ❌ Don't log sensitive data
this.#logger.info('User auth', { password: user.password });
// ✅ Log safe identifiers only
this.#logger.info('User auth', { userId: user.id });

// ❌ Don't miss error object
this.#logger.error('Failed');
// ✅ Include error object
this.#logger.error('Failed', error as Error);
```

## Configuration

### App Configuration
```typescript
// app.config.ts
import { ApplicationConfig, isDevMode } from '@angular/core';
import {
  provideLogging, withLogLevel, withSink,
  LogLevel, ConsoleLogSink
} from '@isa/core/logging';

export const appConfig: ApplicationConfig = {
  providers: [
    provideLogging(
      withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
      withSink(ConsoleLogSink),
      withContext({ app: 'ISA', version: '1.0.0' })
    )
  ]
};
```

## Testing

```typescript
import { createComponentFactory, Spectator } from '@ngneat/spectator/jest';
import { LoggingService } from '@isa/core/logging';

describe('MyComponent', () => {
  const createComponent = createComponentFactory({
    component: MyComponent,
    mocks: [LoggingService]
  });

  it('should log error', () => {
    const spectator = createComponent();
    const loggingService = spectator.inject(LoggingService);

    spectator.component.riskyOperation();

    expect(loggingService.error).toHaveBeenCalledWith(
      expect.any(String),
      expect.any(Error),
      expect.any(Function)
    );
  });
});
```

## Code Review Checklist

- [ ] Uses `logger()` factory, not `LoggingService` injection
- [ ] Appropriate log level for each message
- [ ] Context functions for expensive operations
- [ ] No sensitive information (passwords, tokens, PII)
- [ ] No `console.log` statements
- [ ] Error logs include error object
- [ ] No logging in tight loops
- [ ] Component/service identified in context
- [ ] E2E attributes on interactive elements

## Quick Reference

```typescript
// Import
import { logger, provideLoggerContext } from '@isa/core/logging';

// Create logger
#logger = logger();                              // Basic
#logger = logger({ component: 'Name' });         // Static context
#logger = logger(() => ({ id: this.id }));       // Dynamic context

// Log messages
this.#logger.trace('Detailed trace');
this.#logger.debug('Debug info');
this.#logger.info('General info', () => ({ key: value }));
this.#logger.warn('Warning');
this.#logger.error('Error', error, () => ({ context }));

// Component context
@Component({
  providers: [provideLoggerContext({ feature: 'users' })]
})
```

## Additional Resources

- Full documentation: `libs/core/logging/README.md`
- Examples: `.claude/skills/logging-helper/examples.md`
- Quick reference: `.claude/skills/logging-helper/reference.md`
- Troubleshooting: `.claude/skills/logging-helper/troubleshooting.md`
