Logging

يوفر الوحدة Logging وسيلة لتسجيل تاريخ وتقدم عملية حسابية كسجل للأحداث. يتم إنشاء الأحداث عن طريق إدراج عبارة تسجيل في الشيفرة المصدرية، على سبيل المثال:

@warn "Abandon printf debugging, all ye who enter here!"
┌ Warning: Abandon printf debugging, all ye who enter here!
└ @ Main REPL[1]:1

يوفر النظام عدة مزايا على إضافة استدعاءات لـ println() في شفرة المصدر الخاصة بك. أولاً، يتيح لك التحكم في رؤية الرسائل وعرضها دون الحاجة إلى تعديل شفرة المصدر. على سبيل المثال، على عكس @warn أعلاه

@debug "The sum of some values $(sum(rand(100)))"

لن ينتج أي مخرجات بشكل افتراضي. علاوة على ذلك، من الرخيص جدًا ترك بيانات التصحيح مثل هذه في الشيفرة المصدرية لأن النظام يتجنب تقييم الرسالة إذا كان من المقرر تجاهلها لاحقًا. في هذه الحالة، لن يتم تنفيذ sum(rand(100)) ومعالجة السلسلة المرتبطة بها أبدًا ما لم يتم تمكين تسجيل الأخطاء.

ثانيًا، تتيح لك أدوات التسجيل إرفاق بيانات عشوائية بكل حدث كمجموعة من أزواج المفتاح–القيمة. وهذا يسمح لك بالتقاط المتغيرات المحلية وحالة البرنامج الأخرى للتحليل لاحقًا. على سبيل المثال، لإرفاق المتغير المحلي للمصفوفة A ومجموع المتجه v كمفتاح s يمكنك استخدام

A = ones(Int, 4, 4)
v = ones(100)
@info "Some variables"  A  s=sum(v)

# output
┌ Info: Some variables
│   A =
│    4×4 Matrix{Int64}:
│     1  1  1  1
│     1  1  1  1
│     1  1  1  1
│     1  1  1  1
└   s = 100.0

تشارك جميع ماكروهات التسجيل @debug و @info و @warn و @error ميزات شائعة يتم وصفها بالتفصيل في الوثائق الخاصة بالماكرو الأكثر عمومية @logmsg.

Log event structure

كل حدث يولد عدة قطع من البيانات، بعضها مقدمة من المستخدم وبعضها مستخرج تلقائيًا. دعونا نفحص البيانات المحددة من قبل المستخدم أولاً:

  • مستوى السجل هو فئة واسعة للرسالة التي تُستخدم للتصفية المبكرة. هناك عدة مستويات قياسية من النوع LogLevel؛ كما أن المستويات المعرفة من قبل المستخدم ممكنة أيضًا. كل منها مميز في الغرض:

    • Logging.Debug (مستوى السجل -1000) هو معلومات موجهة لمطور البرنامج. هذه الأحداث معطلة بشكل افتراضي.
    • Logging.Info (مستوى السجل 0) هو للمعلومات العامة للمستخدم. اعتبره بديلاً لاستخدام println مباشرة.
    • Logging.Warn (مستوى السجل 1000) يعني أن هناك شيئًا خاطئًا ومن المحتمل أن تكون هناك حاجة إلى إجراء، ولكن حتى الآن لا يزال البرنامج يعمل.
    • Logging.Error (مستوى السجل 2000) يعني أن هناك شيئًا خاطئًا ومن غير المحتمل أن يتم استرداده، على الأقل من خلال هذا الجزء من الشيفرة. غالبًا ما يكون هذا المستوى من السجل غير ضروري حيث يمكن أن ينقل رمي استثناء جميع المعلومات المطلوبة.
  • الـ رسالة هي كائن يصف الحدث. وفقًا للتقاليد، يُفترض أن تكون AbstractStrings المرسلة كرسائل بتنسيق ماركداون. سيتم عرض الأنواع الأخرى باستخدام print(io, obj) أو string(obj) للإخراج النصي، وربما show(io,mime,obj) لعرض الوسائط المتعددة الأخرى المستخدمة في المسجل المثبت.

  • تسمح أزواج المفتاح–القيمة الاختيارية بإرفاق بيانات عشوائية بكل حدث. بعض المفاتيح لها معاني تقليدية يمكن أن تؤثر على كيفية تفسير الحدث (انظر @logmsg).

