Sử dụng tính năng gỡ lỗi, ngăn xếp, ghi nhật ký và các điểm ghi nhật ký trong ngăn xếp Google

Hướng dẫn này sẽ hướng dẫn bạn về Google Stackdriver, cho phép bạn thực hiện những việc sau đây bằng các ứng dụng của Google Cloud Platform:

  • Chụp Tổng quan gỡ lỗi trong các ứng dụng chạy trên App Engine, Compute Engine và Engine Engine.
  • Xem Nhật ký ứng dụng.
  • Thiết lập các chỉ số, theo dõi các chỉ số đó và nhận cảnh báo.
  • Theo dõi lệnh gọi API của bạn và nhận chi tiết về thời gian phản hồi và các nút thắt cổ chai tiềm ẩn trong mã của bạn.
  • Thêm điểm nhật ký vào một ứng dụng đang chạy mà không cần triển khai ứng dụng. Đây là một tính năng thực sự độc đáo (và hy vọng hữu ích).

Trong hướng dẫn này, chúng ta sẽ làm những việc sau từ đầu:

  1. Tạo một dự án Google Cloud Platform (cụ thể là App Engine)
  2. Thiết lập kho lưu trữ nguồn dự án Google Cloud Platform
  3. Sử dụng nguồn Ứng dụng Python sổ tay chuẩn có sẵn từ Github
  4. Triển khai mã
  5. Xem cách chúng ta có thể tải ảnh chụp nhanh Gỡ lỗi của ứng dụng đang chạy
  6. Xem nhật ký cuộc gọi và nhật ký ghi nhật ký ứng dụng
  7. Thêm điểm ghi nhật ký vào ứng dụng đang chạy hiện tại. Tính năng này ban đầu được đề cập trong bài đăng trên blog này: Thêm nhật ký ứng dụng vào ứng dụng mà không khởi động lại

Hãy cùng bắt đầu!

Nội dung này ban đầu do Romin Irani tạo ra và được đăng tại đây.

Thiết lập môi trường theo tiến độ riêng

Nếu chưa có Tài khoản Google (Gmail hoặc Google Apps), thì bạn phải tạo một tài khoản. Đăng nhập vào bảng điều khiển của Google Cloud Platform (console.cloud.google.com) và tạo một dự án mới:

Ảnh chụp màn hình từ 2016-02-10 12:45:26.png

Hãy ghi nhớ mã dự án, một tên duy nhất trên tất cả các dự án Google Cloud (tên ở trên đã được sử dụng và sẽ không hoạt động cho bạn!). Lớp học này sẽ được gọi sau này trong lớp học lập trình này là PROJECT_ID.

Tiếp theo, bạn sẽ cần bật tính năng thanh toán trong Cloud Console để sử dụng tài nguyên của Google Cloud.

Nếu tham gia lớp học lập trình này, bạn sẽ không mất quá vài đô la, nhưng có thể sẽ hiệu quả hơn nếu bạn quyết định sử dụng nhiều tài nguyên hơn hoặc nếu bạn để các tài nguyên đó hoạt động (xem "cleanup" ở cuối tài liệu này).

Người dùng mới của Google Cloud Platform đủ điều kiện dùng thử 300 đô la dùng thử miễn phí.

Google Cloud Shell

Trong lớp học lập trình này, chúng ta sẽ dùng Google Cloud Shell, một môi trường dòng lệnh chạy trong Cloud.

Máy ảo dựa trên Debian này được tải bằng tất cả công cụ phát triển mà bạn cần. cung cấp một thư mục gốc 5GB cố định và chạy trên Google Cloud, qua đó nâng cao đáng kể hiệu suất và khả năng xác thực mạng. Điều này có nghĩa là tất cả những gì bạn cần cho lớp học lập trình này là trình duyệt (có, nó hoạt động trên Chromebook).

Để kích hoạt Google Cloud Shell, từ bảng điều khiển dành cho nhà phát triển, bạn chỉ cần nhấp vào nút ở phía trên cùng bên phải (chỉ mất vài phút để cấp phép và kết nối với môi trường):

ActivateCloudShell.png.

Sau đó, chấp nhận các điều khoản dịch vụ và nhấp vào đường liên kết "Start Cloud Shell":

x.png

