Module: JsonLogging

Defined in:
lib/json_logging.rb,
lib/json_logging/helpers.rb,
lib/json_logging/version.rb,
lib/json_logging/severity.rb,
lib/json_logging/formatter.rb,
lib/json_logging/sanitizer.rb,
lib/json_logging/json_logger.rb,
lib/json_logging/line_encoder.rb,
lib/json_logging/message_parser.rb,
lib/json_logging/payload_builder.rb,
lib/json_logging/formatter_with_tags.rb,
lib/activesupport/json_logging/railtie.rb,
lib/json_logging/json_logger_extension.rb,
lib/json_logging/structured_hash_sanitizer.rb,
lib/json_logging/structured_hash_json_encoder.rb,
sig/json_logging.rbs

Defined Under Namespace

Modules: Helpers, JsonLoggerExtension, LineEncoder, LocalTagStorage, MessageParser, PayloadBuilder, Sanitizer, Severity, StructuredHashJsonEncoder Classes: Formatter, FormatterWithTags, JsonLogger, Railtie

Constant Summary collapse

THREAD_CONTEXT_KEY =
:__json_logging_context
SANITIZED_CONTEXT_CACHE_KEY =
:__json_logging_sanitized_context
VERSION =

Returns:

  • (String)
"1.2.2"

Class Method Summary collapse

Class Method Details

.additional_context(*args, &block) ⇒ Object

Returns the current thread-local context when called without arguments, or sets a transformer when called with a block or assigned a proc.

Examples:

Getting context

JsonLogging.additional_context  # => {user_id: 123, ...}

Setting transformer with block

JsonLogging.additional_context do |context|
  context.merge(environment: Rails.env, hostname: Socket.gethostname)
end

Setting transformer with assignment

JsonLogging.additional_context = ->(context) { context.merge(env: Rails.env) }


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/json_logging.rb', line 55

def self.additional_context(*args, &block)
  if args.any? || block_given?
    return public_send(:additional_context=, args.first || block)
  end

  begin
    base_context = (context_storage[THREAD_CONTEXT_KEY] || {}).dup
  rescue => e
    warn_additional_context_once(:dup, "thread context dup failed", e)
    base_context = {}
  end

  transformer = @additional_context_transformer
  if transformer.is_a?(Proc)
    begin
      transformer.call(base_context)
    rescue => e
      warn_additional_context_once(:transformer, "additional_context transformer failed", e)
      base_context
    end
  else
    base_context
  end
end

.additional_context=(proc_or_block) ⇒ Object



80
81
82
83
# File 'lib/json_logging.rb', line 80

def self.additional_context=(proc_or_block)
  @additional_context_transformer = proc_or_block
  invalidate_sanitized_context_cache(context_storage)
end

.additional_context_for_payloadObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/json_logging.rb', line 85

def self.additional_context_for_payload
  if @additional_context_transformer.is_a?(Proc)
    return sanitized_context_from(compact_context(additional_context))
  end

  storage = context_storage
  cached = storage[SANITIZED_CONTEXT_CACHE_KEY]
  return cached if cached

  raw_context = storage[THREAD_CONTEXT_KEY]
  if raw_context.nil? || (raw_context.is_a?(Hash) && raw_context.empty?)
    return storage[SANITIZED_CONTEXT_CACHE_KEY] = {}.freeze
  end

  sanitized = sanitized_context_from(compact_context(raw_context))
  storage[SANITIZED_CONTEXT_CACHE_KEY] = sanitized.freeze
  sanitized
end

.logger(*args, **kwargs) ⇒ Logger

Returns an ActiveSupport::Logger that has already been wrapped with JSON logging concern.

Examples:

logger = JsonLogging.logger($stdout)
logger.info("Stuff")  # Logs JSON formatted entry

Parameters:

  • *args

    Arguments passed to ActiveSupport::Logger.new

  • **kwargs

    Keyword arguments passed to ActiveSupport::Logger.new

Returns:

  • (Logger)

    A logger wrapped with JSON formatting and tagged logging support



147
148
149
# File 'lib/json_logging.rb', line 147

def self.logger(*args, **kwargs)
  new(ActiveSupport::Logger.new(*args, **kwargs))
end

.new(logger) ⇒ Logger

Wraps any standard Logger object to provide JSON formatting capabilities. Similar to ActiveSupport::TaggedLogging.new

Examples:

logger = JsonLogging.new(Logger.new(STDOUT))
logger.info("Stuff")  # Logs JSON formatted entry

With tagged logging

logger = JsonLogging.new(Logger.new(STDOUT))
logger.tagged("BCX") { logger.info("Stuff") }  # Logs with tags
logger.tagged("BCX").info("Stuff")  # Logs with tags (non-block form)

Parameters:

  • logger (Logger)

    Any standard Logger object (Logger, ActiveSupport::Logger, etc.)

Returns:

  • (Logger)

    A logger extended with JSON formatting and tagged logging support



165
166
167
168
169
170
171
172
173
174
# File 'lib/json_logging.rb', line 165

def self.new(logger)
  logger = prepare_logger_clone(logger)
  logger.extend(JsonLoggerExtension)
  formatter_with_tags = FormatterWithTags.new(logger)
  logger.instance_variable_set(:@formatter_with_tags, formatter_with_tags)
  # Custom formatters are not supported: JSON output requires FormatterWithTags.
  logger.formatter = formatter_with_tags

  logger
end

.safe_hash(obj) ⇒ Object



104
105
106
107
108
# File 'lib/json_logging.rb', line 104

def self.safe_hash(obj)
  obj.is_a?(Hash) ? obj : {}
rescue
  {}
end

.with_context(extra_context) ⇒ Object



31
32
33
34
35
36
37
38
39
40
# File 'lib/json_logging.rb', line 31

def self.with_context(extra_context)
  storage = context_storage
  original = storage[THREAD_CONTEXT_KEY]
  storage[THREAD_CONTEXT_KEY] = (original || {}).merge(safe_hash(extra_context))
  invalidate_sanitized_context_cache(storage)
  yield
ensure
  storage[THREAD_CONTEXT_KEY] = original
  invalidate_sanitized_context_cache(storage)
end