Node.js lắng nghe, quan sát những thay đổi của File, Folder

Tram Ho

Đã bao giờ một ngày đẹp trời, bạn nhận được một yêu cầu từ khách hàng đó là viết cho họ một cái Service bằng Node.js có nhiệm vụ quan sát, lắng nghe một dự án khác nếu server bên dự án đó có vấn đề gì thì bắn thông báo lỗi về cho quản trị viên ngay lập tức chưa? Hôm nay chúng ta sẽ cùng nhau đi giải quyết bài toán thực tế này nhé.

1. Tản mạn phân tích bài toán

Okay, đề bài thì đã cho như trên kia rồi, bây giờ chúng ta đặt giấy bút ra nghiên cứu một chút xem những đầu việc cần làm là gì nhé:

Đầu tiên, clear rõ ràng luôn cho các bạn đó là việc đọc thông báo lỗi bản chất là quan sát cái file log trong dự án, lắng nghe xem file đó có xuất hiện hoặc thay đổi hay không, rồi lấy thông tin từ file đó ra.

Thứ hai, bài toán yêu cầu một service Node.js chạy riêng độc lập với dự án chính, (mình giả sử trong trường hợp này “cái dự án chính” là PHP – Framework Laravel).

Đối với dự án dùng Laravel nếu có lỗi, nội dung lỗi sẽ được sinh ra và ghi vào các file log theo từng ngày trong thư mục /storage/logs/, mình sẽ target vào một file có tên là laravel.log.
Lưu ý với bất kỳ một dự án hay framework nào khác thì các bạn cũng phải cần hiểu và áp dụng chính xác nơi lưu trữ file log của dự án đó nhé.

Luyên thuyên nhiều quá, vậy tóm gọn lại chúng ta phải làm gì?
Đơn giản là các bạn hãy lướt xuống dưới và đọc tiếp phần 2 + 3 nhé.

2. Quan sát, lắng nghe những hành động của một thư mục

Đây là trường hợp mà giả sử khách hàng yêu cầu chúng ta giám sát một đường dẫn thư mục (directory), nếu như có một file error.log xuất hiện bên trong thư mục đó chẳng hạn, thì đọc file đó, lấy ra nội dung lỗi sau đó xóa luôn file đó đi.

Quy trình xử lý cứ xoay vòng như vậy, file lỗi xuất hiện, đọc xong xóa rồi nó lại xuất hiện, lại đọc xong xóa tiếp rồi lại xuất hiện…

Vậy thì làm thế nào để xử lý được cái quy trình trên trong Node.js bây giờ?

Nếu như các bạn search google một hồi thì sẽ nhận được một vài ideas từ stackoverflow như dùng hàm fs.watch, fs.watchFile của module lõi fs trong Node.js. Mình đã thử làm với chúng nó và nó có một số nhược điểm như sau:

  • Thường xuyên bị nhận thông báo file thay đổi 2 lần trong khi mình chỉ chỉnh sửa file và lưu đúng một lần. Mình phải dùng vài tips check md5 của file với kiểm tra datetime thì mới xử lý được.
  • Đổi tên file cũng bị nhận thông báo khi đang lắng nghe sự kiện update nội dung bên trong file.
  • “Does not provide an easy way to recursively watch file trees”: theo mình đang hiểu là không hỗ trợ lắng nghe đệ quy cây thư mục, việc lắng nghe một thư mục là khó khăn.
  • Ngoài ra còn một vài lỗi trên hệ điều hành MacOS rồi cả việc sử dụng chúng thì tiêu tốn nhiều CPU nữa….vv

Những thông tin trên kia có những cái thực tế mình làm và nhận thấy, ngoài ra còn những cái khác thì mình lấy ở đâu? Có xác thực hay không?

Câu trả lời nằm ngay sau đây. Giới thiệu đến các bạn module rất nổi tiếng có tên là chokidar, ở thời điểm hiện tại mình vào xem thì lượt dowload hàng tuần của nó lên đến hơn 11 triệu, số star trên githubcũng rất khủng, hơn 5,5k:

Và trong bài viết hôm nay, mình sẽ hướng dẫn các bạn sử dụng thằng Chokidar này để xử lý bài toán của chúng ta nhé.

– Bắt tay vào code thôi, mình sẽ khởi tạo cấu trúc cho ứng dụng Node.js và project laravel-exampleđể chứa các file logs của chúng ta như sau:

“Lưu ý nhé, ở trên kia mình có 2 projects, một cái laravel-example bên trong nó chứa thư mục và các file logs mục đích để test. Và cái còn lại là project Node.js mà tiếp theo đây chúng ta làm việc.”

Các bạn install cho mình 2 packages là chokidar và fs-extra vào dự án bằng lệnh dưới đây: (fs-extramình sẽ giới thiệu ở một bài khác, các bạn cứ hiểu nó cũng giống module fs thôi nhưng nó hỗ trợ cho chúng ta viết code xử lý bất đồng bộ với cú pháp async – await rất xịn nhé.)

npm i --save chokidar fs-extra

Phiên bản mới nhất của chokidar hiện tại mình sử dụng là “3.0.1“, còn fs-extra là “8.1.0“

Chúng ta sẽ bắt đầu viết code xử lý lắng nghe cho file Observe.js như sau nhé:

Ở trên mình cũng đã comment cơ bản rõ ràng hết trong các dòng code rồi (nếu mình có viết sai tiếng anh xin phép các bạn comment dưới bài nhắc mình nhé), bây giờ mình sẽ giải thích rõ ràng code từ trên xuống dưới nếu như các bạn đọc code mà chưa hiểu nha:

  • File Observe.js ở trên là nơi chúng ta viết các function chức năng cho việc lắng nghe file và thư mục.
  • Đầu tiên là nạp các module cần thiết: chokidar, events, fs-extra.
  • Cái biến debug các bạn cứ hiểu đơn giản nó cũng không khác gì console.log, mình tạo ra nó để code phía dưới mỗi lần muốn log thông báo ra console cho nó ngắn gọn hơn thôi.
  • Mình khởi tạo một class Observe kế thừa EventEmitter với hàm constructor gọi function super(); mục đích là để class Observer của chúng ta có thể emit được sự kiện. Kỹ thuật này mình có giải thích rất rõ ràng trong phần 3 của bài Đào sâu module Events trong Node.js này rồi các bạn có thể tham khảo nếu chưa rõ nha.
  • Tiếp đến là function watchFolder, function này chịu trách nhiệm lắng nghe một đường dẫn thư mục được truyền vào (targetFolder)
  • Bên trong watchFolder mình khởi tạo biến watcher bằng hàm chokidar.watch, tham số đầu tiên truyền vào là đường dẫn thư mục (targetFolder), tham số thứ 2 là object {persistent: true} mục đích để thông báo cho thằng watcher biết là dù cho các tệp đã được xem rồi thì nó vẫn nên tiếp tục chạy.
  • Sau đó thằng watcher này lắng nghe một sự kiện là “add”, có nghĩa là cứ mỗi khi có một file nào đó được thêm vào folder mà nó đang lắng nghe thì code sẽ chạy vào đây.
  • Sau khi sự kiện “add” được kích hoạt, mình kiểm tra tiếp filePath nếu tồn tại chuỗi string là error.log, có nghĩa là file mới được thêm vào có tên là error.log
  • Đọc nội dung của file error.log bằng fsExtra.readFile()
  • Emit sự kiện “new-file-has-been-added” kèm nội dung message là nội dung file lấy được ở trên.
  • Xóa file error.log bằng fsExtra.unlink()
  • Cuối cùng luôn là bước nhẹ nhàng nhưng không kém phần quan trọng, export cái module Observe ra ngoài để bên server.js có thể sử dụng.

