File: forked_logger.rb

package info (click to toggle)
ruby-lumberjack 2.0.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 956 kB
  • sloc: ruby: 7,957; makefile: 2
file content (144 lines) | stat: -rw-r--r-- 6,199 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# frozen_string_literal: true

module Lumberjack
  # ForkedLogger provides an isolated logging context that forwards all log entries to a parent logger
  # while maintaining its own independent configuration (level, progname, attributes).
  #
  # This class allows you to create specialized logger instances that:
  # - Inherit initial configuration from a parent logger
  # - Maintain isolated settings (level, progname, attributes) that don't affect the parent
  # - Forward all log entries to the parent logger's output device
  # - Combine their own attributes with the parent logger's attributes
  # - Provide scoped logging behavior without duplicating output infrastructure
  #
  # ForkedLogger is particularly useful for:
  #
  # - Component isolation: Give each component its own logger with specific attributes
  # - Request tracing: Create request-specific loggers with request IDs
  # - Temporary debugging: Create debug-level loggers for specific code paths
  # - Library integration: Allow libraries to have their own logging configuration
  # - Multi-tenant logging: Isolate tenant-specific logging configuration
  #
  # The forked logger inherits the parent's initial state but changes are isolated:
  #
  # - Inherited: Initial level, progname, and device (through forwarding)
  # - Isolated: subsequent changes to level, progname, and attributes do not affect the parent logger
  # - Combined: attributes from the parent and the forked loggers are merged when logging
  #
  # @example Basic forked logger
  #   parent = Lumberjack::Logger.new(STDOUT, level: :info)
  #   forked = Lumberjack::ForkedLogger.new(parent)
  #   forked.level = :debug  # Only affects the forked logger
  #   forked.debug("Debug message")  # Logged because forked logger is debug level
  #
  # @example Component-specific logging
  #   main_logger = Lumberjack::Logger.new("/var/log/app.log")
  #   db_logger = Lumberjack::ForkedLogger.new(main_logger)
  #   db_logger.progname = "Database"
  #   db_logger.tag!(component: "database", version: "1.2.3")
  #   db_logger.info("Connection established")  # Includes component attributes and different progname
  #
  # @example Request-scoped logging
  #   def handle_request(request_id)
  #     request_logger = Lumberjack::ForkedLogger.new(@logger)
  #     request_logger.tag!(request_id: request_id, user_id: current_user.id)
  #
  #     request_logger.info("Processing request")  # Includes request context
  #     # ... process request ...
  #     request_logger.info("Request completed")   # All logs tagged with request info
  #   end
  #
  # @see Lumberjack::Logger
  # @see Lumberjack::ContextLogger
  # @see Lumberjack::Context
  class ForkedLogger < Logger
    include ContextLogger

    # The parent logger that receives all log entries from this forked logger.
    # @return [Lumberjack::Logger, #add_entry] The parent logger instance.
    attr_reader :parent_logger

    # Create a new forked logger that forwards all log entries to the specified parent logger.
    # The forked logger inherits the parent's initial level and progname but maintains
    # its own independent context for future changes.
    #
    # @param logger [Lumberjack::ContextLogger, #add_entry] The parent logger to forward entries to.
    #   Must respond to either +add_entry+ (for Lumberjack loggers) or standard Logger methods.
    def initialize(logger)
      init_context_locals!
      self.isolation_level = logger.isolation_level if logger.respond_to?(:isolation_level)
      @parent_logger = logger
      @context = Context.new
      @context.level ||= logger.level
      @context.progname ||= logger.progname
    end

    # Forward a log entry to the parent logger with the forked logger's configuration applied.
    # This method coordinates between the forked logger's settings and the parent logger's
    # output capabilities.
    #
    # @param severity [Integer, Symbol, String] The severity level of the log entry.
    # @param message [Object] The message to log.
    # @param progname [String, nil] The program name (defaults to this logger's progname).
    # @param attributes [Hash, nil] Additional attributes to include with the log entry.
    # @return [Boolean] Returns the result of the parent logger's logging operation.
    #
    # @api private
    def add_entry(severity, message, progname = nil, attributes = nil)
      parent_logger.with_level(level || Logger::DEBUG) do
        attributes = merge_attributes(local_attributes, attributes)
        progname ||= self.progname

        if parent_logger.is_a?(ContextLogger)
          parent_logger.add_entry(severity, message, progname, attributes)
        else
          parent_logger.tag(attributes) do
            parent_logger.add(severity, message, progname)
          end
        end
      end
    end

    # Flush any buffered log entries in the parent logger's device.
    #
    # @return [void]
    def flush
      parent_logger.flush
    end

    # Return the log device of the parent logger, if available.
    #
    # @return [Lumberjack::Device] The parent logger's output device.
    def device
      parent_logger.device if parent_logger.respond_to?(:device)
    end

    # Return the formatter of the parent logger, if available.
    #
    # @return [Lumberjack::EntryFormatter] The parent logger's formatter.
    def formatter
      parent_logger.formatter if parent_logger.respond_to?(:formatter)
    end

    private

    # Return the default context for this forked logger. This provides the isolated
    # configuration context that doesn't affect the parent logger.
    #
    # @return [Lumberjack::Context] The forked logger's private context.
    # @api private
    def default_context
      @context
    end

    # Merge all attributes that should be included with log entries from this forked logger.
    # This combines the default context attributes (set with tag!) and any local context
    # attributes (set within context blocks).
    #
    # @return [Hash, nil] The merged attributes hash, or nil if no attributes are set.
    # @api private
    def local_attributes
      merge_attributes(default_context&.attributes, local_context&.attributes)
    end
  end
end