Skip to content

Add type restrictions to Log directory #15777

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/log/backend.cr
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ abstract class Log::Backend
end

# :nodoc:
def dispatch(entry : Entry)
def dispatch(entry : Entry) : Nil
@dispatcher.dispatch entry, self
end
end
2 changes: 1 addition & 1 deletion src/log/broadcast_backend.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class Log::BroadcastBackend < Log::Backend
end

# :nodoc:
def single_backend?
def single_backend? : Tuple(Log::Backend, Log::Severity)?
if @backends.size == 1
@backends.first
end
Expand Down
4 changes: 2 additions & 2 deletions src/log/dispatch.cr
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class Log
module DirectDispatcher
extend Dispatcher

def self.dispatch(entry : Entry, backend : Backend)
def self.dispatch(entry : Entry, backend : Backend) : Nil
backend.write(entry)
end
end
Expand Down Expand Up @@ -72,7 +72,7 @@ class Log
end
end

def finalize
def finalize : Nil
close
end
end
Expand Down
14 changes: 7 additions & 7 deletions src/log/format.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Log

# Creates an instance of a `Log::Formatter` that calls
# the specified `Proc` for every entry
def self.new(&proc : (Log::Entry, IO) ->)
def self.new(&proc : (Log::Entry, IO) ->) : self
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.new methods are always returning self, making return type annotation IMO pretty redundant.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For documentation purpose, yes, but there are cases where the compiler doesn't always infer that a specific .new returns a self.

ProcFormatter.new proc
end
end
Expand Down Expand Up @@ -69,7 +69,7 @@ class Log
end

# Write a fixed string
def string(str) : Nil
def string(str : String) : Nil
@io << str
end

Expand All @@ -96,7 +96,7 @@ class Log
# Log.setup(:info, Log::IOBackend.new(formatter: TestFormatter))
# Log.for("foo.bar").info { "Hello" } # => - [foo.bar] Hello
# ```
def source(*, before = nil, after = nil)
def source(*, before : _ = nil, after : _ = nil) : Nil
if @entry.source.size > 0
@io << before << @entry.source << after
end
Expand All @@ -107,7 +107,7 @@ class Log
# It doesn't write any output if the entry data is empty.
# Parameters `before` and `after` can be provided to be written around
# the value.
def data(*, before = nil, after = nil) : Nil
def data(*, before : _ = nil, after : _ = nil) : Nil
unless @entry.data.empty?
@io << before << @entry.data << after
end
Expand All @@ -118,7 +118,7 @@ class Log
# It doesn't write any output if the context is empty.
# Parameters `before` and `after` can be provided to be written around
# the value.
def context(*, before = nil, after = nil)
def context(*, before : _ = nil, after : _ = nil) : Nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe we're on the side of preferring no type restriction instead of _ or forall if the type can't be restricted. See #15334

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the type is really unrestricted - like in this case - I think it makes a lot of sense to explicate that in the type restriction _.
Otherwise, it would be indiscernible from an unknown type restriction.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is also my preference, especially from a documentation perspective, but I know others on the team disagreed.

Another alternative proposed in that issue:

def context(*, before : B = nil, after : A = nil) : Nil forall A, B
end

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_ is more concise than forall, so I'd use that.

unless @entry.context.empty?
@io << before << @entry.context << after
end
Expand All @@ -130,7 +130,7 @@ class Log
# Parameters `before` and `after` can be provided to be written around
# the value. `before` defaults to `'\n'` so the exception is written
# on a separate line
def exception(*, before = '\n', after = nil) : Nil
def exception(*, before : _ = '\n', after : _ = nil) : Nil
if ex = @entry.exception
@io << before
ex.inspect_with_backtrace(@io)
Expand All @@ -149,7 +149,7 @@ class Log
end

# Write the `Log::Entry` to the `IO` using this pattern
def self.format(entry, io) : Nil
def self.format(entry : Log::Entry, io : IO) : Nil
new(entry, io).run
end

Expand Down
4 changes: 2 additions & 2 deletions src/log/log.cr
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class Log
end

# Change this log severity level filter.
def level=(value : Severity)
def level=(value : Severity) : Log::Severity
@level = value
if (backend = @backend).responds_to?(:level=)
backend.level = value
Expand All @@ -34,7 +34,7 @@ class Log
end

# :nodoc:
def backend=(value : Backend?)
def backend=(value : Backend?) : Backend?
@backend = value
end

Expand Down
8 changes: 4 additions & 4 deletions src/log/main.cr
Original file line number Diff line number Diff line change
Expand Up @@ -64,12 +64,12 @@ class Log
end

# Sets the current fiber logging context.
def self.context=(value : Log::Metadata)
def self.context=(value : Log::Metadata) : Log::Metadata
Fiber.current.logging_context = value
end

