Skip to content

Instantly share code, notes, and snippets.

@kigster
Last active July 30, 2024 19:04
Show Gist options
  • Save kigster/ef013c457801af0a9b6bd99f34bf5769 to your computer and use it in GitHub Desktop.
Save kigster/ef013c457801af0a9b6bd99f34bf5769 to your computer and use it in GitHub Desktop.
Ruby pattern for including a logging module everywhere you want. Just add “include Logging” at the top and you get both class and instance methods for logging such as #info, #debug, #error, #warn, #fatal. The threading at the bottom shows that the Logger is thread-safe and can be used without Mutexes.
# (c) 2024 Konstantin Gredeskoul
# Under the MIT License
#
# This gist documents a frequently thed pattern of decorating multiple classes with Logging methods
# by sharing just one Logger typically at the top of the module namespace (Application) in this case.
# If the concurrency issues come up, each class can be given its own logger, or perhaps the Logging moduloe
# might have decorators for each of the logging methods (eg #info, #debug) by using a Mutex.
#
# The output of this Ruby Code should be as follows:
#
# I, [2024-07-30T19:00:40.887991 #1] INFO -- : Appplication Starting
# I, [2024-07-30T19:00:40.889128 #1] INFO -- : CLASS | TestClass [ $TestClass ] finished loading
# I, [2024-07-30T19:00:40.889204 #1] INFO -- : INSTANCE | TestClass [ #<TestClass:0x00007f7e093cd3c0> ] : #initialize
# E, [2024-07-30T19:00:40.889245 #1] ERROR -- : CLASS | TestClass [ main | logging
# I, [2024-07-30T19:00:40.994221 #1] INFO -- : Thread 1
# I, [2024-07-30T19:00:40.994387 #1] INFO -- : Thread 27
# I, [2024-07-30T19:00:40.994444 #1] INFO -- : Thread 28
# I, [2024-07-30T19:00:40.994492 #1] INFO -- : Thread 15
# I, [2024-07-30T19:00:40.994569 #1] INFO -- : Thread 5
# I, [2024-07-30T19:00:40.994697 #1] INFO -- : Thread 4
# I, [2024-07-30T19:00:40.994755 #1] INFO -- : Thread 0
# I, [2024-07-30T19:00:40.994874 #1] INFO -- : Thread 23
# I, [2024-07-30T19:00:40.994952 #1] INFO -- : Thread 24
# I, [2024-07-30T19:00:40.995027 #1] INFO -- : Thread 37
# I, [2024-07-30T19:00:40.995076 #1] INFO -- : Thread 35
# I, [2024-07-30T19:00:40.995130 #1] INFO -- : Thread 7
# I, [2024-07-30T19:00:40.995177 #1] INFO -- : Thread 30
# I, [2024-07-30T19:00:40.995541 #1] INFO -- : Thread 6
# I, [2024-07-30T19:00:40.995859 #1] INFO -- : Thread 33
# I, [2024-07-30T19:00:40.995938 #1] INFO -- : Thread 36
# I, [2024-07-30T19:00:40.995988 #1] INFO -- : Thread 34
# I, [2024-07-30T19:00:40.997059 #1] INFO -- : Thread 12
# I, [2024-07-30T19:00:40.997150 #1] INFO -- : Thread 8
# I, [2024-07-30T19:00:40.997210 #1] INFO -- : Thread 10
# I, [2024-07-30T19:00:40.997253 #1] INFO -- : Thread 14
# I, [2024-07-30T19:00:40.997295 #1] INFO -- : Thread 21
# I, [2024-07-30T19:00:40.997338 #1] INFO -- : Thread 16
# I, [2024-07-30T19:00:40.997379 #1] INFO -- : Thread 20
# I, [2024-07-30T19:00:40.997418 #1] INFO -- : Thread 19
# I, [2024-07-30T19:00:40.997492 #1] INFO -- : Thread 18
# I, [2024-07-30T19:00:40.997538 #1] INFO -- : Thread 17
# I, [2024-07-30T19:00:40.997588 #1] INFO -- : Thread 22
# I, [2024-07-30T19:00:40.997631 #1] INFO -- : Thread 13
# I, [2024-07-30T19:00:40.997706 #1] INFO -- : Thread 3
# I, [2024-07-30T19:00:40.998416 #1] INFO -- : Thread 32
# I, [2024-07-30T19:00:40.998491 #1] INFO -- : Thread 26
# I, [2024-07-30T19:00:40.998559 #1] INFO -- : Thread 29
# I, [2024-07-30T19:00:40.998644 #1] INFO -- : Thread 38
# I, [2024-07-30T19:00:40.998686 #1] INFO -- : Thread 31
# I, [2024-07-30T19:00:40.998766 #1] INFO -- : Thread 2
# I, [2024-07-30T19:00:40.998819 #1] INFO -- : Thread 25
# I, [2024-07-30T19:00:40.998861 #1] INFO -- : Thread 11
# I, [2024-07-30T19:00:40.998902 #1] INFO -- : Thread 9
# I, [2024-07-30T19:00:40.998982 #1] INFO -- : Thread 39
require 'forwardable'
require 'logger'
# Lets clear the screen
puts `reset` rescue nil
module Application
class << self
attr_accessor :logger
end
end
Application.logger = ::Logger.new($stdout)
Application.logger.info("Appplication Starting\n")
module Application
# Globals such as the logger
module Logging
module InstanceMethods
extend Forwardable
def_delegators :logger, :debug, :info, :warn, :error, :fatal
def logger
::Application.logger
end
end
class << self
def included(base)
base.include(InstanceMethods)
base.extend(InstanceMethods)
end
end
end
end
# This is our test clienbt
class TestClass
include Application::Logging
def initialize
info("INSTANCE | TestClass [ #{self} ] : #initialize")
end
# And at the class level
info("CLASS | TestClass [ $#{self} ] finished loading")
end
# This shoulod print the instance logger
instance = TestClass.new
# This should print the class level error
TestClass.error("CLASS | TestClass [ #{self} | logging")
if (instance.logger != TestClass.logger) ||
(TestClass.logger != Application.logger) ||
(TestClass.logger.nil? || instance.logger.nil?)
raise(ArgumentError, "The loggers are not identical.")
end
require 'thread'
threads = []
40.times do |index|
threads << Thread.new do
sleep 0.1
instance.info("Thread #{index}")
end
end
threads.each(&:join)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment