كيفية بناء Apache APISIX Plugin من الصفر إلى الواحد؟
خلال الأشهر القليلة الماضية، أضاف مستخدمو المجتمع العديد من الإضافات إلى Apache APISIX، مما أدى إلى إثراء نظام Apache APISIX البيئي. من وجهة نظر المستخدم، فإن ظهور إضافات أكثر تنوعًا هو بالتأكيد أمر جيد، حيث تلبي المزيد من توقعات المستخدمين لجعل البوابة "شاملة" و"متعددة الوظائف" بالإضافة إلى تحسين الأداء العالي وزمن الاستجابة المنخفض لـ Apache APISIX.
لا يبدو أن أيًا من المقالات على مدونة Apache APISIX يتعمق في تفاصيل عملية تطوير الإضافات. لذلك، دعونا نلقي نظرة على العملية من منظور مطور الإضافات ونرى كيف يتم إنشاء الإضافة!
هذه المقالة توثق عملية تطوير إضافة file-logger
بواسطة مهندس واجهة أمامية بدون خبرة في الخلفية. قبل الخوض في تفاصيل عملية التنفيذ، سنقدم باختصار وظيفة file-logger
.
مقدمة عن إضافة file-logger
تدعم إضافة file-logger
إنشاء تنسيقات سجلات مخصصة باستخدام بيانات وصفية لإضافات Apache APISIX. يمكن للمستخدمين إلحاق بيانات الطلب والاستجابة بتنسيق JSON إلى ملفات السجلات عبر إضافة file-logger
، أو دفع تدفق بيانات السجل إلى موقع محدد.
تخيل هذا: عند مراقبة سجل الوصول لمسار معين، لا نهتم فقط بقيمة بيانات الطلب والاستجابة، ولكننا نريد أيضًا كتابة بيانات السجل إلى ملف محدد. هنا يمكن استخدام إضافة file-logger
لتحقيق هذه الأهداف.
يمكننا استخدام file-logger
لكتابة بيانات السجل إلى ملف سجل محدد لتبسيط عملية المراقبة والتصحيح.
كيفية تنفيذ إضافة؟
بعد تقديم ميزات file-logger، سيكون لديك فهم أفضل لهذه الإضافة. فيما يلي شرح تفصيلي لكيفية قيامي، كمطور واجهة أمامية بدون خبرة في الخلفية، بتطوير الإضافة لـ Apache APISIX وإضافة الاختبارات المناسبة لها.
تأكيد اسم الإضافة وأولويتها
افتح دليل تطوير إضافات Apache APISIX وبالترتيب، تحتاج إلى تحديد الأمرين التاليين:
- تحديد فئة الإضافة.
- تحديد أولوية الإضافات وتحديث ملف
conf/config-default.yaml
.
نظرًا لأن تطوير file-logger
هذه هو إضافة من نوع السجلات، فقد قمت بالرجوع إلى أسماء وترتيب الإضافات الحالية للسجلات في Apache APISIX ووضعت file-logger
هنا.
بعد التشاور مع مؤلفي الإضافات الآخرين وأعضاء المجتمع المتحمسين، تم تأكيد اسم الإضافة file-logger
وأولويتها 399
.
ملاحظة: أولوية الإضافة مرتبطة بترتيب التنفيذ؛ كلما زادت قيمة الأولوية، كان التنفيذ أكثر تقدمًا. وترتيب أسماء الإضافات لا علاقة له بترتيب التنفيذ.
إنشاء ملف إضافة قابل للتنفيذ بأقل قدر ممكن
بعد تأكيد اسم الإضافة وأولويتها، يمكنك إنشاء ملف كود الإضافة في دليل apisix/plugins/
. هناك نقطتان يجب ملاحظتهما هنا:
- إذا تم إنشاء ملف كود الإضافة مباشرة في دليل
apisix/plugins/
، فلا حاجة لتغيير ملفMakefile
. - إذا كانت الإضافة لديها دليل كود خاص بها، فستحتاج إلى تحديث ملف
Makefile
، يرجى الرجوع إلى دليل تطوير إضافات Apache APISIX للخطوات التفصيلية.
- هنا نقوم بإنشاء ملف
file-logger.lua
في دليلapisix/plugins/
. - ثم سنكمل نسخة مهيأة بناءً على
example-plugin
.
-- نقدم الوحدة التي نحتاجها في الرأس
local log_util = require("apisix.utils.log-util")
local core = require("apisix.core")
local plugin = require("apisix.plugin")
local ngx = ngx
-- نعلن عن اسم الإضافة
local plugin_name = "file-logger"
-- نحدد تنسيق مخطط الإضافة
local schema = {
type = "object",
properties = {
path = {
type = "string"
},
},
required = {"path"}
}
-- مخطط بيانات وصفية للإضافة
local metadata_schema = {
type = "object",
properties = {
log_format = log_util.metadata_schema_log_format
}
}
local _M = {
version = 0.1,
priority = 399,
name = plugin_name,
schema = schema,
metadata_schema = metadata_schema
}
-- نتحقق مما إذا كانت تكوينات الإضافة صحيحة
function _M.check_schema(conf, schema_type)
if schema_type == core.schema.TYPE_METADATA then
return core.schema.check(metadata_schema, conf)
end
return core.schema.check(schema, conf)
end
-- مرحلة السجل
function _M.log(conf, ctx)
core.log.warn("conf: ", core.json.encode(conf))
core.log.warn("ctx: ", core.json.encode(ctx, true))
end
return _M
بمجرد أن يصبح ملف الإضافة القابل للتنفيذ بأقل قدر ممكن جاهزًا، يمكن إخراج بيانات تكوين الإضافة وبيانات الطلب ذات الصلة إلى ملف error.log
عبر core.log.warn(core.json.encode(conf))
و core.log.warn("ctx: ", core.json.encode(ctx, true))
.
تمكين الإضافة واختبارها
فيما يلي خطوتان للاختبار. لاختبار ما إذا كانت الإضافة يمكنها طباعة بيانات الإضافة وبيانات الطلب ذات الصلة التي قمنا بتكوينها لها بنجاح إلى ملف سجل الأخطاء، نحتاج إلى تمكين الإضافة وإنشاء مسار اختبار.
-
قم بإعداد مسار اختبار محليًا (مسار الاختبار المستخدم في هذه المقالة هو
127.0.0.1:3030/api/hello
، والذي قمت بإنشائه محليًا). -
قم بإنشاء مسار عبر أمر
curl
وتمكين الإضافة الجديدة.curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "plugins": { "file-logger": { "path": "logs/file.log" } }, "upstream": { "type": "roundrobin", "nodes": { "127.0.0.1:3030": 1 } }, "uri": "/api/hello" }'
سترى بعد ذلك رمز الحالة
200
، مما يشير إلى أن المسار تم إنشاؤه بنجاح. -
قم بتشغيل أمر
curl
لإرسال طلب إلى المسار لاختبار ما إذا كانت إضافةfile-logger
قد تم تشغيلها.curl -i http://127.0.0.1:9080/api/hello HTTP/1.1 200 OK ... hello, world
-
في ملف
logs/error.log
سيكون هناك سجل:كما ترى، تم حفظ
path: logs/file.log
الذي قمنا بتكوينه للإضافة في معلمة conf بنجاح. في هذه المرحلة، قمنا بإنشاء إضافة قابلة للتنفيذ بأقل قدر ممكن والتي تطبع معلماتconf
وctx
في مرحلة السجل.بعد ذلك، يمكننا كتابة الوظيفة الأساسية لإضافة
file-logger.lua
مباشرة في ملف الكود الخاص بها. هنا يمكننا تشغيل أمرapisix reload
لإعادة تحميل أحدث كود للإضافة دون إعادة تشغيل Apache APISIX.
كتابة الوظيفة الأساسية لإضافة file-logger
الوظيفة الرئيسية لإضافة file-logger هي كتابة بيانات السجل. بعد الاستفسار من أشخاص آخرين في المجتمع والتحقق من المعلومات، تعرفت على مكتبة IO في Lua، وتأكدت من أن منطق وظيفة الإضافة يتلخص في الخطوات التالية.
-
بعد كل طلب مقبول، إخراج بيانات السجل إلى
path
الذي تم تكوينه بواسطة الإضافة.- أولاً، الحصول على قيمة
path
فيfile-logger
عبرconf
في مرحلة السجل. - ثم، استخدام مكتبة IO في Lua لإنشاء وفتح وكتابة وتحديث الذاكرة المؤقتة وإغلاق الملف.
- أولاً، الحصول على قيمة
-
التعامل مع الأخطاء مثل فشل فتح الملف، فشل إنشاء الملف، إلخ.
local function write_file_data(conf, log_message) local msg, err = core.json.encode(log_message) if err then return core.log.error("message json serialization failed, error info : ", err) end local file, err = io_open(conf.path, 'a+') if not file then core.log.error("failed to open file: ", conf.path, ", error info: ", err) else local ok, err = file:write(msg, '\n') if not ok then core.log.error("failed to write file: ", conf.path, ", error info: ", err) else file:flush() end file:close() end end
-
بالرجوع إلى كود مصدر إضافة
http-logger
، قمت بإنهاء طريقة تمرير بيانات السجل إلى كتابة بيانات السجل وبعض التحقق ومعالجة البيانات الوصفية.function _M.log(conf, ctx) local metadata = plugin.plugin_metadata(plugin_name) local entry if metadata and metadata.value.log_format and core.table.nkeys(metadata.value.log_format) > 0 then entry = log_util.get_custom_format_log(ctx, metadata.value.log_format) else entry = log_util.get_full_log(ngx, conf) end write_file_data(conf, entry) end
التحقق وإضافة الاختبارات
التحقق من سجلات السجل
نظرًا لأنه تم تمكين إضافة file-logger
عند إنشاء مسار الاختبار وتم تكوين المسار كـ logs/file.log
، يمكننا ببساطة إرسال طلب إلى مسار الاختبار للتحقق من نتائج جمع السجل في هذه المرحلة.
curl -i http://127.0.0.1:9080/api/hello
في ملف logs/file.log
المقابل، يمكننا أن نرى أن كل سجل يتم حفظه بتنسيق JSON. بعد تنسيق أحد البيانات، تبدو كالتالي.
{
"server": {
"hostname": "....",
"version": "2.11.0"
},
"client_ip": "127.0.0.1",
"upstream": "127.0.0.1:3030",
"route_id": "1",
"start_time": 1641285122961,
"latency": 13.999938964844,
"response": {
"status": 200,
"size": 252,
"headers": {
"server": "APISIX/2.11.0",
"content-type": "application/json; charset=utf-8",
"date": "Tue, 04 Jan 2022 08:32:02 GMT",
"vary": "Accept-Encoding",
"content-length": "19",
"connection": "close",
"etag": "\"13-5j0ZZR0tI549fSRsYxl8c9vAU78\""
}
},
"service_id": "",
"request": {
"querystring": {},
"size": 87,
"method": "GET",
"headers": {
"host": "127.0.0.1:9080",
"accept": "*/*",
"user-agent": "curl/7.77.0"
},
"url": "http://127.0.0.1:9080/api/hello",
"uri": "/api/hello"
}
}
هذا يختتم التحقق من جمع سجلات السجل. تشير نتائج التحقق إلى أن الإضافة تم تشغيلها بنجاح وعادت بالبيانات المناسبة.
إضافة المزيد من الاختبارات للإضافة
بالنسبة لجزء add_block_preprocessor
من الكود، كنت في حيرة عندما بدأت في كتابته لأنني لم يكن لدي خبرة سابقة في Perl. بعد البحث، أدركت الطريقة الصحيحة لاستخدامه: إذا لم نكتب تأكيدات request
و no_error_log
في قسم البيانات، فإن التأكيد الافتراضي هو كما يلي.
--- request
GET /t
--- no_error_log
[error]
بعد أخذ بعض ملفات اختبار السجلات الأخرى في الاعتبار، قمت بإنشاء ملف file-logger.t
في دليل t/plugin/
.
كل ملف اختبار مقسم بواسطة **DATA**
إلى قسم تمهيدي وقسم بيانات. نظرًا لعدم وجود تصنيف واضح للوثائق المتعلقة بالاختبارات على الموقع الرسمي، يمكنك الرجوع إلى المواد ذات الصلة في نهاية المقالة لمزيد من التفاصيل. فيما يلي أحد حالات الاختبار التي أكملتها بعد الرجوع إلى المواد ذات الصلة.
use t::APISIX 'no_plan';
no_long_string();
no_root_location();
add_block_preprocessor(sub {
my ($block) = @_;
if (! $block->request) {
$block->set_value("request", "GET /t");
}
if (! $block->no_error_log && ! $block->error_log) {
$block->set_value("no_error_log", "[error]");
}
});
run_tests;
__DATA__
=== TEST 1: sanity
--- config
location /t {
content_by_lua_block {
local configs = {
-- full configuration
{
path = "file.log"
},
-- property "path" is required
{
path = nil
}
}
local plugin = require("apisix.plugins.file-logger")
for i = 1, #configs do
ok, err = plugin.check_schema(configs[i])
if err then
ngx.say(err)
else
ngx.say("done")
end
end
}
}
--- response_body_like
done
property "path" is required
هذا يختتم جلسة إضافة اختبار الإضافة.
الخلاصة
ما سبق هو العملية الكاملة لتنفيذ إضافة Apache APISIX من الصفر كمبتدئ في الخلفية. واجهت بالفعل العديد من العقبات في عملية تطوير الإضافة، ولكن لحسن الحظ هناك العديد من الإخوة المتحمسين في مجتمع Apache APISIX لمساعدتي في حل المشكلات، مما جعل تطوير واختبار إضافة file-logger سلسًا نسبيًا طوال الوقت. إذا كنت مهتمًا بهذه الإضافة، أو تريد رؤية تفاصيل الإضافة، يمكنك الرجوع إلى وثائق Apache APISIX الرسمية.
تعمل Apache APISIX أيضًا حاليًا على إضافات أخرى لدعم المزيد من خدمات التكامل، لذا إذا كنت مهتمًا، فلا تتردد في بدء مناقشة في GitHub Discussion، أو عبر قائمة البريد.