Logging Service
Structured JSON logging with Azure Application Insights integration, TraceId propagation, and environment-based filtering.
Features
- ✅ Structured Logging: JSON format with typed properties
- ✅ TraceId Propagation: Correlation across requests and logs
- ✅ Log Levels: Trace, Debug, Info, Warning, Error, Critical
- ✅ Environment Filtering: Different levels per environment
- ✅ Azure App Insights: Automatic telemetry integration
- ✅ Performance Tracking: Method execution timing
- ✅ Error Tracking: Automatic error logging with stack traces
- ✅ Custom Properties: Add contextual metadata
Installation
npm install @tess-ui-library/core @microsoft/applicationinsights-web
Configuration
1. Environment Setup
// src/environments/environment.ts
export const environment = {
production: false,
logging: {
level: 'Debug', // Trace | Debug | Info | Warning | Error | Critical
enableConsole: true,
appInsights: {
instrumentationKey: 'YOUR_INSTRUMENTATION_KEY',
enableAutoRouteTracking: true
}
}
};
2. App Configuration
// src/app/app.config.ts
import { ApplicationConfig } from '@angular/core';
import { provideLogging } from '@tess-ui-library/core';
import { environment } from '../environments/environment';
export const appConfig: ApplicationConfig = {
providers: [
provideLogging({
minLevel: environment.logging.level,
enableConsole: environment.logging.enableConsole,
appInsights: environment.logging.appInsights
}),
// ... other providers
]
};
Basic Usage
Simple Logging
import { Component, inject, OnInit } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
@Component({
selector: 'app-dashboard',
template: `<h1>Dashboard</h1>`
})
export class DashboardComponent implements OnInit {
private logger = inject(LoggerService);
ngOnInit() {
this.logger.info('Dashboard initialized');
}
}
Log with Context
import { inject } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
export class UserService {
private logger = inject(LoggerService);
async getUser(id: string) {
this.logger.debug('Fetching user', { userId: id });
try {
const user = await this.api.getUser(id);
this.logger.info('User fetched successfully', { userId: id, userName: user.name });
return Result.ok(user);
} catch (error) {
this.logger.error('Failed to fetch user', { userId: id, error });
return Result.fail({ code: 'USER_FETCH_ERROR', message: 'Failed to fetch user' });
}
}
}
API Reference
Log Levels
| Level | Priority | Usage |
|---|---|---|
Trace | 0 | Very detailed debugging (method entry/exit) |
Debug | 1 | Debugging information (variable values) |
Info | 2 | General informational messages |
Warning | 3 | Potential issues, but not errors |
Error | 4 | Error events (recoverable) |
Critical | 5 | Critical failures (unrecoverable) |
LoggerService Methods
Basic Logging
| Method | Parameters | Description |
|---|---|---|
trace() | message: string, properties?: object | Log trace message |
debug() | message: string, properties?: object | Log debug message |
info() | message: string, properties?: object | Log info message |
warning() | message: string, properties?: object | Log warning message |
error() | message: string, properties?: object | Log error message |
critical() | message: string, properties?: object | Log critical message |
Performance Tracking
| Method | Parameters | Returns | Description |
|---|---|---|---|
startTracking() | operationName: string | string (operationId) | Start tracking operation |
stopTracking() | operationId: string, properties?: object | void | Stop tracking and log duration |
Error Tracking
| Method | Parameters | Description |
|---|---|---|
trackException() | error: Error, properties?: object | Log exception with stack trace |
trackEvent() | name: string, properties?: object | Track custom event |
Log Entry Format
interface LogEntry {
timestamp: string; // ISO 8601 format
level: LogLevel; // Trace | Debug | Info | Warning | Error | Critical
message: string; // Log message
traceId: string; // TraceId (userId or generated UUID)
component: string; // Component/service name
properties?: object; // Additional contextual data
stack?: string; // Stack trace (for errors)
}
Advanced Usage
Performance Tracking
import { inject } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
export class DataService {
private logger = inject(LoggerService);
async fetchLargeDataset() {
// Start tracking
const operationId = this.logger.startTracking('fetchLargeDataset');
try {
const data = await this.api.getData();
// Stop tracking (automatically logs duration)
this.logger.stopTracking(operationId, {
recordCount: data.length,
success: true
});
return Result.ok(data);
} catch (error) {
this.logger.stopTracking(operationId, { success: false });
this.logger.trackException(error, { operation: 'fetchLargeDataset' });
return Result.fail({ code: 'FETCH_ERROR', message: 'Failed to fetch data' });
}
}
}
Custom Logger per Component
import { Component, inject, OnInit } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
@Component({
selector: 'app-user-list',
template: `<!-- ... -->`
})
export class UserListComponent implements OnInit {
private logger = inject(LoggerService).createChild('UserListComponent');
// All logs from this component will have component: 'UserListComponent'
ngOnInit() {
this.logger.info('Component initialized');
}
}
Conditional Logging
import { inject } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
export class DebugService {
private logger = inject(LoggerService);
debugOperation(data: any) {
if (this.logger.isEnabled('Debug')) {
this.logger.debug('Detailed debug info', {
data: JSON.stringify(data, null, 2)
});
}
}
}
Track User Actions
import { inject } from '@angular/core';
import { LoggerService } from '@tess-ui-library/core';
export class AnalyticsService {
private logger = inject(LoggerService);
trackButtonClick(buttonName: string) {
this.logger.trackEvent('ButtonClick', {
buttonName,
timestamp: new Date().toISOString(),
page: window.location.pathname
});
}
trackPageView(pageName: string) {
this.logger.trackEvent('PageView', {
pageName,
timestamp: new Date().toISOString(),
referrer: document.referrer
});
}
}
Azure Application Insights Integration
Automatic Telemetry
When configured with App Insights instrumentation key:
provideLogging({
appInsights: {
instrumentationKey: 'YOUR_KEY',
enableAutoRouteTracking: true
}
})
Automatically tracked:
- Page views
- Route changes
- Exceptions
- HTTP requests (via interceptor)
- Custom events
- Performance metrics
View Logs in Azure Portal
- Navigate to Application Insights resource
- Go to Logs section
- Query logs:
traces
| where timestamp > ago(1h)
| where customDimensions.traceId == "user-id-123"
| project timestamp, message, severityLevel, customDimensions
| order by timestamp desc
Custom Dimensions
All properties logged are available as customDimensions:
traces
| where customDimensions.userId == "123"
| where customDimensions.operation == "fetchUsers"
| project timestamp, message, customDimensions.duration
Environment-Based Configuration
Development
// environment.ts
export const environment = {
logging: {
level: 'Debug',
enableConsole: true,
appInsights: {
instrumentationKey: '', // Optional in dev
enableAutoRouteTracking: false
}
}
};
Production
// environment.prod.ts
export const environment = {
logging: {
level: 'Warning', // Only warnings, errors, critical
enableConsole: false, // No console logs in prod
appInsights: {
instrumentationKey: process.env['APPINSIGHTS_INSTRUMENTATION_KEY']!,
enableAutoRouteTracking: true
}
}
};
Testing
Mock LoggerService
// src/testing/mocks/logger.service.mock.ts
export const mockLoggerService = {
trace: jest.fn(),
debug: jest.fn(),
info: jest.fn(),
warning: jest.fn(),
error: jest.fn(),
critical: jest.fn(),
trackException: jest.fn(),
trackEvent: jest.fn(),
startTracking: jest.fn().mockReturnValue('mock-operation-id'),
stopTracking: jest.fn(),
createChild: jest.fn().mockReturnThis(),
isEnabled: jest.fn().mockReturnValue(true)
};
Component Test with LoggerService
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserListComponent } from './user-list.component';
import { LoggerService } from '@tess-ui-library/core';
import { mockLoggerService } from '../testing/mocks/logger.service.mock';
describe('UserListComponent', () => {
let component: UserListComponent;
let fixture: ComponentFixture<UserListComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [UserListComponent],
providers: [
{ provide: LoggerService, useValue: mockLoggerService }
]
}).compileComponents();
fixture = TestBed.createComponent(UserListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should log on init', () => {
expect(mockLoggerService.info).toHaveBeenCalledWith('Component initialized');
});
it('should log errors', () => {
const error = new Error('Test error');
component.handleError(error);
expect(mockLoggerService.error).toHaveBeenCalled();
});
});
Best Practices
1. Use Appropriate Log Levels
// ✅ GOOD: Appropriate levels
this.logger.trace('Entering method', { methodName: 'getUser' });
this.logger.debug('User data', { user });
this.logger.info('User fetched successfully');
this.logger.warning('Token expiring soon');
this.logger.error('API request failed', { error });
this.logger.critical('Database connection lost');
// ❌ BAD: Everything as info
this.logger.info('Entering method');
this.logger.info('User data: ' + JSON.stringify(user));
this.logger.info('Error occurred: ' + error);
2. Add Contextual Properties
// ✅ GOOD: Structured properties
this.logger.info('Order created', {
orderId: order.id,
userId: user.id,
totalAmount: order.total,
itemCount: order.items.length
});
// ❌ BAD: String concatenation
this.logger.info(`Order ${order.id} created for user ${user.id}`);
3. Track Performance for Expensive Operations
// ✅ GOOD: Track performance
const opId = this.logger.startTracking('complexCalculation');
const result = this.performComplexCalculation();
this.logger.stopTracking(opId, { resultSize: result.length });
// ❌ BAD: No performance tracking
const result = this.performComplexCalculation();
4. Use TraceId for Correlation
// ✅ GOOD: TraceId automatically included
this.logger.info('API request', { endpoint: '/users' });
// Log entry: { traceId: 'user-123', message: 'API request', ... }
// ❌ BAD: Manual correlation ID
this.logger.info(`[${correlationId}] API request to /users`);
Troubleshooting
Logs Not Appearing in Console
Problem: No logs in browser console.
Solution: Check log level configuration:
// Ensure level is not too high
provideLogging({
minLevel: 'Debug', // Not 'Error' or 'Critical'
enableConsole: true // Must be true
})
Logs Not in Application Insights
Problem: Logs missing from Azure portal.
Solution: Verify instrumentation key:
// Check environment variable
console.log('APPINSIGHTS_KEY:', environment.logging.appInsights.instrumentationKey);
// Verify key is valid in Azure Portal
Performance Impact
Problem: Logging causing performance issues.
Solution: Adjust log level in production:
// environment.prod.ts
logging: {
level: 'Warning', // Only log warnings and above
enableConsole: false // Disable console logs
}