---
title: مقاييس Prometheus مخصصة في Node.js +
description: 'إضافة مقاييس Prometheus لتطبيق Node.js + Express باستخدام prom-client: نقطة نهاية /metrics، و counters و histograms مخصصة، و labels آمنة ذات low-cardinality.'
url: https://nerdleveltech.com/ar/prometheus-metrics-nodejs-express-prom-client-tutorial
language: ar
date: '2026-06-29'
category: cloud-devops
tags:
  - prometheus
  - node.js
  - prom-client
  - express
  - observability
  - metrics
  - cloud-devops
markdown_url: https://nerdleveltech.com/ar/prometheus-metrics-nodejs-express-prom-client-tutorial.md
attribution: 'Source: NerdLevelTech — https://nerdleveltech.com/ar/prometheus-metrics-nodejs-express-prom-client-tutorial'
terms: https://nerdleveltech.com/llms.txt
---

# مقاييس Prometheus مخصصة في Node.js +

أنت تقوم بعرض مقاييس Prometheus في Node.js عن طريق تثبيت نقطة نهاية `/metrics` تُرجع المخرجات النصية لسجل `prom-client`. يبني هذا البرنامج التعليمي واحدًا على Express: مقاييس العمليات الافتراضية، وعداد طلبات مخصص، ورسم بياني (histogram) للمدة، وتسميات منخفضة العلاقة (low-cardinality) يمكن لـ Prometheus كشطها بأمان — تم تشغيل كل سطر والتحقق منه قبل النشر.

## ملخص

يتطلب ربط مقاييس Prometheus في Node.js سجلاً واحدًا، ومقياسين مخصصين، ووسيطًا (middleware) واحدًا. ستقوم بتجهيز تطبيق Express باستخدام **prom-client 15.1.3**[^npm] و **Express 5.2.1**، مع عرض نقطة نهاية `/metrics` التي تبلغ عن مقاييس عملية Node الافتراضية بالإضافة إلى عداد `http_requests_total` ورسم بياني `http_request_duration_seconds`. الأمر برمته عبارة عن ملفين صغيرين وحوالي 15 دقيقة. الخطأ الذي يدمر الإعدادات الساذجة بهدوء — التسمية حسب عنوان URL الخام بدلاً من قالب المسار (route template)، مما يؤدي لانفجار عدد السلاسل الزمنية — يتم إصلاحه في الخطوة 4، مع إعادة إنتاج المخرجات لإثبات ذلك.

## ما ستتعلمه

- كيفية إنشاء سجل prom-client وجمع مقاييس Node.js الافتراضية باستخدام `collectDefaultMetrics`
- كيفية تحديد عداد مخصص (`http_requests_total`) مع التسميات (labels)
- كيفية تسجيل زمن انتقال الطلب كرسم بياني (`http_request_duration_seconds`) باستخدام `startTimer`
- كيفية ربط وسيط Express واحد يسجل كليهما عند كل طلب
- كيفية عرض نقطة نهاية `/metrics` بنوع المحتوى الذي يتوقعه Prometheus
- كيفية الحفاظ على انخفاض علاقة التسمية (label cardinality) باستخدام قالب المسار، وليس عنوان URL الخام
- كيفية قراءة والتحقق من تنسيق عرض Prometheus
- كيفية إصلاح الأخطاء الشائعة التي يواجهها الجميع مع prom-client

## المتطلبات الأساسية

- **Node.js 18+** (يتطلب prom-client إصدار `^16 || ^18 || >=20`؛ ويتطلب Express 5 إصدار `>= 18`). يوصى باستخدام إصدار LTS الحالي — Node 20 أو 22.[^npm]
- **prom-client 15.1.3** و **Express 5.2.1**، مثبتين لضمان إمكانية إعادة إنتاج البناء الخاص بك.
- إلمام أساسي بمسارات Express و `async/await`.

