When I started working on Angular app, I noticed one common thing: console.log everywhere. Debug messages scattered across components, warnings mixed with production logs, and the occasional sensitive info sneaking into the console.
With guidance from my peers, I learned about a much better approach-a centralized Logger Service. In this post, Iβll walk you through a production-ready Logger Service implementation for Angular that makes logging clean, powerful, and safe.
The Problem π€
Weβve all been there-debugging with console.log()
sprinkled across the codebase.
The issues?
- Cluttered production logs
- Manual cleanup before deployment
- Inconsistent formatting
- No central control
- Potential exposure of sensitive info
The Solution β¨
A Logger Service that provides:
- β Environment-aware logging (disabled in production)
- β Multiple log levels: DEBUG, INFO, WARN, ERROR
- β Consistent formatting with timestamps
- β Advanced features (grouping, timing, styled logs, tables)
- β Type safety with full TypeScript support
Implementation π»
Core Logger Service
export enum LogLevel {
DEBUG = 0,
INFO = 1,
WARN = 2,
ERROR = 3
}
@Injectable({ providedIn: 'root' })
export class LoggerService {
private isEnabled: boolean = !environment.production;
private logLevel: LogLevel = LogLevel.DEBUG;
constructor() {
if (environment.hasOwnProperty('enableLogging')) {
this.isEnabled = (environment as any).enableLogging;
}
if (environment.hasOwnProperty('logLevel')) {
this.logLevel = (environment as any).logLevel;
}
}
debug(message: any, ...params: any[]): void {
this.log(LogLevel.DEBUG, message, ...params);
}
info(message: any, ...params: any[]): void {
this.log(LogLevel.INFO, message, ...params);
}
warn(message: any, ...params: any[]): void {
this.log(LogLevel.WARN, message, ...params);
}
error(message: any, ...params: any[]): void {
console.error(message, ...params);
}
private log(level: LogLevel, message: any, ...params: any[]): void {
if (!this.isEnabled || level < this.logLevel) return;
const timestamp = new Date().toISOString();
const prefix = `[${timestamp}] [${LogLevel[level]}]`;
switch (level) {
case LogLevel.DEBUG: console.log(prefix, message, ...params); break;
case LogLevel.INFO: console.info(prefix, message, ...params); break;
case LogLevel.WARN: console.warn(prefix, message, ...params); break;
case LogLevel.ERROR: console.error(prefix, message, ...params); break;
}
}
}
Key highlights:
- Disabled automatically in production
- Errors always logged regardless of environment
- ISO timestamps for precise log tracking
Advanced Features π―
1. Grouping Related Logs
group(name: string, collapsed = false): void {
if (this.isEnabled) {
collapsed ? console.groupCollapsed(name) : console.group(name);
}
}
groupEnd(): void {
if (this.isEnabled) console.groupEnd();
}
2. Performance Timing
time(name: string): void {
if (this.isEnabled && this.logLevel <= LogLevel.DEBUG) console.time(name);
}
timeEnd(name: string): void {
if (this.isEnabled && this.logLevel <= LogLevel.DEBUG) console.timeEnd(name);
}
3. Table Display
table(data: any): void {
if (this.isEnabled && this.logLevel <= LogLevel.DEBUG) console.table(data);
}
4. Styled Logs
styled(msg: string, styles = 'color: #2196F3; font-weight: bold;'): void {
if (this.isEnabled && this.logLevel <= LogLevel.INFO) {
console.log(`%c${msg}`, styles);
}
}
Migration Guide π
Before:
console.log('Debug info');
console.warn('Warning message');
console.error('Error occurred');
After:
this.logger.debug('Debug info');
this.logger.warn('Warning message');
this.logger.error('Error occurred');
Dynamic Configuration βοΈ
Enable/disable logging at runtime:
constructor(private logger: LoggerService) {
if (window.location.href.includes('debug=true')) {
this.logger.setEnabled(true);
this.logger.setLogLevel(LogLevel.DEBUG);
}
}
Benefits π
- Cleaner production logs
- Rich formatting and timestamps
- Toggle logging with a single flag
- Zero overhead when disabled
- Type safety with TypeScript
- Consistent across the app
- No accidental leakage of sensitive info
Best Practices π
- Use
logger.error()
for errors (always logged) - Use
debug()
for diagnostics,info()
for general messages,warn()
for potential issues - Group related logs for readability
- Time performance-critical operations
- Replace all
console.
withlogger.
Conclusion π―
A Logger Service is one of those simple but powerful upgrades that every Angular app should have.
It makes logs consistent, keeps production clean, and gives developers richer debugging tools.
Since adding it to our project, debugging has become easier and our production logs are finally clean.
No more scattered console.logs
π«
π¬ Whatβs your logging strategy? Share your approach in the comments!
Top comments (0)