Last active
July 30, 2024 19:04
-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# (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