لا يلزم أي شيء آخر: لا يحتاج prom-client إلى خادم Prometheus قيد التشغيل لتقديم المقاييس، لذا يمكنك بناء واختبار نقطة النهاية بالكامل محليًا باستخدام `curl`. خادم Prometheus الحقيقي يهم فقط بمجرد رغبتك في كشط ورسم ما قمت بعرضه بيانيًا.

## الخطوة 1 — إنشاء هيكل مشروع Node.js + Express

أنشئ مشروعًا، وقم بتمكين وحدات ES، وقم بتثبيت الحزمتين المثبتتين:

```bash
mkdir prom-demo && cd prom-demo
npm init -y
npm pkg set type=module
npm install prom-client@15.1.3 express@5.2.1
```

يتيح لك ضبط `type=module` استخدام صيغة `import`. يتم شحن prom-client كـ CommonJS، ولكن Node تسمح لك باستخدام الواردات المسماة منه، لذا فإن `import { Counter } from 'prom-client'` تعمل دون أي وسيط توافق.[^promclient]

## الخطوة 2 — إنشاء سجل prom-client وجمع المقاييس الافتراضية

**السجل (registry)** هو الكائن الذي يحمل مقاييسك ويقدمها بتنسيق نص Prometheus. أنشئ `metrics.js` وابدأ بسجل بالإضافة إلى مقاييس العمليات المدمجة في Node:

```js
// metrics.js
import { Counter, Histogram, Registry, collectDefaultMetrics } from 'prom-client';

// One registry for this process. Every metric registers itself here.
export const register = new Registry();

// Optional: a label attached to every series (handy when many instances scrape).
register.setDefaultLabels({ app: 'demo-API' });

// Node/process metrics (CPU, memory, event-loop lag, GC...) — collected on scrape.
collectDefaultMetrics({ register });
```

تقوم `collectDefaultMetrics` بتسجيل مجموعة من مقاييس وقت التشغيل الموصى بها ويتم **جمعها عند الكشط، وليس بناءً على مؤقت** — يقرأ prom-client القيم الحالية في كل مرة تستدعي فيها `register.metrics()`.[^promclient] هذا الاستدعاء الواحد يمنحك، من بين أمور أخرى:

- `process_cpu_seconds_total`، `process_resident_memory_bytes`، `process_start_time_seconds`
- `nodejs_eventloop_lag_seconds` (بالإضافة إلى متغيرات `_p50`، `_p90`، `_p99`)
- `nodejs_heap_size_total_bytes`، `nodejs_gc_duration_seconds`، `nodejs_version_info`

بعض مقاييس `process_*` لواصف الملفات والذاكرة مخصصة لنظام Linux فقط، وهو ما تشير إليه وثائق prom-client صراحةً.[^promclient] استدعِ `collectDefaultMetrics` **مرة واحدة** لكل سجل — استدعاؤها مرتين يؤدي لخطأ (المزيد عن ذلك في استكشاف الأخطاء وإصلاحها).

## الخطوة 3 — تحديد عداد ورسم بياني مخصصين

تخبرك المقاييس الافتراضية عن العملية؛ بينما تخبرك المقاييس المخصصة عن تطبيقك. أضف اثنين إلى `metrics.js`. **العداد (counter)** يرتفع فقط، وهو مناسب تمامًا لـ "كم عدد الطلبات التي خدمتها"؛[^types] بينما يسجل **الرسم البياني (histogram)** توزيعًا، وهو ما تريده لزمن الانتقال:[^types]

```js
// metrics.js (continued)

// A counter: total HTTP requests, split by method, route, and status code.
export const httpRequestsTotal = new Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register],
});

// A histogram: request duration in seconds (default buckets suit web latencies).
export const httpRequestDuration = new Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  registers: [register],
});
```

خيارا التسمية هنا متعمدان ويتبعان اتفاقيات Prometheus الخاصة: تنتهي العدادات بـ `_total`، وتستخدم المدد الوحدة الأساسية `seconds`، وليس المللي ثانية.[^naming] هذان الاسمان — `http_requests_total` و `http_request_duration_seconds` — هما في الواقع الأمثلة المتعارف عليها التي يستخدمها دليل تسمية Prometheus.[^naming]