Ảnh chụp màn hình lúc 10:13,43 chiều ngày 14/6/2017

Sau khi kết nối với shell Cloud, bạn sẽ thấy rằng bạn đã được xác thực và dự án này đã được đặt thành PROJECT_ID của bạn:

gcloud auth list

Đầu ra lệnh

Credentialed accounts:
 - <myaccount>@<mydomain>.com (active)
gcloud config list project

Đầu ra lệnh

[core]
project = <PROJECT_ID>

Nếu vì lý do nào đó mà dự án chưa được đặt, bạn chỉ cần đưa ra lệnh sau :

gcloud config set project <PROJECT_ID>

Bạn đang tìm kiếm PROJECT_ID của mình? Kiểm tra mã nhận dạng mà bạn đã sử dụng trong các bước thiết lập hoặc tìm kiếm mã đó trong trang tổng quan bảng điều khiển:

Project_ID.png.

LƯU Ý QUAN TRỌNG: Cuối cùng, hãy đặt cấu hình dự án và vùng mặc định:

gcloud config set compute/zone us-central1-f

Bạn có thể chọn nhiều khu vực khác nhau. Tìm hiểu thêm trong Tài liệu về khu vực và amp; khu vực.

Xem các API StackDriver đã bật

Hãy xem các API đã được bật cho dự án của bạn. Sử dụng thanh tìm kiếm để tìm Trang tổng quan API như hiển thị bên dưới.

Quan sát các API cụ thể đã được bật cho dự án của bạn :

Mọi dự án Google Cloud Platform đều cung cấp dịch vụ lưu trữ Git riêng tư, nhưng trước tiên, chúng tôi cần tạo kho lưu trữ mặc định để hoạt động. Chuyển đến vị trí lưu trữ nguồn bằng cách sử dụng hộp tìm kiếm trên bảng điều khiển :

Nhấp "TẠO REPOSITORY" để tạo kho lưu trữ mã mới có tên "default" :

Sử dụng Cloud Shell, bây giờ chúng ta sẽ sao chép thư mục này vào phiên bản Google Cloud Shell. Để làm điều đó, trước tiên, chúng ta hãy tạo một thư mục trong thực thể Google Cloud Shell rồi chuyển đến thư mục đó như bên dưới (đầu ra mẫu):

mkdir stackdriver-demo
cd stackdriver-demo/

Bây giờ, chúng ta có thể sao chép kho lưu trữ mặc định tại đây thông qua lệnh gcloud như bên dưới:

gcloud source repos clone default

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

Cloning into '/home/gcp123_student/default'...
warning: You appear to have cloned an empty repository.
Project [qwiklabs-gcp-1234abc1234] repository [default] was cloned to [/home/gcp123_student/default].

Tuyệt vời! Hãy dành chút thời gian để tìm hiểu sâu hơn về các điều khiển từ xa git đã được thiết lập. Điều này không nhất thiết chỉ là điều gì đó để bạn hiểu rõ hơn điều gì đã xảy ra hậu trường.

Vào thư mục mặc định được tạo và kích hoạt lệnh git remote -v như được hiển thị bên dưới

cd default
git remote -v

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy tương tự như sau:

origin https://source.developers.google.com/p/qwiklabs-gcp-1234abc1234/r/default (fetch)
origin https://source.developers.google.com/p/qwiklabs-gcp-1234abc1234/r/default (push)

Bạn có thể thấy rằng điểm này chính xác đến Kho lưu trữ Git được liên kết với Dự án GCP của chúng tôi.

Kéo ứng dụng Sổ tay từ Github

Ứng dụng mà chúng ta sẽ sử dụng là một ứng dụng App Engine tiêu chuẩn có tên là Hostbook và có sẵn tại kho lưu trữ chính thức của Google Cloud Platform Github. Ứng dụng này cũng là một phần của tài liệu chính thức để bắt đầu. Dự án Github có tại: https://github.com/GoogleCloudPlatform/appengine-Guestbook-python

Bây giờ, chúng ta sẽ đưa mã này vào thực thể Cloud Shell. Lệnh và đầu ra của lệnh sẽ hiển thị ở đây :

git pull https://github.com/GoogleCloudPlatform/appengine-guestbook-python

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

