Menyelami log terlebih dahulu

Saat saya bergabung dengan Amazon setelah selesai kuliah, salah satu latihan orientasi saya adalah mempersiapkan server amazon.com untuk berjalan di desktop pengembang saya. Saya tidak langsung berhasil di percobaan pertama, dan saya tidak tahu di mana kesalahan saya. Rekan kerja yang sangat membantu menyarankan saya untuk melihat log agar dapat mengetahui apa yang salah. Untuk melakukannya, ia berkata bahwa saya harus “melakukan cat pada file log”. Saya merasa bahwa mereka sedang berkelakar atau membuat lelucon tentang kucing yang tidak saya mengerti. Saya hanya menggunakan Linux di kampus untuk mengompilasi, menggunakan kontrol sumber, dan menggunakan editor teks. Oleh karena itu saya tidak tahu bahwa “cat” sebenarnya adalah perintah untuk mencetak file ke terminal yang dapat saya umpankan ke program lain untuk mencari pola.

Rekan kerja saya memberi tahu tentang alat seperti cat, grep, sed, dan awk. Dilengkapi dengan kumpulan alat baru tersebut, saya menyelami log server web amazon.com pada desktop pengembang saya. Aplikasi server web telah diintrumentasi untuk mengeluarkan seluruh jenis informasi yang bermanfaat ke dalam log-nya. Hal ini memungkinkan saya melihat konfigurasi apa yang mencegah server web memulai, yang menunjukkan tempatmana terjadinya crash, atau yang mengindikasikan tempat terjadinya kegagalan berbicara ke layanan hilir. Situs web tersusun atas banyak bagian yang bergerak, dan pada awalnya ini adalah kotak hitam bagi saya. Namun, setelah menyelami sistem terlebih dahulu, saya mempelajari cara kerja server dan cara berinteraksi dengan dependensinya hanya dengan melihat output instrumentasi.

Mengapa instrumentasi

Setelah bertahun-tahun saya berpindah dari satu tim ke tim lain di Amazon, saya menemukan bahwa instrumentasi merupakan lensa yang sangat bermanfaat, yang saya dan karyawan lain di Amazon lihat untuk mempelajari cara kerja sistem. Namun, instrumentasi bermanfaat tidak hanya untuk belajar tentang sistem. Ini adalah inti dari budaya pengoperasian di Amazon. Instrumentasi yang hebat membantu kami melihat pengalaman yang kami berikan kepada pelanggan.
 
Instrumentasi berfokus pada kinerja pengoperasian di seluruh perusahaan. Dalam layanan yang berhubungan dengan amazon.com, peningkatan latensi menyebabkan pengalaman belanja yang buruk sehingga tingkat konversi menurun. Dengan pelanggan yang menggunakan AWS, mereka bergantung pada ketersediaan tinggi dan latensi rendah dari layanan AWS.
 
Di Amazon, kami tidak hanya mempertimbangkan latensi rata-rata. Kami jauh lebih berfokus pada titik luar latensi, seperti persentil ke-99,9 dan ke-99,99. Ini dikarenakan jika ada satu permintaan lambat dari 1.000 atau 10.000 permintaan, berarti sama dengan pengalaman yang buruk. Kami menemukan bahwa ketka kami mengurangi latensi persentil tinggi pada sistem, upaya kami memiliki efek samping berupa pengurangan latensi median. Sebaliknya, kami menemukan bahwa jika latensi median dikurangi, pengurangan latensi persentil tinggi lebih jarang berkurang.
 
Alasan lain kami berfokus pada latensi persentil tinggi adalah bahwa latensi tinggi di satu layanan dapat memiliki efek pengganda di seluruh layanan lain. Amazon dibangun di atas arsitektur yang berfokus pada layanan. Banyak layanan yang saling berkolaborasi untuk menyelesaikan suatu tugas, seperti rendering halaman web di amazon.com. Hasilnya, peningkatan di latensi layanan jauh di dalam rantai panggilan—bahkan jika peningkatan berada di persentil tinggi—memiliki efek riak yang besar pada latensi yang dialami oleh pengguna akhir.
 
Sistem besar di Amazon tersusun atas banyak layanan yang saling berkooperasi. Tiap layanan dikembangkan dan dioperasikan oleh satu tim (“layanan” besar terdiri dari beberapa layanan atau komponen di balik layar). Tim yang memiliki layanan dikenal sebagai pemilik layanan. Setiap anggota tim tersebut berpikir seperti seorang pemilik dan operator layanan, baik ia adalah seorang pengembang perangkat lunak, teknisi jaringan, manajer, atau berada di posisi lainnya. Sebagai pemilik, tim mengatur sasaran untuk kinerja pengoperasian dari seluruh layanan terkait. Kami juga memastikan bahwa kami memiliki visibilitas ke dalam pengoperasian layanan untuk memastikan bahwa kami akan memenuhi sasaran tersebut, untuk menghadapi masalah yang timbul, dan menetapkan sasaran yang lebih tinggi bagi diri kami sendiri di tahun berikutnya. Untuk menetapkan sasaran dan mendapat visibilitas tersebut, tim harus menginstrumentasi sistem.
Instrumentasi juga memungkinkan kami mendeteksi dan merespons kejadian pengoperasian secara taktis.
 
Instrumentasi mengumpankan data ke dasbor pengoperasian, sehingga operator dapat melihat metrik real-time. Ini juga mengumpankan data ke alarm, yang memicu dan melibatkan operator ketika sistem berperilaku dengan cara yang tidak diharapkan. Operator menggunakan output terperinci dari instrumentasi untuk dengan cepat mendiagnosis mengapa kesalahan terjadi. Dari sana, kami dapat memitigasi masalah, dan kembali lagi nanti untuk mencegah agar masalah tidak terjadi lagi. Tanpa instrumentasi yang baik di seluruh kode, kami menghabiskan waktu yang berharga untuk mendiagnosis masalah.

Apa yang perlu diukur