نظرًا لعدم تمرير مصفوفة `buckets`، يستخدم الرسم البياني الإعدادات الافتراضية لـ prom-client، والتي تم ضبطها لزمن انتقال طلبات الويب:[^promclient]

```text
[0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, 5, 10]   // seconds
```

إذا كانت أزمنة الانتقال لديك تتجمع في مكان آخر (على سبيل المثال، إصابات ذاكرة التخزين المؤقت في أقل من مللي ثانية أو عمليات التصدير التي تستغرق عدة ثوانٍ)، فقم بتجاوزها باستخدام `buckets: [...]`. اجعل القائمة قصيرة: كل دلو (bucket) هو سلسلة زمنية خاصة به، لذا فإن قوائم الدلاء الواسعة تضاعف التخزين بنفس الطريقة التي تفعلها التسميات.

## الخطوة 4 — تسجيل كل طلب باستخدام وسيط Express واحد

الآن قم بربط المقاييس بحركة المرور. النمط الأنظف هو وسيط واحد يبدأ مؤقتًا عند وصول الطلب ويسجل كلا المقياسين عند انتهاء الاستجابة. أضفه إلى `metrics.js`:

```js
// metrics.js (continued)

// Express middleware: time every request, record both metrics when it finishes.
export function metricsMiddleware(req, res, next) {
  const endTimer = httpRequestDuration.startTimer();
  res.on('finish', () => {
    // Use the ROUTE TEMPLATE (/users/:id), never req.url (/users/123) — or every
    // id becomes its own time series and your cardinality explodes.
    const route = req.route?.path ?? 'unmatched';
    const labels = { method: req.method, route, status_code: res.statusCode };
    endTimer(labels);
    httpRequestsTotal.inc(labels);
  });
  next();
}
```

تُرجع `startTimer()` دالة؛ استدعاؤها لاحقًا يرصد الثواني المنقضية في الرسم البياني ويسمح لك بإرفاق تسميات لا تعرفها إلا في النهاية، مثل رمز الحالة.[^promclient] التسجيل في حدث `finish` يعني أنك تلتقط رمز الحالة الحقيقي ووقت الاستجابة الكامل، بما في ذلك التسلسل (serialization).

أهم سطر على الإطلاق هو `req.route?.path`. بحلول الوقت الذي يتم فيه تشغيل `finish`، يكون Express قد ملأ `req.route` بـ **القالب** الذي تطابق — `/users/:id`، وليس `/users/123` الملموس. هذا هو ما يبقي تسمية `route` محدودة. الطلبات التي لا تطابق أي مسار (خطأ 404) ليس لها `req.route`، لذا فإن البديل `?? 'unmatched'` يدمجها في سلسلة واحدة بدلاً من السماح لكل مسار يتم فحصه بأن يصبح مقياسًا خاصًا به.

## الخطوة 5 — عرض نقطة نهاية المقاييس

يقوم Prometheus بسحب البيانات من نقطة نهاية HTTP عادية تُرجع المقاييس بتنسيق العرض النصي الخاص به. أنشئ `server.js`:

```js
// server.js
import express from 'express';
import { register, metricsMiddleware } from './metrics.js';

const app = express();
app.use(metricsMiddleware);

app.get('/', (req, res) => res.send('Hello'));
app.get('/users/:id', (req, res) => res.json({ id: req.params.id }));

// Prometheus scrapes this endpoint on an interval.
app.get('/metrics', async (req, res) => {
  try {
    res.set('Content-Type', register.contentType);
    res.end(await register.metrics());
  } catch (err) {
    res.status(500).end(err.message);
  }
});

const port = Number(process.env.PORT ?? 3000);
app.listen(port, () => {
  console.log(`Listening on http://localhost:${port}`);
});
```

هناك تفصيلان مهمان. أولاً، `register.metrics()` تُرجع **وعداً (promise)**، لذا يجب انتظارها (await).[^promclient] ثانياً، `register.contentType` هو الترويسة (header) الدقيقة التي يتوقعها Prometheus — وهي `text/plain; version=0.0.4; charset=utf-8`.[^exposition] كتابة `text/plain` بشكل يدوي تتجاهل رمز الإصدار وقد تسبب مشاكل مع أدوات السحب الأكثر صرامة؛ قراءتها من السجل (registry) تضمن دائماً إرسال القيمة الصحيحة، حتى لو قمت بتبديل السجل إلى تنسيق OpenMetrics. تعيد كتلة `try/catch` حالة 500 بدلاً من ترك الطلب معلقاً في حال فشل العرض.

## الخطوة 6 — التشغيل وسحب المقاييس

ابدأ تشغيل الخادم وأرسل بعض الطلبات:

```bash
node server.js
# in another terminal:
curl -s localhost:3000/
curl -s localhost:3000/users/123
curl -s localhost:3000/users/456
curl -s localhost:3000/metrics
```

تبدأ استجابة `/metrics` بمقاييس العمليات الافتراضية، تليها مقاييسك المخصصة. إليك قسم العداد (counter) بعد تلك الطلبات الأربعة (أحدها 404 من مسار غير مطابق):

```text
# HELP http_requests_total Total number of HTTP requests
# TYPE http_requests_total counter
http_requests_total{method="GET",route="/",status_code="200",app="demo-API"} 1
http_requests_total{method="GET",route="/users/:id",status_code="200",app="demo-API"} 2
http_requests_total{method="GET",route="unmatched",status_code="404",app="demo-API"} 1
```

لاحظ السطر الثاني: `/users/123` و `/users/456` أنتجا سلسلة زمنية **واحدة** بقيمة `2`، لأن كليهما طابق قالب `/users/:id`. هذا هو إصلاح عدد العناصر الفريدة (cardinality) من الخطوة 4 وهو يعمل بنجاح.

## التحقق من تنسيق عرض Prometheus

يتم عرض كل مقياس كتعليقات `# HELP` و `# TYPE` تليها العينات. يتوسع المدرج التكراري (histogram) إلى عدة سلاسل لكل مجموعة من الملصقات (labels): واحدة تراكمية `_bucket` لكل حد، و `_sum`، و `_count`.[^types] إليك المدرج التكراري لـ `/users/:id`:

```text
# HELP http_request_duration_seconds Duration of HTTP requests in seconds
# TYPE http_request_duration_seconds histogram
http_request_duration_seconds_bucket{le="0.005",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.01",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.025",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.05",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.1",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.25",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="0.5",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="1",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="2.5",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="5",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="10",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_bucket{le="+Inf",app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
http_request_duration_seconds_sum{app="demo-API",method="GET",route="/users/:id",status_code="200"} 0.0011
http_request_duration_seconds_count{app="demo-API",method="GET",route="/users/:id",status_code="200"} 2
```

الأوعية (Buckets) **تراكمية**: كل `_bucket{le="..."}` يحسب كل ملاحظة أقل من أو تساوي ذلك الحد، لذا فهي تزداد دائماً، و `_bucket{le="+Inf"}` يساوي دائماً `_count`.[^types] وقع كلا الطلبين تحت 5 مللي ثانية، لذا يقرأ كل وعاء القيمة `2`. الـ `_sum` هو الوقت الفعلي وسيختلف على جهازك. لتحويل هذه القيم إلى رسم بياني لوقت الاستجابة (latency)، يحسب Prometheus النسب المئوية (percentiles) من الأوعية باستخدام `histogram_quantile()`.[^histograms]

يظهر ملصق `app="demo-API"` في كل سلسلة بسبب `setDefaultLabels` في الخطوة 2 — وهي طريقة نظيفة لتمييز الخدمة التي أنتجت البيانات دون تكرار نفسك في كل مقياس.

## إبقاء عدد العناصر الفريدة للملصقات تحت السيطرة

