react-azure-config
Version:
🚀 The Ultimate Multi-App Configuration Library! CRITICAL BUG FIXES: Prefixed environment keys no longer sent to Azure. Complete architectural redesign with bulletproof fallback system. Enterprise-grade Azure integration and monorepo support.
511 lines (505 loc) • 19.2 kB
JavaScript
;
var React = require('react');
const getEnvVar = (key, defaultValue = '') => {
try {
return process.env[key] || defaultValue;
}
catch {
return defaultValue;
}
};
const environment = {
isServer: typeof window === 'undefined',
isClient: typeof window !== 'undefined',
isBrowser: typeof window !== 'undefined' && typeof window.document !== 'undefined',
isDevelopment: getEnvVar('NODE_ENV') === 'development',
isProduction: getEnvVar('NODE_ENV') === 'production',
isTest: getEnvVar('NODE_ENV') === 'test'
};
class Logger {
constructor(config = {}) {
const defaultLevel = this.getDefaultLogLevel();
const defaultPrefix = getEnvVar('LOG_PREFIX', '[ReactAzureConfig]');
this.config = {
level: getEnvVar('LOG_LEVEL') || defaultLevel,
prefix: defaultPrefix,
enableTimestamp: environment.isProduction,
enableColors: !environment.isProduction,
...config
};
}
getDefaultLogLevel() {
if (environment.isProduction)
return 'warn';
if (environment.isTest)
return 'silent';
return 'debug';
}
formatMessage(level, message) {
let formatted = `${this.config.prefix} ${message}`;
if (this.config.enableTimestamp) {
const timestamp = new Date().toISOString();
formatted = `[${timestamp}] ${formatted}`;
}
if (this.config.enableColors && typeof window === 'undefined') {
const colors = {
debug: '\x1b[36m',
info: '\x1b[32m',
warn: '\x1b[33m',
error: '\x1b[31m',
silent: ''
};
const reset = '\x1b[0m';
formatted = `${colors[level]}${formatted}${reset}`;
}
return formatted;
}
shouldLog(level) {
const levels = ['debug', 'info', 'warn', 'error', 'silent'];
const currentLevelIndex = levels.indexOf(this.config.level);
const targetLevelIndex = levels.indexOf(level);
return targetLevelIndex >= currentLevelIndex && this.config.level !== 'silent';
}
debug(message, ...args) {
if (this.shouldLog('debug')) {
console.debug(this.formatMessage('debug', message), ...args);
}
}
info(message, ...args) {
if (this.shouldLog('info')) {
console.info(this.formatMessage('info', message), ...args);
}
}
warn(message, ...args) {
if (this.shouldLog('warn')) {
console.warn(this.formatMessage('warn', message), ...args);
}
}
error(message, ...args) {
if (this.shouldLog('error')) {
console.error(this.formatMessage('error', message), ...args);
}
}
setLevel(level) {
this.config.level = level;
}
}
const logger = new Logger();
const AppInsightsContext = React.createContext(null);
function AppInsightsProvider({ children, config: providedConfig, connectionString: overrideConnectionString }) {
const [isClient, setIsClient] = React.useState(false);
React.useEffect(() => {
setIsClient(true);
}, []);
const [appInsights, setAppInsights] = React.useState(null);
const [reactPlugin, setReactPlugin] = React.useState(null);
const [isInitialized, setIsInitialized] = React.useState(false);
const [error, setError] = React.useState(null);
const [currentConfig, setCurrentConfig] = React.useState(null);
const connectionString = overrideConnectionString || providedConfig?.connectionString;
const initializeAppInsights = React.useCallback(async (connString, config) => {
if (typeof window === 'undefined') {
logger.debug('Skipping Application Insights initialization during SSR');
return;
}
try {
logger.info('Initializing Application Insights', {
hasConnectionString: !!connString,
enableAutoRouteTracking: config.enableAutoRouteTracking
});
const [webModule, reactModule] = await Promise.all([
import('@microsoft/applicationinsights-web').catch(() => null),
import('@microsoft/applicationinsights-react-js').catch(() => null)
]);
const ApplicationInsights = webModule && ('ApplicationInsights' in webModule)
? webModule.ApplicationInsights
: webModule && ('default' in webModule)
? webModule.default?.ApplicationInsights
: null;
const ReactPlugin = reactModule && ('ReactPlugin' in reactModule)
? reactModule.ReactPlugin
: reactModule && ('default' in reactModule)
? reactModule.default?.ReactPlugin
: null;
if (!ApplicationInsights) {
const errorMsg = 'Application Insights packages not installed. Install @microsoft/applicationinsights-web and @microsoft/applicationinsights-react-js';
setError(errorMsg);
logger.debug(errorMsg);
return;
}
let plugin = null;
if (config.enableReactPlugin !== false && ReactPlugin) {
plugin = new ReactPlugin();
setReactPlugin(plugin);
}
const appInsightsConfig = {
connectionString: connString,
enableAutoRouteTracking: config.enableAutoRouteTracking,
enableCookiesUsage: config.enableCookiesUsage,
enableRequestHeaderTracking: config.enableRequestHeaderTracking,
enableResponseHeaderTracking: config.enableResponseHeaderTracking,
extensions: plugin ? [plugin] : undefined,
...config.additionalConfig
};
const appInsightsInstance = new ApplicationInsights({
config: appInsightsConfig
});
appInsightsInstance.loadAppInsights();
if (typeof window !== 'undefined') {
appInsightsInstance.trackPageView();
}
setAppInsights(appInsightsInstance);
setIsInitialized(true);
setError(null);
logger.info('Application Insights initialized successfully');
}
catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Failed to initialize Application Insights';
setError(errorMessage);
setIsInitialized(false);
logger.error('Failed to initialize Application Insights', err);
}
}, []);
React.useEffect(() => {
if (!isClient) {
logger.debug('Skipping Application Insights initialization during SSR');
return;
}
if (!connectionString) {
logger.debug('No Application Insights connection string found, skipping initialization');
return;
}
const config = {
connectionString,
autoInitialize: true,
enableReactPlugin: true,
enableAutoRouteTracking: true,
enableCookiesUsage: false,
enableRequestHeaderTracking: false,
enableResponseHeaderTracking: false,
...providedConfig
};
setCurrentConfig(config);
if (config.autoInitialize !== false && !isInitialized && !appInsights) {
logger.debug('Auto-initializing Application Insights with provided configuration');
initializeAppInsights(connectionString, config);
}
}, [
isClient,
connectionString,
providedConfig,
initializeAppInsights,
isInitialized,
appInsights
]);
const contextValue = {
appInsights,
reactPlugin,
isInitialized,
config: currentConfig,
error,
connectionString: connectionString || null
};
return (React.createElement(AppInsightsContext.Provider, { value: contextValue }, children));
}
const useAppInsightsContext = () => {
const context = React.useContext(AppInsightsContext);
if (!context) {
throw new Error('useAppInsightsContext must be used within an AppInsightsProvider');
}
return context;
};
const useAppInsightsAvailable = () => {
const [isAvailable, setIsAvailable] = React.useState(false);
React.useEffect(() => {
if (typeof window === 'undefined') {
return;
}
const checkAvailability = async () => {
try {
const [webModule, reactModule] = await Promise.all([
import('@microsoft/applicationinsights-web').catch(() => null),
import('@microsoft/applicationinsights-react-js').catch(() => null)
]);
const hasWebModule = !!(webModule && (('ApplicationInsights' in webModule && webModule.ApplicationInsights) ||
('default' in webModule && webModule.default?.ApplicationInsights)));
const hasReactModule = !!(reactModule && (('ReactPlugin' in reactModule && reactModule.ReactPlugin) ||
('default' in reactModule && reactModule.default?.ReactPlugin)));
setIsAvailable(hasWebModule && hasReactModule);
}
catch {
setIsAvailable(false);
logger.debug('Application Insights packages not available - install @microsoft/applicationinsights-web and @microsoft/applicationinsights-react-js to enable telemetry');
}
};
checkAvailability();
}, []);
return isAvailable;
};
const useSafeAppInsightsContext = () => {
try {
return useAppInsightsContext();
}
catch (error) {
return {
appInsights: null,
reactPlugin: null,
isInitialized: false,
config: null,
error: null,
connectionString: null
};
}
};
const useAppInsights = () => {
const context = useSafeAppInsightsContext();
const { appInsights, isInitialized, connectionString } = context;
const trackEvent = React.useCallback((event) => {
if (!appInsights || !isInitialized) {
logger.debug('Application Insights not ready, skipping event tracking', { eventName: event.name });
return;
}
try {
appInsights.trackEvent(event, event.properties);
logger.debug('Event tracked successfully', { eventName: event.name });
}
catch (error) {
logger.error('Failed to track event', error);
}
}, [appInsights, isInitialized]);
const trackException = React.useCallback((exceptionData) => {
if (!appInsights || !isInitialized) {
logger.debug('Application Insights not ready, skipping exception tracking', {
error: exceptionData.exception.message
});
return;
}
try {
appInsights.trackException({
exception: exceptionData.exception,
severityLevel: exceptionData.severityLevel,
properties: exceptionData.properties,
measurements: exceptionData.measurements
});
logger.debug('Exception tracked successfully', {
error: exceptionData.exception.message
});
}
catch (error) {
logger.error('Failed to track exception', error);
}
}, [appInsights, isInitialized]);
const trackPageView = React.useCallback((pageViewData) => {
if (!appInsights || !isInitialized) {
logger.debug('Application Insights not ready, skipping page view tracking');
return;
}
try {
if (pageViewData) {
appInsights.trackPageView({
name: pageViewData.name,
uri: pageViewData.url,
properties: pageViewData.properties,
measurements: pageViewData.measurements
});
}
else {
appInsights.trackPageView();
}
logger.debug('Page view tracked successfully');
}
catch (error) {
logger.error('Failed to track page view', error);
}
}, [appInsights, isInitialized]);
const trackMetric = React.useCallback((name, value, properties) => {
if (!appInsights || !isInitialized) {
logger.debug('Application Insights not ready, skipping metric tracking', { metricName: name });
return;
}
try {
appInsights.trackMetric({ name, average: value }, properties);
logger.debug('Metric tracked successfully', { metricName: name, value });
}
catch (error) {
logger.error('Failed to track metric', error);
}
}, [appInsights, isInitialized]);
const setContext = React.useCallback((telemetryContext) => {
if (!appInsights || !isInitialized) {
logger.debug('Application Insights not ready, skipping context setup');
return;
}
try {
if (telemetryContext.userId) {
appInsights.setAuthenticatedUserContext(telemetryContext.userId);
}
if (telemetryContext.properties) {
Object.entries(telemetryContext.properties).forEach(([key, value]) => {
appInsights.addTelemetryInitializer((envelope) => {
envelope.data = envelope.data || {};
envelope.data.baseData = envelope.data.baseData || {};
envelope.data.baseData.properties = envelope.data.baseData.properties || {};
envelope.data.baseData.properties[key] = value;
return true;
});
});
}
if (telemetryContext.appVersion) {
appInsights.addTelemetryInitializer((envelope) => {
envelope.tags = envelope.tags || {};
envelope.tags['ai.application.ver'] = telemetryContext.appVersion;
return true;
});
}
if (telemetryContext.environment) {
appInsights.addTelemetryInitializer((envelope) => {
envelope.tags = envelope.tags || {};
envelope.tags['ai.cloud.role'] = telemetryContext.environment;
return true;
});
}
logger.debug('Telemetry context set successfully');
}
catch (error) {
logger.error('Failed to set telemetry context', error);
}
}, [appInsights, isInitialized]);
return {
appInsights,
trackEvent,
trackException,
trackPageView,
trackMetric,
setContext,
isReady: isInitialized,
connectionString
};
};
const useTrackEvent = () => {
const { trackEvent, isReady } = useAppInsights();
return React.useCallback((eventName, properties) => {
if (!isReady)
return;
trackEvent({
name: eventName,
properties: {
timestamp: new Date().toISOString(),
...properties
}
});
}, [trackEvent, isReady]);
};
const useTrackException = () => {
const { trackException, isReady } = useAppInsights();
return React.useCallback((error, context) => {
if (!isReady)
return;
trackException({
exception: error,
severityLevel: 3,
properties: {
timestamp: new Date().toISOString(),
stack: error.stack,
...context
}
});
}, [trackException, isReady]);
};
const useTrackPageView = () => {
const { trackPageView, isReady } = useAppInsights();
React.useEffect(() => {
if (isReady) {
trackPageView({
name: document.title,
url: window.location.href,
properties: {
timestamp: new Date().toISOString(),
userAgent: navigator.userAgent
}
});
}
}, [trackPageView, isReady]);
return React.useCallback((pageName, additionalProperties) => {
if (!isReady)
return;
trackPageView({
name: pageName || document.title,
url: window.location.href,
properties: {
timestamp: new Date().toISOString(),
...additionalProperties
}
});
}, [trackPageView, isReady]);
};
const useInsightsConfig = () => {
const isAvailable = useAppInsightsAvailable();
const [defaultConfig] = React.useState({
autoInitialize: false,
enableReactPlugin: false,
enableAutoRouteTracking: false,
enableCookiesUsage: false,
enableRequestHeaderTracking: false,
enableResponseHeaderTracking: false
});
const context = useSafeAppInsightsContext();
if (!isAvailable) {
return {
...defaultConfig,
isConfigured: false,
isInitialized: false,
error: 'Application Insights packages not installed'
};
}
const { config, isInitialized, error, connectionString } = context;
return {
...(config || defaultConfig),
isConfigured: !!connectionString,
isInitialized,
error
};
};
const useTrackPerformance = () => {
const { trackMetric, isReady } = useAppInsights();
const trackTiming = React.useCallback((name, duration, properties) => {
if (!isReady)
return;
trackMetric(`${name}_duration_ms`, duration, {
timestamp: new Date().toISOString(),
...properties
});
}, [trackMetric, isReady]);
const createTimer = React.useCallback((name) => {
const startTime = Date.now();
return {
stop: (properties) => {
const duration = Date.now() - startTime;
trackTiming(name, duration, properties);
return duration;
}
};
}, [trackTiming]);
const trackCounter = React.useCallback((name, value = 1, properties) => {
if (!isReady)
return;
trackMetric(`${name}_count`, value, {
timestamp: new Date().toISOString(),
...properties
});
}, [trackMetric, isReady]);
return {
trackTiming,
createTimer,
trackCounter,
isReady
};
};
exports.AppInsightsProvider = AppInsightsProvider;
exports.useAppInsights = useAppInsights;
exports.useAppInsightsAvailable = useAppInsightsAvailable;
exports.useInsightsConfig = useInsightsConfig;
exports.useTrackEvent = useTrackEvent;
exports.useTrackException = useTrackException;
exports.useTrackPageView = useTrackPageView;
exports.useTrackPerformance = useTrackPerformance;