Untuk mengoperasikan layanan sesuai dengan standar kami yang tinggi dalam ketersediaan dan latensi, kami sebagai pemilik layanan perlu mengukur bagaimana perilaku sistem kami.

Untuk mendapat telemetri yang diperlukan, pemilik layanan mengukur kinerja pengoperasian dari beberapa tempat untuk mendapat beberapa perspektif mengenai bagaimana perilakunya secara menyeluruh. Ini adalah hal yang rumit, bahkan dalam arsitektur yang sederhana. Mempertimbangkan layanan yang pelanggan panggil melalui penyeimbang muatan: layanan berbicara ke cache jarak jauh dan database jarak jauh. Kami ingin tiap komponen mengeluarkan metrik tentang perilakunya. Kami juga menginginkan metrik mengenai bagaimana tiap komponen memahami perilaku komponen lainnya. Saat metrik dari seluruh perspektif tersebut digabungkan, pemilik layanan dapat melacak sumber masalah dengan cepat, dan menggali untuk menemukan penyebabnya.

Banyak layanan AWS yang secara otomatis menyediakan wawasan pengoperasian tentang sumber daya Anda. Contohnya, Amazon DynamoDB menyediakan metrik Amazon CloudWatch berisi tingkat keberhasilan dan kesalahan serta latensi, seperti yang diukur oleh layanan. Namun, saat kami membangun sistem yang menggunakan layanan tersebut, kami memerlukan lebih banyak visibilitas ke dalam bagaimana sistem kami berperilaku. Instrumentasi memerlukan kode eksplisit yang mencatat berapa lama tugas berlangsung, seberapa sering jalur kode tertentu digunakan, metadata tentang apa yang dikerjakan tugas, dan manakah bagian tugas yang berhasil atau gagal. Jika tim tidak menambahkan instrumentasi eksplisit, instrumentasi akan dipaksa untuk mengoperasikan layanannya sendiri sebagai kotak hitam.

Contohnya, jika kami menerapkan pengoperasian API layanan yang menerima informasi produk berdasarkan ID produk, kodenya mungkin tampak seperti contoh berikut. Kode ini mencari info produk di cache lokal, diikuti dengan cache jarak jauh, diikuti dengan database:

public GetProductInfoResponse getProductInfo(GetProductInfoRequest request) {

  // check our local cache
  ProductInfo info = localCache.get(request.getProductId());
  
  // check the remote cache if we didn't find it in the local cache
  if (info == null) {
    info = remoteCache.get(request.getProductId());
	
	localCache.put(info);
  }
  
  // finally check the database if we didn't have it in either cache
  if (info == null) {
    info = db.query(request.getProductId());
	
	localCache.put(info);
	remoteCache.put(info);
  }
  
  return info;
}

Jika saya mengoperasikan layanan ini, saya akan memerlukan banyak instrumentasi di kode ini untuk dapat memahami perilakunya dalam produksi. Saya akan memerlukan kemampuan untuk memecahkan masalah permintaan yang gagal atau lambat, dan memantau tren serta tanda bahwa dependensi yang berbeda kurang diskalakan atau berprilaku buruk. Ini adalah kode yang sama, dianotasi dengan beberapa pertanyaan yang harus dapat saya jawab tentang sistem produksi sebagai satu kesatuan, atau untuk permintaan tertentu:

public GetProductInfoResponse getProductInfo(GetProductInfoRequest request) {

  // Which product are we looking up?
  // Who called the API? What product category is this in?

  // Did we find the item in the local cache?
  ProductInfo info = localCache.get(request.getProductId());
  
  if (info == null) {
    // Was the item in the remote cache?
    // How long did it take to read from the remote cache?
    // How long did it take to deserialize the object from the cache?
    info = remoteCache.get(request.getProductId());
	
    // How full is the local cache?
    localCache.put(info);
  }
  
  // finally check the database if we didn't have it in either cache
  if (info == null) {
    // How long did the database query take?
    // Did the query succeed? 
    // If it failed, is it because it timed out? Or was it an invalid query? Did we lose our database connection?
    // If it timed out, was our connection pool full? Did we fail to connect to the database? Or was it just slow to respond?
    info = db.query(request.getProductId());
	
    // How long did populating the caches take? 
    // Were they full and did they evict other items? 
    localCache.put(info);
    remoteCache.put(info);
  }
  
  // How big was this product info object? 
  return info;
}

Kode untuk menjawab seluruh pertanyaan tersebut (dan pertanyaan lain) sedikit lebih panjang dibanding logika bisnis sebenarnya. Beberapa pustaka dapat membantu mengurangi jumlah kode instrumentasi, tetapi pengembang tetap harus bertanya tentang visibilitas yang akan diperlukan pustaka tersebut, lalu pengembang harus mengerjakan pemasangan instrumentasi.

Saat Anda memecahkan masalah permintaan yang mengalir melalui sistem terdistribusi, Anda dapat kesulitan memahami apa yang terjadi jika hanya melihat permintaan tersebut berdasarkan satu interaksi. Untuk menyelesaikan puzzle ini, kami merasa terbantu dengan menyatukan seluruh pengukuran tentang seluruh sistem tersebut. Sebelum kami dapat melakukannya, tiap layanan harus diinstrumentasi untuk mencatat ID jejak untuk tiap tugas, dan untuk menyebarkan ID jejak tersebut ke tiap layanan yang berkolaborasi pada tugas tersebut. Mengumpulkan instrumentasi di seluruh sistem untuk ID jejak tertentu dapat dilakukan setelah fakta yang diperlukan, atau mendekati real-time menggunakan layanan seperti AWS X-Ray.

Menelusuri

Instrumentasi memungkinkan pemecahan masalah di beberapa tingkat, dari melirik metrik untuk melihat apakah ada anomali yang terlalu samar untuk memicu alarm, hingga melakukan investigasi untuk menemukan penyebab anomali tersebut.

Pada tingkat tertinggi, instrumentasi diagregat menjadi metrik yang dapat memicu alarm dan tampilan di dasbor. Metrik agregat tersebut memungkinkan operator memantau tingkat permintaan keseluruhan, latensi panggilan layanan, dan tingkat kesalahan. Alarm dan metrik tersebut membuat kami menyadari anomali atau perubahan yang harus diinvestigasi.