هذا هو القسم الذي يفصل بين إعداد المقاييس الذي يصمد في بيئة الإنتاج وبين الإعداد الذي قد يتسبب في تعطل خادم Prometheus الخاص بك. كل مجموعة فريدة من قيم الملصقات تمثل **سلسلة زمنية منفصلة**. وثائق Prometheus صريحة بشأن هذا الأمر:

> تذكر أن كل مجموعة فريدة من أزواج الملصقات (مفتاح-قيمة) تمثل سلسلة زمنية جديدة، مما قد يؤدي إلى زيادة هائلة في كمية البيانات المخزنة. لا تستخدم الملصقات لتخزين أبعاد ذات عدد عناصر فريدة مرتفع (قيم ملصقات كثيرة ومختلفة)، مثل معرفات المستخدمين، أو عناوين البريد الإلكتروني، أو أي مجموعات قيم غير محدودة أخرى.[^naming]

الخطأ الشائع في Node.js هو تسمية الطلبات باستخدام `req.url`. مع استخدام `req.url`، تصبح المسارات `/users/123` و `/users/124` و `/users/125` ثلاث سلاسل زمنية — ويمكن لتطبيق يضم مليون مستخدم وعدد قليل من نقاط النهاية أن يولد ملايين السلاسل، وكل واحدة منها تتضاعف مرة أخرى مع كل وعاء مدرج تكراري. استخدم قالب المسار (`req.route?.path`) وسيبقى العدد مساوياً لعدد المسارات التي حددتها بالفعل، وهو ما أثبته مخرج الخطوة 6: معرفان مختلفان للمستخدمين، سلسلة زمنية واحدة.

هناك قاعدتان ذهبيتان للحفاظ على سلامة النظام. اجعل *قيم* الملصقات محصورة في مجموعة معروفة وصغيرة — الطرق (methods)، قوالب المسارات، رموز الحالة، كلها جيدة؛ أما المعرفات (IDs)، والبريد الإلكتروني، والمسارات الخام، فلا تستخدمها أبداً. وتذكر أن كل مدرج تكراري يصدر سلسلة واحدة لكل وعاء بالإضافة إلى وعاء `+Inf`، و `_sum`، و `_count` — وهذا يعني 14 سلسلة لكل مجموعة ملصقات للوضع الافتراضي المكون من 11 وعاءً — لذا فإن قائمة أوعية كبيرة مضروبة في بضعة ملصقات تتضاعف بشكل أسرع مما يتوقعه الناس.

## الأخطاء الشائعة في prom-client

**`A metric with the name http_requests_total has already been registered.`** لقد قمت بإنشاء نفس اسم المقياس مرتين في نفس السجل. يعني هذا عادةً أنه تم استيراد وحدة (module) تحدد المقاييس مرتين، أو أن إعادة التحميل السريع (hot reload) لخادم التطوير أعادت تشغيل التعريفات. حدد كل مقياس مرة واحدة بالضبط في وحدة مخصصة (مثل `metrics.js` هنا) واستورد النسخ في مكان آخر — لا تقم بإعادة تعريفها أبداً.[^promclient]

**`A metric with the name process_cpu_user_seconds_total has already been registered.`** نفس السبب، ولكن يتم تحفيزه عن طريق استدعاء `collectDefaultMetrics({ register })` مرتين لنفس السجل. استدعها مرة واحدة عند بدء التشغيل. إذا كان برنامج إعادة التحميل السريع يستمر في إعادة تشغيل ملف الإدخال الخاص بك، فقم بحماية الاستدعاء أو أعد التشغيل بشكل نظيف.

**`/metrics` تفتقد مقاييسك المخصصة.** يصدر المقياس المصنف ترويسة `# HELP` و `# TYPE` الخاصة به ولكن **بدون أسطر عينات** حتى يتم رصده مرة واحدة على الأقل، لأن prom-client لا يمكنه معرفة قيم الملصقات مسبقاً.[^promclient] أرسل طلباً عبر مساراتك أولاً، ثم اسحب البيانات — أو استدعِ `histogram.zero({ ... })` لتسجيل مجموعات الملصقات المعروفة مسبقاً. إذا كانت المقاييس الافتراضية فقط هي التي تحتوي على قيم، فتأكد من تمرير مقاييسك المخصصة عبر `registers: [register]` (أو تمت إضافتها إلى نفس السجل الذي تعرضه).

