@thomkjel/logger
Version:
Security-focused event logging library for Next.js applications (Work in Progress)
291 lines (290 loc) • 10.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecurityLoggerPresets = void 0;
exports.SecurityLogger = SecurityLogger;
const react_1 = __importStar(require("react"));
const logger_1 = require("./logger");
const logger = logger_1.Logger.getInstance();
function SecurityLogger({ children, config = {}, apiKey, disabled = false }) {
const defaultConfig = {
apiLogging: true,
authLogging: true,
errorLogging: true,
navigationLogging: false,
captureUserAgent: true,
captureIPAddress: false,
excludeRoutes: ['/api/health', '/_next'],
batchEvents: false,
flushInterval: 5000,
...config
};
(0, react_1.useEffect)(() => {
if (disabled)
return;
// Configure logger with API key if provided
if (apiKey) {
logger.configure({ apiKey });
}
const cleanup = [];
// 1. Auto-track API calls
if (defaultConfig.apiLogging) {
const apiTracker = setupAPITracking(defaultConfig);
cleanup.push(apiTracker);
}
// 2. Auto-track authentication events
if (defaultConfig.authLogging) {
const authTracker = setupAuthTracking(defaultConfig);
cleanup.push(authTracker);
}
// 3. Auto-track errors
if (defaultConfig.errorLogging) {
const errorTracker = setupErrorTracking(defaultConfig);
cleanup.push(errorTracker);
}
// 4. Auto-track navigation (optional)
if (defaultConfig.navigationLogging) {
const navTracker = setupNavigationTracking(defaultConfig);
cleanup.push(navTracker);
}
// Log that security logging is active
logger.info('security_logger_initialized', {
config: {
apiLogging: defaultConfig.apiLogging,
authLogging: defaultConfig.authLogging,
errorLogging: defaultConfig.errorLogging
},
timestamp: new Date().toISOString()
});
// Cleanup on unmount
return () => {
cleanup.forEach(fn => fn());
logger.info('security_logger_disabled', {
timestamp: new Date().toISOString()
});
};
}, [disabled, apiKey, defaultConfig]);
return react_1.default.createElement(react_1.default.Fragment, null, children);
}
// API Call Tracking
function setupAPITracking(config) {
if (typeof window === 'undefined')
return () => { };
const originalFetch = window.fetch;
window.fetch = async function (url, options) {
const urlString = url.toString();
// Skip excluded routes
if (config.excludeRoutes?.some(route => urlString.includes(route))) {
return originalFetch(url, options);
}
// Include only specified routes if configured
if (config.includeOnlyRoutes &&
!config.includeOnlyRoutes.some(route => urlString.includes(route))) {
return originalFetch(url, options);
}
const startTime = Date.now();
const metadata = {
url: urlString,
method: options?.method || 'GET',
timestamp: new Date().toISOString()
};
// Add user agent if enabled
if (config.captureUserAgent) {
metadata.userAgent = navigator.userAgent;
}
try {
logger.info('api_request_start', metadata);
const response = await originalFetch(url, options);
const duration = Date.now() - startTime;
const responseMetadata = {
...metadata,
status: response.status,
success: response.ok,
duration: `${duration}ms`
};
if (response.ok) {
logger.info('api_request_success', responseMetadata);
}
else {
logger.warn('api_request_failure', {
...responseMetadata,
statusText: response.statusText
});
}
return response;
}
catch (error) {
const duration = Date.now() - startTime;
logger.error('api_request_error', {
...metadata,
error: error instanceof Error ? error.message : 'Unknown error',
duration: `${duration}ms`
});
throw error;
}
};
// Return cleanup function
return () => {
window.fetch = originalFetch;
};
}
// Authentication Event Tracking
function setupAuthTracking(config) {
if (typeof window === 'undefined')
return () => { };
// Track localStorage changes for auth tokens
const originalSetItem = localStorage.setItem;
localStorage.setItem = function (key, value) {
if (key.includes('token') || key.includes('auth') || key.includes('session')) {
logger.info('session_token_set', {
tokenType: key,
timestamp: new Date().toISOString()
});
}
return originalSetItem.call(this, key, value);
};
const originalRemoveItem = localStorage.removeItem;
localStorage.removeItem = function (key) {
if (key.includes('token') || key.includes('auth') || key.includes('session')) {
logger.warn('session_token_removed', {
tokenType: key,
reason: 'explicit_removal',
timestamp: new Date().toISOString()
});
}
return originalRemoveItem.call(this, key);
};
// Return cleanup function
return () => {
localStorage.setItem = originalSetItem;
localStorage.removeItem = originalRemoveItem;
};
}
// Error Tracking
function setupErrorTracking(config) {
if (typeof window === 'undefined')
return () => { };
// Global error handler
const handleError = (event) => {
logger.error('javascript_error', {
error: event.error?.message || event.message,
filename: event.filename,
line: event.lineno,
column: event.colno,
stack: event.error?.stack,
timestamp: new Date().toISOString()
});
};
// Unhandled promise rejections
const handleRejection = (event) => {
logger.error('unhandled_promise_rejection', {
reason: event.reason,
timestamp: new Date().toISOString()
});
};
window.addEventListener('error', handleError);
window.addEventListener('unhandledrejection', handleRejection);
// Return cleanup function
return () => {
window.removeEventListener('error', handleError);
window.removeEventListener('unhandledrejection', handleRejection);
};
}
// Navigation Tracking (optional)
function setupNavigationTracking(config) {
if (typeof window === 'undefined')
return () => { };
// Track page changes (for SPAs)
let currentPath = window.location.pathname;
const trackNavigation = () => {
const newPath = window.location.pathname;
if (newPath !== currentPath) {
logger.info('page_navigation', {
from: currentPath,
to: newPath,
timestamp: new Date().toISOString()
});
currentPath = newPath;
}
};
// Listen for navigation events
window.addEventListener('popstate', trackNavigation);
// Override pushState and replaceState for SPA navigation
const originalPushState = history.pushState;
history.pushState = function (...args) {
const result = originalPushState.apply(this, args);
trackNavigation();
return result;
};
const originalReplaceState = history.replaceState;
history.replaceState = function (...args) {
const result = originalReplaceState.apply(this, args);
trackNavigation();
return result;
};
// Return cleanup function
return () => {
window.removeEventListener('popstate', trackNavigation);
history.pushState = originalPushState;
history.replaceState = originalReplaceState;
};
}
// Export convenience configurations
exports.SecurityLoggerPresets = {
minimal: {
apiLogging: true,
authLogging: true,
errorLogging: true,
navigationLogging: false
},
standard: {
apiLogging: true,
authLogging: true,
errorLogging: true,
navigationLogging: true,
captureUserAgent: true
},
comprehensive: {
apiLogging: true,
authLogging: true,
errorLogging: true,
navigationLogging: true,
captureUserAgent: true,
captureIPAddress: false, // Usually handled server-side
batchEvents: true,
flushInterval: 3000
}
};
exports.default = SecurityLogger;