Trung Quân
  • Node.js
  • ReactJS
    • Learn React Hooks
  • MERN Stack
  • Thuật Toán
  • Công Nghệ
    • Amazon AWS
    • Hosting – Domain
  • Về Tác Giả
  • Bản Quyền
Top Posts
Lộ trình học lập trình Web từ con...
Nhận diện, xác thực khuôn mặt với ReactJS...
Tạo hiệu ứng thẻ bài Magic với HTML...
NodeJS viết API gửi Email với OAuth2 và...
Tạo hiệu ứng Parallax đơn giản chỉ với...
Git – GitHub • Học Git thực tế...
Tạo một trang GitHub Profile phiên bản vũ...
Cài đặt iTerm2, Oh My Zsh, Zsh-autosuggestions và...
Cài đặt NVM, NodeJS và GIT trên MacOS...
Thuật toán Tìm phần tử bị lặp đầu...

Trung Quân

  • Node.js
  • ReactJS
    • Learn React Hooks
  • MERN Stack
  • Thuật Toán
  • Công Nghệ
    • Amazon AWS
    • Hosting – Domain
  • Về Tác Giả
  • Bản Quyền
Common Problems

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

by trungquandev July 4, 2019
written by trungquandev July 4, 2019
nodejs-monitoring-file-folder-trungquandev

Xin chào tất cả các bạn, mình là Quân, đã 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é.

“Bài này nằm trong loạt bài Lập Trình Nodejs từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com“

Những nội dung có trong bài:

  1. Tản mạn phân tích bài toán.
  2. Quan sát, lắng nghe những hành động của một thư mục.
  3. Quan sát, lắng nghe những hành động của một tệp tin cụ thể.

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…..(cứ thế đi mãi đi mãi không về =)))

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 github cũng rất khủng, hơn 5,5k:

NPM: https://www.npmjs.com/package/chokidar
Github: https://github.com/paulmillr/chokidar

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:

laravel-example

storage

logs

error.log

laravel.log


nodejs-monitoring-files

modules

observe.js

package.json

server.js

 

Bạn nào chưa rành việc tạo project Node.js thì có thể tham khảo lại bài khởi tạo ứng dụng Node.js trong loạt bài Lập Trình Nodejs từ cơ bản đến nâng cao trên trang blog chính thức trungquandev.com của mình nhé.

“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-extra mì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é:

/**
 * Created by trungquandev.com's author on 02/07/2019.
 * Observe.js
 */
const chokidar = require("chokidar");
const EventEmitter = require("events").EventEmitter;
const fsExtra = require("fs-extra");

let debug = console.log.bind(console);

class Observe extends EventEmitter {
  constructor() {
    super();
  }

  /**
   * Function responsible for watching a folder
   * @param {string} targetFolder 
   */
  watchFolder(targetFolder) {
    try {
      debug(`[${new Date().toLocaleString()}] Watching for folder changes on: ${targetFolder}`);

      // initialize watcher
      let watcher = chokidar.watch(targetFolder, {persistent: true});

      // listen when a file has been added
      watcher.on("add", async (filePath) => {
        // if the new file's name is exactly error.log
        if (filePath.includes("error.log")) {
          debug(`[${new Date().toLocaleString()}] ${filePath} has been added.`);

          // Read content of new file
          let fileContent = await fsExtra.readFile(filePath);

          // emit an event when new file has been added
          this.emit("new-file-has-been-added", {message: fileContent.toString()});

          // remove file error.log
          await fsExtra.unlink(filePath);
          debug(`[${new Date().toLocaleString()}] ${filePath} has been removed.`);
        }
      });
    } catch (error) {
      debug(error.toString());
    }
  }
}

module.exports = Observe;

Ở 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

/**
 * Created by trungquandev.com's author on 02/07/2019.
 * server.js
 */
const ObserveClass = require("./modules/Observe");

// Init Observe object
let Observe = new ObserveClass();

// Define folder to watching, in real project, you should put it in file config or env
let targetFolder = "../laravel-example/storage/logs";

// Listen event new file has been added
Observe.on("new-file-has-been-added", (logData) => {
  // In this step, you can do anything you want, like to push alert message to chatwork, slack...vv
  // I just print error message to console
  console.log(logData.message);
});

// Start watching folder...
Observe.watchFolder(targetFolder);
  • 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)

/**
 * Created by trungquandev.com's author on 02/07/2019.
 * Observe.js
 */

// require module read-last-lines
const readLastLines = require("read-last-lines");

class Observe extends EventEmitter {
  /**
   * Function responsible for watching a file
   * @param {string} targetFile 
   */
  watchFile(targetFile) {
    try {
      debug(`[${new Date().toLocaleString()}] Watching for file changes on: ${targetFile}`);

      // initialize watcher
      let watcher = chokidar.watch(targetFile, {persistent: true});

      // listen when a file has been added
      watcher.on("change", async (filePath) => {
        debug(`[${new Date().toLocaleString()}] ${filePath} has been updated.`);

        // Get update content of file, in this case is one line
        let updateContent = await readLastLines.read(filePath, 1);

        // emit an event when the file has been updated
        this.emit("file-has-been-updated", {message: updateContent});
      });
    } catch (error) {
      debug(error.toString());
    }
  }
}

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

/**
 * Created by trungquandev.com's author on 02/07/2019.
 * server.js
 */

// Define file to watching, in real project, you should put it in file config or env
let targetFile = "../laravel-example/storage/logs/laravel.log";

// Listen event file has been updated
Observe.on("file-has-been-updated", (logData) => {
  // In this step, you can do anything you want, like to push alert message to chatwork, slack...vv
  // I just print error message to console
  console.log(logData.message);
});

// Start watching file...
Observe.watchFile(targetFile);

Đ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:


Mình có để full source code của bài hôm nay ở repo này cho các bạn tham khảo nhé:
https://github.com/trungquandev/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.

Xin chào và hẹn gặp lại các bạn ở những bài viết tiếp theo.

Best Regards – Trung Quân – Green Cat


Tham khảo kiến thức:

https://thisdavej.com/how-to-watch-for-files-changes-in-node-js/
https://www.npmjs.com/package/chokidar
https://www.npmjs.com/package/read-last-lines

“Thanks for awesome knowledges.”

Khóa học lập trình làm việc thực tế:

Nếu các bạn thấy bài viết của mình có ích thì hãy ủng hộ mình bằng cách tham khảo bài viết giới thiệu khóa học lập trình MERN Stack Miễn Phí cực kỳ chất lượng và chính chủ dưới đây của mình nhé, cảm ơn các bạn ^^
KHOÁ HỌC LẬP TRÌNH MERN STACK 100% DỰ ÁN THỰC TẾ và CHẤT LƯỢNG
mern-stack-course-trungquandev
chokidarfs-extramodule fsmonitoringFoldernode.jsnodejsread-last-lines
1 comment
0
FacebookTwitterGoogle +Pinterest
trungquandev

previous post
Node.js & MongoDB – Xây dựng một ứng dụng Messenger trò chuyện trực tuyến.
next post
Upload multiple files trong NodeJS

Related Posts

Framework và CMS khác nhau như thế nào?...

September 3, 2017

Tất cả hướng đi để trở thành một...

April 20, 2017

Ứng dụng web tìm kiếm “cặp số bạn...

June 28, 2016

Hướng dẫn tạo Domain ảo tại Localhost sử...

May 6, 2017

Ứng dụng web liệt kê danh sách số...

June 3, 2016

Ứng dụng web chuyển năm dương lịch bất...

June 8, 2016

Tổng Quan Về Laravel Một Framework Khá Mạnh...

July 22, 2016

Hướng dẫn build một con Facebook Messenger Bot...

January 19, 2017

Cấu hình bộ phát wifi TP-Link cho gia...

November 23, 2018

Tản mạn về cái tên “Lập trình viên...

March 25, 2018

Bát Chánh Đạo

batchanhdao-trungquandev

About Me

About Me

Trung Quân

Software Engineer at Pod Foods

Academy Of Cryptography Techniques

"I am a dog and cat lover, love green, love to read books, write blog and oil painting ..."

Read my CV → https://cv.trungquandev.com/

My Maxim Live

A bit of fragrance clings to the hand that gives flowers!

For Vietnamese young generation

Đừng bao giờ làm người khổng lồ trong tư tưởng nhưng chỉ là thằng lùn trong hành động.

Keep in touch

Facebook Instagram Linkedin Youtube Email Github

Fanpage Facebook

Facebook

Học lập trình “MERN Stack Cơ Bản” qua ứng dụng thực tế

mern-stack-trello-app-trungquandev


  Khóa học lập trình MERN Stack (NodeJS, ReactJS, ExpressJS, MongoDB) cực kỳ chất lượng và Miễn Phí do chính mình hướng dẫn nhé:
  MERN Stack - xây dựng ứng dụng quản lý công việc dạng Kanban tương tự Trello.

Học lập trình “MERN Stack Nâng Cao” qua ứng dụng thực tế

mern-stack-advanced-trello-app-trungquandev


  * Học lập trình MERN Stack Nâng Cao (NodeJS, ReactJS, ExpressJS, MongoDB). Đây là một khóa học mà mình đã làm cực kỳ tâm huyết, với phong cách dạy lập trình làm dự án thực tế, chuyên nghiệp. Để các bạn có một hành trang kiến thức vững chắc cho hành trình làm lập trình viên trong tương lai nhé.
  Lập Trình MERN Stack Nâng Cao - Học Thực Tế Để Đi Làm

YouTube: Trungquandev Official

youtube-channel-trungquandev-official


  - Các bạn tham khảo kênh YouTube chính thức của mình và ủng hộ mình bằng cách tặng mình một lượt Đăng Ký Kênh nha.
Cảm ơn các bạn ^^

  YouTube: TRUNGQUANDEV OFFICIAL

© Copyright

Bản quyền:
  © DMCA (Digital Millennium Copyright Act)


DMCA.com Protection Status

License

Giấy phép nội dung:
  Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 Quốc tế.


Giấy phép Creative Commons

Other links

Portfolio của mình:
  https://cv.trungquandev.com

Lập trình cuộc sống:
  https://laptrinhcuocsong.com

Nhiều bài viết hay về javascript:
  https://codetheworld.io

  • Facebook
  • Youtube
  • GitHub
  • Linkedin
  • Instagram

@2022 - trungquandev.com. Since 2016 - All Right Reserved. Developed by Trung Quân Dev