ينشئ النظام أيضًا بعض المعلومات القياسية لكل حدث:

  • الوحدة التي تم فيها توسيع ماكرو السجل.
  • ملف و سطر حيث يحدث ماكرو التسجيل في شفرة المصدر.
  • معرف id هو معرف فريد وثابت ل عبارة كود المصدر حيث يظهر ماكرو التسجيل. تم تصميم هذا المعرف ليكون مستقرًا إلى حد ما حتى لو تغير كود المصدر في الملف، طالما أن عبارة التسجيل نفسها تظل كما هي.
  • مجموعة group للحدث، والتي يتم تعيينها إلى الاسم الأساسي للملف بشكل افتراضي، بدون امتداد. يمكن استخدام ذلك لتجميع الرسائل في فئات بشكل أكثر دقة من مستوى السجل (على سبيل المثال، جميع تحذيرات الإهمال لها مجموعة :depwarn)، أو في تجميعات منطقية عبر أو داخل الوحدات.

لاحظ أن بعض المعلومات المفيدة مثل وقت الحدث غير مدرجة بشكل افتراضي. وذلك لأن مثل هذه المعلومات قد تكون مكلفة للاستخراج وهي أيضًا متاحة ديناميكيًا للمسجل الحالي. من السهل تعريف custom logger لتعزيز بيانات الحدث بالوقت، وتتبع الأخطاء، وقيم المتغيرات العالمية وغيرها من المعلومات المفيدة حسب الحاجة.

Processing log events

كما ترى في الأمثلة، فإن عبارات السجل لا تذكر أين تذهب أحداث السجل أو كيف تتم معالجتها. هذه ميزة تصميم رئيسية تجعل النظام قابلاً للتكوين وطبيعيًا للاستخدام المتزامن. يقوم بذلك من خلال فصل قضيتين مختلفتين:

  • إنشاء أحداث السجل هو من اهتمامات مؤلف الوحدة الذي يحتاج إلى تحديد مكان حدوث الأحداث والمعلومات التي يجب تضمينها.
  • معالجة أحداث السجل — أي العرض، والتصفية، والتجميع، والتسجيل — هي من اهتمامات مؤلف التطبيق الذي يحتاج إلى دمج عدة وحدات معًا في تطبيق متعاون.

Loggers

يتم تنفيذ معالجة الأحداث بواسطة مسجل، وهو أول قطعة من الشيفرة القابلة للتكوين من قبل المستخدم لرؤية الحدث. يجب أن تكون جميع المسجلات من الأنواع الفرعية لـ AbstractLogger.

عند حدوث حدث، يتم العثور على السجل المناسب من خلال البحث عن سجل محلي للمهام مع سجل عالمي كخيار احتياطي. الفكرة هنا هي أن كود التطبيق يعرف كيف يجب معالجة أحداث السجل ويكون موجودًا في مكان ما في أعلى مكدس الاستدعاء. لذا يجب علينا البحث لأعلى من خلال مكدس الاستدعاء لاكتشاف السجل — أي أن السجل يجب أن يكون محددًا ديناميكيًا. (هذه نقطة تباين مع أطر السجل حيث يكون السجل محددًا نحويًا؛ يتم توفيره بشكل صريح من قبل مؤلف الوحدة أو كمتغير عالمي بسيط. في مثل هذا النظام، يكون من المحرج التحكم في السجل أثناء تجميع الوظائف من وحدات متعددة.)

يمكن تعيين السجل العالمي باستخدام global_logger، ويتم التحكم في سجلات المهام المحلية باستخدام with_logger. المهام التي تم إنشاؤها حديثًا ترث السجل من المهمة الأصلية.