Setelah melihat anomali, kami harus mencari tahu mengapa anomali tersebut terjadi. Untuk menjawab pertanyaan tersebut, kami mengandalkan metrik yang tersedia berkat lebih banyak instrumentasi. Dengan menginstrumentasi waktu yang diperlukan untuk melakukan beragam bagian dalam melayani permintaan, kami dapat melihat manakah bagian pemrosesan yang lebih lambat dari kecepatan normal, atau lebih sering memicu kesalahan.

Meskipun pengatur waktu dan metrik agregat dapat membantu kami menyingkirkan penyebab atau menyorot area investigasi, mereka tidak selalu menyediakan penjelasan yang lengkap. Contohnya, kami mungkin dapat melihat dari metrik bahwa kesalahan muncul dari pengoperasian API tertentu, namun metrik mungkin tidak mengungkapkan rincian yang cukup tentang mengapa pengoperasian tersebut gagal. Pada titik ini, kami melihat data log mentah dan terperinci yang dikeluarkan oleh layanan untuk jendela waktu tersebut. Log mentah lalu menunjukkan sumber masalah—baik kesalahan tertentu yang sedang terjadi, atau aspek tertentu pada permintaan yang memicu beberapa kasus edge.

Bagaimana kami menginstrumentasi

Instrumentasi memerlukan pengodean. Ini berarti bahwa saat kami menerapkan fungsi baru, kami memerlukan waktu untuk menambahkan kode tambahan guna mengindikasikan apa yang terjadi, baik berhasil atau gagal, dan berapa lama waktu yang diperlukan. Karena instrumentasi merupakan tugas pengodean umum, praktik muncul di Amazon selama bertahun-tahun untuk mengatasi pola yang umum: standardisasi untuk pustaka instrumentasi umum, dan standardisasi untuk pelaporan metrik berbasis log terstruktur.

Standardisasi pustaka instrumentasi metrik membantu penulis pustaka memberikan konsumen visibilitas pustaka mereka tentang bagaimana pustaka beroperasi. Contohnya, klien HTTP yang biasa digunakan terintegrasi dengan pustaka umum tersebut, jadi jika tim layanan menerapkan panggilan jarak jauh ke layanan lain, secara otomatis mereka mendapat instrumentasi tentang panggilan tersebut.

Saat aplikasi yang diinstrumentasikan berjalan dan melakukan pekerjaan, data telemetri yang dihasilkan ditulis ke file log terstruktur. Umumnya dikeluarkan sebagai satu entri log per “unit pekerjaan”, baik itu merupakan permintaan ke layanan HTTP, atau pesan yang ditarik dari antrean.

Di Amazon, pengukuran di aplikasi tidak diagregat dan sesekali dibuang ke sistem agregasi metrik. Seluruh pengatur waktu dan penghitung untuk tiap bagian pekerjaan tertulis di dalam file log. Dari sana, log diproses dan metrik agregat dikomputasi setelah fakta oleh beberapa sistem lain. Dengan cara ini, kami akhirnya mendapat semuanya, dari metrik pengoperasian agregat leve; tinggi hingga data pemecahan masalah terperinci pada tingkat permintaan, seluruhnya dengan satu pendekatan ke kode instrumentasi. Di Amazon kami membuat log terlebih dahulu, lalu memproduksi metrik agregat.

Instrumentasi melalui logging

Kami umumnya menginstrumentasi layanan untuk mengeluarkan dua jenis data log: data permintaan dan data debugging. Data log permintaan biasanya direpresentasikan sebagai sebuah entri log terstruktur untuk tiap unit pekerjaan. Data ini berisi properti tentang permintaan dan siapa yang membuat permintaan, apa tujuan permintaan, penghitung seberapa sering tiap hal terjadi, dan pengatur waktu untuk berapa lama suatu hal berlangsung. Log permintaan berperan sebagai log audit dan jejak bagi semua hal yang terjadi di layanan. Data debugging meliputi data tidak terstruktur atau terstruktur secara longgar dari baris debugging apa pun yang dikeluarkan aplikasi. Umumnya ini adalah entri log tidak terstruktur seperti kesalahan Log4j atau baris log peringatan. Di Amazon, kedua jenis data tersebut dikeluarkan ke dalam file log terpisah, sebagian untuk alasan riwayat, tetapi juga karena melakukan analisis pada format entri homogen dapat memudahkan Anda.

Agen seperti CloudWatch Logs Agent memproses kedua jenis data log secara real-time dan mengirimkan log ke CloudWatch Logs. Setelah itu, CloudWatch Logs memproduksi metrik agregat tentang layanan dalam waktu mendekati real-time. Alarm Amazon CloudWatch membaca metrik agregat tersebut dan memicu alarm.

Meskipun mahal untuk melakukan logging begitu banyak perincian tentang setiap permintaan, di Amazon kami merasa bahwa ini adalah hal sangat penting yang harus dilakukan. Lagipula, kami perlu menginvestigasi ketersediaan blip, lonjakan latensi, dan masalah yang dilaporkan pelanggan. Tanpa log yang terperinci, kami tidak dapat menjawab pelanggan, dan kami tidak akan dapat meningkatkan layanan mereka.  

Mendapatkan perincian

Topik pemantauan dan alarm sangatlah luas. Di artikel ini, kami tidak akan membahas topik seperti mengatur dan menyetel ambang batas alarm, mengelola dasbor pengoperasian, mengukur kinerja dari sisi server dan sisi klien, secara terus-menerus menjalankan aplikasi “canary”, serta memilih sistem yang tepat untuk menggunakan metrik agregat juga menganalisis log.

Artikel ini berfokus pada perlunya menginstrumentasi aplikasi demi menghasilkan data pengukuran mentah yang tepat. Kami akan menjelaskan hal yang diupayakan tim di Amazon agar disertakan (atau dihindari) saat menginstrumentasi aplikasi mereka.

Praktik terbaik log permintaan

Di bagian ini, saya akan menjelaskan kebiasaan baik yang kami pelajari selama bekerja di Amazon tentang logging data “per unit pekerjaan” terstruktur. Sebuah log yang memenuhi kriteria tersebut berisi penghitung yang mewakili seberapa sering hal-hal terjadi, pengatur waktu berisi durasi yang diperlukan, dan properti yang menyertakan metadata mengenai tiap unit pekerjaan.

Bagaimana cara kami menghasilkan log

Mengeluarkan satu entri log permintaan untuk setiap unit pekerjaan. Unit pekerjaan umumnya adalah permintaan yang layanan kami terima atau pesan yang ditarik dari antrean. Kami menulis satu entri log layanan untuk tiap permimtaan yang layanan kami terima. Kami tidak menggabungkan beberapa unit yang bekerja sama. Dengan begini, saat memecahkan masalah permintaan gagal, kami memiliki satu entri log yang dapat dilihat. Entri ini berisi parameter input yang relevan tentang permintaan untuk melihat apa yang sedang dicoba, informasi tentang siapa pemanggilnya, dan seluruh informasi pengatur waktu juga penghitung dalam satu tempat.
Mengeluarkan tidak lebih dari satu entri log permintaan untuk permintaan tertentu. Pada implementasi layanan nonblok, mungkin tampak mudah untuk mengeluarkan entri log terpisah bagi tiap tahap dalam jalur pipa pemrosesan. Kami justru lebih sering berhasil dalam memecahkan masalah sistem tersebut dengan memasang pipa pegangan ke sekeliling satu “objek metrik” di antara tahap pada jalur pipa, lalu mengurutkan metrik sebagai sebuah unit setelah seluruh tahap telah selesai. Memiliki beberapa antri log per unit pekerjaan dapat mempersulit analisis log, dan berkali-kali lipat meningkatkan overhead logging yang mahal. Jika kami menulis layanan nonblok baru, kami mencoba merencanakan siklus hidup logging metrik terlebih dahulu, karena akan menjadi sangat sulit untuk memfaktorkan kembali dan memperbaikinya nanti.
Membagi tugas yang telah lama berjalan ke dalam beberapa entri log. Berlawanan dengan rekomendasi sebelumnya, jika kami memliki tugas berdurasi beberapa menit yang telah berjalan lama atau tugas berdurasi beberapa jam yang seperti alur kerja, kami dapat memutuskan untuk mengeluarkan entri log terpisah secara berkala sehingga kami dapat menentukan jika kemajuan sedang berlangsung, atau di manakah terjadinya penurunan kecepatan.
Mencatat rincian tentang permintaan sebelum melakukan hal seperti validasi. Kami merasa penting bagi pemecahan masalah dan logging audit untuk menghasilkan log informasi yang cukup tentang permintaan agar kami tahu apa yang ingin coba dicapai. Kami juga menemukan bahwa penting untuk me-log informasi ini sedini mungkin, sebelum permintaan memiliki peluang untuk ditolak oleh validasi, otentikasi, atau logika pembatasan. Jika kami me-logging informasi dari permintaan masuk, kami memastikan untuk mensanitasi input (enkode, tandai, dan potong) sebelum kami membuat log. Contohnya, kami tidak ingin menyertakan string panjang berukuran 1 MB di entri log layanan jika pemanggil meneruskannya. Melakukan hal tersebut berisiko membuat disk kami penuh dan menghabiskan lebih banyak penyimpanan log dibanding dugaan kami. Contoh lainnya dari sanitasi adalah memfilter karakter kontrol ASCII atau menandai urutan yang relevan dengan format log. Hal ini dapat membingungkan jika pemanggil meneruskan entri log layanan mereka sendiri, dan dapat memasukkannya ke dalam log kami! Lihat juga: https://xkcd.com/327/
Merencanakan cara untuk me-log saat verbositas meningkat. Untuk memecahkan beberapa jenis masalah, log tidak akan memiliki rincian yang cukup tentang permintaan bermasalah untuk menemukan alasan kegagalan mereka. Informasi tersebut munkin tersedia di layanan, tetapi volume informasi mungkin terlalu besar untuk membenarkan logging sepanjang waktu. Memiliki kenop konfigurasi yang dapat Anda putar untuk meningkatkan verbositas log secara sementara saat Anda menginvestigasi masalah merupakan tindakan yang cukup membantu. Anda dapat memutar kenop pada host individu, atau untuk klien individu, atau pada tingkat sampel di seluruh armada. Anda harus ingat untuk memutar kembali kenop setelah selesai.
Memilih nama yang pendek untuk metrik (tapi tidak terlalu pendek). Amazon telah menggunakan serialisasi log layanan yang sama selama lebih dari 15 tahun. Pada serialisasi ini, tiap nama penghitung dan pengatur waktu diulang pada teks biasa di setiap entri log layanan. Untuk membantu meminimalkan overhead logging, kami menggunakan nama yang singkat namun deskriptif untuk pengatur waktu. Amazon mulai mengadopsi format serialisasi baru yang berdasarkan pada protokol serialisasi biner yang dikenal sebagai Amazon Ion. Tentunya, penting untuk memilih format yang dapat dipahami alat analisis log dan juga seefisien mungkin dalam melakukan serialisasi, membatalkan serialisasi, dan menyimpan.
Memastikan volume log cukup beasr untuk menangani logging pada throughput maks. Kami menguji muatan layanan pada muatan berkelanjutan maksimum (atau bahkan kelebihan muatan) selama berjam-jam. Kami harus memastikan bahwa ketika layanan kami menangani lalu lintas berlebih, layanan tetap memiliki sumber daya untuk mengirim log secara off-box pada tingkat ketika mereka menghasilkan entri log baru, atau akhirnya disk akan penuh. Anda juga dapat mengonfigurasi logging agar terjadi di partisi sistem file yang berbeda dengan partisi akar, sehingga sistem tidak terhenti dalam menghadapi logging yang berlebihan. Kami mendiskusikan mitigasi lainnya nanti, seperti menggunakan sampling dinamis yang proporsional dengan throughput, tapi bagaimanapun strateginya, pengujian adalah hal yang paling penting.
Mempertimbangkan perilaku sistem saat disk penuh. Saat disk server penuh, server tidak dapat me-log ke disk. Saat hal tersebut terjadi, haruskah layanan berhenti menerima permintaan, atau menjatuhkan log dan melanjutkan pengoperasian tanpa pemantauan? Pengoperasian tanpa logging sangatlah berisiko, jadi kami menguji sistem untuk memastikan bahwa server dengan disk yang hampir penuh dapat terdeteksi.
Menyinkronkan jam. Gagasan mengenai “waktu” pada sistem terdistribusi sangatlah luar biasa rumit. Kami tidak bergantung pada sinkronisasi jam dalam algoritme terdistribusi, namun tetap perlu untuk membuat log. Kami menjalankan daemon seperti Chrony atau ntpd untuk sinkronisasi jam, dan kami memantau server untuk perubahan jam. Untuk memudahkan Anda, lihat Amazon Time Sync Service.
Mengeluarkan jumlah nol untuk metrik ketersediaan. Penghitungan kesalahan bermanfaat, namun persentase kesalahan juga tidak kalah bermanfaat. Untuk menginstrumentasi metrik “persentase ketersediaan”, strategi yang kami temukan bermanfaat untuk mengeluarkan 1 saat permintaan berhasil dan 0 saat permintaan gagal. Lalu statistik “rata-rata” dari metrik yang dihasilkan adalah tingkat ketersediaan. Secara sengaja mengeluarkan poin data 0 juga dapat bermanfaat pada situasi lain. Contohnya, jika aplikasi melakukan pemilihan pemimpin, mengeluarkan 1 secara berkala ketika satu proses adalah pemimpin, dan 0 ketika proses bukan pemimpin dapat bermanfaat untuk memantau status pengikut. Dengan cara ini, jika proses berhenti mengeluarkan 0, artinya lebih mudah untuk mengetahui bahwa ada sesuatu yang rusak di dalamnya, dan tidak akan dapat mengambil alih jika terjadi sesuatu kepada pemimpin.