remote: Counting objects: 485, done.
remote: Total 485 (delta 0), reused 0 (delta 0), pack-reused 485
Receiving objects: 100% (485/485), 436.42 KiB | 163.00 KiB/s, done.
Resolving deltas: 100% (195/195), done.
From https://github.com/GoogleCloudPlatform/appengine-guestbook-python
* branch HEAD -> FETCH_HEAD

Giờ đây, tất cả mã của chúng tôi đều xuất hiện cục bộ trong phiên bản Google Cloud Shell. Bạn có thể xem các tệp khác nhau đã được lấy từ dự án Github.

Đẩy mã hiện tại bằng Cloud Shell vào Kho lưu trữ dự án Git

Bây giờ, hãy đẩy mã này vào kho lưu trữ Git của Dự án GCP để chúng ta có thể đặt các điểm ngắt, điểm ghi nhật ký và nhiều giá trị khác cho mã của mình. Xin lưu ý rằng đây không phải là bước bắt buộc vì bạn có thể tích hợp trực tiếp với Github, máy cục bộ của bạn và các cách khác để liên kết mã nguồn.

Nhưng vì mục đích của chúng tôi ở đây, chúng tôi sẽ đẩy mã này vào Kho lưu trữ Git của dự án GCP. Việc này được thực hiện thông qua lệnh đẩy git chuẩn như bên dưới:

git push origin master

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

Counting objects: 485, done.
Compressing objects: 100% (280/280), done.
Writing objects: 100% (485/485), 436.42 KiB | 0 bytes/s, done.
Total 485 (delta 195), reused 485 (delta 195)
remote: Storing objects: 100% (485/485), done.
remote: Processing commits: 100% (152/152), done.
To https://source.developers.google.com/p/qwiklabs-gcp-1234abc1234/r/default
* [new branch] master -> master

Bây giờ, hãy quay lại Bảng điều khiển Cloud Cloud GCP và cụ thể là trong phần Phát triển. Nhấp vào Mã nguồn và đối với kho lưu trữ mặc định, bạn sẽ có thể xem tất cả tệp dự án. Kết quả mẫu được trình bày bên dưới:

Bây giờ, chúng ta đã sẵn sàng để triển khai ứng dụng Hostbook App Engine của chúng tôi. Để triển khai ứng dụng này, hãy đảm bảo rằng bạn đang ở trong Google Cloud Shell và trong thư mục mặc định như chúng ta đã làm xong. Hãy dùng lệnh gcloud app deploy như bên dưới:

gcloud app deploy --version 1

Khi được yêu cầu chọn khu vực, hãy chọn [1] Us-east1.

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