يوجد ثلاثة أنواع من المسجلات مقدمة من المكتبة. ConsoleLogger هو المسجل الافتراضي الذي تراه عند بدء REPL. يعرض الأحداث بتنسيق نصي قابل للقراءة ويحاول تقديم تحكم بسيط ولكن سهل الاستخدام في التنسيق والتصفية. NullLogger هو وسيلة مريحة للتخلص من جميع الرسائل عند الضرورة؛ إنه المعادل للتسجيل لـ devnull التدفق. SimpleLogger هو مسجل بتنسيق نصي بسيط للغاية، مفيد بشكل أساسي لتصحيح نظام التسجيل نفسه.

يجب أن تأتي سجلات مخصصة مع تحميلات للوظائف الموصوفة في reference section.

Early filtering and message handling

عند حدوث حدث، تحدث بعض خطوات التصفية المبكرة لتجنب توليد رسائل سيتمdiscardها:

  1. يتم التحقق من مستوى سجل الرسائل مقابل مستوى الحد الأدنى العالمي (المحدد عبر disable_logging). هذه إعداد عالمي بدائي ولكنه رخيص للغاية.
  2. يتم البحث عن حالة السجل الحالية والتحقق من مستوى الرسالة مقابل الحد الأدنى المخزن مؤقتًا للسجل، كما هو موجود من خلال استدعاء Logging.min_enabled_level. يمكن تجاوز هذا السلوك عبر متغيرات البيئة (المزيد عن ذلك لاحقًا).
  3. تتم استدعاء دالة Logging.shouldlog مع السجل الحالي، مع أخذ بعض المعلومات الأساسية (المستوى، الوحدة، المجموعة، المعرف) التي يمكن حسابها بشكل ثابت. بشكل أكثر فائدة، يتم تمرير shouldlog حدث id الذي يمكن استخدامه للتخلص من الأحداث مبكرًا بناءً على دالة شرطية مخزنة.

إذا اجتاز كل هذه الفحوصات، يتم تقييم الرسالة وأزواج المفتاح–القيمة بالكامل وتمريرها إلى المسجل الحالي عبر دالة Logging.handle_message. قد تقوم handle_message() بإجراء تصفية إضافية حسب الحاجة وعرض الحدث على الشاشة، أو حفظه في ملف، إلخ.

يتم التقاط وتسجيل الاستثناءات التي تحدث أثناء إنشاء حدث السجل بشكل افتراضي. يمنع ذلك الأحداث المعطلة الفردية من تعطل التطبيق، وهو مفيد عند تمكين أحداث التصحيح القليلة الاستخدام في نظام الإنتاج. يمكن تخصيص هذا السلوك لكل نوع من أنواع المسجلين عن طريق تمديد Logging.catch_exceptions.

Testing log events

تعتبر أحداث السجل نتيجة جانبية لتشغيل الكود العادي، ولكن قد تجد نفسك ترغب في اختبار رسائل معلوماتية وتحذيرات معينة. يوفر لك وحدة Test ماكرو @test_logs الذي يمكن استخدامه لمطابقة الأنماط ضد تدفق أحداث السجل.

Environment variables

يمكن أن يتأثر تصفية الرسائل من خلال متغير البيئة JULIA_DEBUG، ويعمل كطريقة سهلة لتمكين تسجيل الأخطاء لملف أو وحدة. تحميل جوليا مع JULIA_DEBUG=loading سيفعل رسائل سجل @debug في loading.jl. على سبيل المثال، في قذائف لينكس:

$ JULIA_DEBUG=loading julia -e 'using OhMyREPL'
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji due to it containing an incompatible cache header
└ @ Base loading.jl:1328
[ Info: Recompiling stale cache file /home/user/.julia/compiled/v0.7/OhMyREPL.ji for module OhMyREPL
┌ Debug: Rejecting cache file /home/user/.julia/compiled/v0.7/Tokenize.ji due to it containing an incompatible cache header
└ @ Base loading.jl:1328
...

على ويندوز، يمكن تحقيق نفس الشيء في CMD عن طريق تشغيل set JULIA_DEBUG="loading" أولاً وفي Powershell عبر $env:JULIA_DEBUG="loading".

بالمثل، يمكن استخدام متغير البيئة لتمكين تسجيل الأخطاء لوحدات معينة، مثل Pkg، أو جذور الوحدات (انظر Base.moduleroot). لتمكين جميع تسجيلات الأخطاء، استخدم القيمة الخاصة all.

لتفعيل تسجيل الأخطاء من REPL، قم بتعيين ENV["JULIA_DEBUG"] إلى اسم الوحدة المعنية. الوظائف المعرفة في REPL تنتمي إلى الوحدة Main؛ يمكن تفعيل التسجيل لها بهذه الطريقة:

julia> foo() = @debug "foo"
foo (generic function with 1 method)

julia> foo()

julia> ENV["JULIA_DEBUG"] = Main
Main

julia> foo()
┌ Debug: foo
└ @ Main REPL[1]:1

استخدم فاصلة لفصل لتمكين التصحيح لعدة وحدات: JULIA_DEBUG=loading,Main.

Examples

Example: Writing log events to a file

أحيانًا قد يكون من المفيد كتابة أحداث السجل إلى ملف. إليك مثال على كيفية استخدام مسجل محلي للمهمة ومسجل عالمي لكتابة المعلومات إلى ملف نصي:

# Load the logging module
julia> using Logging

# Open a textfile for writing
julia> io = open("log.txt", "w+")
IOStream(<file log.txt>)

# Create a simple logger
julia> logger = SimpleLogger(io)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())

# Log a task-specific message
julia> with_logger(logger) do
           @info("a context specific log message")
       end

# Write all buffered messages to the file
julia> flush(io)

# Set the global logger to logger
julia> global_logger(logger)
SimpleLogger(IOStream(<file log.txt>), Info, Dict{Any,Int64}())

# This message will now also be written to the file
julia> @info("a global log message")

# Close the file
julia> close(io)

Example: Enable debug-level messages

هنا مثال على إنشاء ConsoleLogger الذي يسمح بمرور أي رسائل بمستوى سجل أعلى من، أو يساوي، Logging.Debug.

julia> using Logging

# Create a ConsoleLogger that prints any log messages with level >= Debug to stderr
julia> debuglogger = ConsoleLogger(stderr, Logging.Debug)

# Enable debuglogger for a task
julia> with_logger(debuglogger) do
           @debug "a context specific log message"
       end

# Set the global logger
julia> global_logger(debuglogger)

Reference

Logging module

Logging.LoggingModule

أدوات لالتقاط وتصفية وعرض تدفقات أحداث السجل. عادةً لا تحتاج إلى استيراد Logging لإنشاء أحداث السجل؛ لهذا فإن الماكرو القياسي للتسجيل مثل @info تم تصديره بالفعل بواسطة Base ومتاحة بشكل افتراضي.

source

Creating events

Logging.@logmsgMacro
@debug رسالة  [مفتاح=قيمة | قيمة ...]
@info  رسالة  [مفتاح=قيمة | قيمة ...]
@warn  رسالة  [مفتاح=قيمة | قيمة ...]
@error رسالة  [مفتاح=قيمة | قيمة ...]

@logmsg مستوى رسالة [مفتاح=قيمة | قيمة ...]

قم بإنشاء سجل تسجيل مع `رسالة` معلوماتية. لراحة المستخدم، تم تعريف أربعة ماكرو للتسجيل `@debug`، `@info`، `@warn` و `@error` والتي تسجل عند مستويات الشدة القياسية `Debug`، `Info`، `Warn` و `Error`. يسمح `@logmsg` بتعيين `مستوى` برمجياً إلى أي `LogLevel` أو أنواع مستويات تسجيل مخصصة.

يجب أن تكون `رسالة` تعبيرًا يتم تقييمه إلى سلسلة نصية تكون وصفًا قابلًا للقراءة البشرية لحدث التسجيل. وفقًا للتقاليد، سيتم تنسيق هذه السلسلة كـ markdown عند تقديمها.

تدعم القائمة الاختيارية من أزواج `مفتاح=قيمة` بيانات تعريف المستخدم العشوائية التي سيتم تمريرها إلى واجهة تسجيل السجلات كجزء من سجل التسجيل. إذا تم تقديم تعبير `قيمة` فقط، سيتم إنشاء مفتاح يمثل التعبير باستخدام [`Symbol`](@ref). على سبيل المثال، `x` تصبح `x=x`، و `foo(10)` تصبح `Symbol("foo(10)")=foo(10)`. لتفكيك قائمة من أزواج المفتاح والقيمة، استخدم بناء الجملة العادي للتفكيك، `@info "blah" kws...`.

