Class: Gaskit::Logger

Inherits:
Object
  • Object
show all
Defined in:
lib/gaskit/logger.rb

Overview

A logger class designed for structured logging with support for JSON, contextual data, and environment-aware formatting.

This logger wraps a configurable ‘Logger` instance and supports:

  • Structured or human-readable log formatting

  • Inclusion of global and per-call context (with filtering of sensitive keys)

  • Support for custom log levels, formatters, and disabling output via ‘Gaskit.config`

By default, logs are pretty-printed in development and JSON-formatted in production. These defaults can be overridden via configuration.

Examples:

Basic usage with default formatting

logger = Gaskit::Logger.new('MyOperation', context: { user_id: 42 })

logger.info("Operation started")
logger.debug(context: { details: "debug info" }) { "Deferred message" }

Using logger within a class

class UserRepository
  def self.logger
    @logger ||= Gaskit::Logger.new(self)
  end

  def self.find(id)
    logger.info("Looking up user", context: { user_id: id })
  end
end

Customizing the log formatter globally

Gaskit.config do |c|
  c.log_formatter = ->(severity, time, _progname, msg) do
    message, ctx = msg.is_a?(Array) ? msg : [msg, {}]
    "[#{time.strftime('%T')}] #{severity}: #{message} #{ctx.to_json}\n"
  end
end

Configuring JSON or pretty formatters

Gaskit.config do |c|
  c.setup_logger(Logger.new($stdout), formatter: Gaskit::Logger.formatter(:json))
end

# or

Gaskit.config do |c|
  c.setup_logger(Logger.new($stdout), formatter: Gaskit::Logger.formatter(:pretty))
end

Disabling logs entirely

Gaskit.config do |c|
  c.disable_logging = true
end

See Also:

Constant Summary collapse

SENSITIVE_KEYS =
i[email ip_address password auth_token secret ssn jwt token].freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(klass, context: {}) ⇒ Logger

Initializes a new logger instance.

Parameters:

  • klass (Class, Object, String, Symbol)

    The name of the class being logged.

  • context (Hash) (defaults to: {})

    Optional additional context to include in every log entry.



180
181
182
183
184
185
186
187
188
# File 'lib/gaskit/logger.rb', line 180

def initialize(klass, context: {})
  @class_name = Gaskit::Helpers.resolve_name(klass)

  @context = apply_context(context).merge(class: @class_name)
  @logger = Gaskit.configuration.logger || ::Logger.new($stdout)
rescue StandardError
  ::Logger.new($stdout).error "Failed to initialize logger: #{$ERROR_INFO}"
  @logger = ::Logger.new(nil) # fallback null logger
end

Instance Attribute Details

#contextObject (readonly)

Returns the value of attribute context.



174
175
176
# File 'lib/gaskit/logger.rb', line 174

def context
  @context
end

Class Method Details

.formatter(formatter = :pretty) ⇒ Proc

Returns a built-in log formatter.

Use this method to obtain either the JSON or pretty formatter for logs. This is useful when customizing the logger via ‘Gaskit.config`.

Examples:

Use JSON formatter

Gaskit.config do |c|
  c.setup_logger(Logger.new($stdout), formatter: Gaskit::Logger.formatter(:json))
end

Use pretty formatter

Gaskit.config do |c|
  c.setup_logger(Logger.new($stdout), formatter: Gaskit::Logger.formatter(:pretty))
end

Parameters:

  • formatter (Symbol) (defaults to: :pretty)

    The formatter type to use (‘:json` or `:pretty`)

Returns:

  • (Proc)

    A formatter proc suitable for use with ‘Logger#formatter`

Raises:

  • (ArgumentError)

    If an unknown formatter symbol is provided



87
88
89
90
91
92
93
94
95
96
# File 'lib/gaskit/logger.rb', line 87

def formatter(formatter = :pretty)
  case formatter
  when :json
    json_formatter
  when :pretty
    pretty_formatter
  else
    raise ArgumentError, "Invalid log formatter: #{formatter}"
  end
end

.json_formatterProc

JSON formatter (for production or structured logs)

Returns:

  • (Proc)

    The formatter callable.



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/gaskit/logger.rb', line 101

