Add bin and edit workflow
Gitea Actions Demo / Explore-Gitea-Actions (push) Failing after 9s

This commit is contained in:
2026-09-16 13:11:16 -06:00
parent c8ac4fcae5
commit 4cee170d66
17576 changed files with 895740 additions and 2 deletions
@@ -0,0 +1,229 @@
require 'monitor'
module Yell #:nodoc:
module Adapters #:nodoc:
# This class provides the basic interface for all allowed operations on any
# adapter implementation. Other adapters should inherit from it for the methods
# used by the {Yell::Logger}.
#
# Writing your own adapter is really simple. Inherit from the base class and use
# the `setup`, `write` and `close` methods. Yell requires the `write` method to be
# specified (`setup` and `close` are optional).
#
#
# The following example shows how to define a basic Adapter to format and print
# log events to STDOUT:
#
# class PutsAdapter < Yell::Adapters::Base
# include Yell::Formatter::Helpers
#
# setup do |options|
# self.format = options[:format]
# end
#
# write do |event|
# message = format.call(event)
#
# STDOUT.puts message
# end
# end
#
#
# After the Adapter has been written, we need to register it to Yell:
#
# Yell::Adapters.register :puts, PutsAdapter
#
# Now, we can use it like so:
#
# logger = Yell.new :puts
# logger.info "Hello World!"
class Base < Monitor
include Yell::Helpers::Base
include Yell::Helpers::Level
class << self
# Setup your adapter with this helper method.
#
# @example
# setup do |options|
# @file_handle = File.new( '/dev/null', 'w' )
# end
def setup( &block )
compile!(:setup!, &block)
end
# Define your write method with this helper.
#
# @example Printing messages to file
# write do |event|
# @file_handle.puts event.message
# end
def write( &block )
compile!(:write!, &block)
end
# Define your open method with this helper.
#
# @example Open a file handle
# open do
# @stream = ::File.open( 'test.log', ::File::WRONLY|::File::APPEND|::File::CREAT )
# end
def open( &block )
compile!(:open!, &block)
end
# Define your close method with this helper.
#
# @example Closing a file handle
# close do
# @stream.close
# end
def close( &block )
compile!(:close!, &block)
end
private
# Pretty funky code block, I know but here is what it basically does:
#
# @example
# compile! :write! do |event|
# puts event.message
# end
#
# # Is actually defining the `:write!` instance method with a call to super:
#
# def write!( event )
# puts event.method
# super
# end
def compile!( name, &block )
# Get the already defined method
m = instance_method( name )
# Create a new method with leading underscore
define_method("_#{name}", &block)
_m = instance_method("_#{name}")
remove_method("_#{name}")
# Define instance method
define!(name, _m, m, &block)
end
# Define instance method by given name and call the unbound
# methods in order with provided block.
def define!( name, _m, m, &block )
if block.arity == 0
define_method(name) do
_m.bind(self).call
m.bind(self).call
end
else
define_method(name) do |*args|
_m.bind(self).call(*args)
m.bind(self).call(*args)
end
end
end
end
# Initializes a new Adapter.
#
# You should not overload the constructor, use #setup instead.
def initialize( options = {}, &block )
super() # init the monitor superclass
reset!
setup!(options)
# eval the given block
block.arity > 0 ? block.call(self) : instance_eval(&block) if block_given?
end
# The main method for calling the adapter.
#
# The method receives the log `event` and determines whether to
# actually write or not.
def write( event )
synchronize { write!(event) } if write?(event)
rescue Exception => e
# make sure the adapter is closed and re-raise the exception
synchronize { close }
raise(e)
end
# Close the adapter (stream, connection, etc).
#
# Adapter classes should provide their own implementation
# of this method.
def close
close!
end
# Get a pretty string representation of the adapter, including
def inspect
inspection = inspectables.map { |m| "#{m}: #{send(m).inspect}" }
"#<#{self.class.name} #{inspection * ', '}>"
end
private
# Setup the adapter instance.
#
# Adapter classes should provide their own implementation
# of this method (if applicable).
def setup!( options )
self.level = Yell.__fetch__(options, :level)
end
# Perform the actual write.
#
# Adapter classes must provide their own implementation
# of this method.
def write!( event )
# Not implemented
end
# Perform the actual open.
#
# Adapter classes should provide their own implementation
# of this method.
def open!
# Not implemented
end
# Perform the actual close.
#
# Adapter classes should provide their own implementation
# of this method.
def close!
# Not implemented
end
# Determine whether to write at the given severity.
#
# @example
# write? Yell::Event.new( 'INFO', 'Hello Wold!' )
#
# @param [Yell::Event] event The log event
#
# @return [Boolean] true or false
def write?( event )
level.nil? || level.at?(event.level)
end
# Get an array of inspected attributes for the adapter.
def inspectables
[:level]
end
end
end
end
@@ -0,0 +1,192 @@
module Yell #:nodoc:
module Adapters #:nodoc:
# The +Datefile+ adapter is similar to the +File+ adapter. However, it
# rotates the file at midnight (by default).
class Datefile < Yell::Adapters::File
# The default date pattern, e.g. "19820114" (14 Jan 1982)
DefaultDatePattern = "%Y%m%d"
# Metadata
Header = lambda { |date, pattern| "# -*- #{date.iso8601} (#{date.to_f}) [#{pattern}] -*-" }
HeaderRegexp = /^# -\*- (.+) \((\d+\.\d+)\) \[(.+)\] -\*-$/
# The pattern to be used for the files
#
# @example
# date_pattern = "%Y%m%d" # default
# date_pattern = "%Y-week-%V"
attr_accessor :date_pattern
# Tell the adapter to create a symlink onto the currently
# active (timestamped) file. Upon rollover, the symlink is
# set to the newly created file, and so on.
#
# @example
# symlink = true
attr_accessor :symlink
# Set the amount of logfiles to keep when rolling over.
# By default, no files will be cleaned up.
#
# @example Keep the last 5 logfiles
# keep = 5
# keep = '10'
#
# @example Do not clean up any files
# keep = 0
attr_accessor :keep
# You can suppress the first line of the logfile that contains
# the metadata. This is important upon rollover, because on *nix
# systems, it is not possible to determine the creation time of a file,
# on the last access time. The header compensates this.
#
# @example
# header = false
attr_accessor :header
private
# @overload setup!( options )
def setup!( options )
self.header = Yell.__fetch__(options, :header, default: true)
self.date_pattern = Yell.__fetch__(options, :date_pattern, default: DefaultDatePattern)
self.keep = Yell.__fetch__(options, :keep, default: false)
self.symlink = Yell.__fetch__(options, :symlink, default: true)
@original_filename = ::File.expand_path(Yell.__fetch__(options, :filename, default: default_filename))
options[:filename] = @original_filename
@date = Time.now
@date_strftime = @date.strftime(date_pattern)
super
end
# @overload write!( event )
def write!( event )
# do nothing when not closing
return super unless close?
close
# exit when file ready present
return super if ::File.exist?(@filename)
header! if header?
symlink! if symlink?
cleanup! if cleanup?
super
end
# @overload close!
def close!
@filename = filename_for(@date)
super
end
# Determine whether to close the file handle or not.
#
# It is based on the `:date_pattern` (can be passed as option upon initialize).
# If the current time hits the pattern, it closes the file stream.
#
# @return [Boolean] true or false
def close?
_date = Time.now
_date_strftime = _date.strftime(date_pattern)
if @stream.nil? or _date_strftime != @date_strftime
@date, @date_strftime = _date, _date_strftime
return true
end
false
end
# Removes old logfiles of the same date pattern.
#
# By reading the header of the files that match the date pattern, the
# adapter determines whether to remove them or not. If no header is present,
# it makes the best guess by checking the last access time (which may result
# in false cleanups).
def cleanup!
files = Dir[ @original_filename.sub(/(\.\w+)?$/, ".*\\1") ].sort.select do |file|
_, pattern = header_from(file)
# Select if the date pattern is nil (no header info available within the file) or
# when the pattern matches.
pattern.nil? || pattern == self.date_pattern
end
::File.unlink( *files[0..-keep-1] )
end
# Cleanup old logfiles?
#
# @return [Boolean] true or false
def cleanup?
!!keep && keep.to_i > 0
end
# Symlink the current filename to the original one.
def symlink!
# do nothing, because symlink is already correct
return if ::File.symlink?(@original_filename) && ::File.readlink(@original_filename) == @filename
::File.unlink(@original_filename) if ::File.exist?(@original_filename) || ::File.symlink?(@original_filename)
::File.symlink(@filename, @original_filename)
end
# Symlink the original filename?
#
# @return [Boolean] true or false
def symlink?
!!symlink
end
# Write the header information into the file
def header!
stream.puts( Header.call(@date, date_pattern) )
end
# Write header into the file?
#
# @return [Boolean] true or false
def header?
!!header
end
# Sets the filename with the `:date_pattern` appended to it.
def filename_for( date )
@original_filename.sub(/(\.\w+)?$/, ".#{date.strftime(date_pattern)}\\1")
end
# Fetch the header form the file
def header_from( file )
if m = ::File.open(file, &:readline).match(HeaderRegexp)
# in case there is a Header present, we can just read from it
[ Time.at(m[2].to_f), m[3] ]
else
# In case there is no header: we need to take a good guess
#
# Since the pattern can not be determined, we will just return the Posix ctime.
# That is NOT the creatint time, so the value will potentially be wrong!
[::File.ctime(file), nil]
end
end
# @overload inspectables
def inspectables
super.concat %i[date_pattern header keep symlink]
end
end
end
end
@@ -0,0 +1,34 @@
module Yell #:nodoc:
module Adapters #:nodoc:
# The +File+ adapter is the most basic. As one would expect, it's used
# for logging into files.
class File < Yell::Adapters::Io
private
# @overload setup!( options )
def setup!( options )
@filename = ::File.expand_path(Yell.__fetch__(options, :filename, default: default_filename))
super
end
# @overload open!
def open!
@stream = ::File.open(@filename, ::File::WRONLY|::File::APPEND|::File::CREAT)
super
end
def default_filename #:nodoc:
logdir = ::File.expand_path("log")
::File.expand_path(::File.directory?(logdir) ? "#{logdir}/#{Yell.env}.log" : "#{Yell.env}.log")
end
end
end
end
@@ -0,0 +1,97 @@
module Yell #:nodoc:
module Adapters #:nodoc:
class Io < Yell::Adapters::Base
include Yell::Helpers::Formatter
# The possible unix log colors
TTYColors = {
0 => "\033[1;32m", # green
1 => "\033[0m", # normal
2 => "\033[1;33m", # yellow
3 => "\033[1;31m", # red
4 => "\033[1;35m", # magenta
5 => "\033[1;36m", # cyan
-1 => "\033[0m" # normal
}
# Sets the “sync mode” to true or false.
#
# When true (default), every log event is immediately written to the file.
# When false, the log event is buffered internally.
attr_accessor :sync
# Sets colored output on or off (default off)
#
# @example Enable colors
# colors = true
#
# @example Disable colors
# colors = false
attr_accessor :colors
# Shortcut to enable colors.
#
# @example
# colorize!
def colorize!; @colors = true; end
private
# @overload setup!( options )
def setup!( options )
@stream = nil
self.colors = Yell.__fetch__(options, :colors, default: false)
self.formatter = Yell.__fetch__(options, :format, :formatter)
self.sync = Yell.__fetch__(options, :sync, default: true)
super
end
# @overload write!( event )
def write!( event )
message = formatter.call(event)
# colorize if applicable
if colors and color = TTYColors[event.level]
message = color + message + TTYColors[-1]
end
stream.syswrite(message)
super
end
# @overload open!
def open!
@stream.sync = self.sync if @stream.respond_to?(:sync)
@stream.flush if @stream.respond_to?(:flush)
super
end
# @overload close!
def close!
@stream.close if @stream.respond_to?(:close)
@stream = nil
super
end
# The IO stream
#
# Adapter classes should provide their own implementation
# of this method.
def stream
synchronize { open! if @stream.nil?; @stream }
end
# @overload inspectables
def inspectables
super.concat [:formatter, :colors, :sync]
end
end
end
end
@@ -0,0 +1,30 @@
module Yell #:nodoc:
module Adapters #:nodoc:
class Stdout < Yell::Adapters::Io
private
# @overload open!
def open!
@stream = $stdout.clone
super
end
end
class Stderr < Yell::Adapters::Io
private
# @overload open!
def open!
@stream = $stderr.clone
super
end
end
end
end