Setiap kali Loggermemanggil objek putsAnda MultiIO, itu akan menulis ke keduanya STDOUTdan file log Anda.
Sunting: Saya melanjutkan dan menemukan sisa antarmuka. Perangkat log harus merespons writedan close(tidak puts). Selama MultiIOmenanggapi itu dan memproksinya ke objek IO yang sebenarnya, ini harus berfungsi.
jika Anda melihat ctor logger Anda akan melihat bahwa ini akan mengacaukan rotasi log. def initialize(log = nil, opt = {}) @dev = @filename = @shift_age = @shift_size = nil @mutex = LogDeviceMutex.new if log.respond_to?(:write) and log.respond_to?(:close) @dev = log else @dev = open_logfile(log) @dev.sync = true @filename = log @shift_age = opt[:shift_age] || 7 @shift_size = opt[:shift_size] || 1048576 end end
JeffCharter
3
Catatan di Ruby 2.2, @targets.each(&:close)disusutkan.
xis
Bekerja untuk saya sampai saya menyadari bahwa saya perlu secara berkala memanggil: tutup pada log_file agar log_file memperbarui apa yang telah dicatat logger (pada dasarnya "simpan"). STDOUT tidak suka: hampir dipanggil, semacam mengalahkan ide MultoIO. Menambahkan peretasan untuk melewati: tutup kecuali untuk File kelas, tetapi berharap saya memiliki solusi yang lebih elegan.
Kim Miller
48
Solusi @ David sangat bagus. Saya telah membuat kelas delegator generik untuk beberapa target berdasarkan kodenya.
require 'logger'classMultiDelegatordef initialize(*targets)@targets= targets
enddefself.delegate(*methods)
methods.each do|m|
define_method(m)do|*args|@targets.map {|t| t.send(m,*args)}endendselfendclass<<selfalias to new
endend
log_file =File.open("debug.log","a")
log =Logger.new MultiDelegator.delegate(:write,:close).to(STDOUT, log_file)
Bisakah Anda menjelaskan, bagaimana ini lebih baik atau apa kegunaan yang disempurnakan dari pendekatan ini daripada yang biasa disarankan oleh David
Manish Sapariya
5
Ini pemisahan perhatian. MultiDelegator hanya tahu tentang mendelegasikan panggilan ke beberapa target. Fakta bahwa perangkat logging memerlukan metode tulis dan tutup diterapkan di pemanggil. Ini membuat MultiDelegator dapat digunakan dalam situasi selain logging.
jonas054
Solusi bagus. Saya mencoba menggunakan ini untuk mengirim output dari tugas menyapu saya ke file log. Untuk membuatnya berfungsi dengan put meskipun (agar dapat memanggil $ stdout.puts tanpa mendapatkan "metode pribadi` put 'dipanggil "), saya harus menambahkan beberapa metode lagi: log_file = File.open (" tmp / rake.log "," a ") $ stdout = MultiDelegator.delegate (: write,: close,: put,: print) .to (STDOUT, log_file) Akan lebih baik jika memungkinkan untuk membuat kelas Tee yang diwarisi dari MultiDelegator, seperti yang dapat Anda lakukan dengan kelas Delegator di stdlib ...
Tyler Rick
Saya datang dengan implementasi seperti Delegator dari ini yang saya sebut DelegatorToAll. Dengan cara ini Anda tidak perlu membuat daftar semua metode yang ingin Anda delegasikan, karena ini akan mendelegasikan semua metode yang didefinisikan di kelas delegasi (IO): class Tee <DelegateToAllClass (IO) end $ stdout = Tee.new (STDOUT , File.open ("# { FILE } .log", "a")) Lihat gist.github.com/TylerRick/4990898 untuk detail selengkapnya.
Tyler Rick
1
Saya sangat menyukai solusi Anda, tetapi tidak baik sebagai delegator umum yang dapat digunakan berkali-kali karena setiap delegasi mencemari semua instance dengan metode baru. Saya memposting jawaban di bawah ( stackoverflow.com/a/36659911/123376 ) yang memperbaiki masalah ini. Saya memposting jawaban daripada edit karena mungkin mendidik untuk melihat perbedaan antara dua implementasi karena saya juga memposting contoh.
Ini didasarkan pada ActiveSupport, jadi jika Anda sudah memiliki ketergantungan itu, Anda bisa contoh extendapa pun ActiveSupport::Loggerseperti yang ditunjukkan di atas.
pembuat phillbaker
Terima kasih, ini sangat membantu.
Lucas
Saya pikir ini adalah jawaban yang paling sederhana dan efektif, meskipun saya memiliki beberapa keanehan menggunakan config.logger.extend()konfigurasi di dalam lingkungan saya. Sebaliknya, saya mengatur config.loggerke STDOUTdalam lingkungan saya, kemudian memperpanjang logger di penginisialisasi yang berbeda.
mattsch
14
Bagi yang suka sederhana:
log =Logger.new("| tee test.log")# note the pipe ( '|' )
log.info "hi"# will log to both STDOUT and test.log
log =Logger.new("test.log")
log.formatter = proc do|severity, datetime, progname, msg|
puts msg
msg
end
log.info "hi"# will log to both STDOUT and test.log
Saya sebenarnya menggunakan teknik ini untuk mencetak ke file log, layanan cloud logger (logentries) dan jika itu lingkungan dev - juga mencetak ke STDOUT.
"| tee test.log"akan menimpa keluaran lama, mungkin sebagai "| tee -a test.log"gantinya
fangxing
13
Meskipun saya cukup menyukai saran lainnya, saya menemukan bahwa saya memiliki masalah yang sama tetapi menginginkan kemampuan untuk memiliki tingkat logging yang berbeda untuk STDERR dan file.
Saya berakhir dengan strategi perutean yang multipleks di tingkat logger daripada di tingkat IO, sehingga setiap logger kemudian dapat beroperasi di tingkat log independen:
Saya paling suka solusi ini karena (1) sederhana, dan (2) mendorong Anda untuk menggunakan kembali kelas Logger Anda alih-alih mengasumsikan semuanya masuk ke file. Dalam kasus saya, saya ingin login ke STDOUT dan appender GELF untuk Graylog. Memiliki MultiLoggersuka yang dijelaskan @dsz sangat cocok. Terima kasih telah berbagi!
Eric Kramer
Menambahkan bagian untuk menangani pseudovariabel (setter / getter)
Eric Kramer
11
Anda juga dapat menambahkan fungsionalitas pencatatan beberapa perangkat langsung ke Logger:
logger =Logger.new(STDOUT)
logger.warn('This message goes to stdout')
logger.attach('logfile.txt')
logger.warn('This message goes both to stdout and logfile.txt')
logger.detach('logfile.txt')
logger.warn('This message goes just to stdout')
Berikut implementasi lainnya, terinspirasi oleh jawaban @ jonas054 .
Ini menggunakan pola yang mirip dengan Delegator. Dengan cara ini Anda tidak perlu membuat daftar semua metode yang ingin Anda delegasikan, karena ini akan mendelegasikan semua metode yang didefinisikan di salah satu objek target:
Jawaban @ jonas054 di atas bagus, tetapi mengotori MultiDelegatorkelas dengan setiap delegasi baru. Jika Anda menggunakan MultiDelegatorbeberapa kali, itu akan terus menambahkan metode ke kelas, yang tidak diinginkan. (Lihat di bawah untuk contoh)
Berikut adalah implementasi yang sama, tetapi menggunakan kelas anonim sehingga metode tidak mencemari kelas delegator.
classBetterMultiDelegatordefself.delegate(*methods)Class.new dodef initialize(*targets)@targets= targets
end
methods.each do|m|
define_method(m)do|*args|@targets.map {|t| t.send(m,*args)}endendclass<<selfalias to new
endend# new classend# delegateend
Berikut adalah contoh metode pencemaran dengan implementasi asli, berbeda dengan implementasi yang dimodifikasi:
tee =MultiDelegator.delegate(:write).to(STDOUT)
tee.respond_to?:write
# => true
tee.respond_to?:size
# => false
Semuanya bagus di atas. teememiliki writemetode, tetapi tidak ada sizemetode seperti yang diharapkan. Sekarang, pertimbangkan ketika kita membuat delegasi lain:
tee2 =MultiDelegator.delegate(:size).to("bar")
tee2.respond_to?:size
# => true
tee2.respond_to?:write
# => true !!!!! Bad
tee.respond_to?:size
# => true !!!!! Bad
Oh tidak, tee2menanggapi sizeseperti yang diharapkan, tetapi juga menanggapi writekarena delegasi pertama. Bahkan teesekarang menanggapi sizekarena metode pencemaran.
Bandingkan ini dengan solusi kelas anonim, semuanya seperti yang diharapkan:
require 'log4r'
LOGGER =Log4r::Logger.new('mylog')
LOGGER.outputters <<Log4r::StdoutOutputter.new('stdout')
LOGGER.outputters <<Log4r::FileOutputter.new('file',:filename =>'test.log')#attach to existing log-file
LOGGER.info('aa')#Writs on STDOUT and sends to file
Satu keuntungan: Anda juga dapat menentukan tingkat log yang berbeda untuk stdout dan file.
Saya pergi ke ide yang sama tentang "Mendelegasikan semua metode ke sub-elemen" yang sudah dieksplorasi orang lain, tetapi saya mengembalikan untuk masing-masing metode tersebut nilai kembalian dari panggilan terakhir metode tersebut. Jika saya tidak, itu rusak logger-colorsyang mengharapkan Integerdan peta kembali Array.
classMultiIOdefself.delegate_all
IO.methods.each do|m|
define_method(m)do|*args|
ret =nil@targets.each {|t| ret = t.send(m,*args)}
ret
endendenddef initialize(*targets)@targets= targets
MultiIO.delegate_all
endend
Ini akan mendelegasikan ulang setiap metode ke semua target, dan hanya mengembalikan nilai kembali dari panggilan terakhir.
Juga, jika Anda ingin warna, STDOUT atau STDERR harus diletakkan terakhir, karena hanya dua warna yang seharusnya menjadi keluaran. Tapi kemudian, itu juga akan menampilkan warna ke file Anda.
logger =Logger.new MultiIO.new(File.open("log/test.log",'w'), STDOUT)
logger.error "Roses are red"
logger.unknown "Violets are blue"
Satu cara lagi. Jika Anda menggunakan logging yang diberi tag dan juga memerlukan tag di file log lain, Anda dapat melakukannya dengan cara ini
# backported from rails4# config/initializers/active_support_logger.rbmoduleActiveSupportclassLogger<::Logger# Broadcasts logs to multiple loggers. Returns a module to be# `extended`'ed into other logger instances.defself.broadcast(logger)Module.new do
define_method(:add)do|*args,&block|
logger.add(*args,&block)super(*args,&block)end
define_method(:<<)do|x|
logger << x
super(x)end
define_method(:close)do
logger.close
super()end
define_method(:progname=)do|name|
logger.progname = name
super(name)end
define_method(:formatter=)do|formatter|
logger.formatter = formatter
super(formatter)end
define_method(:level=)do|level|
logger.level = level
super(level)endend# Module.newend# broadcastdef initialize(*args)super@formatter=SimpleFormatter.new
end# Simple formatter which only displays the message.classSimpleFormatter<::Logger::Formatter# This method is invoked when a log event occursdef call(severity, time, progname, msg)
element = caller[4]? caller[4].split("/").last :"UNDEFINED""#{Thread.current[:activesupport_tagged_logging_tags]||nil } # {time.to_s(:db)} #{severity} #{element} -- #{String === msg ? msg : msg.inspect}\n"endendend# class Loggerend# module ActiveSupport
custom_logger =ActiveSupport::Logger.new(Rails.root.join("log/alternative_#{Rails.env}.log"))Rails.logger.extend(ActiveSupport::Logger.broadcast(custom_logger))
Setelah ini Anda akan mendapatkan tag uuid di logger alternatif
["fbfea87d1d8cc101a4ff9d12461ae810"]2015-03-1216:54:04 INFO logger.rb:28:in`call_app' --
["fbfea87d1d8cc101a4ff9d12461ae810"] 2015-03-12 16:54:04 INFO logger.rb:31:in `call_app' -- Started POST "/psp/entrypoint" for 192.168.56.1 at 2015-03-12 16:54:04 +0700
Sederhana, andal, dan bekerja dengan sangat baik. Terima kasih! Perhatikan bahwa ActiveSupport::Loggerbekerja di luar kotak dengan ini - Anda hanya perlu menggunakan Rails.logger.extenddengan ActiveSupport::Logger.broadcast(...).
Saya suka pendekatan MultiIO . Ini bekerja dengan baik dengan Ruby Logger . Jika Anda menggunakan IO murni, ia berhenti bekerja karena tidak memiliki beberapa metode yang diharapkan dimiliki oleh objek IO. Pipa disebutkan sebelumnya di sini: Bagaimana saya bisa memiliki output log ruby logger ke stdout serta file? . Inilah yang paling cocok untuk saya.
def watch(cmd)
output =StringIO.new
IO.popen(cmd)do|fd|until fd.eof?
bit = fd.getc
output << bit
$stdout.putc bit
endend
output.rewind
[output.read, $?.success?]ensure
output.close
end
result, success = watch('./my/shell_command as a String')
Catatan Saya tahu ini tidak menjawab pertanyaan secara langsung tetapi sangat terkait. Setiap kali saya mencari output ke beberapa IO, saya menemukan utas ini. Jadi, saya harap ini juga berguna bagi Anda.
def delegator(*methods)Class.new dodef initialize(*targets)@targets= targets
end
methods.each do|m|
define_method(m)do|*args|@targets.map {|t| t.send(m,*args)}endendclass<<selfaliasfor new
endend# new classend# delegate
Ini memiliki semua manfaat yang sama seperti miliknya tanpa perlu pembungkus kelas luar. Ini adalah utilitas yang berguna untuk dimiliki dalam file ruby terpisah.
Gunakan sebagai one-liner untuk menghasilkan instance delegator seperti ini:
Jika Anda tidak keberatan menggunakannya ActiveSupport, saya akan sangat menyarankan untuk memeriksa ActiveSupport::Logger.broadcast, yang merupakan cara yang sangat baik dan ringkas untuk menambahkan tujuan log tambahan ke logger.
Faktanya, jika Anda menggunakan Rails 4+ (seperti dari commit ini ), Anda tidak perlu melakukan apa pun untuk mendapatkan perilaku yang diinginkan - setidaknya jika Anda menggunakan rails console. Setiap kali Anda menggunakan rails console, Rails secara otomatis meluas Rails.loggersedemikian rupa sehingga menghasilkan output ke tujuan file biasanya ( log/production.log, misalnya) dan STDERR:
Untuk beberapa alasan yang tidak diketahui dan disayangkan, metode ini tidak berdokumen tetapi Anda dapat merujuk ke kode sumber atau posting blog untuk mempelajari cara kerjanya atau melihat contoh.
Saya juga memiliki kebutuhan ini baru-baru ini, jadi saya menerapkan perpustakaan yang melakukan ini. Saya baru saja menemukan pertanyaan StackOverflow ini, jadi saya menaruhnya untuk siapa saja yang membutuhkannya: https://github.com/agis/multi_io .
Dibandingkan dengan solusi lain yang disebutkan di sini, ini berusaha untuk menjadi IOobjeknya sendiri, sehingga dapat digunakan sebagai pengganti drop-in untuk objek IO reguler lainnya (file, soket, dll.)
Meskipun demikian, saya belum mengimplementasikan semua metode IO standar, tetapi yang mengikuti semantik IO (misalnya, #writemengembalikan jumlah dari jumlah byte yang ditulis ke semua target IO yang mendasarinya).
| tee
sebelum file berhasil untuk saya, jadiLogger.new("| tee test.log")
. Perhatikan pipanya. Ini dari tip di coderwall.com/p/y_b3ra/…tee --append test.log
untuk mencegah penimpaan.Jawaban:
Anda dapat menulis
IO
kelas semu yang akan menulis ke banyakIO
objek. Sesuatu seperti:Kemudian atur itu sebagai file log Anda:
Setiap kali
Logger
memanggil objekputs
AndaMultiIO
, itu akan menulis ke keduanyaSTDOUT
dan file log Anda.Sunting: Saya melanjutkan dan menemukan sisa antarmuka. Perangkat log harus merespons
write
danclose
(tidakputs
). SelamaMultiIO
menanggapi itu dan memproksinya ke objek IO yang sebenarnya, ini harus berfungsi.sumber
def initialize(log = nil, opt = {}) @dev = @filename = @shift_age = @shift_size = nil @mutex = LogDeviceMutex.new if log.respond_to?(:write) and log.respond_to?(:close) @dev = log else @dev = open_logfile(log) @dev.sync = true @filename = log @shift_age = opt[:shift_age] || 7 @shift_size = opt[:shift_size] || 1048576 end end
@targets.each(&:close)
disusutkan.Solusi @ David sangat bagus. Saya telah membuat kelas delegator generik untuk beberapa target berdasarkan kodenya.
sumber
Jika Anda berada di Rails 3 atau 4, seperti yang ditunjukkan oleh posting blog ini , Rails 4 memiliki fungsi bawaan ini . Jadi Anda bisa melakukan:
Atau jika Anda menggunakan Rails 3, Anda dapat melakukan backport:
sumber
extend
apa punActiveSupport::Logger
seperti yang ditunjukkan di atas.config.logger.extend()
konfigurasi di dalam lingkungan saya. Sebaliknya, saya mengaturconfig.logger
keSTDOUT
dalam lingkungan saya, kemudian memperpanjang logger di penginisialisasi yang berbeda.Bagi yang suka sederhana:
sumber
Atau cetak pesan di pemformat Logger:
Saya sebenarnya menggunakan teknik ini untuk mencetak ke file log, layanan cloud logger (logentries) dan jika itu lingkungan dev - juga mencetak ke STDOUT.
sumber
"| tee test.log"
akan menimpa keluaran lama, mungkin sebagai"| tee -a test.log"
gantinyaMeskipun saya cukup menyukai saran lainnya, saya menemukan bahwa saya memiliki masalah yang sama tetapi menginginkan kemampuan untuk memiliki tingkat logging yang berbeda untuk STDERR dan file.
Saya berakhir dengan strategi perutean yang multipleks di tingkat logger daripada di tingkat IO, sehingga setiap logger kemudian dapat beroperasi di tingkat log independen:
sumber
MultiLogger
suka yang dijelaskan @dsz sangat cocok. Terima kasih telah berbagi!Anda juga dapat menambahkan fungsionalitas pencatatan beberapa perangkat langsung ke Logger:
Misalnya:
sumber
Berikut implementasi lainnya, terinspirasi oleh jawaban @ jonas054 .
Ini menggunakan pola yang mirip dengan
Delegator
. Dengan cara ini Anda tidak perlu membuat daftar semua metode yang ingin Anda delegasikan, karena ini akan mendelegasikan semua metode yang didefinisikan di salah satu objek target:Anda harus dapat menggunakan ini dengan Logger juga.
delegate_to_all.rb tersedia dari sini: https://gist.github.com/TylerRick/4990898
sumber
Cepat dan kotor (ref: https://coderwall.com/p/y_b3ra/log-to-stdout-and-a-file-at-the-same-time )
sumber
Jawaban @ jonas054 di atas bagus, tetapi mengotori
MultiDelegator
kelas dengan setiap delegasi baru. Jika Anda menggunakanMultiDelegator
beberapa kali, itu akan terus menambahkan metode ke kelas, yang tidak diinginkan. (Lihat di bawah untuk contoh)Berikut adalah implementasi yang sama, tetapi menggunakan kelas anonim sehingga metode tidak mencemari kelas delegator.
Berikut adalah contoh metode pencemaran dengan implementasi asli, berbeda dengan implementasi yang dimodifikasi:
Semuanya bagus di atas.
tee
memilikiwrite
metode, tetapi tidak adasize
metode seperti yang diharapkan. Sekarang, pertimbangkan ketika kita membuat delegasi lain:Oh tidak,
tee2
menanggapisize
seperti yang diharapkan, tetapi juga menanggapiwrite
karena delegasi pertama. Bahkantee
sekarang menanggapisize
karena metode pencemaran.Bandingkan ini dengan solusi kelas anonim, semuanya seperti yang diharapkan:
sumber
Apakah Anda dibatasi untuk logger standar?
Jika tidak, Anda dapat menggunakan log4r :
Satu keuntungan: Anda juga dapat menentukan tingkat log yang berbeda untuk stdout dan file.
sumber
Saya pergi ke ide yang sama tentang "Mendelegasikan semua metode ke sub-elemen" yang sudah dieksplorasi orang lain, tetapi saya mengembalikan untuk masing-masing metode tersebut nilai kembalian dari panggilan terakhir metode tersebut. Jika saya tidak, itu rusak
logger-colors
yang mengharapkanInteger
dan peta kembaliArray
.Ini akan mendelegasikan ulang setiap metode ke semua target, dan hanya mengembalikan nilai kembali dari panggilan terakhir.
Juga, jika Anda ingin warna, STDOUT atau STDERR harus diletakkan terakhir, karena hanya dua warna yang seharusnya menjadi keluaran. Tapi kemudian, itu juga akan menampilkan warna ke file Anda.
sumber
Saya telah menulis RubyGem kecil yang memungkinkan Anda melakukan beberapa hal ini:
Anda dapat menemukan kode di github: teerb
sumber
Satu cara lagi. Jika Anda menggunakan logging yang diberi tag dan juga memerlukan tag di file log lain, Anda dapat melakukannya dengan cara ini
Setelah ini Anda akan mendapatkan tag uuid di logger alternatif
Harapan yang membantu seseorang.
sumber
ActiveSupport::Logger
bekerja di luar kotak dengan ini - Anda hanya perlu menggunakanRails.logger.extend
denganActiveSupport::Logger.broadcast(...)
.Satu opsi lagi ;-)
sumber
Saya suka pendekatan MultiIO . Ini bekerja dengan baik dengan Ruby Logger . Jika Anda menggunakan IO murni, ia berhenti bekerja karena tidak memiliki beberapa metode yang diharapkan dimiliki oleh objek IO. Pipa disebutkan sebelumnya di sini: Bagaimana saya bisa memiliki output log ruby logger ke stdout serta file? . Inilah yang paling cocok untuk saya.
Catatan Saya tahu ini tidak menjawab pertanyaan secara langsung tetapi sangat terkait. Setiap kali saya mencari output ke beberapa IO, saya menemukan utas ini. Jadi, saya harap ini juga berguna bagi Anda.
sumber
Ini adalah penyederhanaan dari solusi @ rado.
Ini memiliki semua manfaat yang sama seperti miliknya tanpa perlu pembungkus kelas luar. Ini adalah utilitas yang berguna untuk dimiliki dalam file ruby terpisah.
Gunakan sebagai one-liner untuk menghasilkan instance delegator seperti ini:
ATAU gunakan sebagai pabrik seperti:
sumber
Anda dapat menggunakan
Loog::Tee
objek dariloog
permata:Persis apa yang Anda cari.
sumber
Jika Anda tidak keberatan menggunakannya
ActiveSupport
, saya akan sangat menyarankan untuk memeriksaActiveSupport::Logger.broadcast
, yang merupakan cara yang sangat baik dan ringkas untuk menambahkan tujuan log tambahan ke logger.Faktanya, jika Anda menggunakan Rails 4+ (seperti dari commit ini ), Anda tidak perlu melakukan apa pun untuk mendapatkan perilaku yang diinginkan - setidaknya jika Anda menggunakan
rails console
. Setiap kali Anda menggunakanrails console
, Rails secara otomatis meluasRails.logger
sedemikian rupa sehingga menghasilkan output ke tujuan file biasanya (log/production.log
, misalnya) danSTDERR
:Untuk beberapa alasan yang tidak diketahui dan disayangkan, metode ini tidak berdokumen tetapi Anda dapat merujuk ke kode sumber atau posting blog untuk mempelajari cara kerjanya atau melihat contoh.
https://www.joshmcarthur.com/til/2018/08/16/logging-to-multiple-destinations-using-activesupport-4.html memiliki contoh lain:
sumber
Saya juga memiliki kebutuhan ini baru-baru ini, jadi saya menerapkan perpustakaan yang melakukan ini. Saya baru saja menemukan pertanyaan StackOverflow ini, jadi saya menaruhnya untuk siapa saja yang membutuhkannya: https://github.com/agis/multi_io .
Dibandingkan dengan solusi lain yang disebutkan di sini, ini berusaha untuk menjadi
IO
objeknya sendiri, sehingga dapat digunakan sebagai pengganti drop-in untuk objek IO reguler lainnya (file, soket, dll.)Meskipun demikian, saya belum mengimplementasikan semua metode IO standar, tetapi yang mengikuti semantik IO (misalnya,
#write
mengembalikan jumlah dari jumlah byte yang ditulis ke semua target IO yang mendasarinya).sumber
Saya pikir STDOUT Anda digunakan untuk info runtime kritis dan kesalahan yang diangkat.
Jadi saya gunakan
untuk log debug dan logging biasa, dan kemudian menulis beberapa
di mana saya perlu melihat informasi STDOUT bahwa skrip saya berjalan sama sekali!
Bah, hanya 10 sen saya :-)
sumber