@ebowwa/crm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +174 -0
- package/dist/cli/commands/activities.d.ts +11 -0
- package/dist/cli/commands/activities.d.ts.map +1 -0
- package/dist/cli/commands/activities.js +427 -0
- package/dist/cli/commands/activities.js.map +1 -0
- package/dist/cli/commands/contacts.d.ts +11 -0
- package/dist/cli/commands/contacts.d.ts.map +1 -0
- package/dist/cli/commands/contacts.js +458 -0
- package/dist/cli/commands/contacts.js.map +1 -0
- package/dist/cli/commands/deals.d.ts +11 -0
- package/dist/cli/commands/deals.d.ts.map +1 -0
- package/dist/cli/commands/deals.js +498 -0
- package/dist/cli/commands/deals.js.map +1 -0
- package/dist/cli/commands/media.d.ts +11 -0
- package/dist/cli/commands/media.d.ts.map +1 -0
- package/dist/cli/commands/media.js +417 -0
- package/dist/cli/commands/media.js.map +1 -0
- package/dist/cli/commands/search.d.ts +11 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +346 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/index.d.ts +13 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +173 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/repl.d.ts +15 -0
- package/dist/cli/repl.d.ts.map +1 -0
- package/dist/cli/repl.js +318 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/cli/utils/config.d.ts +91 -0
- package/dist/cli/utils/config.d.ts.map +1 -0
- package/dist/cli/utils/config.js +212 -0
- package/dist/cli/utils/config.js.map +1 -0
- package/dist/cli/utils/output.d.ts +136 -0
- package/dist/cli/utils/output.d.ts.map +1 -0
- package/dist/cli/utils/output.js +323 -0
- package/dist/cli/utils/output.js.map +1 -0
- package/dist/cli/utils/prompt.d.ts +81 -0
- package/dist/cli/utils/prompt.d.ts.map +1 -0
- package/dist/cli/utils/prompt.js +341 -0
- package/dist/cli/utils/prompt.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +8 -0
- package/dist/cli.js.map +1 -0
- package/dist/core/index.d.ts +6 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +32 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/schemas.d.ts +3050 -0
- package/dist/core/schemas.d.ts.map +1 -0
- package/dist/core/schemas.js +667 -0
- package/dist/core/schemas.js.map +1 -0
- package/dist/core/types.d.ts +597 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +8 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/mcp/index.d.ts +14 -0
- package/dist/mcp/index.d.ts.map +1 -0
- package/dist/mcp/index.js +11 -0
- package/dist/mcp/index.js.map +1 -0
- package/dist/mcp/server.d.ts +13 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +18 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/storage/client.d.ts +109 -0
- package/dist/mcp/storage/client.d.ts.map +1 -0
- package/dist/mcp/storage/client.js +355 -0
- package/dist/mcp/storage/client.js.map +1 -0
- package/dist/mcp/storage/index.d.ts +7 -0
- package/dist/mcp/storage/index.d.ts.map +1 -0
- package/dist/mcp/storage/index.js +6 -0
- package/dist/mcp/storage/index.js.map +1 -0
- package/dist/mcp/storage/types.d.ts +44 -0
- package/dist/mcp/storage/types.d.ts.map +1 -0
- package/dist/mcp/storage/types.js +35 -0
- package/dist/mcp/storage/types.js.map +1 -0
- package/dist/mcp/tools/definitions.d.ts +16 -0
- package/dist/mcp/tools/definitions.d.ts.map +1 -0
- package/dist/mcp/tools/definitions.js +914 -0
- package/dist/mcp/tools/definitions.js.map +1 -0
- package/dist/mcp/tools/handlers.d.ts +50 -0
- package/dist/mcp/tools/handlers.d.ts.map +1 -0
- package/dist/mcp/tools/handlers.js +760 -0
- package/dist/mcp/tools/handlers.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +7 -0
- package/dist/mcp/tools/index.d.ts.map +1 -0
- package/dist/mcp/tools/index.js +6 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/types.d.ts +314 -0
- package/dist/mcp/tools/types.d.ts.map +1 -0
- package/dist/mcp/tools/types.js +5 -0
- package/dist/mcp/tools/types.js.map +1 -0
- package/dist/mcp/transports/stdio.d.ts +27 -0
- package/dist/mcp/transports/stdio.d.ts.map +1 -0
- package/dist/mcp/transports/stdio.js +237 -0
- package/dist/mcp/transports/stdio.js.map +1 -0
- package/dist/telemetry/index.d.ts +58 -0
- package/dist/telemetry/index.d.ts.map +1 -0
- package/dist/telemetry/index.js +109 -0
- package/dist/telemetry/index.js.map +1 -0
- package/dist/telemetry/logger.d.ts +116 -0
- package/dist/telemetry/logger.d.ts.map +1 -0
- package/dist/telemetry/logger.js +256 -0
- package/dist/telemetry/logger.js.map +1 -0
- package/dist/telemetry/metrics.d.ts +115 -0
- package/dist/telemetry/metrics.d.ts.map +1 -0
- package/dist/telemetry/metrics.js +292 -0
- package/dist/telemetry/metrics.js.map +1 -0
- package/dist/telemetry/tracer.d.ts +227 -0
- package/dist/telemetry/tracer.d.ts.map +1 -0
- package/dist/telemetry/tracer.js +355 -0
- package/dist/telemetry/tracer.js.map +1 -0
- package/dist/web/app.d.ts +2 -0
- package/dist/web/app.d.ts.map +1 -0
- package/dist/web/app.js +115 -0
- package/dist/web/app.js.map +1 -0
- package/dist/web/components/ContactList.d.ts +3 -0
- package/dist/web/components/ContactList.d.ts.map +1 -0
- package/dist/web/components/ContactList.js +262 -0
- package/dist/web/components/ContactList.js.map +1 -0
- package/dist/web/components/Dashboard.d.ts +3 -0
- package/dist/web/components/Dashboard.d.ts.map +1 -0
- package/dist/web/components/Dashboard.js +158 -0
- package/dist/web/components/Dashboard.js.map +1 -0
- package/dist/web/components/DealPipeline.d.ts +3 -0
- package/dist/web/components/DealPipeline.d.ts.map +1 -0
- package/dist/web/components/DealPipeline.js +306 -0
- package/dist/web/components/DealPipeline.js.map +1 -0
- package/dist/web/index.d.ts +2 -0
- package/dist/web/index.d.ts.map +1 -0
- package/dist/web/index.js +269 -0
- package/dist/web/index.js.map +1 -0
- package/dist/web/types.d.ts +75 -0
- package/dist/web/types.d.ts.map +1 -0
- package/dist/web/types.js +3 -0
- package/dist/web/types.js.map +1 -0
- package/native/index.d.ts +571 -0
- package/native/index.js +687 -0
- package/package.json +105 -0
- package/src/cli/commands/activities.ts +543 -0
- package/src/cli/commands/contacts.ts +563 -0
- package/src/cli/commands/deals.ts +637 -0
- package/src/cli/commands/media.ts +521 -0
- package/src/cli/commands/search.ts +426 -0
- package/src/cli/index.ts +203 -0
- package/src/cli/repl.ts +379 -0
- package/src/cli/utils/config.ts +299 -0
- package/src/cli/utils/output.ts +386 -0
- package/src/cli/utils/prompt.ts +444 -0
- package/src/cli.ts +11 -0
- package/src/core/index.ts +184 -0
- package/src/core/schemas.ts +770 -0
- package/src/core/types.ts +969 -0
- package/src/index.ts +8 -0
- package/src/mcp/index.ts +17 -0
- package/src/mcp/server.ts +26 -0
- package/src/mcp/storage/client.ts +408 -0
- package/src/mcp/storage/index.ts +7 -0
- package/src/mcp/storage/types.ts +72 -0
- package/src/mcp/tools/definitions.ts +961 -0
- package/src/mcp/tools/handlers.ts +805 -0
- package/src/mcp/tools/index.ts +7 -0
- package/src/mcp/tools/types.ts +390 -0
- package/src/mcp/transports/stdio.ts +225 -0
- package/src/telemetry/index.ts +131 -0
- package/src/telemetry/logger.ts +318 -0
- package/src/telemetry/metrics.ts +393 -0
- package/src/telemetry/tracer.ts +487 -0
- package/src/web/api/activities.ts +41 -0
- package/src/web/api/contacts.ts +114 -0
- package/src/web/api/deals.ts +108 -0
- package/src/web/api/media.ts +98 -0
- package/src/web/app.tsx +143 -0
- package/src/web/components/ActivityFeed.tsx +195 -0
- package/src/web/components/ContactList.tsx +340 -0
- package/src/web/components/Dashboard.tsx +214 -0
- package/src/web/components/DealPipeline.tsx +405 -0
- package/src/web/components/MediaGallery.tsx +334 -0
- package/src/web/index.html +14 -0
- package/src/web/index.ts +326 -0
- package/src/web/styles/main.css +180 -0
- package/src/web/types.ts +311 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telemetry module exports
|
|
3
|
+
*
|
|
4
|
+
* Provides unified access to logging, metrics, and tracing.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { Logger, createLogger, logger, type LogLevel, type LogContext, type LogEntry, type LoggerConfig } from './logger.js';
|
|
8
|
+
export { MetricsRegistry, metrics, initializeCrmMetrics, type MetricType, type MetricLabel, type MetricValue, type MetricDefinition, type HistogramOptions } from './metrics.js';
|
|
9
|
+
export { Tracer, SpanHandle, tracer, createTracer, withSpan, type TraceContext, type SpanAttribute, type SpanEvent, type SpanLink, type SpanStatus, type Span, type TracerConfig } from './tracer.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Combined telemetry context for a request/operation
|
|
13
|
+
*/
|
|
14
|
+
export class TelemetryContext {
|
|
15
|
+
public readonly logger: ReturnType<typeof logger.child>;
|
|
16
|
+
private span?: SpanHandle;
|
|
17
|
+
private timer?: () => number;
|
|
18
|
+
|
|
19
|
+
constructor(
|
|
20
|
+
public readonly operation: string,
|
|
21
|
+
context?: Record<string, unknown>
|
|
22
|
+
) {
|
|
23
|
+
this.logger = logger.child({ operation, ...context });
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Start telemetry tracking for an operation
|
|
28
|
+
*/
|
|
29
|
+
start(labels?: Record<string, string>): void {
|
|
30
|
+
this.span = tracer.startSpan(this.operation, {
|
|
31
|
+
kind: 'internal',
|
|
32
|
+
attributes: Object.entries(labels ?? {}).map(([key, value]) => ({
|
|
33
|
+
key,
|
|
34
|
+
value,
|
|
35
|
+
})),
|
|
36
|
+
});
|
|
37
|
+
this.timer = metrics.startTimer('operation_duration_seconds', {
|
|
38
|
+
operation: this.operation,
|
|
39
|
+
...labels,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Add an event to the span
|
|
45
|
+
*/
|
|
46
|
+
addEvent(name: string, attributes?: Record<string, unknown>): void {
|
|
47
|
+
this.span?.addEvent(name, attributes as Record<string, string | number | boolean>);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Set an attribute on the span
|
|
52
|
+
*/
|
|
53
|
+
setAttribute(key: string, value: string | number | boolean): void {
|
|
54
|
+
this.span?.setAttribute(key, value);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Increment a metric
|
|
59
|
+
*/
|
|
60
|
+
incrementMetric(name: string, value: number = 1, labels?: Record<string, string | number>): void {
|
|
61
|
+
metrics.increment(name, value, labels ?? {});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Mark the operation as successful and end tracking
|
|
66
|
+
*/
|
|
67
|
+
success(message?: string): void {
|
|
68
|
+
if (message) {
|
|
69
|
+
this.logger.info(message);
|
|
70
|
+
}
|
|
71
|
+
this.span?.ok();
|
|
72
|
+
this.end();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Mark the operation as failed and end tracking
|
|
77
|
+
*/
|
|
78
|
+
failure(error: Error | string): void {
|
|
79
|
+
const err = typeof error === 'string' ? new Error(error) : error;
|
|
80
|
+
this.logger.error(err.message, err);
|
|
81
|
+
this.span?.recordException(err);
|
|
82
|
+
this.end();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* End telemetry tracking
|
|
87
|
+
*/
|
|
88
|
+
private end(): void {
|
|
89
|
+
if (this.timer) {
|
|
90
|
+
const duration = this.timer();
|
|
91
|
+
this.span?.setAttribute('duration_ms', duration * 1000);
|
|
92
|
+
}
|
|
93
|
+
this.span?.end();
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Create a telemetry context for an operation
|
|
99
|
+
*/
|
|
100
|
+
export function createTelemetryContext(
|
|
101
|
+
operation: string,
|
|
102
|
+
context?: Record<string, unknown>
|
|
103
|
+
): TelemetryContext {
|
|
104
|
+
const ctx = new TelemetryContext(operation, context);
|
|
105
|
+
ctx.start();
|
|
106
|
+
return ctx;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Wrap an async function with telemetry
|
|
111
|
+
*/
|
|
112
|
+
export async function withTelemetry<T>(
|
|
113
|
+
operation: string,
|
|
114
|
+
fn: (ctx: TelemetryContext) => Promise<T>,
|
|
115
|
+
options?: {
|
|
116
|
+
context?: Record<string, unknown>;
|
|
117
|
+
labels?: Record<string, string>;
|
|
118
|
+
}
|
|
119
|
+
): Promise<T> {
|
|
120
|
+
const ctx = new TelemetryContext(operation, options?.context);
|
|
121
|
+
ctx.start(options?.labels);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const result = await fn(ctx);
|
|
125
|
+
ctx.success();
|
|
126
|
+
return result;
|
|
127
|
+
} catch (error) {
|
|
128
|
+
ctx.failure(error instanceof Error ? error : String(error));
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logger module for CRM system
|
|
3
|
+
*
|
|
4
|
+
* Provides structured logging with multiple log levels and formatters.
|
|
5
|
+
* Supports JSON output for production and pretty printing for development.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export type LogLevel = 'trace' | 'debug' | 'info' | 'warn' | 'error' | 'fatal';
|
|
9
|
+
|
|
10
|
+
export interface LogContext {
|
|
11
|
+
[key: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface LogEntry {
|
|
15
|
+
timestamp: string;
|
|
16
|
+
level: LogLevel;
|
|
17
|
+
message: string;
|
|
18
|
+
context?: LogContext;
|
|
19
|
+
error?: {
|
|
20
|
+
name: string;
|
|
21
|
+
message: string;
|
|
22
|
+
stack?: string;
|
|
23
|
+
};
|
|
24
|
+
duration?: number;
|
|
25
|
+
traceId?: string;
|
|
26
|
+
spanId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface LoggerConfig {
|
|
30
|
+
level: LogLevel;
|
|
31
|
+
service: string;
|
|
32
|
+
version: string;
|
|
33
|
+
env: 'development' | 'production' | 'test';
|
|
34
|
+
prettyPrint?: boolean;
|
|
35
|
+
includeStackTrace?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const LOG_LEVELS: Record<LogLevel, number> = {
|
|
39
|
+
trace: 0,
|
|
40
|
+
debug: 1,
|
|
41
|
+
info: 2,
|
|
42
|
+
warn: 3,
|
|
43
|
+
error: 4,
|
|
44
|
+
fatal: 5,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Structured logger with context support
|
|
49
|
+
*/
|
|
50
|
+
export class Logger {
|
|
51
|
+
private readonly level: number;
|
|
52
|
+
private readonly config: LoggerConfig;
|
|
53
|
+
private readonly defaultContext: LogContext;
|
|
54
|
+
|
|
55
|
+
constructor(config: Partial<LoggerConfig> & { service: string }) {
|
|
56
|
+
this.config = {
|
|
57
|
+
level: config.level ?? (process.env.NODE_ENV === 'production' ? 'info' : 'debug'),
|
|
58
|
+
version: config.version ?? '0.1.0',
|
|
59
|
+
env: (config.env as LoggerConfig['env']) ?? (process.env.NODE_ENV as LoggerConfig['env']) ?? 'development',
|
|
60
|
+
prettyPrint: config.prettyPrint ?? process.env.LOG_PRETTY === 'true',
|
|
61
|
+
includeStackTrace: config.includeStackTrace ?? true,
|
|
62
|
+
...config,
|
|
63
|
+
};
|
|
64
|
+
this.level = LOG_LEVELS[this.config.level];
|
|
65
|
+
this.defaultContext = {
|
|
66
|
+
service: this.config.service,
|
|
67
|
+
version: this.config.version,
|
|
68
|
+
env: this.config.env,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Create a child logger with additional context
|
|
74
|
+
*/
|
|
75
|
+
child(context: LogContext): ChildLogger {
|
|
76
|
+
return new ChildLogger(this, { ...this.defaultContext, ...context });
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Log at trace level (most verbose)
|
|
81
|
+
*/
|
|
82
|
+
trace(message: string, context?: LogContext): void {
|
|
83
|
+
this.log('trace', message, context);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Log at debug level
|
|
88
|
+
*/
|
|
89
|
+
debug(message: string, context?: LogContext): void {
|
|
90
|
+
this.log('debug', message, context);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Log at info level
|
|
95
|
+
*/
|
|
96
|
+
info(message: string, context?: LogContext): void {
|
|
97
|
+
this.log('info', message, context);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Log at warn level
|
|
102
|
+
*/
|
|
103
|
+
warn(message: string, context?: LogContext): void {
|
|
104
|
+
this.log('warn', message, context);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Log at error level
|
|
109
|
+
*/
|
|
110
|
+
error(message: string, error?: Error | unknown, context?: LogContext): void {
|
|
111
|
+
const entry: LogEntry = this.createEntry('error', message, context);
|
|
112
|
+
|
|
113
|
+
if (error instanceof Error) {
|
|
114
|
+
entry.error = {
|
|
115
|
+
name: error.name,
|
|
116
|
+
message: error.message,
|
|
117
|
+
stack: this.config.includeStackTrace ? error.stack : undefined,
|
|
118
|
+
};
|
|
119
|
+
} else if (error !== undefined) {
|
|
120
|
+
entry.context = { ...entry.context, error: String(error) };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this.output(entry);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Log at fatal level (application should exit after this)
|
|
128
|
+
*/
|
|
129
|
+
fatal(message: string, error?: Error | unknown, context?: LogContext): void {
|
|
130
|
+
const entry: LogEntry = this.createEntry('fatal', message, context);
|
|
131
|
+
|
|
132
|
+
if (error instanceof Error) {
|
|
133
|
+
entry.error = {
|
|
134
|
+
name: error.name,
|
|
135
|
+
message: error.message,
|
|
136
|
+
stack: this.config.includeStackTrace ? error.stack : undefined,
|
|
137
|
+
};
|
|
138
|
+
} else if (error !== undefined) {
|
|
139
|
+
entry.context = { ...entry.context, error: String(error) };
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
this.output(entry);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Log with timing information
|
|
147
|
+
*/
|
|
148
|
+
time(message: string, durationMs: number, context?: LogContext): void {
|
|
149
|
+
const entry = this.createEntry('info', message, context);
|
|
150
|
+
entry.duration = durationMs;
|
|
151
|
+
this.output(entry);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Create a timer that logs duration when stopped
|
|
156
|
+
*/
|
|
157
|
+
startTimer(message: string, context?: LogContext): () => number {
|
|
158
|
+
const start = performance.now();
|
|
159
|
+
return () => {
|
|
160
|
+
const duration = performance.now() - start;
|
|
161
|
+
this.time(message, duration, context);
|
|
162
|
+
return duration;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Add trace context to logs
|
|
168
|
+
*/
|
|
169
|
+
withTrace(traceId: string, spanId?: string): Logger {
|
|
170
|
+
const logger = new Logger(this.config);
|
|
171
|
+
(logger as unknown as { defaultContext: LogContext }).defaultContext = {
|
|
172
|
+
...this.defaultContext,
|
|
173
|
+
traceId,
|
|
174
|
+
spanId,
|
|
175
|
+
};
|
|
176
|
+
return logger;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private log(level: LogLevel, message: string, context?: LogContext): void {
|
|
180
|
+
if (LOG_LEVELS[level] < this.level) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const entry = this.createEntry(level, message, context);
|
|
185
|
+
this.output(entry);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
private createEntry(level: LogLevel, message: string, context?: LogContext): LogEntry {
|
|
189
|
+
const entry: LogEntry = {
|
|
190
|
+
timestamp: new Date().toISOString(),
|
|
191
|
+
level,
|
|
192
|
+
message,
|
|
193
|
+
context: { ...this.defaultContext, ...context },
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
return entry;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
private output(entry: LogEntry): void {
|
|
200
|
+
if (this.config.prettyPrint && this.config.env !== 'production') {
|
|
201
|
+
this.prettyOutput(entry);
|
|
202
|
+
} else {
|
|
203
|
+
this.jsonOutput(entry);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
private jsonOutput(entry: LogEntry): void {
|
|
208
|
+
const output = JSON.stringify(entry);
|
|
209
|
+
|
|
210
|
+
if (entry.level === 'fatal' || entry.level === 'error') {
|
|
211
|
+
console.error(output);
|
|
212
|
+
} else if (entry.level === 'warn') {
|
|
213
|
+
console.warn(output);
|
|
214
|
+
} else {
|
|
215
|
+
console.log(output);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
private prettyOutput(entry: LogEntry): void {
|
|
220
|
+
const levelColors: Record<LogLevel, string> = {
|
|
221
|
+
trace: '\x1b[90m', // gray
|
|
222
|
+
debug: '\x1b[36m', // cyan
|
|
223
|
+
info: '\x1b[32m', // green
|
|
224
|
+
warn: '\x1b[33m', // yellow
|
|
225
|
+
error: '\x1b[31m', // red
|
|
226
|
+
fatal: '\x1b[35m', // magenta
|
|
227
|
+
};
|
|
228
|
+
const reset = '\x1b[0m';
|
|
229
|
+
const color = levelColors[entry.level];
|
|
230
|
+
|
|
231
|
+
const timestamp = entry.timestamp.split('T')[1]?.split('.')[0] ?? entry.timestamp;
|
|
232
|
+
const level = entry.level.toUpperCase().padEnd(5);
|
|
233
|
+
|
|
234
|
+
let output = `${color}[${timestamp}] ${level}${reset} ${entry.message}`;
|
|
235
|
+
|
|
236
|
+
if (entry.context && Object.keys(entry.context).length > 0) {
|
|
237
|
+
output += ` ${reset}\x1b[90m${JSON.stringify(entry.context)}\x1b[0m`;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (entry.duration !== undefined) {
|
|
241
|
+
output += ` ${reset}\x1b[90m(${entry.duration.toFixed(2)}ms)\x1b[0m`;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (entry.error) {
|
|
245
|
+
output += `\n \x1b[31m${entry.error.name}: ${entry.error.message}\x1b[0m`;
|
|
246
|
+
if (entry.error.stack && this.config.includeStackTrace) {
|
|
247
|
+
const stackLines = entry.error.stack.split('\n').slice(1, 5);
|
|
248
|
+
output += `\n \x1b[90m${stackLines.join('\n ')}\x1b[0m`;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (entry.level === 'fatal' || entry.level === 'error') {
|
|
253
|
+
console.error(output);
|
|
254
|
+
} else if (entry.level === 'warn') {
|
|
255
|
+
console.warn(output);
|
|
256
|
+
} else {
|
|
257
|
+
console.log(output);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Child logger with bound context
|
|
264
|
+
*/
|
|
265
|
+
class ChildLogger {
|
|
266
|
+
constructor(
|
|
267
|
+
private readonly parent: Logger,
|
|
268
|
+
private readonly context: LogContext
|
|
269
|
+
) {}
|
|
270
|
+
|
|
271
|
+
trace(message: string, additionalContext?: LogContext): void {
|
|
272
|
+
this.parent.trace(message, { ...this.context, ...additionalContext });
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
debug(message: string, additionalContext?: LogContext): void {
|
|
276
|
+
this.parent.debug(message, { ...this.context, ...additionalContext });
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
info(message: string, additionalContext?: LogContext): void {
|
|
280
|
+
this.parent.info(message, { ...this.context, ...additionalContext });
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
warn(message: string, additionalContext?: LogContext): void {
|
|
284
|
+
this.parent.warn(message, { ...this.context, ...additionalContext });
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
error(message: string, error?: Error | unknown, additionalContext?: LogContext): void {
|
|
288
|
+
this.parent.error(message, error, { ...this.context, ...additionalContext });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
fatal(message: string, error?: Error | unknown, additionalContext?: LogContext): void {
|
|
292
|
+
this.parent.fatal(message, error, { ...this.context, ...additionalContext });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
time(message: string, durationMs: number, additionalContext?: LogContext): void {
|
|
296
|
+
const stop = this.parent.startTimer(message, { ...this.context, ...additionalContext });
|
|
297
|
+
// Already logged by startTimer
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
startTimer(message: string, additionalContext?: LogContext): () => number {
|
|
301
|
+
return this.parent.startTimer(message, { ...this.context, ...additionalContext });
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Default logger instance
|
|
307
|
+
*/
|
|
308
|
+
export const logger = new Logger({
|
|
309
|
+
service: 'crm',
|
|
310
|
+
version: '0.1.0',
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Create a new logger instance
|
|
315
|
+
*/
|
|
316
|
+
export function createLogger(config: Partial<LoggerConfig> & { service: string }): Logger {
|
|
317
|
+
return new Logger(config);
|
|
318
|
+
}
|