Skip to content

Commit c837042

Browse files
authored
Merge pull request #7 from smartlabsAT/feature/replace-console-logging-with-logger-system
Replace direct console usage with proper logging system
2 parents c37ccc1 + 246f5e0 commit c837042

File tree

4 files changed

+256
-12
lines changed

4 files changed

+256
-12
lines changed

src/BreakpointContext.tsx

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, { createContext, useContext, useMemo, useEffect } from 'react';
22
import { useResizeDetector } from 'react-resize-detector';
3+
import { logger } from './utils/logger';
34

45
export type Breakpoint = string; // Allow arbitrary breakpoint names
56

@@ -96,9 +97,10 @@ export const BreakpointProvider: React.FC<BreakpointProviderProps> = ({
9697

9798
if (duplicates.length > 0) {
9899
if (shouldLog) {
99-
console.error(
100-
'❌ BreakpointProvider: Duplicate breakpoint values detected. This may lead to unexpected behavior.'
101-
);
100+
logger.error('Duplicate breakpoint values detected', {
101+
duplicates: duplicates.map(([key, value]) => ({ key, value })),
102+
message: 'This may lead to unexpected behavior',
103+
});
102104
}
103105
}
104106
}, [sortedBreakpoints, shouldLog]);
@@ -119,7 +121,10 @@ export const BreakpointProvider: React.FC<BreakpointProviderProps> = ({
119121
useEffect(() => {
120122
if (width === undefined || width === 0) {
121123
if (shouldLog) {
122-
console.error('❌ BreakpointProvider: element width is undefined or 0');
124+
logger.error('Element width is undefined or 0', {
125+
width,
126+
message: 'Element cannot be measured properly',
127+
});
123128
}
124129
}
125130
}, [width, shouldLog]);
@@ -129,15 +134,20 @@ export const BreakpointProvider: React.FC<BreakpointProviderProps> = ({
129134
if (width !== undefined && width > 0 && currentBreakpoint === null) {
130135
if (sortedBreakpoints.length > 0 && width < sortedBreakpoints[0][1]) {
131136
if (shouldLog) {
132-
console.error(
133-
`❌ BreakpointProvider: The current width (${width}px) is less than the smallest breakpoint value (${sortedBreakpoints[0][1]}px). Consider including a breakpoint with a value of 0 to cover all cases.`
134-
);
137+
logger.error('Current width is less than smallest breakpoint', {
138+
currentWidth: width,
139+
smallestBreakpoint: sortedBreakpoints[0][1],
140+
smallestBreakpointName: sortedBreakpoints[0][0],
141+
suggestion: 'Consider including a breakpoint with a value of 0 to cover all cases',
142+
});
135143
}
136144
} else {
137145
if (shouldLog) {
138-
console.error(
139-
'❌ BreakpointProvider: No breakpoint could be determined from the provided configuration. Check your breakpoints object.'
140-
);
146+
logger.error('No breakpoint could be determined', {
147+
width,
148+
breakpointsConfig: sortedBreakpoints,
149+
suggestion: 'Check your breakpoints object configuration',
150+
});
141151
}
142152
}
143153
}

src/utils/logger.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
export interface Logger {
2+
debug(message: string, data?: Record<string, unknown>): void;
3+
info(message: string, data?: Record<string, unknown>): void;
4+
warn(message: string, data?: Record<string, unknown>): void;
5+
error(message: string, data?: Record<string, unknown>): void;
6+
}
7+
8+
interface LogEntry {
9+
level: 'debug' | 'info' | 'warn' | 'error';
10+
message: string;
11+
data?: Record<string, unknown>;
12+
timestamp: string;
13+
context: string;
14+
}
15+
16+
class BreakpointLogger implements Logger {
17+
private shouldLog(level: string): boolean {
18+
const isProduction = process.env.NODE_ENV === 'production';
19+
if (isProduction) return level === 'error';
20+
return true; // Log everything in non-production environments
21+
}
22+
23+
private formatMessage(level: string, message: string, data?: Record<string, unknown>): void {
24+
if (!this.shouldLog(level)) return;
25+
26+
const entry: LogEntry = {
27+
level: level as LogEntry['level'],
28+
message,
29+
data,
30+
timestamp: new Date().toISOString(),
31+
context: 'BreakpointProvider',
32+
};
33+
34+
// Use appropriate console method based on level
35+
const consoleMethod = level === 'error' ? console.error : level === 'warn' ? console.warn : console.log;
36+
37+
const isProduction = process.env.NODE_ENV === 'production';
38+
// In production, only log errors. In non-production, log everything.
39+
if (!isProduction || level === 'error') {
40+
consoleMethod(`[${entry.context}] ${entry.message}`, entry.data || '');
41+
}
42+
}
43+
44+
debug(message: string, data?: Record<string, unknown>): void {
45+
this.formatMessage('debug', message, data);
46+
}
47+
48+
info(message: string, data?: Record<string, unknown>): void {
49+
this.formatMessage('info', message, data);
50+
}
51+
52+
warn(message: string, data?: Record<string, unknown>): void {
53+
this.formatMessage('warn', message, data);
54+
}
55+
56+
error(message: string, data?: Record<string, unknown>): void {
57+
this.formatMessage('error', message, data);
58+
}
59+
}
60+
61+
export const logger = new BreakpointLogger();

tests/BreakpointContext.test.tsx

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,12 @@ describe('BreakpointContext', () => {
111111
</BreakpointProvider>
112112
);
113113
expect(console.error).toHaveBeenCalledWith(
114-
expect.stringContaining('The current width (100px) is less than the smallest breakpoint value (200px).')
114+
'[BreakpointProvider] Current width is less than smallest breakpoint',
115+
expect.objectContaining({
116+
currentWidth: 100,
117+
smallestBreakpoint: 200,
118+
smallestBreakpointName: 'XS'
119+
})
115120
);
116121
});
117122

@@ -128,7 +133,11 @@ describe('BreakpointContext', () => {
128133
</BreakpointProvider>
129134
);
130135
expect(console.error).toHaveBeenCalledWith(
131-
'❌ BreakpointProvider: Duplicate breakpoint values detected. This may lead to unexpected behavior.'
136+
'[BreakpointProvider] Duplicate breakpoint values detected',
137+
expect.objectContaining({
138+
duplicates: expect.any(Array),
139+
message: 'This may lead to unexpected behavior'
140+
})
132141
);
133142
});
134143
});