Apa yang kami log

Me-log ketersediaan dan latensi seluruh dependensi. Kami menemukan bahwa hal ini sangat membantu dalam menjawab pertanyaan “mengapa permintaan lambat?” atau “mengapa permintaan gagal?” Tanpa log ini, kami hanya dapat membandingkan grafik dependensi dengan grafik layanan, lalu menebak apakah satu lonjakan pada latensi layanan dependensi menyebabkan kegagalan permintaan yang sedang kami investigasi. Banyak kerangka kerja layanan dan klien yang mengukur metrik secara otomatis, namun kerangka kerja lain (seperti AWS SDK, misalnya), memerlukan instrumentasi manual.
Memisahkan metrik dependensi per panggilan, per sumber daya, per kode status, dll. Jika kami berinteraksi dengan dependensi yang sama berkali-kali di unit pekerjaan yang sama, kami menyertakan metrik tentang tiap panggilan secara terpisah, dan memperjelas sumber daya mana yang berinteraksi dengan tiap permintaan. Contohnya, saat memanggil Amazon DynamoDB, beberapa tim menemukan bahwa menyertakan metrik penentuan waktu dan latensi per tabel akan membantu, serta per kode kesalahan, dan bahkan per jumlah pengulangan. Tindakan ini memudahkan pemecahan masalah kasus ketika layanan menjadi lambat karena pengulangan akibat kegagalan pemeriksaan kondisional. Metrik tersebut juga mengungkapkan kasus berupa peningkatan latensi yang dirasikan klien benar-benar akibat pengulangan pembatasan atau pemberian nomor melalui kumpulan hasil, dan bukan dari kehilangan paket atau latensi jaringan.
Mencatat kedalaman antrean memori saat mengaksesnya. Jika permintaan berinteraksi dengan antrean, dan kami menarik objek dari atau memasukkan sesuatu ke dalamnya, kami mencatat kedalaman antrean saat ini ke objek metrik saat hal tersebut berlangsung. Untuk antean dalam memori, memperoleh informasi ini sangatlah murah. Untuk antrean terdistribusi, metadata ini mungkin tersedia secara gratis sebagai respons dari panggilan API. Logging ini akan membantu menemukan backlog dan sumber latensi di masa mendatang. Selain itu, saat kami mengeluarkan sesuatu dari antrean, kami mengukur berapa lama hal tersebut berada di dalam antrean. Hal ini berarti kami perlu menambahkan metrik “waktu penambahan antrean” ke pesan terlebih dahulu sebelum kami memasukkannya ke antrean.
Menambahkan penghitung tambahan untuk setiap alasan kesalahan. Pertimbangkan menambahkan kode yang menghitung alasan kesalahan spesifik untuk setiap permintaan gagal. Log aplikasi akan menyertakan informasi yang menyebabkan kegagalan, dan pesan pengecualian terperinci. Namun, kami juga menemukan bahwa melihat tren alasan kesalahan pada metrik dari waktu ke waktu tanpa perlu menggali informasi tersebut di log aplikasi juga bermanfaat. Lebih praktis untuk memulai dengan metrik terpisah bagi tiap kelas pengecualian kegagalan.
Mengelola kesalahan menurut kategori penyebab. Jika seluruh kesalahan ditumpuk ke dalam metrik yang sama, metrik menjadi bising dan tidak bermanfaat. Paling tidak, kami telah menemukan pentingnya memisahkan kesalahan yang merupakan “kesalahan klien” dari kesalahan yang merupakan “kesalahan server”. Di luar itu, pemisahan lebih jauh mungkin bermanfaat. Contohnya di DynamoDB, klien dapat membuat permintaan tulis kondisional yang mengembalikan kesalahan jika item yang mereka modifikasi tidak sesuai dengan prakondisi pada permintaan. Kesalahan tersebut sangat jelas, dan kami berharap agar ini tidak sering terjadi. Sedangkan kesalahan “permintaan tidak valid” dari klien cenderung merupakan bug yang harus kami perbaiki.
Me-log metadata penting tentang unit pekerjaan. Pada log metrik terstruktur, kami juga menyertakan cukup metadata tentang permintaan sehingga nanti kami dapat menentukan dari mana permintaan berasal dan apa yang permintaan coba lakukan. Ini meliputi metadata yang pelanggan harapkan kami miliki di dalam log saat mereka menghubungi karena menemukan masalah. Contohnya, DynamoDB me-log nama tabel yang berinteraksi dengan permintaan, dan metadata seperti apakah pengoperasian baca merupakan bacaan yang konsisten atau bukan. Namun, DynamoDB tidak me-log data yang disimpan ke atau diterima dari database.
Melindungi log dengan kontrol akses dan enkripsi. Karena log berisi beberapa tingkat informasi sensitif, kami mengambil langkah untuk melindungi dan mengamankan data tersebut. Pengukuran tersebut termasuk mengenkripsi log, membatasi akses ke operator yang memecahkan masalah, dan mendasari akses tersebut secara rutin.
Menghindari memasukkan informasi yang terlalu sensitif ke log. Log harus berisi beberapa informasi sensitif agar bermanfaat. Di Amazon, kami menemukan bahwa penting bagi log untuk menyertakan informasi yang cukup demi mengetahui dari mana asal permintaan tertentu, namun tidak memasukkan informasi yang terlalu sensitif, seperti parameter permintaan yang tidak memengaruhi perutean atau perilaku pemrosesan permintaan. Contohnya, jika kode menguraikan pesan pelanggan, dan penguraian tersebut gagal, penting untuk tidak me-log payload demi melindungi privasi pelanggan, sesulit melakukan pemecahan masalah nanti. Kami menggunakan alat untuk mengambil keputusan tentang apa yang dapat dimasukkan ke log dengan mengikutsertakannya, bukan dengan mengeluarkannya, untuk mencegah logging parameter sensitif baru yang ditambahkan nanti. Layanan seperti Amazon API Gateway memungkinkan mengonfigurasi data mana yang akan disertakan ke dalam log akses, yang bertindak sebagai mekanisme keikutsertaan yang baik.
Me-log ID jejak dan menyebarkannya di panggilan backend. Permintaan pelanggan tertentu sepertinya akan melibatkan banyak layanan yang saling bekerja sama. Ini dapat berarti dua atau tiga layanan untuk banyak permintaan AWS, jauh lebih banyak layanan untuk permintaan amazon.com. Untuk memahami apa yang terjadi saat kami memecahkan masalah sistem terdistribusi, kami menyebarkan ID jejak yang sama di antara sistem tersebut sehingga kami dapat mengurutkan log dari beragam sistem untuk melihat di mana kegagalan terjadi. ID jejak adalah sejenis ID permintaan meta yang distempel ke unit pekerjaan terdistribusi oleh layanan “pintu depan” yang merupakan titik awal bagi unit pekerjaan. AWS X-Ray adalah satu layanan yang dapat membantu dengan menyediakan beberapa penyebaran ini. Kami menemukan bahwa meneruskan jejak ke dependensi merupakan tindakan yang penting. Pada lingkungan multithreaded, sangat sulit dan rentan terjadi kesalahan bagi kerangka kerja untuk melakukan penyebaran ini atas nama kami, jadi kami telah terbiasa meneruskan ID jejak, dan konten permintaan lain (seperti objek metrik!) dalam tanda tangan metode kami. Kami juga menemukan bahwa meneruskan objek Konteks di tanda tangan metode kami sangat bermanfaat, sehingga kami tidak perlu memfaktirkan ulang saat menemukan pola yang serupa untuk diteruskan di masa mendatang. Untuk tim AWS, ini tidak hanya tentang pemecahan masalah sistem kami, ini juga tentang pelanggan yang memecahkan masalah mereka. Pelanggan mengandalkan jejak AWS X-Ray yang diteruskan di antara layanan AWS saat layanan saling berinteraksi atas nama pelanggan. Oleh karena itu, kami perlu menyebarkan ID jejak AWS X-Ray di antara layanan sehingga mereka mendapat data jejak yang lengkap.
Me-log metrik latensi yang berbeda tergantung pada kode status dan ukuran. Kesalahan sering terjadi dengan cepat—seperti penolakan akses, pembatasan, dan respons kesalahan validasi. Jika klien mulai dibatasi pada tingkat tinggi, latensi mungkin tampak baik. Untuk menghindari polusi metrik ini, kami me-log pengatur waktu terpisah untuk respons yang berhasil, dan berfokus pada metrik tersebut di dasbor dan alarm kami, bukan menggunakan metrik Waktu umum. Sama halnya jika ada pengoperasian yang dapat melambat tergantung ukuran input dan ukuran respons, kami mempertimbangkan untuk mengeluarkan metrik latensi yang dikategoorikan, seperti SmallRequestLatency dan LargeRequestLatency. Selain itu, kami memastikan permintaan dan respons dibatasi dengan tepat untuk menghindari pembatasan yang rumit dan mode kegagalan, namun bahkan dalam layanan yang didesain dengan saksama, teknik bucket metrik ini dapat mengisolasi perilaku pelanggan dan menjauhkan kebisingan dari dasbor.

