We'll start with JavaScript and then move on to TypeScript for those who want stronger type safety and scalability in their projects.
Why Build a Global Logger?
A global logger is a centralized logging mechanism that allows you to log important messages from anywhere in your application. It helps:
- Track function execution and errors.
- Monitor the application behavior.
- Debug issues by logging detailed information.
- Separate different types of logs visually with color-coding (e.g., errors in red, warnings in orange).
Requirements for Our Logger:
- Color-Coded Output: To easily identify different log levels.
- Global Access: Should be accessible from anywhere in the program.
- Timestamps: Each log should include a timestamp to track when an event occurred.
- Function Name Logging: Optionally, log the function name where the logger is called.
JavaScript Implementation
We’ll start by building a color-coded global logger in plain JavaScript using the browser’s console.log
method and CSS-like formatting.
Step-by-Step Implementation:
const Logger = (() => {
const logLevels = {
INFO: 'INFO',
DEBUG: 'DEBUG',
ERROR: 'ERROR',
WARN: 'WARN',
};
const colors = {
INFO: 'color: green', // Green for info
DEBUG: 'color: blue', // Blue for debug
ERROR: 'color: red', // Red for error
WARN: 'color: orange', // Orange for warning
};
// Helper function to get the current timestamp
const getCurrentTimestamp = () => {
return new Date().toISOString();
};
// Logger function
const log = (level, message, functionName) => {
const color = colors[level] || 'color: black';
console.log(`%c[${getCurrentTimestamp()}] [${level}] ${functionName ? '[' + functionName + ']' : ''} - ${message}`, color);
};
return {
info: (message, functionName = '') => log(logLevels.INFO, message, functionName),
debug: (message, functionName = '') => log(logLevels.DEBUG, message, functionName),
error: (message, functionName = '') => log(logLevels.ERROR, message, functionName),
warn: (message, functionName = '') => log(logLevels.WARN, message, functionName),
};
})();
// Example usage of the global logger
function someFunction() {
Logger.info('Starting someFunction', 'someFunction');
Logger.debug('Processing data', 'someFunction');
try {
// Simulating execution step
let data = [1, 2, 3];
Logger.info('Data initialized', 'someFunction');
// Simulate a step that throws an error
throw new Error('Something went wrong');
} catch (error) {
Logger.error(`Error occurred: ${error.message}`, 'someFunction');
}
Logger.warn('Potential issue with someFunction', 'someFunction');
Logger.info('Ending someFunction', 'someFunction');
}
function anotherFunction() {
Logger.info('Starting anotherFunction', 'anotherFunction');
// Other execution steps...
Logger.info('Ending anotherFunction', 'anotherFunction');
}
// Call the functions to demonstrate the logger
someFunction();
anotherFunction();
How It Works:
- Color Coding: We use the %c syntax in console.log to apply CSS styles to log messages. For example, error messages are in red, info messages in green, and warn messages in orange.
- Global Access: The logger is encapsulated in an Immediately Invoked Function Expression (IIFE), creating a singleton that can be accessed anywhere in the program.
- Timestamps: Each log message includes a timestamp using the new Date().toISOString() method.
- Function Name: Optionally, you can pass the function name where the logger is called, making the logs more informative.
Example Output:
This approach is simple and effective, especially for browser-based JavaScript applications. However, if you're working with TypeScript, you’ll want a more type-safe version.
TypeScript Implementation
TypeScript brings the added benefit of type safety, which makes it easier to manage large-scale applications. We can improve the JavaScript logger by adding type annotations and stricter rules with TypeScript.
TypeScript Version:
type LogLevel = 'INFO' | 'DEBUG' | 'ERROR' | 'WARN';
class Logger {
private static logLevels: Record<LogLevel, string> = {
INFO: 'INFO',
DEBUG: 'DEBUG',
ERROR: 'ERROR',
WARN: 'WARN',
};
private static colors: Record<LogLevel, string> = {
INFO: 'color: green', // Green for info
DEBUG: 'color: blue', // Blue for debug
ERROR: 'color: red', // Red for error
WARN: 'color: orange', // Orange for warning
};
// Helper function to get the current timestamp
private static getCurrentTimestamp(): string {
return new Date().toISOString();
}
// Main logging function
private static log(level: LogLevel, message: string, functionName?: string): void {
const color = Logger.colors[level] || 'color: black';
console.log(`%c[${Logger.getCurrentTimestamp()}] [${level}] ${functionName ? '[' + functionName + ']' : ''} - ${message}`, color);
}
public static info(message: string, functionName?: string): void {
Logger.log(Logger.logLevels.INFO, message, functionName);
}
public static debug(message: string, functionName?: string): void {
Logger.log(Logger.logLevels.DEBUG, message, functionName);
}
public static error(message: string, functionName?: string): void {
Logger.log(Logger.logLevels.ERROR, message, functionName);
}
public static warn(message: string, functionName?: string): void {
Logger.log(Logger.logLevels.WARN, message, functionName);
}
}
// Example usage
function someFunction(): void {
Logger.info('Starting someFunction', 'someFunction');
Logger.debug('Processing data', 'someFunction');
try {
let data = [1, 2, 3];
Logger.info('Data initialized', 'someFunction');
// Simulating an error
throw new Error('Something went wrong');
} catch (error) {
Logger.error(`Error occurred: ${(error as Error).message}`, 'someFunction');
}
Logger.warn('Potential issue with someFunction', 'someFunction');
Logger.info('Ending someFunction', 'someFunction');
}
function anotherFunction(): void {
Logger.info('Starting anotherFunction', 'anotherFunction');
Logger.info('Ending anotherFunction', 'anotherFunction');
}
someFunction();
anotherFunction();
Key Differences in TypeScript:
- Type Safety: The LogLevel type is a union type, restricting log levels to specific values. This prevents mistakes like passing invalid log levels.
- Function Overloading: The log() method is private, while info(), debug(), error(), and warn() are public methods with strict typing for both message and functionName.
- Error Handling: TypeScript ensures we explicitly cast the error as Error when logging error messages.
Example Output:
Just like the JavaScript example, the output will be color-coded and include function names and timestamps.
Conclusion
By building a global logger in both JavaScript and TypeScript, we can easily track our application's behavior while making debugging more efficient. Whether you're working with a smaller script or a large enterprise application, having a consistent and color-coded logging system ensures that your logs are both readable and informative.
- JavaScript: Perfect for smaller applications or browser-based projects.
- TypeScript: Ideal for larger, more complex applications where type safety and scalability are essential.
By following these steps, you can integrate color-coded logging into your projects today and enjoy better insight into your application’s inner workings!
Hope you find this helpful !!!
How to Build a Global Color-Coded Logger in JavaScript and TypeScript (with Examples)