Master JavaScript Tracking: 7 تکنیک اساسی برای برنامه های قابل اعتماد

من به عنوان یک نویسنده پرفروش ، شما را دعوت می کنم تا کتابهای من را در آمازون کشف کنید. فراموش نکنید که مرا در متوسط دنبال کنید و پشتیبانی خود را نشان دهید. ممنون حمایت شما به معنای جهان است!
ردیابی خطای JavaScript فقط مربوط به رسیدگی به خطاها نیست. این در مورد درک آنها ، جلوگیری از آنها و یادگیری از آنها است. من سالها در ساخت سیستم های نظارت بر خطا در پروژه های مختلف صرف کرده ام و فهمیدم که رسیدگی به خطای مناسب اغلب تفاوت بین برنامه هایی را که رشد می کنند و مواردی که دائماً کاربران را ناامید می کند ، ایجاد می کند.
ردیابی خطای مؤثر نیاز به یک رویکرد سیستماتیک دارد که فراتر از بلوک های اصلی تلاش است. باید زمینه را ضبط کند ، موضوعات مهم را در اولویت قرار دهد و بینش های عملی را ارائه دهد. در اینجا راهنمای جامع من برای ساختن یک سیستم ردیابی خطای JavaScript قوی آورده شده است.
خطای متنی ضبط
با ارزش ترین گزارش های خطا شامل زمینه در مورد آنچه اتفاق می افتد هنگام خطا رخ داده است. بدون زمینه ، اشکال زدایی به یک بازی حدس می زند.
من همیشه یک کنترل کننده خطای جهانی را به عنوان اولین خط دفاع پیاده سازی می کنم:
window.addEventListener('error', function(event) {
const { message, filename, lineno, colno, error } = event;
// Collect environmental context
const context = {
url: window.location.href,
userAgent: navigator.userAgent,
timestamp: new Date().toISOString(),
viewportWidth: window.innerWidth,
viewportHeight: window.innerHeight,
// Add application-specific state here
};
// Send to tracking system
reportError({
type: 'runtime',
message,
stack: error?.stack,
lineNumber: lineno,
columnNumber: colno,
fileName: filename,
context
});
});
برای رد وعده ، که توسط کنترل کننده خطای استاندارد گرفتار نمی شوند:
window.addEventListener('unhandledrejection', function(event) {
reportError({
type: 'promise',
message: event.reason?.message || 'Unhandled Promise Rejection',
stack: event.reason?.stack,
context: {
url: window.location.href,
timestamp: new Date().toISOString()
}
});
});
در برنامه های React ، من مرزهای خطا را برای گرفتن خطاهای مؤلفه اجرا می کنم:
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, info) {
reportError({
type: 'react',
message: error.message,
stack: error.stack,
componentStack: info.componentStack,
componentName: this.constructor.name,
props: JSON.stringify(this.props)
});
}
render() {
if (this.state.hasError) {
return this.props.fallback || <h2>Something went wrong.</h2>;
}
return this.props.children;
}
}
پیشرفت ردیابی پشته
آثار پشته Raw JavaScript ، به ویژه از کد Minified ، می تواند رمزنگاری شود. نقشه های منبع برای ترجمه این موارد به کد قابل خواندن ضروری است:
async function enhanceStackTrace(stack, sourceMapUrl) {
if (!stack) return stack;
try {
const sourceMapConsumer = await new SourceMapConsumer(sourceMapUrl);
const lines = stack.split('\n');
const enhancedLines = lines.map(line => {
const match = line.match(/at\s+(.+)\s+\((.+):(\d+):(\d+)\)/);
if (!match) return line;
const [_, functionName, file, lineNumber, columnNumber] = match;
const position = sourceMapConsumer.originalPositionFor({
line: parseInt(lineNumber, 10),
column: parseInt(columnNumber, 10)
});
if (!position.source) return line;
return `at ${functionName} (${position.source}:${position.line}:${position.column})`;
});
sourceMapConsumer.destroy();
return enhancedLines.join('\n');
} catch (err) {
console.warn('Failed to enhance stack trace:', err);
return stack;
}
}
فهمیدم که حفظ نقشه های منبع به طور ایمن بر روی سرور شما و پردازش آنها در هنگام گزارش خطا ، بهترین تعادل بین قابلیت اشکال زدایی و محافظت از کد را فراهم می کند.
طبقه بندی خطا
همه خطاها برابر نیستند. برخی ممکن است مسائل مربوط به شبکه گذرا باشند در حالی که برخی دیگر می توانند اشکالات مهمی بر عملکرد اصلی باشند.
من با استفاده از ترکیبی از تطبیق الگوی و ارزیابی شدت ، خطاها را طبقه بندی می کنم:
function categorizeError(error) {
// Define error patterns
const patterns = [
{
regex: /network|failed to fetch|cors|timeout/i,
category: 'network',
severity: 'warning'
},
{
regex: /undefined is not a function|cannot read property/i,
category: 'type',
severity: 'error'
},
{
regex: /syntax error|unexpected token/i,
category: 'syntax',
severity: 'critical'
},
{
regex: /out of memory|stack size exceeded/i,
category: 'memory',
severity: 'critical'
}
];
const message = error.message || '';
const stack = error.stack || '';
const content = message + ' ' + stack;
// Find matching pattern
for (const pattern of patterns) {
if (pattern.regex.test(content)) {
return {
category: pattern.category,
severity: pattern.severity
};
}
}
// Default categorization
return {
category: 'unknown',
severity: 'error'
};
}
گروه بندی خطاهای مشابه باعث کاهش نویز می شود و به تمرکز روی موضوعات متمایز کمک می کند. من از یک تکنیک اثر انگشت استفاده می کنم:
function getErrorFingerprint(error) {
const message = error.message || '';
const stack = error.stack || '';
// Extract the first line from the stack trace (most specific to the error location)
const stackLine = stack.split('\n')[1] || '';
// Remove variable values from the message and line numbers that might change
const normalizedMessage = message.replace(/(['"]).*?\1/g, '$1...$1')
.replace(/\d+/g, 'N');
// Create a stable identifier
return md5(normalizedMessage + ' at ' + stackLine.trim());
}
خطا
سیل خطا می تواند سیستم ردیابی شما را تحت الشعاع قرار دهد. من محدودیت نرخ را با پشتوانه نمایی پیاده سازی می کنم:
class ErrorThrottler {
constructor() {
this.errorCounts = new Map();
this.lastReported = new Map();
}
shouldReport(errorFingerprint) {
const now = Date.now();
const count = (this.errorCounts.get(errorFingerprint) || 0) + 1;
this.errorCounts.set(errorFingerprint, count);
const lastTime = this.lastReported.get(errorFingerprint) || 0;
const timeSinceLast = now - lastTime;
// Calculate backoff time: 5 seconds × 2^(count-1), maxed at 2 hours
const backoffTime = Math.min(5000 * Math.pow(2, count - 1), 7200000);
if (timeSinceLast < backoffTime) {
return false;
}
this.lastReported.set(errorFingerprint, now);
return true;
}
reset(errorFingerprint) {
this.errorCounts.delete(errorFingerprint);
this.lastReported.delete(errorFingerprint);
}
}
این رویکرد تضمین می کند که مسائل مهم بدون سیل سیستم شما قابل مشاهده است.
انواع خطای سفارشی
ایجاد کلاسهای خطای خاص دامنه ، دقت در رسیدگی به خطا را بهبود می بخشد:
class ApiError extends Error {
constructor(message, statusCode, endpoint, requestData) {
super(message);
this.name = 'ApiError';
this.statusCode = statusCode;
this.endpoint = endpoint;
this.requestData = requestData;
this.timestamp = new Date().toISOString();
// Capture stack trace, excluding constructor call
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ApiError);
}
}
toJSON() {
return {
name: this.name,
message: this.message,
statusCode: this.statusCode,
endpoint: this.endpoint,
timestamp: this.timestamp
};
}
}
// Usage example
async function fetchUserData(userId) {
try {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) {
throw new ApiError(
`Failed to fetch user data: ${response.statusText}`,
response.status,
`/api/users/${userId}`,
{ userId }
);
}
return await response.json();
} catch (error) {
if (error instanceof ApiError) {
// Handle API-specific error
errorTracker.trackError(error);
return null;
}
// Handle other errors (network, parsing, etc.)
errorTracker.trackError(new ApiError(
'Network or parsing error',
0,
`/api/users/${userId}`,
{ userId, originalError: error.message }
));
return null;
}
}
این رویکرد داده های خطای ساختاری را ارائه می دهد که تجزیه و تحلیل و طبقه بندی آن آسان تر است.
ردیابی تأثیر کاربر
اتصال خطاها به معیارهای تجاری به اولویت بندی رفع کمک می کند:
class UserImpactTracker {
constructor() {
this.sessionId = generateUniqueId();
this.userJourney = [];
this.errorsEncountered = new Map();
}
recordStep(stepName, metadata = {}) {
this.userJourney.push({
step: stepName,
timestamp: Date.now(),
metadata
});
}
recordError(error, stepName) {
const errorFingerprint = getErrorFingerprint(error);
if (!this.errorsEncountered.has(errorFingerprint)) {
this.errorsEncountered.set(errorFingerprint, []);
}
this.errorsEncountered.get(errorFingerprint).push({
timestamp: Date.now(),
step: stepName || this.getCurrentStep(),
errorMessage: error.message
});
// Associate error with user journey
reportError({
...error,
sessionId: this.sessionId,
currentStep: stepName || this.getCurrentStep(),
journeyLength: this.userJourney.length
});
}
getCurrentStep() {
if (this.userJourney.length === 0) return 'app_start';
return this.userJourney[this.userJourney.length - 1].step;
}
getJourneyAnalytics() {
return {
sessionId: this.sessionId,
steps: this.userJourney.length,
completedJourney: this.hasCompletedJourney(),
errorCount: Array.from(this.errorsEncountered.values())
.reduce((total, occurrences) => total + occurrences.length, 0),
uniqueErrorCount: this.errorsEncountered.size
};
}
hasCompletedJourney() {
// Implement your business logic for a completed journey
return this.userJourney.some(step => step.step === 'checkout_complete');
}
}
این امکان محاسبه معیارهای مهم مانند:
- نرخ خطا توسط مرحله سفر
- تأثیر تبدیل خطاهای خاص
- همبستگی ترک کاربر با وقوع خطا
مکانیسم های بهبودی خود
خطاهای خاصی را می توان به طور خودکار از:
class ResilienceWrapper {
constructor(options = {}) {
this.maxRetries = options.maxRetries || 3;
this.backoffFactor = options.backoffFactor || 2;
this.initialDelay = options.initialDelay || 1000;
this.recoveryStrategies = options.recoveryStrategies || {};
}
async retryable(fn, options = {}) {
const context = options.context || {};
const operationName = options.name || fn.name || 'anonymous operation';
let lastError;
for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
try {
return await fn(context);
} catch (error) {
lastError = error;
// Log the retry attempt
console.warn(`Operation ${operationName} failed (attempt ${attempt}/${this.maxRetries}):`, error.message);
// Check if we can apply a recovery strategy
const strategyName = this.findMatchingStrategy(error);
if (strategyName) {
try {
const recoveryStrategy = this.recoveryStrategies[strategyName];
const recoveryResult = await recoveryStrategy(error, context);
// If recovery was successful, return the result
if (recoveryResult.recovered) {
console.info(`Recovery strategy '${strategyName}' succeeded for ${operationName}`);
return recoveryResult.value;
}
} catch (recoveryError) {
console.error(`Recovery strategy '${strategyName}' failed:`, recoveryError);
}
}
// If this is the last attempt, don't delay
if (attempt === this.maxRetries) break;
// Calculate backoff delay
const delay = this.initialDelay * Math.pow(this.backoffFactor, attempt - 1);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
// If we've exhausted all retries and recovery strategies, report and throw
reportError({
...lastError,
operationName,
retriesAttempted: this.maxRetries
});
throw lastError;
}
findMatchingStrategy(error) {
return Object.keys(this.recoveryStrategies).find(strategyName => {
const strategy = this.recoveryStrategies[strategyName];
return strategy.matches && strategy.matches(error);
});
}
}
// Example usage:
const resilience = new ResilienceWrapper({
recoveryStrategies: {
cachedDataFallback: {
matches: (error) => error.name === 'ApiError' && error.statusCode >= 500,
execute: async (error, context) => {
const cachedData = localStorage.getItem(`cache_${context.endpoint}`);
if (cachedData) {
return {
recovered: true,
value: JSON.parse(cachedData)
};
}
return { recovered: false };
}
},
networkReroute: {
matches: (error) => error.message.includes('CORS') || error.message.includes('Network Error'),
execute: async (error, context) => {
// Try an alternative API endpoint or proxy
try {
const fallbackUrl = context.fallbackUrl || context.url.replace('api.', 'fallback-api.');
const response = await fetch(fallbackUrl, context.options);
const data = await response.json();
return {
recovered: true,
value: data
};
} catch {
return { recovered: false };
}
}
}
}
});
// Using the resilience wrapper
async function loadUserProfile(userId) {
return resilience.retryable(
async (context) => {
const response = await fetch(`/api/users/${userId}`);
if (!response.ok) throw new ApiError('User profile fetch failed', response.status, `/api/users/${userId}`);
const data = await response.json();
// Cache successful responses
localStorage.setItem(`cache_/api/users/${userId}`, JSON.stringify(data));
return data;
},
{
name: 'loadUserProfile',
context: {
endpoint: `/api/users/${userId}`,
fallbackUrl: `/api/backup/users/${userId}`
}
}
);
}
این رویکرد می تواند به طور قابل توجهی مقاومت در برابر برنامه را بهبود بخشد.
قرار دادن همه اینها
در اینجا یک سیستم ردیابی خطای کامل وجود دارد که شامل تمام این تکنیک ها است:
class EnhancedErrorTracker {
constructor(options = {}) {
this.endpoint = options.endpoint || '/api/errors';
this.appVersion = options.appVersion || '1.0.0';
this.sampleRate = options.sampleRate || 1.0; // 1.0 = track all errors
this.throttler = new ErrorThrottler();
this.userImpact = new UserImpactTracker();
this.sourceMapUrl = options.sourceMapUrl;
this.init();
}
init() {
window.addEventListener('error', this.handleGlobalError.bind(this));
window.addEventListener('unhandledrejection', this.handlePromiseRejection.bind(this));
// Record important user journey steps
if (typeof window.history.pushState === 'function') {
const originalPushState = window.history.pushState;
window.history.pushState = function() {
originalPushState.apply(this, arguments);
this.userImpact.recordStep('navigation', { path: window.location.pathname });
}.bind(window.history);
}
window.addEventListener('popstate', () => {
this.userImpact.recordStep('navigation', { path: window.location.pathname });
});
}
handleGlobalError(event) {
const { message, filename, lineno, colno, error } = event;
this.trackError({
type: 'runtime',
message,
source: filename,
lineno,
colno,
stack: error?.stack,
originalError: error
});
return true;
}
handlePromiseRejection(event) {
this.trackError({
type: 'promise',
message: event.reason?.message || 'Promise rejected',
stack: event.reason?.stack,
originalError: event.reason
});
}
async trackError(errorData) {
// Apply sampling
if (Math.random() > this.sampleRate) return;
const error = errorData.originalError || new Error(errorData.message);
const fingerprint = getErrorFingerprint(error);
// Apply throttling
if (!this.throttler.shouldReport(fingerprint)) return;
// Categorize error
const { category, severity } = categorizeError(error);
// Enhance stack trace if possible
let enhancedStack = errorData.stack;
if (this.sourceMapUrl && enhancedStack) {
enhancedStack = await enhanceStackTrace(enhancedStack, this.sourceMapUrl);
}
// Record user impact
this.userImpact.recordError(error);
const payload = {
...errorData,
stack: enhancedStack || errorData.stack,
fingerprint,
category,
severity,
timestamp: new Date().toISOString(),
appVersion: this.appVersion,
userAgent: navigator.userAgent,
url: window.location.href,
sessionId: this.userImpact.sessionId,
userJourney: this.userImpact.getJourneyAnalytics()
};
try {
const response = await fetch(this.endpoint, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
keepalive: true
});
if (!response.ok) {
console.error('Failed to send error report:', await response.text());
}
} catch (err) {
console.error('Error in error reporting:', err);
// Last resort - log to console
console.error('Original error details:', payload);
}
}
}
// Initialize tracker
const errorTracker = new EnhancedErrorTracker({
endpoint: 'https://errors.myapp.com/collect',
appVersion: APP_VERSION,
sampleRate: 0.9, // Track 90% of errors
sourceMapUrl: '/sourcemaps/'
});
افکار نهایی
ایجاد یک سیستم ردیابی خطای مؤثر ، سرمایه گذاری است که سود سهام را در قابلیت اطمینان برنامه و رضایت کاربر پرداخت می کند. من دیدم که تیم ها از طریق ردیابی خطای سیستماتیک و اولویت بندی ، بیش از 70 درصد اشکالات مهم را کاهش می دهند.
نکته مهم این است که فراتر از خطاهای فردی برای ایجاد سیستمی که بینش در مورد الگوهای و تأثیر را فراهم می کند ، فکر می کند. با ضبط خطای اساسی شروع کنید ، سپس به تدریج سیستم خود را با زمینه ، طبقه بندی و تجزیه و تحلیل تأثیر کاربر تقویت کنید.
به یاد داشته باشید که ردیابی خطا فقط مربوط به رفع اشکالات نیست – این در مورد بهبود مداوم درک شما در مورد نحوه تعامل کاربران با برنامه شما و جایی است که با اصطکاک روبرو می شوند. با وجود این تکنیک ها ، خطاها را از حوادث ناامیدکننده به فرصت های ارزشمندی برای بهبود تبدیل می کنید.
101 کتاب
101 کتاب یک شرکت انتشارات AI محور است که توسط نویسنده تأسیس شده است آراو جوشیبشر با استفاده از فناوری پیشرفته هوش مصنوعی ، ما هزینه های انتشار خود را فوق العاده کم نگه می داریم – برخی از کتاب ها به اندازه کم قیمت هستند 4 دلار– ایجاد دانش با کیفیت در دسترس همه.
کتاب ما را بررسی کنید کد تمیز Golang در آمازون موجود است.
برای به روزرسانی ها و اخبار هیجان انگیز با ما در ارتباط باشید. هنگام خرید کتاب ، جستجو کنید آراو جوشی برای یافتن بیشتر عناوین ما. برای لذت بردن از لینک ارائه شده استفاده کنید تخفیف های خاص!
خلاقیت های ما
حتما خلاقیت های ما را بررسی کنید:
سرمایه گذار مرکزی | سرمایه گذار اسپانیایی مرکزی | سرمایه گذار آلمانی مرکزی | زندگی هوشمند | دوره ها و پژواک | اسرار گیج کننده | هندوتوا | نخبه | مدارس JS
ما در متوسط هستیم
بینش های فنی Koala | Epochs & Echoes World | سرمایه گذار رسانه مرکزی | رمز و رازهای گیج کننده متوسط | علوم و دوره های متوسط | هندوتوا مدرن