Class: LogStruct::SemanticLogger::Logger

Inherits:
SemanticLogger::Logger
  • Object
show all
Extended by:
T::Sig
Includes:
Concerns::LogMethods
Defined in:
lib/log_struct/semantic_logger/logger.rb

Overview

Rails.logger's replacement: a SemanticLogger logger that also takes LogStruct's typed entries and Hashes, and passes each line on to the loggers added with broadcast_to.

logger = LogStruct::SemanticLogger::Logger.new("MyApp")

# A typed entry
logger.info(LogStruct::Log::Plain.new(message: "User authenticated", additional_data: {user_id: 123}))

# A Hash: its keys are written at the top level of the line
logger.info(action: "user_login", user_id: 123)

# A message, with a Hash or an exception beside it
logger.info("User logged in", user_id: 123)
logger.error("Import failed", error)

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name = "Application", level: nil, filter: nil) ⇒ void

Parameters:

  • name (String, Symbol, Module, T::Class[T.anything]) (defaults to: "Application")
  • level (Symbol, nil) (defaults to: nil)
  • filter (T.untyped) (defaults to: nil)


30
31
32
33
34
35
36
37
38
# File 'lib/log_struct/semantic_logger/logger.rb', line 30

def initialize(name = "Application", level: nil, filter: nil)
  # SemanticLogger::Logger expects positional arguments, not named arguments
  super(name, level, filter)
  # T.untyped because users can pass any logger: ::Logger, ActiveSupport::Logger,
  # custom loggers (FakeLogger in tests), or third-party loggers
  @broadcasts = T.let([], T::Array[T.untyped])
  # ActiveJob expects logger.formatter to exist and respond to current_tags
  @formatter = T.let(FormatterProxy.new, FormatterProxy)
end

Instance Attribute Details

#broadcasts ⇒ Array<T.untyped> (readonly)

ActiveSupport::BroadcastLogger compatibility These methods allow Rails.logger to broadcast to multiple loggers

Returns:

  • (Array<T.untyped>)


43
44
45
# File 'lib/log_struct/semantic_logger/logger.rb', line 43

def broadcasts
  @broadcasts
end

#formatter ⇒ FormatterProxy (readonly)

ActiveJob compatibility - expects logger.formatter.current_tags

Returns:



47
48
49
# File 'lib/log_struct/semantic_logger/logger.rb', line 47

def formatter
  @formatter
end

Instance Method Details

#<<(msg) ⇒ T.self_type

Support for << operator (used by RailsLogSplitter). info passes the message on to the broadcast loggers.

Parameters:

  • msg (String)

Returns:

  • (T.self_type)


111
112
113
114
# File 'lib/log_struct/semantic_logger/logger.rb', line 111

def <<(msg)
  info(msg)
  self
end

#broadcast_to(logger) ⇒ T.untyped

T.untyped for logger param because we accept any logger-like object: ::Logger, ActiveSupport::Logger, test doubles, etc.

Parameters:

  • logger (T.untyped)

Returns:

  • (T.untyped)


52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/log_struct/semantic_logger/logger.rb', line 52

def broadcast_to(logger)
  # Every SemanticLogger logger writes to the same appenders, so broadcasting to one
  # would write each message twice. Sidekiq's Rails integration broadcasts Rails.logger
  # to Sidekiq's logger, for example.
  return logger if logger.is_a?(::SemanticLogger::Logger)
  # `rails server` in development broadcasts to a STDOUT logger, which would print each
  # message again as text beside LogStruct's line
  return logger if console_logger_with_console_appender?(logger)

  @broadcasts << logger
  logger
end

#clear_tags! ⇒ void

This method returns an undefined value.



91
92
93
94
95
# File 'lib/log_struct/semantic_logger/logger.rb', line 91

def clear_tags!
  # SemanticLogger doesn't have clear_tags!, use pop_tags instead
  count = ::SemanticLogger.tags.length
  ::SemanticLogger.pop_tags(count) if count > 0
end

#current_tags ⇒ Array<String, Symbol>

Ensure compatibility with Rails.logger interface

Returns:

  • (Array<String, Symbol>)


86
87
88
# File 'lib/log_struct/semantic_logger/logger.rb', line 86

def current_tags
  ::SemanticLogger.tags
end

#pop_tags(count = 1) ⇒ void

This method returns an undefined value.

Parameters:

  • count (Integer) (defaults to: 1)


105
106
107
# File 'lib/log_struct/semantic_logger/logger.rb', line 105

def pop_tags(count = 1)
  ::SemanticLogger.pop_tags(count)
end

#push_tags(*tags) ⇒ Array<T.untyped>

Parameters:

  • tags (T.untyped)

Returns:

  • (Array<T.untyped>)


98
99
100
101
102
# File 'lib/log_struct/semantic_logger/logger.rb', line 98

def push_tags(*tags)
  flat = tags.flatten.compact
  flat.each { |tag| ::SemanticLogger.push_tags(tag) }
  flat
end

#stop_broadcasting_to(logger) ⇒ void

This method returns an undefined value.

Parameters:

  • logger (T.untyped)


66
67
68
# File 'lib/log_struct/semantic_logger/logger.rb', line 66

def stop_broadcasting_to(logger)
  @broadcasts.delete(logger)
end

#tagged(*tags, &block) ⇒ T.untyped

Support for tagged logging

Parameters:

  • tags (T.untyped)
  • block (T.proc.returns(T.untyped))

Returns:

  • (T.untyped)


74
75
76
77
78
79
80
81
82
# File 'lib/log_struct/semantic_logger/logger.rb', line 74

def tagged(*tags, &block)
  tag_array = tags.flatten
  if tag_array.empty?
    super(&block)
  else
    # Sorbet can't check a splat of an Array whose size it doesn't know
    super(*T.unsafe(tag_array), &block)
  end
end