در برنامه های بزرگ C، ورود یک ابزار حیاتی برای اشکال زدایی، نظارت و حفظ سلامت سیستم است، در حالی که پایه (FLT:0) بیانیه ها یا کتابخانه هایی مانند ممکن است برای اجرای پروژه های کوچک کافی باشد، آنها اغلب در هنگام عملکرد، انعطاف پذیری و مقیاس پذیری تبدیل به یک سیستم ورودی سفارشی می شوند که برای پیاده سازی دقیق ترین سطوح خروجی، و تنظیمات سیستم تولید، و سیستم های سیستم های سیستم های سیستم های سیستم های سیستم های سیستم های پیشرفته مناسب مناسب است.

چرا یک سیستم ثبت نام سفارشی ایجاد کنید

مکانیسم های استاندارد ورود مانند یا POSIX API]، اغلب فاقد دانه و عملکرد مورد نیاز توسط برنامه های بزرگ است.

  • عملکرد عملیاتی: [FLT 1] از تخصیص و قالب بندی غیر ضروری هنگام سرکوب سطوح ثبت نام اجتناب کنید.
  • قالب بندی قابل تنظیم: زمان های ثابت، شناسه های موضوعی، و پیشوند ماژول بهبود خوانایی و تجزیه و تحلیل.
  • کنترل منظم: [FLT1] - فعال کردن / غیر قابل تشخیص سطوح ورودی خاص در زمان اجرا بدون استفاده مجدد.
  • مقاصد چند منظوره: نوشتن به کنسول، فایل ها، سوکت های شبکه، و یا سیستم های نظارت خارجی به طور همزمان.
  • ایمنی را بخوانید از خاموش یا عملیات اتمی برای جلوگیری از خروجی بین رشته ای در برنامه های چند رشته ای استفاده کنید.

طراحی هسته

قبل از نوشتن هر کد، اجزای اصلی سیستم ورود به سیستم را تعریف کنید.این تصمیمات همه چیز را از طراحی API برای اجرای زمان شکل می دهند.

سطح های Log

در این میان، میزان حجمی که در آن به کار می رود، به صورت زیر است: ۴٫۵، [۱] [۱۰] [۳] [۳] [۳] [۳] [۳]، [۱۰]، [۳]، [۱۰] [۱۰] [۱۰] برای اطمینان از ایمنی نوع، هر سطح باید حداقل پیام های آستانه داشته باشد؛ و تولید سربار در تولید را کاهش می دهد.

مقصد های Log

