Files
ISA-Frontend/.claude/skills/logging/SKILL.md
Lorenz Hilpert fd8e0194ac 🚚 refactor(skills): reorganize skill structure
- Rename logging-helper to logging for consistency
- Remove git-commit-helper (superseded by /commit command)
- Add git-workflow skill for Git Flow operations

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-29 13:39:14 +01:00

6.8 KiB

name, description
name description
logging-helper Ensures consistent usage of the @isa/core/logging library across the codebase with best practices for performance and maintainability

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

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):

#logger = logger({ component: 'UserProfileComponent' });

Dynamic Context (instance level):

#logger = logger(() => ({
  userId: this.authService.currentUserId,
  storeId: this.config.storeId
}));

Message Context (use functions for performance):

// ✅ 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

@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

@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

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

@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

// ❌ 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

// 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

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

// 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