⁦Node.js⁩، Python، Go و زبان‌های دیگر

در این آموزش
  1. متغیرهای محیطی مشترک
  2. ⁦Node.js⁩
  3. Python
  4. Go
  5. PHP، Ruby، Rust و بقیه
  6. عیب‌یابی
  7. منابع و مطالعهٔ بیشتر

هر زبانی که کتابخانهٔ OpenTelemetry دارد، از جمله ⁦Node.js⁩، Python، Go، PHP، Ruby و Rust، با پروتکل OTLP/HTTP به LogMug وصل می‌شود. در این برگه اول متغیرهای محیطی مشترک را می‌گذاریم و بعد برای سه زبان رایج‌تر گام‌های دقیق را می‌آوریم. اگر زبان یا محیطی کتابخانهٔ OpenTelemetry ندارد، ارسال با HTTP و JSON همیشه کار می‌کند.

متغیرهای محیطی مشترک

این متغیرها در مشخصات OpenTelemetry استاندارد شده‌اند و SDK همهٔ زبان‌ها آن‌ها را می‌خوانند:

OTEL_SERVICE_NAME=billing
OTEL_RESOURCE_ATTRIBUTES=deployment.environment.name=production
OTEL_LOGS_EXPORTER=otlp
OTEL_TRACES_EXPORTER=none
OTEL_METRICS_EXPORTER=none
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL=http/protobuf
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT=https://ingest.logmug.ir/v1/logs
OTEL_EXPORTER_OTLP_LOGS_HEADERS=x-logmug-key=lm_ingest_...
  • LogMug فقط لاگ می‌پذیرد؛ برای همین exporter trace و metric را none می‌گذاریم.
  • متغیر ..._LOGS_ENDPOINT نشانی کامل است و باید /v1/logs داشته باشد. اگر به‌جای آن OTEL_EXPORTER_OTLP_ENDPOINT=https://ingest.logmug.ir بدهید، SDK خودش مسیر را اضافه می‌کند.
  • هدر به شکل نام=مقدار نوشته می‌شود و کلید را در یک secret نگه دارید، نه در مخزن کد.

⁦Node.js⁩

با بستهٔ auto-instrumentations، لاگ‌های pino (نسخهٔ 7 به بعد) و winston (نسخهٔ 3) خودکار فرستاده می‌شوند:

npm install --save @opentelemetry/api @opentelemetry/auto-instrumentations-node
# only if you use winston:
npm install --save @opentelemetry/winston-transport

متغیرهای بالا را تنظیم کنید و برنامه را با --require اجرا کنید:

node --require @opentelemetry/auto-instrumentations-node/register app.js

یا در محیطی که دستور اجرا دست شما نیست:

NODE_OPTIONS="--require @opentelemetry/auto-instrumentations-node/register"

یک نمونه با pino؛ کد لاگ‌نویسی هیچ تغییری نمی‌کند:

const pino = require('pino');
const logger = pino();

logger.info({ orderId: 991 }, 'order created');
logger.error({ err: new Error('card declined'), orderId: 991 }, 'payment failed');

توجه کنید که console.log گرفته نمی‌شود؛ فقط لاگ‌هایی که از pino یا winston (یا مستقیم از Logs API در OpenTelemetry) بگذرند.

Python

با بستهٔ distro و اجرای برنامه از طریق opentelemetry-instrument، ماژول استاندارد logging به LogMug وصل می‌شود:

pip install opentelemetry-distro opentelemetry-exporter-otlp
opentelemetry-bootstrap -a install

علاوه بر متغیرهای مشترک، این دو را هم بگذارید:

export OTEL_EXPORTER_OTLP_PROTOCOL=http/protobuf
export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true
opentelemetry-instrument python app.py

در نسخه‌های جدید OpenTelemetry Python گرفتن خودکار لاگ به‌طور پیش‌فرض روشن است و متغیر دوم فقط برای نسخه‌های قدیمی‌تر لازم است؛ گذاشتنش ضرری ندارد. سطح logger ریشهٔ Python به‌طور پیش‌فرض WARNING است، پس اگر لاگ‌های info را هم می‌خواهید سطح را پایین بیاورید:

import logging

logging.basicConfig(level=logging.INFO)
log = logging.getLogger("billing")

log.info("invoice created", extra={"invoice_id": 5512})
try:
    1 / 0
except ZeroDivisionError:
    log.exception("invoice total failed")

مقادیر extra ویژگی قابل‌فیلتر می‌شوند و log.exception نوع و stack trace خطا را جدا می‌فرستد. برای Django و Flask همین روش با opentelemetry-instrument جلوی gunicorn یا uwsgi کار می‌کند.

Go

Go ابزار بدون‌کد برای لاگ ندارد؛ چند خط کد لازم است. با پل otelslog، پکیج استاندارد log/slog به OpenTelemetry وصل می‌شود (بخش لاگ OpenTelemetry Go هنوز در مرحلهٔ beta است):

go get go.opentelemetry.io/otel/sdk/log \
  go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp \
  go.opentelemetry.io/contrib/bridges/otelslog
package main

import (
	"context"

	"go.opentelemetry.io/contrib/bridges/otelslog"
	"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp"
	sdklog "go.opentelemetry.io/otel/sdk/log"
)

func main() {
	ctx := context.Background()

	// Reads OTEL_EXPORTER_OTLP_LOGS_ENDPOINT and _HEADERS from the environment.
	exp, err := otlploghttp.New(ctx)
	if err != nil {
		panic(err)
	}
	provider := sdklog.NewLoggerProvider(
		sdklog.WithProcessor(sdklog.NewBatchProcessor(exp)),
	)
	defer provider.Shutdown(ctx) // flushes the last batch

	logger := otelslog.NewLogger("billing", otelslog.WithLoggerProvider(provider))
	logger.InfoContext(ctx, "invoice created", "invoice_id", 5512)
}

نام سرویس از OTEL_SERVICE_NAME خوانده می‌شود. از نسخه‌های ...Context مثل InfoContext استفاده کنید تا اگر در context یک span باشد، trace_id روی لاگ بنشیند.

PHP، Ruby، Rust و بقیه

برای این زبان‌ها هم SDK رسمی OpenTelemetry همان متغیرهای مشترک را می‌خواند؛ کافی است پل (bridge) کتابخانهٔ لاگ آن زبان را طبق مستندات OpenTelemetry همان زبان نصب کنید. اگر نصب SDK برای کارتان زیادی است، فرستادن JSON با endpoint ساده از هر زبانی در چند خط شدنی است.

عیب‌یابی

  • هیچ درخواستی نمی‌رسد: پروتکل روی gRPC مانده است (پیش‌فرض برخی SDKها) یا /v1/logs از انتهای ..._LOGS_ENDPOINT افتاده است. موقتاً OTEL_LOGS_EXPORTER=console بگذارید تا ببینید اصلاً لاگی تولید می‌شود یا نه.
  • 401: کلید نادرست یا باطل‌شده است، یا هدر به شکل x-logmug-key=... نوشته نشده است.
  • 400: بدنه خوانده نشد؛ پروتکل را http/protobuf بگذارید.
  • 403: سهمیهٔ ماهانهٔ پلن رایگان تمام شده است.
  • 429 و 503: عبور از نرخ مجاز یا شلوغی موقت؛ پاسخ Retry-After دارد و exporterها دوباره تلاش می‌کنند.
  • لاگ‌های آخر برنامه‌های کوتاه‌عمر (اسکریپت، job) نمی‌رسند: پیش از خروج provider را Shutdown یا flush کنید.

منابع و مطالعهٔ بیشتر

کلید پروژه‌تان را هنوز ندارید؟

در پلن رایگان یک پروژه بسازید؛ راهنمای اتصال با نشانی و کلید واقعی خودتان همان‌جا آماده است.

شروع رایگان