Praktik terbaik log aplikasi

Bagian ini menjelaskan perilaku baik yang telah kami pelajari di Amazon tentang logging data log debug tidak terstruktur.

Membuat log aplikasi bebas dari spam. Meskipun kami memiliki pernyataan log tingkat INFO dan DEBUG pada jalur permintaan untuk membantu pengembangan serta debugging dalam lingkungan uji, kami memilih untuk menonaktifkan tingkat log tersebut dalam produksi. Daripada mengandalkan log aplikasi untuk informasi pelacakan permintaan, kami menganggap log layanan sebagai lokasi untuk informasi jejak tempat kami memproduksi metrik dengan mudah dan melihat tren agregat dari waktu ke waktu. Namun, tidak ada peraturan yang jelas di sini. Pendekatan kami adalah secara terus-menerus meninjau log untuk melihat apakah log terlalu bising (atau tidak cukup bising), dan menyesuaikan tingkat log dari waktu ke waktu. Contohnya, saat menyalami log, kami sering menemukan pernyataan log yang terlalu bising, atau metrik yang kami harapkan kami memilikinya. Untungnya, peningkatan tersebut sering kali mudah dibuat, jadi kami telah terbiasa untuk mengisi item backlog tindak lanjut cepat guna menjaga log kami agar tetap bersih.
Menyertakan ID permintaan yang sesuai. Saat kami memecahkan masalah kesalahan di log aplikasi, kami sering kali ingin melihat rincian mengenai permintaan atau pemanggil yang memicu kesalahan. Jika kedua log berisi ID permintaan yang sama, kami dapat dengan mudah melompat dari satu log ke lainnya. Pustaka logging aplikasi akan menuliskan ID permintaan yang sesuai jika dikonfigurasi dengan tepat, dan jika ID permintaan diatur sebagai ThreadLocal. Jika aplikasi di-multithread, pertimbangkan untuk memberikan perhatian khusus guna mengatur ID permintaan yang tepat saat thread mulai bekerja pada permintaan baru.
Membatasi tingkat spam kesalahan log aplikasi. Umumya, layanan tidak akan mengeluarkan banyak hal ke log aplikasi, tapi jika layanan mulai menunjukkan kesalahan dalam volume besar, layanan mungkin secara mendadak mulai menuliskan entri log yang sangat besar dalam tingkat tinggi dengan jejak tumpukan. Sebuah cara yang kami temukan untuk menghindari hal ini adalah dengan membatasi tingkat dari seberapa sering logger tertentu akan me-log.
Lebih memilih string format daripada String#format atau rangkaian string. Pengoprasian API log aplikasi lama menerima satu pesan string, bukan api string format varargs log4j2. Jika kode diinstrumentasikan dengan pernyataan DEBUG, tetapi produksi dikonfigurasi pada tingkat KESALAHAN, mungkin saja ini dapat menyia-nyiakan pekerjaan memformat string pesan DEBUG yang diabaikan. Beberapa pengoperasian API logging mendukung penerusan pada objek arbitrer yang akan dipanggil metode toString()-nya hanya jika entri log akan dituliskan.
Me-log ID permintaan dari panggilan layanan yang gagal. Jika layanan dipanggul dan mengembalikan kesalahan, layanan tampaknya mengembalikan ID permintaan. Kami menemukan bahwa penting menyertakan ID permintaan di log agar saat kami perlu menindaklanjuti pemilik layanan tersebut, kami memiliki cara bagi mereka untuk dengan mudah menemukan entri log layanan yang sesuai milik mereka. Kesalahan batas waktu membuat hal ini tidak mudah dilakukan karena layanan mungkin belum mengembalikan ID permintaan, atau pustaka klien mungkin belum menguraikannya. Walau demikian, jika kami mendapat ID permintaan kembali dari layanan, kami membuat log-nya.

