skills/logging-angular/SKILL.md
Configure structured logging for Angular frontend applications. Covers custom LoggerService, ErrorHandler integration, HTTP interceptor logging, correlation IDs, and log shipping to backend services. Use when: setting up frontend logging, capturing errors, correlating frontend requests with backend traces, or debugging production issues in Angular.
npx skillsauth add congiuluc/my-awesome-copilot logging-angularInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
console.log in production — use a LoggerService that can be configured per environment.LoggerService with configurable log levelsErrorHandler that delegates to the loggerenvironment.tsimport { Injectable, inject } from '@angular/core';
import { environment } from '../environments/environment';
export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
interface LogEntry {
level: LogLevel;
message: string;
context?: Record<string, unknown>;
timestamp: string;
correlationId?: string;
}
const LOG_LEVELS: Record<LogLevel, number> = { debug: 0, info: 1, warn: 2, error: 3 };
@Injectable({ providedIn: 'root' })
export class LoggerService {
private readonly minLevel: LogLevel = environment.production ? 'warn' : 'debug';
private correlationId?: string;
setCorrelationId(id: string): void {
this.correlationId = id;
}
debug(message: string, context?: Record<string, unknown>): void {
this.log('debug', message, context);
}
info(message: string, context?: Record<string, unknown>): void {
this.log('info', message, context);
}
warn(message: string, context?: Record<string, unknown>): void {
this.log('warn', message, context);
}
error(message: string, context?: Record<string, unknown>): void {
this.log('error', message, context);
}
private log(level: LogLevel, message: string, context?: Record<string, unknown>): void {
if (LOG_LEVELS[level] < LOG_LEVELS[this.minLevel]) return;
const entry: LogEntry = {
level,
message,
context: this.filterSensitive(context),
timestamp: new Date().toISOString(),
correlationId: this.correlationId,
};
console[level](`[${level.toUpperCase()}]`, entry.message, entry.context ?? '');
if (level === 'error') {
this.shipToBackend(entry);
}
}
private filterSensitive(ctx?: Record<string, unknown>): Record<string, unknown> | undefined {
if (!ctx) return undefined;
const filtered = { ...ctx };
const sensitiveKeys = new Set(['password', 'token', 'secret', 'apikey', 'authorization']);
for (const key of Object.keys(filtered)) {
if (sensitiveKeys.has(key.toLowerCase())) {
filtered[key] = '***REDACTED***';
}
}
return filtered;
}
private shipToBackend(entry: LogEntry): void {
const endpoint = environment.logEndpoint;
if (!endpoint) return;
fetch(endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(entry),
keepalive: true,
}).catch(() => { /* logging should never break the app */ });
}
}
import { ErrorHandler, Injectable, inject } from '@angular/core';
import { LoggerService } from './logger.service';
@Injectable()
export class GlobalErrorHandler implements ErrorHandler {
private readonly logger = inject(LoggerService);
handleError(error: unknown): void {
const message = error instanceof Error ? error.message : String(error);
const stack = error instanceof Error ? error.stack : undefined;
this.logger.error('Unhandled error', { message, stack });
}
}
// Register in app.config.ts:
// providers: [{ provide: ErrorHandler, useClass: GlobalErrorHandler }]
import { HttpInterceptorFn } from '@angular/common/http';
import { inject } from '@angular/core';
import { tap, catchError, throwError } from 'rxjs';
import { LoggerService } from './logger.service';
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const logger = inject(LoggerService);
const correlationId = req.headers.get('X-Correlation-ID');
if (correlationId) {
logger.setCorrelationId(correlationId);
}
return next(req).pipe(
catchError((error) => {
logger.error('HTTP request failed', {
url: req.url,
method: req.method,
status: error.status,
message: error.message,
});
return throwError(() => error);
}),
);
};
tools
Build VS Code extensions with TypeScript. Covers extension anatomy, activation events, commands, tree views, webview panels, language features, testing, and publishing. Use when: creating a new VS Code extension, adding commands/views/providers, building webview UIs, implementing language server features, testing extensions, or packaging for the marketplace.
development
Track implementations, features, bugs, and releases in a versioning document. Use when: adding a commit, completing a feature, fixing a bug, or preparing a release. Automatically updates CHANGELOG.md following Keep a Changelog format and Semantic Versioning.
development
Write frontend tests using Vitest and React Testing Library. Use when: testing React components, hooks, user interactions, form submissions, accessibility assertions, or mocking API services.
development
Write Angular frontend tests using Jasmine, Karma, and Angular TestBed. Use when: testing Angular components, services, pipes, directives, user interactions, form submissions, accessibility assertions, or mocking HTTP services.