# :ditto:
def self.context=(value : Log::Context)
def self.context=(value : Log::Context) : Log::Metadata
# NOTE: There is a need for `Metadata` and `Context` setters in
# because `Log.context` returns a `Log::Context` for allowing DSL like `Log.context.set(a: 1)`
# but if the metadata is built manually the construct `Log.context = metadata` will be used.
Expand Down Expand Up @@ -162,12 +162,12 @@ class Log
# Log.context.set extra: h
# Log.info { %q(message with a: 1, b: 2, extra: {"c" => 3} context) }
# ```
def set(**kwargs)
def set(**kwargs) : Log::Metadata
extend_fiber_context(Fiber.current, kwargs)
end

# :ditto:
def set(values) : Nil
def set(values : Hash | NamedTuple) : Nil
extend_fiber_context(Fiber.current, values)
end

Expand Down
8 changes: 4 additions & 4 deletions src/log/metadata.cr
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class Log::Metadata
# @first needs to be the last ivar of Metadata. The entries are allocated together with self
@first = uninitialized Entry

def self.new(parent : Metadata? = nil, entries : NamedTuple | Hash = NamedTuple.new)
def self.new(parent : Metadata? = nil, entries : NamedTuple | Hash = NamedTuple.new) : self
data_size = instance_sizeof(self) + sizeof(Entry) * {entries.size + (parent.try(&.max_total_size) || 0) - 1, 0}.max
data = GC.malloc(data_size).as(self)
data.setup(parent, entries)
Expand Down Expand Up @@ -162,7 +162,7 @@ class Log::Metadata
nil
end

def ==(other : Metadata)
def ==(other : Metadata) : Bool
self_kv = self.to_a
other_kv = other.to_a

Expand Down Expand Up @@ -216,7 +216,7 @@ class Log::Metadata
end

# :nodoc:
def self.to_metadata_value(value) : Metadata::Value
def self.to_metadata_value(value : _) : Metadata::Value
value.is_a?(Value) ? value : Value.new(value)
end
end
Expand All @@ -227,7 +227,7 @@ class Fiber
getter logging_context : Log::Metadata { Log::Metadata.empty }

# :nodoc:
def logging_context=(value : Log::Metadata)
def logging_context=(value : Log::Metadata) : Log::Metadata
@logging_context = value
end
end
8 changes: 4 additions & 4 deletions src/log/setup.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class Log
def self.setup(sources : String = "*",
level : Log::Severity = Log::Severity::Info,
backend : Log::Backend = IOBackend.new,
*, builder : Log::Builder = Log.builder)
*, builder : Log::Builder = Log.builder) : Nil
Log.setup(builder: builder) do |c|
sources.split(',', remove_empty: false) do |source|
source = source.strip
Expand All @@ -29,9 +29,9 @@ class Log
# Setups logging based on `LOG_LEVEL` environment variable.
def self.setup_from_env(*, builder : Log::Builder = Log.builder,
default_level : Log::Severity = Log::Severity::Info,
default_sources = "*",
log_level_env = "LOG_LEVEL",
backend = Log::IOBackend.new)
default_sources : String = "*",
log_level_env : String = "LOG_LEVEL",
backend : Log::Backend = Log::IOBackend.new) : Nil
level = ENV[log_level_env]?.try { |v| Log::Severity.parse(v) } || default_level

Log.setup(default_sources, level, backend, builder: builder)
Expand Down
12 changes: 6 additions & 6 deletions src/log/spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -97,12 +97,12 @@ class Log
end

# Validates that at some point the indicated entry was emitted
def check(level : Severity, message : String, file = __FILE__, line = __LINE__) : self
def check(level : Severity, message : String, file : String = __FILE__, line : Int32 = __LINE__) : self
self.check("#{level} with #{message.inspect}", file, line) { |e| e.severity == level && e.message == message }
end

# :ditto:
def check(level : Severity, pattern : Regex, file = __FILE__, line = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self
def check(level : Severity, pattern : Regex, file : String = __FILE__, line : Int32 = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self
self.check("#{level} matching #{pattern.inspect}", file, line) { |e| e.severity == level && e.message.matches?(pattern, options: options) }
end

Expand All @@ -122,24 +122,24 @@ class Log
end

# Validates that the indicated entry was the next one to be emitted
def next(level : Severity, message : String, file = __FILE__, line = __LINE__) : self
def next(level : Severity, message : String, file : String = __FILE__, line : Int32 = __LINE__) : self
self.next("#{level} with #{message.inspect}", file, line) { |e| e.severity == level && e.message == message }
end

# :ditto:
def next(level : Severity, pattern : Regex, file = __FILE__, line = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self
def next(level : Severity, pattern : Regex, file : String = __FILE__, line : Int32 = __LINE__, *, options : Regex::MatchOptions = Regex::MatchOptions::None) : self
self.next("#{level} matching #{pattern.inspect}", file, line) { |e| e.severity == level && e.message.matches?(pattern, options: options) }
end

# Clears the emitted entries so far
def clear
def clear : self
@entry = nil
@entries.clear
self
end

# Validates that there are no outstanding entries
def empty(file = __FILE__, line = __LINE__)
def empty(file : String = __FILE__, line : Int32 = __LINE__) : self
@entry = nil
if first = @entries.first?
fail("Expected no entries, but got #{first.severity} with #{first.message.inspect} in a total of #{@entries.size} entries", file, line)
Expand Down