How do you implement request logging in Angular?
In Angular, request logging can be implemented globally using an HTTP interceptor.
A logging interceptor can capture information such as:
- HTTP method —
GET,POST,PUT,DELETE - Request URL
- Request start time
- Response status
- Request duration
- HTTP errors
This avoids adding logging code to every Angular service.
Full Simple Example
1. logging.interceptor.ts
import {
HttpErrorResponse,
HttpInterceptorFn,
HttpResponse
} from '@angular/common/http';
import { tap } from 'rxjs';
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
const startTime = Date.now();
console.log('HTTP Request');
console.log('Method:', req.method);
console.log('URL:', req.urlWithParams);
return next(req).pipe(
tap({
next: event => {
if (event instanceof HttpResponse) {
const duration = Date.now() - startTime;
console.log('HTTP Response');
console.log('URL:', req.urlWithParams);
console.log('Status:', event.status);
console.log('Duration:', duration + ' ms');
}
},
error: (error: HttpErrorResponse) => {
const duration = Date.now() - startTime;
console.error('HTTP Error');
console.error('URL:', req.urlWithParams);
console.error('Status:', error.status);
console.error('Message:', error.message);
console.error('Duration:', duration + ' ms');
}
})
);
};
2. app.config.ts
Register the interceptor globally.
import { ApplicationConfig } from '@angular/core';
import {
provideHttpClient,
withInterceptors
} from '@angular/common/http';
import { loggingInterceptor } from './logging.interceptor';
export const appConfig: ApplicationConfig = {
providers: [
provideHttpClient(
withInterceptors([
loggingInterceptor
])
)
]
};
3. product.service.ts
No logging code is required inside the service.
import { inject, Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
export interface Product {
id: number;
name: string;
price: number;
}
@Injectable({
providedIn: 'root'
})
export class ProductService {
private http = inject(HttpClient);
private apiUrl =
'https://localhost:7001/api/products';
getProducts(): Observable<Product[]> {
return this.http.get<Product[]>(
this.apiUrl
);
}
}
Now whenever getProducts() is called, the interceptor automatically logs the request and response.
How does it work?
When Angular executes:
this.http.get(
'https://localhost:7001/api/products'
);
the flow is:
Product Component
↓
ProductService
↓
HttpClient
↓
Logging Interceptor
│
├── Log method
├── Log URL
└── Record start time
↓
ASP.NET Core Web API
↓
HTTP Response
↓
Logging Interceptor
│
├── Log status
└── Calculate duration
↓
ProductService
↓
Component
Understanding the code
1. Get the HTTP method
req.method
For example:
GET
POST
PUT
DELETE
2. Get the request URL
req.urlWithParams
For example:
https://localhost:7001/api/products?page=1&pageSize=10
urlWithParams is useful because it includes query-string parameters.
Compare:
req.url
with:
req.urlWithParams
For a request such as:
/api/products?page=1
urlWithParams gives you the complete URL including ?page=1.
3. Record the request start time
const startTime = Date.now();
Suppose:
Start = 10:30:00.100
After the response arrives:
const duration = Date.now() - startTime;
Suppose the response arrived at:
10:30:00.350
Then:
Duration = 250 ms
This can help identify slow API calls.
4. Send the request
return next(req)
next(req) passes the request to the next interceptor or ultimately to the HTTP backend.
It returns an Observable.
Therefore, we can observe what happens using:
.pipe(...)
5. Use tap() for logging
return next(req).pipe(
tap({
next: event => {
},
error: error => {
}
})
);
tap() is useful for side effects such as:
Logging
Debugging
Metrics
Tracing
It normally doesn't modify the HTTP response.
Why check HttpResponse?
The interceptor receives a stream of HttpEvent objects, not necessarily only the final response.
Therefore:
if (event instanceof HttpResponse)
ensures we log the final HTTP response.
if (event instanceof HttpResponse) {
console.log(event.status);
}
For example:
Status: 200
Example console output
Suppose Angular calls:
GET /api/products
You might see:
HTTP Request
Method: GET
URL: https://localhost:7001/api/products
HTTP Response
URL: https://localhost:7001/api/products
Status: 200
Duration: 185 ms
If the API fails:
HTTP Request
Method: GET
URL: https://localhost:7001/api/products
HTTP Error
URL: https://localhost:7001/api/products
Status: 500
Message: Http failure response...
Duration: 120 ms
Logging multiple request types
The same interceptor automatically handles:
this.http.get(...)
this.http.post(...)
this.http.put(...)
this.http.delete(...)
For example:
GET /api/products
POST /api/products
PUT /api/products/10
DELETE /api/products/10
You don't need separate logging code for each API method.
Logging Request Body
Technically, you could log:
console.log('Body:', req.body);
For example:
console.log(
'Method:', req.method,
'URL:', req.urlWithParams,
'Body:', req.body
);
However, this should be used carefully.
A request body could contain:
Password
Access token
Refresh token
Credit-card information
Personal information
Other sensitive data
Therefore, avoid indiscriminately logging request bodies in production.
Don't log JWT tokens
Similarly, avoid logging the complete headers:
console.log(req.headers);
if they contain:
Authorization: Bearer eyJhbGciOi...
Your logs should never become a source of leaked credentials.
Combining Auth, Logging and Error Interceptors
A real application might register:
provideHttpClient(
withInterceptors([
authInterceptor,
loggingInterceptor,
errorInterceptor
])
)
Conceptually:
Component
↓
Service
↓
HttpClient
↓
Auth Interceptor
│
│ Add JWT
↓
Logging Interceptor
│
│ Log request
↓
Error Interceptor
↓
ASP.NET Core API
Responses travel back through the interceptor chain in the opposite direction.
Better Production Logging
console.log() is fine for learning and local development:
console.log(req.method);
console.log(req.urlWithParams);
For a production application, you may instead use a dedicated telemetry/logging solution.
The general architecture could be:
Angular Application
↓
Logging Interceptor
↓
Telemetry / Monitoring
The backend should also perform server-side request logging. Angular logging cannot replace ASP.NET Core server logs because client-side logs only describe what the browser sees.
Request Logging vs Error Interceptor
They serve different purposes.
| Logging Interceptor | Error Interceptor |
|---|---|
| Logs requests | Handles errors |
| Logs responses | Handles status codes |
| Measures duration | Handles 401, 403, 500, etc. |
| Useful for diagnostics | Useful for centralized error behavior |
Usually uses tap() |
Usually uses catchError() |
You can use both:
withInterceptors([
loggingInterceptor,
errorInterceptor
])
Key Points
- Use an HTTP interceptor for centralized request logging.
req.methodprovides the HTTP method.
req.method
req.urlWithParamsprovides the URL including query parameters.
req.urlWithParams
- Record request time using:
const startTime = Date.now();
- Calculate duration:
const duration = Date.now() - startTime;
tap()is useful for logging without transforming the response.
next(req).pipe(
tap(...)
)
- Check the final response using:
event instanceof HttpResponse
- Avoid logging sensitive information such as passwords, JWTs, refresh tokens, or other secrets.
Interview Questions and Answers
1. How do you implement request logging in Angular?
Use an HTTP interceptor and log request information before calling next(req).
export const loggingInterceptor: HttpInterceptorFn = (req, next) => {
console.log(req.method);
console.log(req.urlWithParams);
return next(req);
};
2. How do you log the response?
Use RxJS tap():
return next(req).pipe(
tap(event => {
if (event instanceof HttpResponse) {
console.log(event.status);
}
})
);
3. How do you measure API execution time?
Record the time before sending the request:
const startTime = Date.now();
Then calculate the difference when the response arrives:
const duration =
Date.now() - startTime;
4. Why use tap() for logging?
tap() allows us to perform side effects such as logging and diagnostics without normally changing the emitted HTTP response.
5. Should request bodies and headers always be logged?
No. They may contain sensitive data such as passwords, JWTs, refresh tokens, or personal information. Production logging should sanitize or omit sensitive fields.
Interview one-line answer
Request logging in Angular can be implemented globally using an HTTP interceptor, where request details are logged before
next(req), and RxJStap()is used to log the response, errors, and request execution time without modifying the HTTP data.