Class: LogStruct::SemanticLogger::Formatter

Inherits:
SemanticLogger::Formatters::Json
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/log_struct/semantic_logger/formatter.rb

Overview

Writes each SemanticLogger line as LogStruct's JSON: LogStruct entries, Hashes, messages and exceptions, with SemanticLogger's tags and named tags (the request ID and the running Sidekiq job's context among them). LogStruct::Formatter filters and scrubs every value, within the size limits in config.limits.

Instance Method Summary collapse

Constructor Details

#initialize ⇒ void



17
18
19
20
# File 'lib/log_struct/semantic_logger/formatter.rb', line 17

def initialize
  super
  @logstruct_formatter = T.let(LogStruct::Formatter.new, LogStruct::Formatter)
end

Instance Method Details

#call(log, logger) ⇒ String

Parameters:

  • log (::SemanticLogger::Log)
  • logger (T.untyped)

Returns:

  • (String)


23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/log_struct/semantic_logger/formatter.rb', line 23

def call(log, logger)
  # Extract LogStruct from various locations where it might be stored
  logstruct = extract_logstruct(log)

  # The request ID, the running Sidekiq job's context and the other named tags (logger.tagged(user_id: 1))
  # go in req_id and ctx, and tags (logger.tagged("billing"), config.log_tags, ActiveJob's) in tags
  named_tags = log.named_tags
  request_id = named_tags[:request_id]
  job_context = named_tags[:sidekiq_context]
  context = named_tags.except(:request_id, :sidekiq_context)
  context = context.merge(job_context) if job_context.is_a?(Hash)
   = {
    request_id: request_id.is_a?(String) ? request_id : nil,
    context: context.empty? ? nil : context,
    tags: log.tags.empty? ? nil : log.tags
  }

  json = if logstruct
    # Use our formatter to process LogStruct types directly
    @logstruct_formatter.call(log.level, log.time, log.name, logstruct, **)
  elsif log.exception
    @logstruct_formatter.call(log.level, log.time, log.name, exception_log(log), **)
  elsif log.payload.is_a?(Hash)
    # Process hashes through our formatter
    payload = with_message(without_reserved_keys(log.payload), log.message)
    @logstruct_formatter.call(log.level, log.time, log.name, payload, **)
  elsif log.payload.is_a?(T::Struct)
    @logstruct_formatter.call(log.level, log.time, log.name, log.payload, **)
  else
    # For plain messages, create a Plain log entry
    message_data = log.payload || log.message
    plain_log = ::LogStruct::Log::Plain.new(
      message: message_data,
      timestamp: log.time
    )
    @logstruct_formatter.call(log.level, log.time, log.name, plain_log, **)
  end

  # SemanticLogger appenders typically add their own newline. Avoid double newlines by stripping ours.
  json.end_with?("\n") ? json.chomp : json
end