Praktik terbaik layanan throughput tinggi

Untuk sebagian besar layanan di Amazon, logging pada setiap permintaan tidak membebankan overhead biaya yang tinggi. Layanan throughput tinggi masuk ke dalam area abu-abu, namun kami masih sering me-log pada setiap permintaan. Contohnya, wajar untuk menganggap bahwa DynamoDB, pada puncaknya melayani 20 juta permintaan per detik dari lalu lintas internal Amazon saja, tidak akan me-log cukup banyak, tetapi kenyataannya DynamoDB me-log setiap permintaan untuk pemecahan masalah dan untuk audit serta alasan kepatuhan. Berikut adalah benerapa kiat tingkat tinggi yang kami gunakan di Amazon untuk membuat logging lebih efisien pada throughput per host yang lebih tinggi:

Sampling log. Daripada menulis setiap entri, pertimbangkan untuk menulis setiap entri N. Tiap entri juga menyertakan berapa banyak entri yang dilewati sehingga sistem agregat metrik dapat memperkirakan volume log yang sebenaranya pada metrik yang dikomputasi. Algoritme sampling lainnya seperti sampling reservoir menyediakan lebih banyak sampel representatif. Algoritme lain memperioritaskan logging kesalahan atau permintaan lambat daripada permintaan cepat yang berhasil. Tetapi dengan sampling, kemampuan hilang untuk membantu pelanggan dan memecahkan masalah kegagalan teretntu. Beberapa persayaratan kepatuhan tidak mengizinkannya.
Memindahkan serialisasi dan penghapusan log ke thread terpisah. Ini adalah perubahan yang mudah dan umum digunakan.
Rotasi log yang sering terjadi. Merotasi file log yang me-log setiap jam mungkin membuat Anda merasa nyaman karena memiliki lebih sedikit file yang harus dikerjakan, namun dengan merotasi setiap menit, beberapa hal meningkat. Contohnya, agen yang membaca dan mengompres file log akan membaca file dari cache halaman, bukan disk, lalu CPU serta IO dari log pengompresan dan pengiriman akan disebarkan selama jam tersebut, bukan selalu memicu di akhir jam.
Menulis log sebelum dikompres. Jika agen pengiriman log mengompres log sebelum mengirimkannya ke layanan pengarsipan, CPU dan disk sistem akan mengalami lonjakan secara berkala. Mengamortisasi biaya ini dan mengurangi IO disk setengahnya mungkin dilakukan dengan streaming log yang dikompres ke disk. Namun, tindakan ini dapat memunculkan beberapa risiko. Kami menemukan bahwa bermanfaat untuk menggunakan algoritme kompresi yang dapat menangani file jika terjadi crash aplikasi.
Menulis ke ramdisk / tmpfs. Mungkin lebih mudah bagi layanan untuk menulis log ke memori hingga log dikirim ke server daripada menulis log ke disk. Menurut pengalaman kami, tindakan ini bekerja paling baik dengan rotasi log setiap menit daripada rotasi log setiap jam.
Agregat dalam memori. Jika Anda perlu menangani ratusan dari ribuan transaksi per detik pada satu mesin, mungkin terlalu mahal untuk menulis satu entri log per permintaan. Namun, Anda kehilangan banyak kemampuan observasi jika melewatkannya, sehingga kami menemukan bahwa bermanfaat untuk tidak mengoptimalkan terlalu dini.
Memantau penggunaan sumber daya. Kami memerhatikan seberapa dekat kami dengan pencapaian beberapa batas penskalaan. Kami mengukur IO dan CPU per server, dan berapa banyak sumber daya tersebut yang dikonsumsi oleh agen logging. Saat kami melakukan uji muatan, kami menjalankannya cukup lama sehingga kami dapat membuktikan bahwa agen pengiriman log dapat mengimbangi throughput kami.

