Skip to main content

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

LevelPriorityUsage
Trace0Very detailed debugging (method entry/exit)
Debug1Debugging information (variable values)
Info2General informational messages
Warning3Potential issues, but not errors
Error4Error events (recoverable)
Critical5Critical failures (unrecoverable)

LoggerService Methods

Basic Logging

MethodParametersDescription
trace()message: string, properties?: objectLog trace message
debug()message: string, properties?: objectLog debug message
info()message: string, properties?: objectLog info message
warning()message: string, properties?: objectLog warning message
error()message: string, properties?: objectLog error message
critical()message: string, properties?: objectLog critical message

Performance Tracking

MethodParametersReturnsDescription
startTracking()operationName: stringstring (operationId)Start tracking operation
stopTracking()operationId: string, properties?: objectvoidStop tracking and log duration

Error Tracking

MethodParametersDescription
trackException()error: Error, properties?: objectLog exception with stack trace
trackEvent()name: string, properties?: objectTrack 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

  1. Navigate to Application Insights resource
  2. Go to Logs section
  3. 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
}