mirror of
https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend
synced 2025-12-28 22:42:11 +01:00
committed by
Nino Righi
parent
f34f2164fc
commit
26fd5cb389
@@ -1,2 +1,2 @@
|
||||
export * from './lib/catalouge-search.service';
|
||||
export * from './lib/services';
|
||||
export * from './lib/models';
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { SearchService } from '@generated/swagger/cat-search-api';
|
||||
import { map, Observable } from 'rxjs';
|
||||
import { Item } from './models';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CatalougeSearchService {
|
||||
#searchService = inject(SearchService);
|
||||
|
||||
searchByEans(...ean: string[]): Observable<Item[]> {
|
||||
return this.#searchService.SearchByEAN(ean).pipe(
|
||||
map((res) => {
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res.result as Item[];
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const SearchByTermParamsSchema = z.object({
|
||||
term: z.string().min(1, 'Search term must not be empty'),
|
||||
skip: z.number().int().min(0).default(0),
|
||||
take: z.number().int().min(1).max(100).default(20),
|
||||
});
|
||||
|
||||
export type SearchByTermParams = z.infer<typeof SearchByTermParamsSchema>;
|
||||
1
libs/catalogue/data-access/src/lib/schemas/index.ts
Normal file
1
libs/catalogue/data-access/src/lib/schemas/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './catalouge-search.schemas';
|
||||
@@ -0,0 +1,51 @@
|
||||
import { inject, Injectable } from '@angular/core';
|
||||
import { SearchService } from '@generated/swagger/cat-search-api';
|
||||
import { firstValueFrom, map, Observable } from 'rxjs';
|
||||
import { takeUntilAborted } from '@isa/common/data-access';
|
||||
import { Item } from '../models';
|
||||
import {
|
||||
SearchByTermParams,
|
||||
SearchByTermParamsSchema,
|
||||
} from '../schemas/catalouge-search.schemas';
|
||||
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CatalougeSearchService {
|
||||
#searchService = inject(SearchService);
|
||||
|
||||
searchByEans(...ean: string[]): Observable<Item[]> {
|
||||
return this.#searchService.SearchByEAN(ean).pipe(
|
||||
map((res) => {
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res.result as Item[];
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async searchByTerm(
|
||||
params: SearchByTermParams,
|
||||
abortSignal: AbortSignal,
|
||||
): Promise<Item[]> {
|
||||
const { term, skip, take } = SearchByTermParamsSchema.parse(params);
|
||||
|
||||
const req$ = this.#searchService
|
||||
.SearchSearch({
|
||||
filter: {
|
||||
qs: term,
|
||||
},
|
||||
skip,
|
||||
take,
|
||||
})
|
||||
.pipe(takeUntilAborted(abortSignal));
|
||||
|
||||
const res = await firstValueFrom(req$);
|
||||
|
||||
if (res.error) {
|
||||
throw new Error(res.message);
|
||||
}
|
||||
|
||||
return res.result as Item[];
|
||||
}
|
||||
}
|
||||
@@ -1,2 +1 @@
|
||||
export * from './catalouge-search.service';
|
||||
export * from './models';
|
||||
@@ -84,7 +84,7 @@ export class PrintButtonComponent {
|
||||
directPrint: this.directPrint(),
|
||||
});
|
||||
} catch (error) {
|
||||
this.#logger.error('Print operation failed', { error });
|
||||
this.#logger.error('Print operation failed', error as Error);
|
||||
}
|
||||
this.printing.set(false);
|
||||
}
|
||||
|
||||
@@ -1,21 +1,103 @@
|
||||
# Core Logging
|
||||
# @isa/core/logging
|
||||
|
||||
A structured, high-performance logging library for Angular applications.
|
||||
A structured, high-performance logging library for Angular applications with hierarchical context support and flexible sink architecture.
|
||||
|
||||
## Overview
|
||||
|
||||
The Core Logging library provides a centralized logging service for the ISA Frontend application. It offers a structured way to log messages, errors, and other information across the application, with support for different log levels and output destinations.
|
||||
The Core Logging library provides a centralized logging service for the ISA Frontend application. It offers a structured way to log messages, errors, and other information across the application, with support for different log levels, multiple output destinations, and rich contextual metadata.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Features](#features)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Core Concepts](#core-concepts)
|
||||
- [API Reference](#api-reference)
|
||||
- [Configuration](#configuration)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Creating Custom Sinks](#creating-custom-sinks)
|
||||
- [Performance Considerations](#performance-considerations)
|
||||
- [Testing](#testing)
|
||||
- [Migration Guide](#migration-guide)
|
||||
|
||||
## Features
|
||||
|
||||
- Multiple log levels (trace, debug, info, warn, error)
|
||||
- Configurable logging targets (console, remote server, etc.)
|
||||
- Context-aware logging with metadata support
|
||||
- Production/development mode detection
|
||||
- Filtering capabilities based on log level or context
|
||||
- Highly optimized for performance
|
||||
- Type-safe APIs
|
||||
- Error resilience - logging failures don't affect application behavior
|
||||
- **Multiple log levels** (trace, debug, info, warn, error, off)
|
||||
- **Flexible sink architecture** - Multiple output destinations (console, remote server, etc.)
|
||||
- **Hierarchical context system** - Component, instance, and message-level contexts
|
||||
- **Performance optimized** - Early filtering and lazy evaluation
|
||||
- **Type-safe APIs** - Full TypeScript support with comprehensive interfaces
|
||||
- **Error resilience** - Logging failures don't affect application behavior
|
||||
- **Angular integration** - Native dependency injection support
|
||||
- **Factory pattern** - Easy logger creation with `logger()` function
|
||||
- **Configurable providers** - Simple configuration through Angular providers
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Install and Configure
|
||||
|
||||
Add the logging provider to your application configuration:
|
||||
|
||||
```typescript
|
||||
// app.config.ts
|
||||
import { ApplicationConfig, isDevMode } from '@angular/core';
|
||||
import {
|
||||
provideLogging,
|
||||
withLogLevel,
|
||||
withSink,
|
||||
LogLevel,
|
||||
ConsoleLogSink
|
||||
} from '@isa/core/logging';
|
||||
|
||||
export const appConfig: ApplicationConfig = {
|
||||
providers: [
|
||||
// Other providers...
|
||||
provideLogging(
|
||||
withLogLevel(isDevMode() ? LogLevel.Debug : LogLevel.Warn),
|
||||
withSink(ConsoleLogSink)
|
||||
)
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
### 2. Use in Components
|
||||
|
||||
```typescript
|
||||
import { Component } from '@angular/core';
|
||||
import { logger } from '@isa/core/logging';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
template: '...'
|
||||
})
|
||||
export class ExampleComponent {
|
||||
#logger = logger();
|
||||
|
||||
ngOnInit(): void {
|
||||
this.#logger.info('Component initialized');
|
||||
}
|
||||
|
||||
handleUserAction(): void {
|
||||
this.#logger.debug('User action triggered', () => ({
|
||||
action: 'button-click',
|
||||
timestamp: Date.now()
|
||||
}));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Error Handling
|
||||
|
||||
```typescript
|
||||
try {
|
||||
await this.processData();
|
||||
} catch (error) {
|
||||
this.#logger.error(
|
||||
'Data processing failed',
|
||||
error as Error,
|
||||
() => ({ operationId: '12345' })
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Core Concepts
|
||||
|
||||
@@ -40,29 +122,191 @@ Log sinks are destinations where log messages are sent. The library supports mul
|
||||
- Send critical errors to a monitoring service
|
||||
- Store logs for later analysis
|
||||
|
||||
## API
|
||||
### Hierarchical Context System
|
||||
|
||||
### LoggingService
|
||||
The logging system supports a powerful hierarchical context system that merges context from multiple levels:
|
||||
|
||||
The main service for logging functionality.
|
||||
1. **Global Context** - Set during application configuration
|
||||
2. **Component Context** - Provided at component/service level
|
||||
3. **Instance Context** - Provided when creating logger instances
|
||||
4. **Message Context** - Provided with individual log calls
|
||||
|
||||
#### Methods
|
||||
|
||||
| Method | Description |
|
||||
| ---------------------------------------------------------- | --------------------------- |
|
||||
| `trace(message: string, context?: unknown)` | Logs trace information |
|
||||
| `debug(message: string, context?: unknown)` | Logs debug information |
|
||||
| `info(message: string, context?: unknown)` | Logs informational messages |
|
||||
| `warn(message: string, context?: unknown)` | Logs warning messages |
|
||||
| `error(message: string, error?: Error, context?: unknown)` | Logs error messages |
|
||||
|
||||
### Logger Factory
|
||||
|
||||
The recommended way to use the logging system is through the `logger()` factory function:
|
||||
Contexts are merged in order, with later contexts taking precedence:
|
||||
|
||||
```typescript
|
||||
export class MyComponent {
|
||||
#logger = logger();
|
||||
// 1. Global context (app.config.ts)
|
||||
provideLogging(
|
||||
withContext({ app: 'ISA', version: '1.0.0' })
|
||||
)
|
||||
|
||||
// 2. Component context
|
||||
@Component({
|
||||
providers: [
|
||||
provideLoggerContext({ component: 'UserProfile', module: 'CRM' })
|
||||
]
|
||||
})
|
||||
export class UserProfileComponent {
|
||||
// 3. Instance context
|
||||
#logger = logger(() => ({
|
||||
userId: this.currentUser?.id,
|
||||
sessionId: this.sessionService.id
|
||||
}));
|
||||
|
||||
saveProfile(): void {
|
||||
// 4. Message context
|
||||
this.#logger.info('Saving profile', () => ({
|
||||
profileId: this.profile.id,
|
||||
changedFields: this.getChangedFields()
|
||||
}));
|
||||
|
||||
// Final merged context will include all levels:
|
||||
// {
|
||||
// app: 'ISA',
|
||||
// version: '1.0.0',
|
||||
// component: 'UserProfile',
|
||||
// module: 'CRM',
|
||||
// userId: '12345',
|
||||
// sessionId: 'sess-abc',
|
||||
// profileId: 'prof-456',
|
||||
// changedFields: ['name', 'email']
|
||||
// }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### Core Interfaces
|
||||
|
||||
#### `LoggerApi`
|
||||
|
||||
The main interface for logging operations returned by the `logger()` factory:
|
||||
|
||||
```typescript
|
||||
interface LoggerApi {
|
||||
trace(message: string, context?: () => LoggerContext): void;
|
||||
debug(message: string, context?: () => LoggerContext): void;
|
||||
info(message: string, context?: () => LoggerContext): void;
|
||||
warn(message: string, context?: () => LoggerContext): void;
|
||||
error(message: string, error?: Error, context?: () => LoggerContext): void;
|
||||
}
|
||||
```
|
||||
|
||||
#### `LoggerContext`
|
||||
|
||||
Type definition for context data passed to logging methods:
|
||||
|
||||
```typescript
|
||||
interface LoggerContext {
|
||||
[key: string]: unknown;
|
||||
}
|
||||
```
|
||||
|
||||
#### `Sink`
|
||||
|
||||
Interface for creating custom logging destinations:
|
||||
|
||||
```typescript
|
||||
interface Sink {
|
||||
log(
|
||||
level: LogLevel,
|
||||
message: string,
|
||||
context?: LoggerContext,
|
||||
error?: Error
|
||||
): void;
|
||||
}
|
||||
```
|
||||
|
||||
### Factory Functions
|
||||
|
||||
#### `logger(ctxFn?: () => LoggerContext): LoggerApi`
|
||||
|
||||
Creates a logger instance with optional dynamic context:
|
||||
|
||||
```typescript
|
||||
// Basic usage
|
||||
const basicLogger = logger();
|
||||
|
||||
// With dynamic context
|
||||
const contextLogger = logger(() => ({
|
||||
userId: getCurrentUserId(),
|
||||
sessionId: getSessionId()
|
||||
}));
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `ctxFn` (optional): Function that returns context data to be included with each log message
|
||||
|
||||
**Returns:** A `LoggerApi` instance configured with the specified context
|
||||
|
||||
### Configuration Functions
|
||||
|
||||
#### `provideLogging(...configs: LoggingConfigData[]): EnvironmentProviders`
|
||||
|
||||
Main configuration function for setting up the logging system:
|
||||
|
||||
```typescript
|
||||
provideLogging(
|
||||
withLogLevel(LogLevel.Debug),
|
||||
withSink(ConsoleLogSink),
|
||||
withContext({ app: 'MyApp' })
|
||||
)
|
||||
```
|
||||
|
||||
#### `withLogLevel(level: LogLevel): LoggingConfigData`
|
||||
|
||||
Sets the minimum log level for processing:
|
||||
|
||||
```typescript
|
||||
withLogLevel(LogLevel.Debug) // Process debug and above
|
||||
withLogLevel(LogLevel.Error) // Process only errors
|
||||
```
|
||||
|
||||
#### `withSink(sink: Sink | Type<Sink>): LoggingConfigData`
|
||||
|
||||
Adds a logging destination:
|
||||
|
||||
```typescript
|
||||
withSink(ConsoleLogSink) // Class reference
|
||||
withSink(new CustomSink()) // Instance
|
||||
```
|
||||
|
||||
#### `withSinkFn(sinkFn: SinkFn): LoggingConfigData`
|
||||
|
||||
Adds a sink factory function:
|
||||
|
||||
```typescript
|
||||
withSinkFn(() => {
|
||||
const http = inject(HttpClient);
|
||||
return (level, message, context) => {
|
||||
// Custom sink implementation
|
||||
};
|
||||
})
|
||||
```
|
||||
|
||||
#### `withContext(context: LoggerContext): LoggingConfigData`
|
||||
|
||||
Adds global context to all log messages:
|
||||
|
||||
```typescript
|
||||
withContext({
|
||||
app: 'ISA',
|
||||
version: '1.0.0',
|
||||
environment: 'production'
|
||||
})
|
||||
```
|
||||
|
||||
#### `provideLoggerContext(context: LoggerContext): Provider[]`
|
||||
|
||||
Provides component-level context:
|
||||
|
||||
```typescript
|
||||
@Component({
|
||||
providers: [
|
||||
provideLoggerContext({ component: 'UserProfile' })
|
||||
]
|
||||
})
|
||||
export class UserProfileComponent {
|
||||
|
||||
ngOnInit(): void {
|
||||
this.#logger.info('Component initialized');
|
||||
@@ -355,36 +599,63 @@ describe('MyService', () => {
|
||||
});
|
||||
```
|
||||
|
||||
## Recent Changes
|
||||
## Migration Guide
|
||||
|
||||
### Logger Factory Enhancements
|
||||
### Migrating from Previous Versions
|
||||
|
||||
- **Context Functionality**: The `logger` factory function now accepts an optional `ctxFn` parameter. This allows additional context to be dynamically merged into log messages at runtime.
|
||||
- **Enhanced Context Merging**: The `mergeContexts` function has been updated to include injector-level context, component-level context, and message-specific context. This ensures a more comprehensive and flexible logging context.
|
||||
|
||||
#### Example Usage
|
||||
#### Context Parameter Changes
|
||||
|
||||
**Old API:**
|
||||
```typescript
|
||||
import { logger } from '@isa/core/logging';
|
||||
|
||||
@Component({
|
||||
selector: 'app-example',
|
||||
template: '...'
|
||||
})
|
||||
export class ExampleComponent {
|
||||
#logger = logger(() => ({ userId: '12345' }));
|
||||
|
||||
ngOnInit(): void {
|
||||
this.#logger.info('Component initialized');
|
||||
}
|
||||
|
||||
performAction(): void {
|
||||
this.#logger.debug('Action performed', { action: 'example' });
|
||||
}
|
||||
}
|
||||
// Previous version - context as direct parameter
|
||||
logger.info('Message', { userId: '123' });
|
||||
logger.error('Error occurred', error, { operationId: '456' });
|
||||
```
|
||||
|
||||
### Benefits
|
||||
**New API:**
|
||||
```typescript
|
||||
// Current version - context as function
|
||||
logger.info('Message', () => ({ userId: '123' }));
|
||||
logger.error('Error occurred', error, () => ({ operationId: '456' }));
|
||||
```
|
||||
|
||||
- **Dynamic Context**: Add runtime-specific context to log messages without modifying the logger instance.
|
||||
- **Improved Debugging**: Enhanced context merging provides richer information for troubleshooting.
|
||||
#### Factory Function Updates
|
||||
|
||||
**Old API:**
|
||||
```typescript
|
||||
// Previous version - no context support in factory
|
||||
const logger = logger();
|
||||
```
|
||||
|
||||
**New API:**
|
||||
```typescript
|
||||
// Current version - dynamic context support
|
||||
const logger = logger(() => ({ sessionId: getSessionId() }));
|
||||
```
|
||||
|
||||
#### Configuration Changes
|
||||
|
||||
**Old API:**
|
||||
```typescript
|
||||
// Previous version - direct service injection
|
||||
constructor(private loggingService: LoggingService) {}
|
||||
```
|
||||
|
||||
**New API:**
|
||||
```typescript
|
||||
// Current version - factory function
|
||||
#logger = logger();
|
||||
```
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
1. **Context parameters** now require function wrappers for lazy evaluation
|
||||
2. **Error parameter** in LoggerApi is now optional instead of unknown type
|
||||
3. **Factory function** signature changed to support dynamic context
|
||||
|
||||
### Migration Steps
|
||||
|
||||
1. **Update context calls** - Wrap context objects in functions
|
||||
2. **Replace service injection** - Use `logger()` factory instead of injecting `LoggingService`
|
||||
3. **Update error handling** - Error parameter is now optional in error() method
|
||||
4. **Add dynamic context** - Leverage new context functionality for better debugging
|
||||
|
||||
@@ -1,23 +1,53 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { LogLevel } from './log-level.enum';
|
||||
import { Sink } from './logging.types';
|
||||
import { Sink, LoggerContext } from './logging.types';
|
||||
|
||||
/**
|
||||
* A sink implementation that outputs logs to the browser console.
|
||||
* This is the primary logging destination for development environments.
|
||||
*
|
||||
* The sink maps each log level to the appropriate console method:
|
||||
* - Trace → console.trace (with stack trace)
|
||||
* - Debug → console.debug
|
||||
* - Info → console.info
|
||||
* - Warn → console.warn
|
||||
* - Error → console.error
|
||||
*
|
||||
* Context data and error objects are passed as additional arguments to provide
|
||||
* rich debugging information in the browser developer tools.
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* // Configure in app initialization
|
||||
* provideLogging(
|
||||
* withLogLevel(LogLevel.Debug),
|
||||
* withSink(ConsoleLogSink)
|
||||
* );
|
||||
* ```
|
||||
*/
|
||||
@Injectable()
|
||||
export class ConsoleLogSink implements Sink {
|
||||
/**
|
||||
* Logs a message to the browser console with the appropriate log level.
|
||||
* @param level The log level.
|
||||
* @param message The log message.
|
||||
* @param context Optional context data.
|
||||
* @param error Optional error object.
|
||||
* The console method used depends on the log level, and context/error data
|
||||
* is passed as additional arguments for improved debugging experience.
|
||||
*
|
||||
* @param level - The severity level of the log message
|
||||
* @param message - The main log message content
|
||||
* @param context - Optional structured data or metadata about the log event
|
||||
* @param error - Optional error object when logging errors
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* const sink = new ConsoleLogSink();
|
||||
* sink.log(LogLevel.Info, 'User logged in', { userId: '123' });
|
||||
* sink.log(LogLevel.Error, 'API failed', { endpoint: '/users' }, new Error('Network error'));
|
||||
* ```
|
||||
*/
|
||||
log(
|
||||
level: LogLevel,
|
||||
message: string,
|
||||
context?: unknown,
|
||||
context?: LoggerContext,
|
||||
error?: Error,
|
||||
): void {
|
||||
switch (level) {
|
||||
|
||||
@@ -133,10 +133,10 @@ export interface LoggerApi {
|
||||
* Use for error conditions that affect functionality.
|
||||
*
|
||||
* @param message - The error message to log
|
||||
* @param error - Any error object or value that caused this error condition
|
||||
* @param error - Optional error object that caused this error condition
|
||||
* @param context - Optional context data associated with the error
|
||||
*/
|
||||
error(message: string, error: unknown, context?: () => LoggerContext): void;
|
||||
error(message: string, error?: Error, context?: () => LoggerContext): void;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -90,7 +90,7 @@ export const ReturnTaskListStore = signalStore(
|
||||
* @param error - The error that occurred during the fetch operation
|
||||
*/
|
||||
fetchError(id: number, error: unknown) {
|
||||
logService.error('Error fetching task list items', error);
|
||||
logService.error('Error fetching task list items', error as Error);
|
||||
patchState(
|
||||
store,
|
||||
updateEntity({
|
||||
|
||||
769
package-lock.json
generated
769
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
13
package.json
13
package.json
@@ -58,7 +58,7 @@
|
||||
"zone.js": "~0.15.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "20.0.2",
|
||||
"@angular-devkit/build-angular": "20.0.3",
|
||||
"@angular-devkit/core": "20.0.2",
|
||||
"@angular-devkit/schematics": "20.0.2",
|
||||
"@angular/cli": "20.0.2",
|
||||
@@ -101,7 +101,7 @@
|
||||
"jest-preset-angular": "14.6.0",
|
||||
"jsonc-eslint-parser": "^2.1.0",
|
||||
"ng-mocks": "14.13.5",
|
||||
"ng-packagr": "^20.0.0",
|
||||
"ng-packagr": "20.0.1",
|
||||
"ng-swagger-gen": "^2.3.1",
|
||||
"nx": "21.2.0",
|
||||
"postcss": "^8.5.3",
|
||||
@@ -119,12 +119,7 @@
|
||||
"@esbuild/linux-x64": "^0.25.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=22.13.0",
|
||||
"npm": ">=11.0.0"
|
||||
},
|
||||
"overrides": {
|
||||
"jest-environment-jsdom": {
|
||||
"jsdom": "26.0.0"
|
||||
}
|
||||
"node": ">=22.0.0",
|
||||
"npm": ">=10.0.0"
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user