در نظر بگیرید که چه زمانی ثبت نام خواهد شد.

  • [در این میان] [از این رو] [[[۱]]] [۱]] [۱]] [۱]] [۱]] [۱]] [۱]] [۱] [۱]] [۱]] [۱]] [۱] [۱]] [۱] [۱] [۱]] [۱] [۱] [۱] [۱] [۱] [۱] [۳] [۳] [۳] [۱] [۱] [۱] [۱] [۳] [۱] [۱] [۱] [۱] [۱] [۵] [۱] [۱] [۱] [۱] [۱] [۱] [۱] [۱] [۳] [۳] [۱] [۳] [۳] [۱] [۱] [۱] [۱] [۱] [۱] [۳] [۱] [۳] [۱] [۱] [۱] [۱] [۱] [۱] [۱] [۱] [۱] [۳] [۳] [۳] [۱] [۳] [۳] [۱] [۱] [۳] [۳]
  • [[۱] [۱۰] [۱۰] [۱۰] [۱] [۱]] [۳]] [۳] [۳] [۱]] [۳] [۱]] [۳] [۳] [۳] [۳]] [۳] [۳] [۳] [۱] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [۳] [
  • [FLT 1] - برای ورود متمرکز بر سیستم های یونیکس.
  • شبکه - سوکت های UDP یا TCP برای تجمع از راه دور (به عنوان مثال، Graylog، پشته ELK).

یک logger به خوبی طراحی شده از یک انتزاع سینک استفاده می کند که به شما اجازه می دهد تا مقصدهای را در زمان اجرا اضافه یا حذف کنید.

فرمت و کنوانسیون

تصمیم بگیرید که یک فرمت log در اوایل برای حفظ سازگاری، یک فرمت معمولی شامل یک زمان بندی، سطح ورود، فایل منبع / خط، نام ماژول، و پیام. فرمت های ساختار یافته مانند JSON ساده سازی تجزیه و نقل خودکار اما اضافه کردن سربار برای عملکرد، استفاده از یک ثابت فیلد de Limiter (به عنوان مثال، لوله ها یا زبانه) و جلوگیری از تخصیص حافظه پویا در مسیرهای گرم.

امنیت Thread Safety

در برنامه های چند بعدی، نوشتن همزمان می تواند خروجی garbled را تولید کند.استفاده از یک (در سیستم های POSIX) یا یک بخش بحرانی (در ویندوز) در اطراف عملیات واقعی I / O. برای نفوذ بالاتر، در نظر گرفتن یک صف قفل یا بافرهای ورودی که به صورت دوره ای تخلیه می شوند.

پیاده سازی یک پایه

با یک پیاده سازی ساده که ملزومات را پوشش می دهد شروع کنید: سطوح log، قالب بندی زمان بندی و ساخت پیام متغیر-argument.کد زیر پایه ای را فراهم می کند.

تابع های سطح Enum و helper

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <time.h>
#include <pthread.h>

typedef enum {
 LOG_LEVEL_TRACE,
 LOG_LEVEL_DEBUG,
 LOG_LEVEL_INFO,
 LOG_LEVEL_WARN,
 LOG_LEVEL_ERROR,
 LOG_LEVEL_FATAL
} LogLevel;

static const char* level_strings[] = {
 "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL"
};

static LogLevel g_min_level = LOG_LEVEL_INFO;
static pthread_mutex_t g_log_mutex = PTHREAD_MUTEX_INITIALIZER;

پیام رسانی

تابع اصلی ورود به سیستم از [FLT13] برای فرمت پیام با خیال راحت استفاده می کند.یک بافر استاتیک از تخصیص توده در مسیر گرم اجتناب می کند، اما از سرفر برای پیام های بسیار طولانی آگاه باشید.

void log_message(LogLevel level, const char* file, int line, const char* func, const char* format, ...) {
 if (level < g_min_level) return;

 time_t now = time(NULL);
 struct tm* tm_info = localtime(&now);
 char time_buf[20];
 strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", tm_info);

 pthread_mutex_lock(&g_log_mutex);

 // Write header
 fprintf(stdout, "[%s] [%s] [%s:%d %s] ", time_buf, level_strings[level], file, line, func);

 va_list args;
 va_start(args, format);
 vfprintf(stdout, format, args);
 va_end(args);

 fprintf(stdout, "\n");
 fflush(stdout); // Ensure immediate output for debugging

 pthread_mutex_unlock(&g_log_mutex);
}

برای راحتی، ماکروهایی را تعریف کنید که به طور خودکار فایل منبع و خط را ثبت می کنند:

#define LOG_TRACE(...) log_message(LOG_LEVEL_TRACE, __FILE__, __LINE__, __func__, __VA_ARGS__)
#define LOG_DEBUG(...) log_message(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __func__, __VA_ARGS__)
// ... similar for INFO, WARN, ERROR, FATAL

مثال استفاده:

int main() {
 LOG_INFO("Application started on port %d", 8080);
 LOG_WARN("Disk usage exceeding %.1f%%", 85.3);
 LOG_ERROR("Failed to open file: %s", "config.dat");
 return 0;
}

اضافه کردن File Output و Platform

خروجی کنسول در طول توسعه مفید است، اما سیستم های تولیدی نیاز به log مداوم دارند.یک سینک فایل را اضافه کنید که به یک مجموعه ی چرخشی از فایل های log می نویسد.

فایل وارد کننده

logger را با یک file pointer گسترش دهید، می توانید یک مسیر را به سختی کد کنید یا آن را قابل تنظیم کنید. همان فایل خاموش محافظت می کند.

static FILE* g_log_file = NULL;

int log_init_file(const char* path) {
 g_log_file = fopen(path, "a");
 return (g_log_file != NULL) ? 0 : -1;
}

void log_message_file(LogLevel level, const char* file, int line, const char* func, const char* format, ...) {
 // Same timestamp and header logic, but write to g_log_file
 // ...
}

استراتژی های Log Platform

برای جلوگیری از مصرف فایل های log از تمام فضای دیسک، پیاده سازی چرخش بر اساس اندازه فایل، تاریخ یا هر دو استراتژی ساده:

  • پس از بررسی حجم فایل فعلی با چک کردن (FLT:18) بعد از هر نوشتن (یا به صورت دوره ای)
  • هنگامی که فایل از آستانه (به عنوان مثال 100 MB) تجاوز می کند، آن را تغییر می دهد ([FLT 19] ، سپس فایل های قدیمی را فشرده می کند و تازه (FLT:21) را باز می کند.
  • حداکثر تعداد فایل های چرخ دار را نگه دارید؛ قدیمی ترین را حذف کنید.

اسکلت مثال:

void log_rotate_if_needed() {
 if (g_log_file && ftell(g_log_file) > MAX_LOG_SIZE) {
 fclose(g_log_file);
 // Rename and compress logic
 g_log_file = fopen(LOG_PATH, "a");
 }
}

ورود همزمان برای عملکرد

Direct I/O در مسیر گرم می تواند موضوعات و عملکرد ضعیف را مسدود کند، همانطور که ورود همزمان پیام قالب بندی شده از دیسک یا شبکه با استفاده از یک صف مصرف کننده می نویسد.

الگوی تولید کننده-Consumer Pattern

از یک بافر حلقه یا یک صف بدون قفل (به عنوان مثال، یا یک لیست ساده مرتبط با خاموش استفاده کنید تا پیام های log را ذخیره کنید.

مزایای:

  • موضوعات کاربردی هرگز منتظر I/O نیستند.
  • پیام های کشویی را می توان به صورت ماهرانه ای (به عنوان مثال، افزایش یک شمارنده) به کار گرفت.
  • Batching می گوید که باعث کاهش هزینه سیستم می شود.

اجرای Sketch

#define QUEUE_SIZE 8192
static char g_log_queue[QUEUE_SIZE][MAX_LOG_LINE];
static volatile int g_queue_head = 0, g_queue_tail = 0;
static pthread_mutex_t g_queue_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t g_queue_notify = PTHREAD_COND_INITIALIZER;

void push_log(const char* logline) {
 pthread_mutex_lock(&g_queue_mutex);
 // Copy and advance head (ring buffer)
 strncpy(g_log_queue[g_queue_head], logline, MAX_LOG_LINE);
 g_queue_head = (g_queue_head + 1) % QUEUE_SIZE;
 pthread_cond_signal(&g_queue_notify);
 pthread_mutex_unlock(&g_queue_mutex);
}

void* log_flush_thread(void* arg) {
 while (1) {
 pthread_mutex_lock(&g_queue_mutex);
 while (g_queue_head == g_queue_tail)
 pthread_cond_wait(&g_queue_notify, &g_queue_mutex);
 // Dequeue and write to file/console
 char buffer[MAX_LOG_LINE];
 strncpy(buffer, g_log_queue[g_queue_tail], MAX_LOG_LINE);
 g_queue_tail = (g_queue_tail + 1) % QUEUE_SIZE;
 pthread_mutex_unlock(&g_queue_mutex);
 // Perform I/O (protected by the same file mutex)
 fputs(buffer, g_log_file);
 }
 return NULL;
}

ویژگی های پیشرفته

هنگامی که سیستم پایه عملیاتی است، این پیشرفت ها را برای برنامه های بزرگتر در نظر بگیرید.

ثبت ساختار (JSON)

log های ساختاری تجزیه و تحلیل خودکار را امکان می دهند.استفاده از یک کتابخانه سبک JSON مانند cJSON برای ساخت اشیاء.

{"timestamp":"2025-03-20T10:15:30Z","level":"ERROR","module":"auth","message":"Login failed","user":"john"}

در حالی که افعال، این فرمت یکپارچه با ابزارهایی مانند Elasticsearch و Splunk ادغام می شود.

از راه دور ورود از طریق Syslog یا Network

برای سیستم های توزیع شده، ورود به یک جمع کننده مرکزی (FLT 26) تابع ( man page ساده اما جایگزین، ارسال داده های UDP به Graylog یا Logstashpoint با استفاده از سوکت های خام است.

فایل Configuration File

اجازه دهید اپراتورهای برای اصلاح پارامترهای ورود بدون تنظیم مجدد: سطح ورود، مسیر فایل، اندازه چرخش و مقصد خروجی. پارس یک فایل ساده INI-style یا متغیرهای محیط استفاده می شود. پیکربندی می تواند از طریق یک سیگنال بارگیری شود (به عنوان مثال، [FLT 27].

بهترین تمرین ها و قرص ها

از اشتباهات رایج که ارزش ثبت نام را تضعیف می کنند، اجتناب کنید.

عملکرد Overhead

ثبت نام نباید تبدیل به یک تنگنا شود، همیشه قبل از بحث های قالب بندی، سطح log را بررسی کنید.از ماکروهایی که سطح را به سرعت ارزیابی می کنند استفاده کنید.

نگرانی های امنیتی

هرگز اطلاعات حساس مانند کلمات عبور، شماره کارت اعتباری یا داده های شخصی را وارد نکنید و زمینه های قرمز سازی را در ساخت های تولید در نظر بگیرید.

سازگاری در سراسر ماژول ها

یک کنوانسیون ورود به شرکت ایجاد کنید.از پیشوندهای ماژول منحصر به فرد (به عنوان مثال، [FLT 29]، و یک فرمت استاندارد (UTC برای سیستم های توزیع شده ترجیح داده می شود) فرمت log را مستند کنید تا تیم های عملیاتی بتوانند آن را به طور قابل اعتماد تجزیه کنند.

نتیجه گیری

یک سیستم ورود سفارشی در C انعطاف پذیری و عملکرد مورد نیاز برای برنامه های بزرگ مقیاس (LT 2: شروع با یک طراحی جامد که سطوح ورود، سینک، ایمنی رشته و قالب بندی را پوشش می دهد، شما می توانید به تدریج ویژگی های پیشرفته مانند خروجی همگام، logs ساختار یافته و ارسال از راه دور را اضافه کنید. مثال در این مقاله به عنوان یک پایه خدمت می کند - آنها را به محیط خاص خود اختصاص دهید و همیشه با استفاده از کتابخانه دقیق تر از یک انسان برای انتقال قوی تر مشورت می کند.