tests/logger.test.ts

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2+
import { logger } from '../src/utils/logger';
3+
4+
describe('Logger', () => {
5+
let consoleSpy: {
6+
log: ReturnType<typeof vi.spyOn>;
7+
warn: ReturnType<typeof vi.spyOn>;
8+
error: ReturnType<typeof vi.spyOn>;
9+
};
10+
11+
beforeEach(() => {
12+
consoleSpy = {
13+
log: vi.spyOn(console, 'log').mockImplementation(() => {}),
14+
warn: vi.spyOn(console, 'warn').mockImplementation(() => {}),
15+
error: vi.spyOn(console, 'error').mockImplementation(() => {})
16+
};
17+
});
18+
19+
afterEach(() => {
20+
vi.restoreAllMocks();
21+
// Reset NODE_ENV to original value
22+
process.env.NODE_ENV = 'test';
23+
});
24+
25+
describe('in development mode', () => {
26+
beforeEach(() => {
27+
process.env.NODE_ENV = 'development';
28+
});
29+
30+
it('should log debug messages', () => {
31+
logger.debug('Test debug message', { key: 'value' });
32+
33+
expect(consoleSpy.log).toHaveBeenCalledWith(
34+
'[BreakpointProvider] Test debug message',
35+
{ key: 'value' }
36+
);
37+
});
38+
39+
it('should log info messages', () => {
40+
logger.info('Test info message', { key: 'value' });
41+
42+
expect(consoleSpy.log).toHaveBeenCalledWith(
43+
'[BreakpointProvider] Test info message',
44+
{ key: 'value' }
45+
);
46+
});
47+
48+
it('should log warn messages', () => {
49+
logger.warn('Test warn message', { key: 'value' });
50+
51+
expect(consoleSpy.warn).toHaveBeenCalledWith(
52+
'[BreakpointProvider] Test warn message',
53+
{ key: 'value' }
54+
);
55+
});
56+
57+
it('should log error messages', () => {
58+
logger.error('Test error message', { key: 'value' });
59+
60+
expect(consoleSpy.error).toHaveBeenCalledWith(
61+
'[BreakpointProvider] Test error message',
62+
{ key: 'value' }
63+
);
64+
});
65+
66+
it('should handle logging without data parameter', () => {
67+
logger.info('Test message without data');
68+
69+
expect(consoleSpy.log).toHaveBeenCalledWith(
70+
'[BreakpointProvider] Test message without data',
71+
''
72+
);
73+
});
74+
});
75+
76+
describe('in production mode', () => {
77+
beforeEach(() => {
78+
process.env.NODE_ENV = 'production';
79+
});
80+
81+
it('should not log debug messages', () => {
82+
logger.debug('Test debug message');
83+
84+
expect(consoleSpy.log).not.toHaveBeenCalled();
85+
});
86+
87+
it('should not log info messages', () => {
88+
logger.info('Test info message');
89+
90+
expect(consoleSpy.log).not.toHaveBeenCalled();
91+
});
92+
93+
it('should not log warn messages', () => {
94+
logger.warn('Test warn message');
95+
96+
expect(consoleSpy.warn).not.toHaveBeenCalled();
97+
});
98+
99+
it('should still log error messages', () => {
100+
logger.error('Test error message', { key: 'value' });
101+
102+
expect(consoleSpy.error).toHaveBeenCalledWith(
103+
'[BreakpointProvider] Test error message',
104+
{ key: 'value' }
105+
);
106+
});
107+
});
108+
109+
describe('in test mode (non-development, non-production)', () => {
110+
beforeEach(() => {
111+
process.env.NODE_ENV = 'test';
112+
});
113+
114+
it('should log all message types in test mode', () => {
115+
logger.debug('Debug in test');
116+
logger.info('Info in test');
117+
logger.warn('Warn in test');
118+
logger.error('Error in test');
119+
120+
expect(consoleSpy.log).toHaveBeenCalledTimes(2); // debug and info
121+
expect(consoleSpy.warn).toHaveBeenCalledTimes(1);
122+
expect(consoleSpy.error).toHaveBeenCalledTimes(1);
123+
});
124+
});
125+
126+
describe('structured data logging', () => {
127+
beforeEach(() => {
128+
process.env.NODE_ENV = 'development';
129+
});
130+
131+
it('should log complex structured data correctly', () => {
132+
const complexData = {
133+
duplicates: [
134+
{ key: 'SM', value: 500 },
135+
{ key: 'MD', value: 500 }
136+
],
137+
message: 'This may lead to unexpected behavior'
138+
};
139+
140+
logger.error('Duplicate breakpoint values detected', complexData);
141+
142+
expect(consoleSpy.error).toHaveBeenCalledWith(
143+
'[BreakpointProvider] Duplicate breakpoint values detected',
144+
complexData
145+
);
146+
});
147+
148+
it('should handle nested object data', () => {
149+
const nestedData = {
150+
currentWidth: 400,
151+
smallestBreakpoint: 500,
152+
smallestBreakpointName: 'SM',
153+
suggestion: 'Consider including a breakpoint with a value of 0'
154+
};
155+
156+
logger.error('Current width is less than smallest breakpoint', nestedData);
157+
158+
expect(consoleSpy.error).toHaveBeenCalledWith(
159+
'[BreakpointProvider] Current width is less than smallest breakpoint',
160+
nestedData
161+
);
162+
});
163+
});
164+
});

0 commit comments

Comments
 (0)