-
Notifications
You must be signed in to change notification settings - Fork 21.4k
/
tagged_logging.rb
149 lines (126 loc) · 3.96 KB
/
tagged_logging.rb
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
145
146
147
148
149
# frozen_string_literal: true
require "active_support/core_ext/module/delegation"
require "active_support/core_ext/object/blank"
require "logger"
require "active_support/logger"
module ActiveSupport
# = Active Support Tagged Logging
#
# Wraps any standard Logger object to provide tagging capabilities.
#
# May be called with a block:
#
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
# logger.tagged('BCX') { logger.info 'Stuff' } # Logs "[BCX] Stuff"
# logger.tagged('BCX', "Jason") { |tagged_logger| tagged_logger.info 'Stuff' } # Logs "[BCX] [Jason] Stuff"
# logger.tagged('BCX') { logger.tagged('Jason') { logger.info 'Stuff' } } # Logs "[BCX] [Jason] Stuff"
#
# If called without a block, a new logger will be returned with applied tags:
#
# logger = ActiveSupport::TaggedLogging.new(Logger.new(STDOUT))
# logger.tagged("BCX").info "Stuff" # Logs "[BCX] Stuff"
# logger.tagged("BCX", "Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
# logger.tagged("BCX").tagged("Jason").info "Stuff" # Logs "[BCX] [Jason] Stuff"
#
# This is used by the default Rails.logger as configured by Railties to make
# it easy to stamp log lines with subdomains, request ids, and anything else
# to aid debugging of multi-user production applications.
module TaggedLogging
module Formatter # :nodoc:
# This method is invoked when a log event occurs.
def call(severity, timestamp, progname, msg)
super(severity, timestamp, progname, tag_stack.format_message(msg))
end
def tagged(*tags)
pushed_count = tag_stack.push_tags(tags).size
yield self
ensure
pop_tags(pushed_count)
end
def push_tags(*tags)
tag_stack.push_tags(tags)
end
def pop_tags(count = 1)
tag_stack.pop_tags(count)
end
def clear_tags!
tag_stack.clear
end
def tag_stack
# We use our object ID here to avoid conflicting with other instances
@thread_key ||= "activesupport_tagged_logging_tags:#{object_id}"
IsolatedExecutionState[@thread_key] ||= TagStack.new
end
def current_tags
tag_stack.tags
end
def tags_text
tag_stack.format_message("")
end
end
class TagStack # :nodoc:
attr_reader :tags
def initialize
@tags = []
@tags_string = nil
end
def push_tags(tags)
@tags_string = nil
tags.flatten!
tags.reject!(&:blank?)
@tags.concat(tags)
tags
end
def pop_tags(count)
@tags_string = nil
@tags.pop(count)
end
def clear
@tags_string = nil
@tags.clear
end
def format_message(message)
if @tags.empty?
message
elsif @tags.size == 1
"[#{@tags[0]}] #{message}"
else
@tags_string ||= "[#{@tags.join("] [")}] "
"#{@tags_string}#{message}"
end
end
end
module LocalTagStorage # :nodoc:
attr_accessor :tag_stack
def self.extended(base)
base.tag_stack = TagStack.new
end
end
def self.new(logger)
logger = logger.clone
if logger.formatter
logger.formatter = logger.formatter.clone
else
# Ensure we set a default formatter so we aren't extending nil!
logger.formatter = ActiveSupport::Logger::SimpleFormatter.new
end
logger.formatter.extend Formatter
logger.extend(self)
end
delegate :push_tags, :pop_tags, :clear_tags!, to: :formatter
def tagged(*tags)
if block_given?
formatter.tagged(*tags) { yield self }
else
logger = ActiveSupport::TaggedLogging.new(self)
logger.formatter.extend LocalTagStorage
logger.push_tags(*formatter.current_tags, *tags)
logger
end
end
def flush
clear_tags!
super if defined?(super)
end
end
end