@ideem/zsm-react-native
Version:
ZSM makes 2FA easy and invisible for everyone, all the time, using advanced cryptography like MPC to establish cryptographic proof of the origin of any transaction or login attempt, while eliminating opportunities for social engineering. ZSM has no relian
183 lines (159 loc) • 5.71 kB
JavaScript
/**
* React Native ZSM Logger - works harmoniously between native and browser
* Provides configurable log levels and consistent formatting across all platforms
*/
import { version as packageVersion } from '../package.json';
/**
* Log levels which correspond to console log methods
* TRACE = verbose, DEBUG = debug, INFO = info, WARN = warn, ERROR = error, FATAL = error
*/
const LogLevel = {
TRACE: { name: 'TRACE', value: -1 },
DEBUG: { name: 'DEBUG', value: 0 },
INFO: { name: 'INFO', value: 1 },
WARN: { name: 'WARN', value: 2 },
ERROR: { name: 'ERROR', value: 3 },
FATAL: { name: 'FATAL', value: 4 }
};
/**
* Parse log level string to LogLevel enum, with case-insensitive matching
* @param {string} name - The log level name (e.g., 'trace', 'Trace', 'TRACE')
* @param {Object} defaultLevel - Default log level if parsing fails
* @returns {Object} LogLevel enum value
*/
function fromStringOrDefault(name, defaultLevel = LogLevel.DEBUG) {
try {
// Convert to uppercase first to ensure consistent matching
const upperName = name?.toString().toUpperCase();
const found = Object.values(LogLevel).find(level => level.name === upperName);
return found || defaultLevel;
} catch (e) {
return defaultLevel;
}
}
/**
* ZSM Logger class providing configurable logging functionality
* Works harmoniously between React Native and browser environments
*/
class ZSMLogger {
static logLevel = LogLevel.TRACE;
static logFunction = null;
/**
* Get the version from package.json
* @returns {string} The package version
*/
static get version() {
return packageVersion;
}
/**
* Set the global log level
* @param {Object|string} level - LogLevel enum or string
*/
static setLogLevel(level) {
if (typeof level === 'string') {
this.logLevel = fromStringOrDefault(level);
} else {
this.logLevel = level || LogLevel.DEBUG;
}
}
/**
* Set a custom logging function
* @param {Function} logFunc - Function that takes (level, message) parameters
*/
static setLogFunction(logFunc) {
this.logFunction = logFunc;
}
/**
* Generate a trace ID for operation tracking
* @returns {string} UUID-like trace ID
*/
static generateTraceId() {
// Use crypto.randomUUID if available (modern browsers/Node), otherwise fallback
if (typeof crypto !== 'undefined' && crypto.randomUUID) {
return crypto.randomUUID();
}
// Fallback for React Native and older environments
return 'xxxx-xxxx-4xxx-yxxx'.replace(/[xy]/g, (c) => {
const r = Math.random() * 16 | 0;
const v = c === 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
/**
* Format a message with trace ID
* @param {string} traceId - The trace ID
* @param {string} message - The message
* @returns {string} Formatted message
*/
static formatTraceMessage(traceId, message) {
if (!traceId) return message;
// Check if message already contains a trace ID
if (message.includes('[TID:')) return message;
return `[TID:${traceId}] ${message}`;
}
/**
* Core logging method
* @param {string} message - The message to log
* @param {Object} level - LogLevel enum
* @param {string} traceId - Optional trace ID
*/
static log(message, level = LogLevel.DEBUG, traceId = null) {
// Filter based on log level
if (level.value < this.logLevel.value) return;
// Format message with trace ID if provided
const formattedMessage = traceId ?
this.formatTraceMessage(traceId, message) : message;
// Use custom log function if provided
if (this.logFunction) {
this.logFunction(level, formattedMessage);
return;
}
// Use appropriate console method based on level
const timestamp = new Date().toISOString();
const logMessage = `ZSM [${timestamp}] level [${level.name}] ${formattedMessage}`;
// React Native provides console methods that work on both platforms
switch (level.name) {
case 'TRACE':
case 'DEBUG':
// Use console.debug for TRACE/DEBUG levels (React Native DevTools can filter this)
console.debug(logMessage);
break;
case 'INFO':
console.info(logMessage);
break;
case 'WARN':
console.warn(logMessage);
break;
case 'ERROR':
case 'FATAL':
console.error(logMessage);
break;
default:
console.log(logMessage);
}
}
/**
* Convenience methods for different log levels
*/
static trace(message, traceId = null) {
this.log(message, LogLevel.TRACE, traceId);
}
static debug(message, traceId = null) {
this.log(message, LogLevel.DEBUG, traceId);
}
static info(message, traceId = null) {
this.log(message, LogLevel.INFO, traceId);
}
static warn(message, traceId = null) {
this.log(message, LogLevel.WARN, traceId);
}
static error(message, traceId = null) {
this.log(message, LogLevel.ERROR, traceId);
}
static fatal(message, traceId = null) {
this.log(message, LogLevel.FATAL, traceId);
}
}
// Export both the class and LogLevel enum
export { ZSMLogger, LogLevel, fromStringOrDefault };
export default ZSMLogger;