def json_formatter
  lambda do |severity, time, _progname, msg|
    message, context = extract_message_and_context(msg)

    log_entry = {
      timestamp: time.utc.iso8601,
      level: severity.downcase,
      class: context[:class],
      message: message,
      context: context&.reject { |k| k == :duration }
    }.compact

    log_entry[:duration] = context[:duration] if context&.key?(:duration)

    "#{JSON.dump(log_entry)}\n"
  end
end

.pretty_formatterProc

Pretty formatter (for dev logs)

Returns:

  • (Proc)

    The formatter callable.



122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/gaskit/logger.rb', line 122

def pretty_formatter
  lambda do |severity, time, _progname, msg|
    message, context = extract_message_and_context(msg)
    context ||= {}
    class_name = context.delete(:class)

    tags = %W[[#{time.utc.iso8601}] [#{severity}]]
    tags << "[#{class_name}]" if class_name
    tags += flatten_context(context).map { |k, v| "[#{k}=#{v}]" }

    "#{tags.join(" ")} #{message}\n"
  end
end

Instance Method Details

#debug(message = nil, context: {}) { ... } ⇒ Object

Logs a debug-level message.

Parameters:

  • message (String, nil) (defaults to: nil)

    The log message (or provide it via the block parameter).

  • context (Hash, nil) (defaults to: {})

    Additional context for this specific log entry.

Yields:

  • Block for deferred log message computation.



195
196
197
# File 'lib/gaskit/logger.rb', line 195

def debug(message = nil, context: {}, &block)
  log(:debug, message, context: context, &block)
end

#error(message = nil, context: {}) { ... } ⇒ Object

Logs an error-level message.

Parameters:

  • message (String, nil) (defaults to: nil)

    The log message (or provide it via the block parameter).

  • context (Hash, nil) (defaults to: {})

    Additional context for this specific log entry.

Yields:

  • Block for deferred log message computation.



222
223
224
# File 'lib/gaskit/logger.rb', line 222

def error(message = nil, context: {}, &block)
  log(:error, message, context: context, &block)
end

#info(message = nil, context: {}) { ... } ⇒ Object

Logs an info-level message.

Parameters:

  • message (String, nil) (defaults to: nil)

    The log message (or provide it via the block parameter).

  • context (Hash, nil) (defaults to: {})

    Additional context for this specific log entry.

Yields:

  • Block for deferred log message computation.



204
205
206
# File 'lib/gaskit/logger.rb', line 204

def info(message = nil, context: {}, &block)
  log(:info, message, context: context, &block)
end

#log(level, message, context: {}) { ... } ⇒ Object

Logs a message at the specified level.

Parameters:

  • level (Symbol)

    The log level (e.g., :debug, :info).

  • message (String, nil)

    The log message (or provide it via the block parameter).

  • context (Hash, nil) (defaults to: {})

    Additional context for this specific log entry.

Yields:

  • Block for deferred log message computation.



232
233
234
235
236
237
238
239
240
241
242
243
# File 'lib/gaskit/logger.rb', line 232

def log(level, message, context: {}, &block)
  return if Gaskit.configuration.disable_logging

  @logger.public_send(level) do
    combined_context = filtered_context(@context.merge(context))
    combined_context[:class] ||= @class_name

    msg = message || block&.call

    [msg, combined_context]
  end
end

#warn(message = nil, context: {}) { ... } ⇒ Object

Logs a warning-level message.

Parameters:

  • message (String, nil) (defaults to: nil)

    The log message (or provide it via the block parameter).

  • context (Hash, nil) (defaults to: {})

    Additional context for this specific log entry.

Yields:

  • Block for deferred log message computation.



213
214
215
# File 'lib/gaskit/logger.rb', line 213

def warn(message = nil, context: {}, &block)
  log(:warn, message, context: context, &block)
end

#with_context(extra_context) ⇒ Gaskit::Logger

Creates a new logger instance with additional merged context.

Parameters:

  • extra_context (Hash)

    Additional context to include.

Returns:

  • (Gaskit::Logger)

    A new logger instance with the updated context.



249
250
251
# File 'lib/gaskit/logger.rb', line 249

def with_context(extra_context)
  self.class.new(@class_name, context: @context.merge(extra_context))
end