هناك بعض المفاتيح التي تسمح بتجاوز بيانات السجل المولدة تلقائيًا:

  * `_module=mod` يمكن استخدامها لتحديد وحدة منشأ مختلفة من موقع مصدر الرسالة.
  * `_group=symbol` يمكن استخدامها لتجاوز مجموعة الرسالة (عادة ما يتم اشتقاقها من الاسم الأساسي لملف المصدر).
  * `_id=symbol` يمكن استخدامها لتجاوز معرف الرسالة الفريد المولد تلقائيًا. هذا مفيد إذا كنت بحاجة إلى ربط الرسائل المولدة عن كثب على خطوط مصدر مختلفة.
  * `_file=string` و `_line=integer` يمكن استخدامها لتجاوز موقع المصدر الظاهر لرسالة سجل.

هناك أيضًا بعض أزواج المفتاح والقيمة التي لها معنى تقليدي:

  * `maxlog=integer` يجب استخدامها كإشارة إلى الواجهة الخلفية بأن الرسالة يجب أن تُعرض لا أكثر من `maxlog` مرات.
  * `exception=ex` يجب استخدامها لنقل استثناء مع رسالة سجل، وغالبًا ما تستخدم مع `@error`. يمكن إرفاق تتبع مرتبط `bt` باستخدام الزوج `exception=(ex,bt)`.

# أمثلة

julia @debug "معلومات تصحيح مفصلة. غير مرئية بشكل افتراضي" @info "رسالة معلوماتية" @warn "كان هناك شيء غريب. يجب أن تولي اهتمامًا" @error "حدث خطأ غير قاتل"

x = 10 @info "بعض المتغيرات مرتبطة بالرسالة" x a=42.0

@debug begin sA = sum(A) "sum(A) = :sA عملية مكلفة، يتم تقييمها فقط عندما تعود shouldlog بـ true" end