Memiliki alat analisis log yang tepat

Di Amazon, kami mengoperasikan layanan yang kami tulis, jadi kami semua harus menjadi ahli dalam memecahkan masalahnya. Termasuk mampu melakukan analisis log dengan mudah. Kami memiliki banyak alat yang dapat digunakan, dari analisis log lokal untuk mencari di log dengan jumlah yan cukup kecil, hingga analisis log tersdistribusi untuk menyaring dan mengagregat hasil dari log dengan volume besar.

Kami menemukan bahwa penting untuk berinvestasi dalam alat dan buku panduan analisis log bagi tim. Jika saat ini log berukuran kecil, tetapi layanan diharapkan untuk tumbuh seiring waktu, kami memerhatikan kapan alat terbaru kami berhenti menskalakan, sehingga kami dapat berinvestasi untuk mengadopsi solusi analisis log terdistribusi.

Analisis log lokal

Proses analisis log mungkin memerlukan pengalaman dalam beragam utilitas baris perintah Linux. Contohnya, “temukan alamat IP pembicara teratas di log” yang umum hanyalah:

cat log | grep -P "^RemoteIp=" | cut -d= -f2 | sort | uniq -c | sort -nr | head -n20

Namun, ada banyak alat lain yang bermanfaat untuk menjawab pertanyaan yang lebih rumit dengan log kami, termasuk:

• jq: https://stedolan.github.io/jq/
• RecordStream: https://github.com/benbernard/RecordStream

Analisis log terdistribusi

Layanan analisis data besar apa pun dapat digunakan untuk melakukan analisis log terdistribusi (contohnya, Amazon EMR, Amazon Athena, Amazon Aurora, dan Amazon Redshift). Namun, beberapa layanan dilengkapi dengan sistem logging, contohnya Amazon CloudWatch Logs.

Wawasan CloudWatch Logs
• AWS X-Ray: https://aws.amazon.com/xray/
• Amazon Athena: https://aws.amazon.com/athena/

Penutup

Sebagai seorang pemilik layanan dan pengembang perangkat lunak, saya menghabiskan banyak waktu untuk melihat output instrumentasi—grafik di dasbor, file log individu—dan menggunakan alat analisis log terdistribusi seperti CloudWatch Logs Insights. Itu adalah beberapa hal kesukaan yang saya lakukan. Saat saya butuh istrirahat setelah menyelesaikan beberapa tugas yang menantang, saya memulihkan tenaga dan menghadiahi diri saya dengan menyelami log. Saya memulai dengan pertanyaan seperti “mengapa metrik ini melonjak di sini?” atau “dapatkah latensi pengoperasian ini menjadi lebih rendah?” Saat pertanyaan saya berakhir dengan kebuntuan, saya sering menemukan beberapa pengukuran yang dapat bermanfaat dalam kode, jadi saya menambahkan instrumentasi, pengujiam, dan mengirim tinjauan kode ke rekan-rekan tim saya.

Terlepas dari fakta bahwa banyak metrik yang dilengkapi dengan layanan terkelola yang kami gunakan, kami perlu memikirkan dengan matang dalam menginstrumentasi layanan kami sendiri sehingga kami memiliki visibilitas yang diperlukan untuk mengoperasikannya secara efektif. Selama kejadian pengoperasian, kami perlu menentukan dengan cepat mengapa kami memiliki masalah dan apa yang dapat kami lakukan untuk memitigasi masalah tersebut. Memiliki metrik yang tepat di dasbor kami sangatlah penting agar kami dapat melakukan diagnosis tersebut dengan cepat. Selain itu, karena kami selalu mengubah layanan, menambahkan fitur baru juga mengubah cara fitur berinteraksi dengan dependensinya, latihan memperbarui dan menambahakan intrumentasi yang tepat akan berlangsung selamanya.

• “Look at your data,” oleh eks Amazonian John Rauser: https://www.youtube.com/watch?v=coNDCIMH8bk (termasuk bagian 13:22, saat ia benar-benar mencetak log agar dapat melihat mereka dengan lebih baik)
• “Investigating anomalies” oleh eks Amazonian John Rauser: https://www.youtube.com/watch?v=-3dw09N5_Aw
• “How humans see data” oleh eks Amazonian John Rauser: https://www.youtube.com/watch?v=fSgEeI2Xpdc
https://www.akamai.com/uk/en/about/news/press/2017-press/akamai-releases-spring-2017-state-of-online-retail-performance-report.jsp


Tentang penulis

David Yanacek adalah Senior Principal Engineer yang bekerja di AWS Lambda. David telah menjadi pengembang perangkat lunak di Amazon sejak tahun 2006, sebelumnya bekerja di Amazon DynamoDB dan AWS IoT, juga kerangka kerja layanan web internal serta sistem automasi pengoperasian armada. Salah satu kegiatan favorit David di kantor adalah melakukan analisis log dan menyelidiki metrik pengoperasian guna menemukan cara untuk membuat sistem berjalan semakin mulus dari waktu ke waktu.

Menggunakan pelepasan muatan untuk mencegah kelebihan muatan Menghindari backlog antrean yang tidak dapat diatasi