Anonystick

anonystick@gmail.com

Tạo module lắng nghe javascript error Client gửi về Server.

Bài viết trước chúng ta đã bàn đến việc tại sao lại track javascript error ở khách hàng. Vì sao việc đó lại quan trọng? Khi tôi viết ra thì có nhiều bạn cùng chung một vấn đề. Mà kể cũng thật, mượn máy khách hàng đâu có dễ như vậy. Chính vì vậy, việc theo dõi log error từ xa là rất quan trọng. Vì sao nó rất quan trọng, và thành phần nào hỗ trợ cho việc lắng nghe log thì mời các bạn cùng xem qua bài trước "Lập trình viên track errors javascript ở người dùng thế nào?"

Trong bài viết đó, chúng tôi nợ các bạn cách triển khai việc "Dùng thư viện nào có thể nhận log error một cách duyên dáng". Chính vì thế, bài viết này sẽ gợi ý cho các bạn một phương pháp hiệu quả, và rất nhanh. Đó là sử dụng webSocket gửi javascript error tới Server.


WebSocket


Nói đến webSocket thì chắc ở đây chúng ta khỏi bàn nữa nhé. Vì sao tôi lại chọn webSocket, và lịch sử phát triển của webSocket như thế nào? Thì tất cả những kiến thức đó đã nói ở những số trước rồi. Ở đây tôi chỉ nói đến việc tại sao không sử dụng socket io thay vì webSocket? Bạn nào có câu hỏi giống như thế này thì tôi cá bạn đó rất khá đấy. 

Thật sự ai cũng có nổi khổ của ai, giàu cũng khổ, mà nghèo cũng khổ tương tự như socket io và webSocket, nó khác nhau. Và khi nào sử dụng chúng thì chắc có lẽ tôi sẽ nói ở số tiếp theo. Còn bây giờ, chúng ta đi vào cụ thể việc triển khai. Go go...


WebSocket thiết lập phía máy khách


Có một tin vui cho các bạn, thay vì tôi phải ngồi code thì ban liên lạc đã giúp tôi có một nguồn thông tin đó là có một tài liệu nói đến chính xác vấn đề này luôn, đó chính là việc sử dụng webSocket để thu thập log error chuyển về cho Server. Vì vậy thay tôi code cùi của tôi, thì tôi quyết định lấy code của tài liệu này. Link tài liệu để của bài viết. Thiết lập WebSocket trên máy khách và gửi tin nhắn đến máy chủ chỉ có 3 dòng JavaScript. Và xin chú ý tới những giải thích comments trong code.

const socketUrl = 'ws://localhost:3000';
const socket = new WebSocket(socketUrl);
// Ở đây, hãy để một chút thời gian để WS sẵn sàng
// or use socket.addEventListener ('open', ...) để gửi tin nhắn ngay lập tức
socket.send('My first message');

Nếu là dân chuyên nghiệp thì có thể đóng gói thành một module dùng mọi lúc mọi nơi. Như dưới dây, tạo một file reporter.js

const reportUrl = 'TODO'; // Url to the WebSocket server "wss://[host]:[port]" 
 
// The reporter object encapsulating the WebSocket
const reporter = {
  socket: null,
 
  init: function() {
    this.socket = new WebSocket(reportUrl);
  },
 
  event: function(eventCode, message) {
    const isReady = this.socket && this.socket.readyState === WebSocket.OPEN;
    if (isReady) { // Messages triggered before the WebSocket is ready are ignored
      this.socket.send(JSON.stringify({eventCode: eventCode, message: message}));
    }
  }
};
 
// Start reporter immediately
reporter.init();
 
// Collect unhandled JavaScript errors and send them to the server
window.addEventListener('error', function(e) {
  reporter.event('JAVASCRIPT_ERROR', e.message + ', ' + e.filename + ', ' + e.lineno + ':' + e.colno);
 
  let stacktrace = e.stack;
  if (!stacktrace && e.error) {
    stacktrace = e.error.stack;
  }
  if (stacktrace) {
    reporter.event('JAVASCRIPT_ERROR_STACKTRACE', stacktrace);
  }
});

Các method reporter.event() được gọi bởi các xử lý sự kiện lỗi của phần đầu tiên. Nó cũng sẽ được sử dụng trong phần cuối cùng để gửi hoạt động của người dùng. Các tin nhắn được gửi bằng JSON, với một eventCode để máy chủ có thể lọc chúng. Trên đây là những gì bạn triển khai trên phía Client. CÒn tiếp theo chúng ta sẽ triển khai cho Server.

Thiết lập WebSocket - phía máy chủ

Phần này chúng ta sẽ setup webSocket trên node.js. Tại sao lại sử dụng node.js thì đã quá rõ về Series Node.js rồi. Trước tiên công việc của bạn phải tìm npm cho ws. Sau đó

npm install ws

Các ws package là một thực hiện tốt đẹp của một máy chủ WebSocket

const WebSocket = require('ws');  
const wss = new WebSocket.Server({ port: 3000 });
 
wss.on('connection', function connection(ws) {
  ws.on('message', function incoming(message) {
    console.log('received: %s', message);
  });
});