You are about to deploy the following services:
— qwiklabs-gcp-1234abc1234/default/1 (from [/home/gcp123-student/default/app.yaml])
Deployed URL: [https://qwiklabs-gcp-1234abc1234.appspot.com]
Do you want to continue (Y/n)? Y
Beginning deployment of service [default]...
File upload done.
Updating service [default]...done.
Deployed service [default] to https://qwiklabs-gcp-1234abc1234.appspot.com]

Xin lưu ý rằng chúng tôi đã cung cấp thông số phiên bản cho lệnh triển khai ứng dụng. Chúng tôi chỉ định giá trị là "1".

Vì ứng dụng Sổ tay lưu trữ sử dụng Google Cloud Datastore để lưu trữ, chúng tôi cần cập nhật các chỉ mục của Datastore. Các chỉ mục được chỉ định trong tệp index.yaml và chúng ta chỉ cần dùng lệnh gcloud datastore create-indexes như trình bày bên dưới:

gcloud datastore create-indexes index.yaml

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

You are about to update the following configurations:
— qwiklabs-gcp-1234abc1234/index From: [/home/gcp123_student/default/index.yaml]
Do you want to continue (Y/n)? Y

Chỉ mục Lưu trữ dữ liệu có thể mất một chút thời gian để cập nhật. Để kiểm tra trạng thái, hãy tìm "chỉ mục Datastore" và nhấp vào Chỉ số. Trong khi chỉ mục đang được tạo, bạn sẽ thấy Trạng thái có giá trị "Lập chỉ mục" như được trình bày bên dưới:

Chúng ta có thể kiểm tra xem Ứng dụng của mình có Phiên bản 1 đã được triển khai hay chưa bằng cách chuyển đến Máy tính → Ứng dụng, rồi nhấp vào các Phiên bản như bên dưới:

Mọi thứ hiện đã ổn và bạn có thể xem dự án của mình bằng cách truy cập vào https://<PROJECT_ID>.appspot.com. Xin lưu ý rằng có thể mất vài phút để chỉ mục của kho dữ liệu sẵn sàng, vì vậy, nếu ứng dụng gặp lỗi (ví dụ: Lỗi máy chủ nội bộ), hãy thử lại sau vài phút.

Bây giờ, hãy sử dụng ứng dụng bằng cách đăng nhập và tạo một vài mục nhập sổ lưu bút như minh họa bên dưới:

Tuyệt vời! Bây giờ, chúng ta đã sẵn sàng để tìm hiểu về các tính năng của Stackdriver.

Trước tiên, hãy xem cách chúng tôi có thể chụp nhanh ứng dụng đang chạy của mình. Trang Tổng quan sẽ hữu ích nếu bạn muốn gỡ lỗi một đoạn mã cụ thể, kiểm tra các biến và nhiều thông tin khác. Tất cả những điều này xảy ra trong khi ứng dụng của bạn vẫn đang được phân phối. Điều này rất hữu ích trong trường hợp bạn đang nhận được các báo cáo về một vấn đề với ứng dụng của mình và bạn muốn cố gắng gỡ lỗi cũng như xem điều gì đang xảy ra với ứng dụng của bạn, kiểm tra nhiều biến, chụp ảnh nhanh có điều kiện và nhiều tính năng khác.

Hãy làm điều đó ngay bây giờ cho ứng dụng Sổ tay. Chúng tôi sẽ yêu cầu ảnh chụp nhanh nếu có người yêu cầu trang chủ và đặc biệt là chúng tôi muốn trang đó truy xuất danh sách lời chào hiện có trong Datastore.

Mã cho thao tác này có trong tệp guestbook.py. Cụ thể, chúng tôi muốn bắt đầu kiểm tra mã vào thời gian chạy sau khi mã đó truy xuất danh sách lời chào từ kho dữ liệu. Việc này được thực hiện trên Dòng số 72. Vì vậy, chúng ta chỉ cần đặt một điểm ngắt trên Dòng 74, để chúng ta biết rằng số 72 sẽ được thực thi.

Để làm điều đó, hãy nhấp vào "Debug" từ chế độ xem phiên bản Appengine hoặc chuyển đến Stackdriver → Gỡ lỗi . Màn hình sẽ hiển thị màn hình bên dưới. Việc bạn cần làm là chọn tệp (guestbook.py) ở bên trái, sau đó nhấp vào số dòng như hiển thị.

Thao tác này sẽ hiển thị thông báo được làm nổi bật trong hộp màu đỏ ở trên, cho biết thông báo này đang chờ kích hoạt ảnh chụp nhanh. Bây giờ, tất cả việc chúng tôi cần làm là truy cập trang của mình tại

https://<PROJECT_ID>.appspot.com.

Sau khi bạn thực hiện việc này, ảnh chụp nhanh sẽ được kích hoạt và bạn sẽ thấy phần Biến và ngăn xếp lệnh gọi được điền như bên dưới. Xem cách các biến được hiển thị và bạn có thể mở rộng các biến đó để kiểm tra giá trị. Điều này cực kỳ hữu ích.

Ví dụ: nếu bạn mở rộng biến lời chào, bạn sẽ thấy rằng biến đó có các bản ghi tương ứng với số mục nhập sổ lưu bút mà bạn đã tạo.

Một tính năng rất hữu ích là chụp lại nhanh ảnh bất cứ lúc nào. Bạn chỉ cần nhấp vào biểu tượng máy ảnh bất kỳ lúc nào và máy ảnh sẽ chờ cho đến khi ảnh chụp nhanh xuất hiện như bên dưới:

Bạn có thể sử dụng trường Biểu thức như bên dưới để theo dõi các biến cụ thể. Ví dụ: chúng tôi biết rằng chúng tôi có những lời chào có thể thay đổi và chúng tôi muốn kiểm tra giá trị của lời chào, khoảnh khắc một ảnh chụp nhanh được truy cập. Chúng ta có thể đặt biến lời chào như trong ảnh chụp màn hình bên dưới. Khi truy cập vào ảnh chụp nhanh, thao tác này sẽ điền giá trị như được hiển thị.

Nếu chỉ muốn ảnh chụp nhanh kích hoạt trên một điều kiện nhất định, thì bạn có thể sử dụng trường Điều kiện như bên dưới. Ở đây chúng tôi nói rằng ảnh chụp nhanh chỉ nên diễn ra nếu số lượng lời chào lớn hơn 1. Hãy thử nghiệm tính năng này nếu bạn muốn.

Bạn cần phải đảm bảo rằng hiệu suất ứng dụng web của mình đáp ứng các yêu cầu mà bạn đặt ra. Stackdriver Trace là một công cụ chính giúp bạn hiểu được độ trễ trong các ứng dụng của mình.

Tính năng này được bật theo mặc định cho tất cả ứng dụng của App Engine và cung cấp cho chúng tôi thông tin chi tiết về hiệu suất rất tiện lợi cho tất cả điểm cuối cùng với thông tin chi tiết về các cuộc gọi khác nhau.

Trong trường hợp này, chúng tôi đã truy cập trang chủ ("/") và xem / thêm các mục nhập sổ lưu bút. Thông tin này đủ để Dấu vết cho chúng tôi biết thêm về độ trễ. Chỉ cần chuyển đến Stackổ Traces Trace (Tổng quan) về dấu vết và chúng ta sẽ thấy một ảnh chụp màn hình như minh họa bên dưới. Hãy chú ý đến các dấu vết gần đây và độ trễ của chúng.

Nếu chúng tôi nhấp vào một dấu vết bất kỳ, tức là nhấp vào liên kết URI, thì nó sẽ hiển thị dấu vết chi tiết như được cho bên dưới:

Xin lưu ý rằng tính năng này có thể cho chúng tôi biết độ trễ như thế nào và cuộc gọi nào mất nhiều thời gian nhất. Bạn có thể thấy từ hình ảnh ở trên rằng truy vấn kho dữ liệu đang mất thời gian. Có thể bạn đã xem xét một tùy chọn có thể là lưu vào bộ nhớ đệm dữ liệu để giảm nút thắt cổ chai đó. Xin nhắc lại, điều này phụ thuộc vào ứng dụng của bạn và việc này sẽ rất hữu ích để xác định những khu vực nào trong dấu vết cuộc gọi của bạn có thể cần tái cấu trúc.

Bạn có thể xem nhật ký ứng dụng của mình bất cứ lúc nào bằng cách chuyển đến ứng dụng Ghi nhật ký Stackdriver (như được hiển thị bên dưới). Có nhiều bộ lọc có sẵn từ nhiều Dịch vụ GCP khác nhau → Các loại nhật ký → Cấp độ nhật ký → Ngày và nhiều bộ lọc khác.

Ảnh chụp màn hình dưới đây cho thấy Nhật ký cho ứng dụng App Engine của chúng tôi và Phiên bản mặc định 1.

log.png

Cuối cùng, hãy tìm hiểu một tính năng giúp bạn hào hứng với những tính năng mà tính năng này mang lại. Thường thì những nhà phát triển luôn cố gắng hết sức để đưa các nhật ký vào mã, triển khai mã của chúng tôi và hy vọng rằng nhật ký sẽ cho chúng tôi biết tất cả những gì chúng tôi muốn biết.

Nhưng chúng tôi biết điều đó là chưa đủ và chỉ gỡ lỗi, chúng tôi nhận thấy rằng có lẽ chúng tôi nên đặt thêm một vài câu lệnh nhật ký ở đây. Sau đó, quy trình làm việc thông thường là sửa đổi mã của bạn, đưa câu lệnh nhật ký bổ sung vào, triển khai lại và giám sát.

Điều này không có vấn đề gì nhưng nếu bạn có thể thêm các câu lệnh nhật ký này (hãy gọi chúng là logpoints ngay bây giờ) vào ứng dụng đang chạy của bạn. Điều này có nghĩa là chúng tôi không cần phải thực hiện quá trình ngừng đăng ký, thay đổi mã và triển khai lại. Thay vào đó, chúng ta có thể quản lý danh sách các điểm ghi nhật ký từ bên ngoài ứng dụng của mình bằng cách sử dụng chức năng hỗ trợ điểm ghi nhật ký.

Vẫn từ trong Cloud Shell, hãy để chúng ta kiểm tra danh sách các điểm ghi nhật ký hiện tại mà chúng ta đã định cấu hình (thường rõ ràng là 0). Việc này được thực hiện thông qua lệnh gcloud như hình dưới đây:

gcloud debug logpoints list

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

Debug target not specified. Using default target: default-1
Listed 0 items.

Bây giờ, chúng ta sẽ thêm một điểm ghi nhật ký vào ứng dụng đang chạy. Để thêm điểm ghi nhật ký, chúng ta cần thực hiện những việc sau:

  • Xác định tệp mã nguồn và số dòng mà chúng tôi muốn thêm điểm ghi nhật ký.
  • Xác định thông điệp nhật ký. Thông điệp nhật ký này có thể được mã hóa cứng hoặc thậm chí là một biểu thức.

Trong trường hợp của chúng ta, chúng ta sẽ thêm một điểm ghi nhật ký vào tệp khách.{0}

gcloud debug logpoints create guestbook.py:74 "Fetched greetings from Datastore. Count of greetings : {len(greetings)}"

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

Debug target not specified. Using default target: default-1
— id: 53538243519d4-f9a0-bdbce
location: guestbook.py:74
logLevel: INFO
logMessageFormat: Fetched greetings from Datastore. Count of greetings : {len(greetings)}
condition: None
status: ACTIVE

Chúng tôi đã cung cấp filename:linenumber và thông điệp nhật ký ở trên. Xin lưu ý rằng thông điệp nhật ký của chúng tôi cũng chứa một biểu thức để in số lượng lời chào mà chúng tôi đã truy xuất từ kho dữ liệu.

Lệnh này sẽ hiển thị lại thông báo cho biết điểm ghi nhật ký đã được thêm. Ảnh chụp màn hình Cloud Shell của chúng tôi được minh họa bên dưới:

Bây giờ, nếu bạn kích hoạt lệnh trong danh sách điểm ghi nhật ký, bạn sẽ thấy kết quả sau:

gcloud debug logpoints list

Đây là kết quả trên bảng điều khiển mà bạn sẽ thấy :

Debug target not specified. Using default target: default-1
STATUS LOCATION CONDITION LOG_LEVEL LOG_MESSAGE_FORMAT ID
ACTIVE
guestbook.py:74 INFO Fetched greetings from Datastore. Count of greetings : {len(greetings)} 53538243519d4-f9a0-bdbce

Để xem điều này trong thực tế, chúng tôi có thể tiếp tục truy cập vào trang chủ tại https://<PROJECT_ID>.appspot.com. Thao tác này sẽ gọi mã và ngược lại. Hãy nhớ rằng theo mặc định, nhật ký này sẽ được ghi lại trong Nhật ký ứng dụng của chúng tôi. Vì vậy, tất cả những gì chúng ta cần làm là truy cập Stackdriver Logging một lần nữa như được hiển thị bên dưới:

Nhấp vào yêu cầu cụ thể và Voila! Trong thông tin chi tiết, bạn sẽ thấy điểm ghi nhật ký của chúng tôi được kích hoạt và thông báo nhật ký sẽ hiển thị.

Chúng tôi hy vọng bạn hài lòng với hướng dẫn này. Nền tảng này tập trung vào một số chức năng mà nền tảng Stackdriver mang lại. Còn rất nhiều điều để khám phá. Hãy xem blog của Romin Irani\39; (tác giả nguyên gốc của lớp học lập trình này) tại https://rominirani.com/ để biết thêm hướng dẫn về Google Cloud Platform.

Bạn cũng có thể xem lớp học lập trình khác này được gọi là "Sử dụng Stackdriver\39; theo dõi và ghi nhật ký để nắm rõ hơn tình trạng ứng dụng của bạn.

Nếu bạn có ý kiến phản hồi hoặc muốn báo cáo sự cố liên quan đến lớp học lập trình này, vui lòng sử dụng &quot.Vui lòng tìm đường liên kết&lỗi; ở dưới cùng bên trái của trang này.