Bây giờ là code cho file server.js:

  • Trong file server.js mình nạp class Observe ở trên, sau đó khởi tạo new đối tượng Observe.
  • Định nghĩa đường dẫn đến thư mục “targetFolder”, lưu ý trong dự án thực tế các bạn nên đưa biến targetFolder này vào file config hoặc biến môi trường env của dự án nhé.
  • Sử dụng Observe.on để lắng nghe khi nào sự kiện “new-file-has-been-added” của chúng ta được kích hoạt thì log nó ra console để nhìn. Vì làm ví dụ nên mình chỉ log ra như vậy, đối với mỗi dự án thì trong này các bạn sẽ viết thêm code xử lý như là bắn thông báo về slack, về chatwork…vv đại loại là làm sao mà người quản lý project có thể biết được là có lỗi nhé.
  • Cuối cùng là gọi đến function watchFolder, và truyền vào đường dẫn tới thư mục muốn quan sát.

Bây giờ chạy service Node lên:

node server.js

Nếu trong targetFolder của các bạn đã có file error.log thì nó sẽ log nội dung ra như hình dưới mình demo, và file error.log cũng sẽ bị xóa luôn.

Các bạn có thể tiếp tục tái hiện lại quá trình này bằng cách tạo một file error.log xong copy paste vào thư mục đang watching và xem log nhé.
Vậy là giải quyết xong việc lắng nghe thư mục, tiếp theo trong phần 3 chúng ta sẽ đi xử lý lắng nghe một file cụ thể.

3. Quan sát, lắng nghe những hành động của một tệp tin cụ thể

Trường hợp này khác phần 2 một chút, lúc này khách hàng không muốn chúng ta xóa file error.log đi nữa, mà lại muốn chúng ta quan sát chỉ riêng file đó, nếu như có lỗi => nội dung file error.log được cập nhật, thì lấy ra đúng phần nội dung cập nhật mới nhất để thông báo thôi.

Quy trình xử lý sẽ xoay vòng như thế này, quan sát file nếu nó được cập nhật, lấy phần nội dung mới cập nhật ra rồi lại quan sát tiếp…….

Với yêu cầu như trên, để giải quyết việc đọc nội dung mới cập nhật của file, các bạn cài thêm cho mình module có tên là read-last-lines như sau:

npm i --save read-last-lines

Chúng ta sẽ viết tiếp chức năng lắng nghe file vào trong Observe.js (mình sẽ lược đi những đoạn code đã có trong phần 2 nhé, lúc làm thì các bạn cứ giữ nguyên, chỉ thêm code mới vào thôi)

Phần code này cũng đơn giản:

  • Lần này thằng watcher lúc khởi tạo sẽ nhận vào tham số là một file cụ thể sau đó lắng nghe sự kiện “change” watcher.on(“change”)
  • Chúng ta sẽ lấy nội dung update của file bằng module read-last-line, thông thường khi có lỗi thì file sẽ được cập nhật từng dòng một và lưu lại nên mình truyền vào tham số là 1 ở đoạn code này để lấy ra nội dung dòng cuối cùng của file: readLastLines.read(filePath, 1);
  • Sau đó là emit một sự kiện có tên là “file-has-been-updated” cho bên server.js lắng nghe.

Code cho file server.js chúng ta sẽ viết tiếp đoạn dưới đây:

Đoạn code trên chắc mình cũng không cần giải thích gì dài dòng thêm nữa nhé, nó tương tự trong phần 2, chỉ khác là chúng ta quan sát cái file laravel.log thay vì cả cái thư mục logs.

Xong, kết quả mỗi khi mình cập nhật dữ liệu vào file có tên là laravel.log:

Đây là full source code của bài hôm nay: https://github.com/trungquan17/nodejs-monitoring-file-folder

Vậy là bài hôm nay mình đã hướng dẫn hoàn thiện cho các bạn về ý tưởng và cách triển khai cho việc quan sát, lắng nghe một file hoặc một folder trong Node.js rồi nhé.

Cảm ơn các bạn đã dành thời gian đọc bài viết.

Chia sẻ bài viết ngay

Nguồn bài viết : trungquandev.com