Các bạn chú ý, ở đây chỉ là việc thu thập lỗi mà thôi, còn lưu ở đâu, xử lý như thế nào thì tuỳ thuộc vào các bạn. Lưu ở DB, file, message... Tuỳ nhé. Và code full dưới đây. 

### reporter-server.js

const { v4: uuidv4 } = require('uuid'); // "npm install uuid"
 
// Open log file
const fs = require('fs');
const log_file = fs.createWriteStream(__dirname + '/events.log', {flags : 'a'});
function handleEvent(message, connectionId) {
  log_file.write(`${timestamp()} [${connectionId}] ${message}\n`);
}
 
// Start server
const yargs = require('yargs');  // To read command line arguments
const argv = yargs.option('port', { type: 'number' }).argv;
const port = argv.port || 3099;  // The listening port can be changed via command line
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: port });
 
wss.on('connection', function connection(ws) {
  // Handle start of connection
  const startedAt = Date.now();
  const connectionId = uuidv4(); // Random UUID
  handleEvent('START_PAGE_VIEW', connectionId);
 
  // Handle message
  ws.on('message', function incoming(message) {
    const json = JSON.parse(message);
    handleEvent(`${json.eventCode} ${json.message}`, connectionId);
  });
 
  // Handle end of connection
  ws.on('close', function() {
    handleEvent('END_PAGE_VIEW ' + millisecondsToStr(Date.now() - startedAt), connectionId);
  });
  ws.on('error', function() {
    handleEvent('END_PAGE_VIEW [error] ' + millisecondsToStr(Date.now() - startedAt), connectionId);
  });
});
 
console.log(`WebSocket server running at http://localhost:${port}/`);
 
// ----- help functions -------
 
// Current time YYYY-MM-DD HH-MM-SS
function timestamp() {
  const d = new Date();
  return pad(d.getFullYear()) + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate()) + ' ' + 
         pad(d.getHours()) + ':' + pad(d.getMinutes()) + ':' + pad(d.getSeconds());
}
function pad(n) { return n < 10 ? "0" + n : n}
 
// Human readable duration: "2 minutes", "34 seconds". From https://stackoverflow.com/a/8212878/1128103
function millisecondsToStr(milliseconds) {
  let temp = Math.floor(milliseconds / 1000);
  const hours = Math.floor((temp %= 86400) / 3600);
  if (hours) {
    return hours + ' hour' + numberEnding(hours);
  }
  const minutes = Math.floor((temp %= 3600) / 60);
  if (minutes) {
    return minutes + ' minute' + numberEnding(minutes);
  }
  const seconds = temp % 60;
  if (seconds) {
    return seconds + ' second' + numberEnding(seconds);
  }
  return 'less than a second';
}
function numberEnding(number) { return (number > 1) ? 's' : ''; }

Hãy xem tệp nhật ký được tạo cho trường hợp người dùng gặp lỗi. Khi kết thúc kết nối, tổng thời gian của người dùng trên trang sẽ được ghi lại.

2021-04-06 08:56:10 [1b9d6b-...] START_SESSION
2021-04-06 08:58:27 [1b9d6b-...] JAVASCRIPT_ERROR TypeError: null is not an object (evaluating 'this.response.size'), http://localhost:3000/assets/application.js, 48:4
2021-04-06 08:58:27 [1b9d6b-...] JAVASCRIPT_ERROR_STACKTRACE getLinesCount@http://localhost:3000/assets/application.js:40:2
                                                             readResponse@http://localhost:3000/assets/application.js:36:2
                                                             processFormat@http://localhost:3000/assets/application.js:20:2
2021-04-06 08:58:35 [1b9d6b-...] END_SESSION 2 minutes

Nhiệm vụ đã hoàn thành! Đã xảy ra lỗi JavaScript ở phía máy khách và tôi có tất cả các chi tiết về phía máy chủ của lỗi để giúp tôi khắc phục. Tuy nhiên, tôi thiếu một số ngữ cảnh có thể hữu ích: người dùng đã làm gì trước khi xảy ra lỗi?


Sử dụng WebSocket để theo dõi hoạt động của người dùng


Trong ví dụ trên, người dùng ở lại khoảng 2 phút, nhưng tôi không biết anh ta đang làm gì. Bên cạnh việc theo dõi lỗi, sẽ rất tốt nếu biết người dùng sử dụng tính năng nào và tính năng nào không có lỗi. Thì bạn có thể tiếp tục đọc tài liệu mà ban liện lạc thông tin mới gửi dưới đây.


Tóm lại

Nếu mà bạn vô tình đọc được bài viết này, tôi nghĩ bạn nên thực hiện ngay cho công việc của bạn nếu có thể. Việc đó giúp bạn có một cái nhìn toàn cảnh tổng thể hơn. Giúp chủ động phòng tránh lỗi, nâng cao năng suất. Có thời gian rảnh nhiều hơn để làm các dự án khác hoặc kiếm tiền ngoài dự án. Giống như bài viết trước đây, chả nhẽ một công ty lại nuôi một nguồn nhân lực, lúc nào cũng đang tình trạng fix bug ư? Lúc đó bạn nghĩ doanh nghiệp đi lên hay xuống? Câu hỏi đó bạn có thể giải đáp được khi đọc được bài viết này. Chào! 


Ref: format-express.dev