**يرفض Prometheus عملية السحب رغم أن `/metrics` تبدو صحيحة في المتصفح.** يجب أن يكون جسم الاستجابة نص عرض صالحاً، وليس JSON — إرجاع `res.json(...)` هنا لن يتم تحليله أبداً. اضبط الترويسة صراحةً باستخدام `res.set('Content-Type', register.contentType)`: إن Prometheus 3.0+ صارم ويمكن أن يفشل في عملية سحب تكون فيها `Content-Type` مفقودة أو غير صالحة ما لم تقم بتكوين `fallback_scrape_protocol`، بينما يتراجع Prometheus 2.x بهدوء إلى المحلل النصي.[^negotiation] قراءة القيمة من السجل تبقيها صحيحة في كلتا الحالتين.[^exposition]

## الخطوات التالية

لديك الآن مقاييس Prometheus جاهزة للإنتاج في Node.js: سجل، ومقاييس عمليات افتراضية، وعداد طلبات، ومدرج تكراري لوقت الاستجابة، وبرمجية وسيطة (middleware) واحدة تسجل كل ذلك مع عدد عناصر فريدة محدود. وجه خادم Prometheus إلى `http://localhost:3000/metrics` مع فاصل زمني للسحب يبلغ 15 ثانية ويمكنك البدء في رسم معدل الطلبات باستخدام `rate(http_requests_total[5m])` ووقت استجابة p95 باستخدام `histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))`.[^histograms]

من هنا، هناك اتجاهان يستحقان المتابعة. لكي تظهر *traces* (تتبعات) مستوى الطلب بجانب هذه المقاييس المجمعة، أضف التتبع الموزع كما هو موضح في [دليل تتبع OpenTelemetry Collector و Node.js](https://nerdleveltech.com/opentelemetry-collector-nodejs-tracing-tutorial) — المقاييس تخبرك *أن* زمن الاستجابة قد ارتفع، بينما التتبعات تخبرك *أين* حدث ذلك. ولتحديد أي المقاييس والتنبيهات تستحق الجهد فعلياً، فإن المبادئ الموجودة في [بناء استراتيجية مراقبة حديثة](https://nerdleveltech.com/building-a-modern-monitoring-strategy-that-actually-works) ستحافظ على دقة لوحات التحكم الخاصة بك. إذا كنت تقوم بسحب هذه المقاييس من الـ pods، فإن نفس نقطة النهاية `/metrics` هي ما يستهدفه Prometheus `ServiceMonitor` بمجرد [تشغيل التطبيق على Kubernetes](https://nerdleveltech.com/resize-Kubernetes-pods-without-restart).

[^npm]: prom-client — npm package (version 15.1.3, engines, license). <https://www.npmjs.com/package/prom-client>

[^promclient]: siimon/prom-client — Prometheus client for Node.js (README, API reference). <https://GitHub.com/siimon/prom-client>

[^types]: Prometheus Documentation — "Metric types" (counter, gauge, histogram; `_bucket`/`_sum`/`_count`). <https://prometheus.io/docs/concepts/metric_types/>

[^naming]: Prometheus Documentation — "Metric and label naming" (base units, `_total` suffix, label cardinality caution). <https://prometheus.io/docs/practices/naming/>

[^exposition]: Prometheus Documentation — "Exposition formats" (text format version 0.0.4, content type). <https://prometheus.io/docs/instrumenting/exposition_formats/>

[^histograms]: Prometheus Documentation — "Histograms and summaries" (`histogram_quantile`, bucket selection). <https://prometheus.io/docs/practices/histograms/>

[^negotiation]: Prometheus Documentation — "Scrape protocol content negotiation" (Content-Type handling, `fallback_scrape_protocol`, PrometheusText0.0.4 default). <https://prometheus.io/docs/instrumenting/content_negotiation/>