for i=1:10000 @info "مع الواجهة الخلفية الافتراضية، سترى فقط (i = :i) عشر مرات" maxlog=10 @debug "Algorithm1" i progress=i/10000 end ```

source
Logging.LogLevelType
LogLevel(level)

شدة/وضوح سجل السجل.

يوفر مستوى السجل مفتاحًا يمكن من خلاله تصفية سجلات السجل المحتملة، قبل القيام بأي عمل آخر لبناء بنية بيانات سجل السجل نفسها.

أمثلة

julia> Logging.LogLevel(0) == Logging.Info
true
source

Processing events with AbstractLogger

يتم التحكم في معالجة الأحداث من خلال تجاوز الوظائف المرتبطة بـ AbstractLogger:

Methods to implementBrief description
Logging.handle_messageHandle a log event
Logging.shouldlogEarly filtering of events
Logging.min_enabled_levelLower bound for log level of accepted events
Optional methodsDefault definitionBrief description
Logging.catch_exceptionstrueCatch exceptions during event evaluation
Logging.AbstractLoggerType

يتحكم المسجل في كيفية تصفية سجلات السجل وإرسالها. عندما يتم إنشاء سجل، يكون المسجل هو أول قطعة من الشيفرة القابلة للتكوين من قبل المستخدم التي تقوم بفحص السجل وتقرر ماذا تفعل به.

source
Logging.handle_messageFunction
handle_message(logger, level, message, _module, group, id, file, line; key1=val1, ...)

سجل رسالة إلى logger عند level. الموقع المنطقي الذي تم فيه إنشاء الرسالة يتم تحديده بواسطة الوحدة _module و group؛ وموقع المصدر بواسطة file و line. id هو قيمة فريدة تعسفية (عادةً ما تكون Symbol) تُستخدم كمفتاح لتحديد عبارة السجل عند التصفية.

source
Logging.shouldlogFunction
shouldlog(logger, level, _module, group, id)

ارجع true عندما يقبل logger رسالة عند level، تم إنشاؤها لـ _module، group ومع معرف سجل فريد id.

source
Logging.min_enabled_levelFunction
min_enabled_level(logger)

إرجاع الحد الأدنى لمستوى التمكين لـ logger للتصفية المبكرة. أي أن مستوى السجل الذي يكون أقل أو يساوي جميع الرسائل التي يتم تصفيتها.

source
Logging.catch_exceptionsFunction
catch_exceptions(logger)

ارجع true إذا كان يجب على السجل التقاط الاستثناءات التي تحدث أثناء إنشاء سجل السجل. بشكل افتراضي، يتم التقاط الرسائل

بشكل افتراضي، يتم التقاط جميع الاستثناءات لمنع توليد رسائل السجل من تعطل البرنامج. هذا يسمح للمستخدمين بتبديل الوظائف القليلة الاستخدام - مثل تسجيل الأخطاء - في نظام الإنتاج بثقة.

إذا كنت ترغب في استخدام التسجيل كمسار تدقيق، يجب عليك تعطيل ذلك لنوع السجل الخاص بك.

source
Logging.disable_loggingFunction
disable_logging(level)

تعطيل جميع رسائل السجل عند مستويات السجل التي تساوي أو تقل عن level. هذه إعداد عالمي، يهدف إلى جعل تسجيل الأخطاء رخيصًا للغاية عند تعطيله.

أمثلة

Logging.disable_logging(Logging.Info) # تعطيل الأخطاء والمعلومات
source

Using Loggers

تثبيت الفاحص والتفتيش:

Logging.global_loggerFunction
global_logger()

إرجاع السجل العالمي، المستخدم لاستقبال الرسائل عندما لا يوجد سجل محدد للمهمة الحالية.

global_logger(logger)

تعيين السجل العالمي إلى logger، وإرجاع السجل العالمي السابق.

source
Logging.with_loggerFunction
with_logger(function, logger)

نفذ function، موجهًا جميع رسائل السجل إلى logger.

أمثلة

function test(x)
    @info "x = $x"
end

with_logger(logger) do
    test(1)
    test([1,2])
end
source
Logging.current_loggerFunction
current_logger()

إرجاع السجل للمهام الحالية، أو السجل العالمي إذا لم يكن هناك سجل مرتبط بالمهام.

source

سجلات يتم تزويدها مع النظام:

Logging.NullLoggerType
NullLogger()

مسجل يقوم بتعطيل جميع الرسائل ولا ينتج أي مخرجات - المعادل المسجّل لـ /dev/null.

source
Base.CoreLogging.ConsoleLoggerType
ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt,
              show_limited=true, right_justify=0)

مسجل مع تنسيق محسّن للقراءة في وحدة التحكم النصية، على سبيل المثال العمل التفاعلي مع REPL جوليا.

يتم تصفية مستويات السجل التي تقل عن min_level.

يمكن التحكم في تنسيق الرسائل عن طريق تعيين وسائط الكلمات الرئيسية:

  • meta_formatter هي دالة تأخذ بيانات تعريف حدث السجل (level, _module, group, id, file, line) وتعيد لونًا (كما سيتم تمريره إلى printstyled)، وبادئة ولاحقة لرسالة السجل. الافتراضي هو أن يتم البادئة بمستوى السجل ولاحقة تحتوي على الوحدة، والملف وموقع السطر.
  • show_limited يحد من طباعة الهياكل البيانية الكبيرة إلى شيء يمكن أن يتناسب مع الشاشة عن طريق تعيين مفتاح :limit في IOContext أثناء التنسيق.
  • right_justify هو العمود الصحيح الذي يتم فيه تبرير بيانات تعريف السجل. الافتراضي هو صفر (تذهب بيانات التعريف إلى سطرها الخاص).
source
Logging.SimpleLoggerType
SimpleLogger([stream,] min_level=Info)

مسجل بسيط لتسجيل جميع الرسائل بمستوى أكبر من أو يساوي min_level إلى stream. إذا كان stream مغلقًا، فسيتم تسجيل الرسائل بمستوى سجل أكبر أو يساوي Warn إلى stderr وما دونه إلى stdout.

source