semantic_logger_ecs_addon 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +1 -0
- data/LICENSE.md +20 -0
- data/README.md +98 -0
- data/lib/semantic_logger_ecs_addon.rb +15 -0
- data/lib/semantic_logger_ecs_addon/formatters/base.rb +206 -0
- data/lib/semantic_logger_ecs_addon/formatters/json.rb +19 -0
- data/lib/semantic_logger_ecs_addon/formatters/raw.rb +115 -0
- data/lib/semantic_logger_ecs_addon/identity.rb +12 -0
- data/lib/semantic_logger_ecs_addon/utils/backtrace_cleaner.rb +126 -0
- data/lib/semantic_logger_ecs_addon/utils/hash.rb +19 -0
- metadata +116 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9d56d0ab9e926ca66fd4889271eb4b5157b41b2b6719b6fc22ebf2acd29745eb
|
4
|
+
data.tar.gz: 518de9e56bd832649fdf2cda28da0a0453461bafa2895ac9cf4169058625d817
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ca8f68e438c4f018e1205b513455120f8b8cefbe311b980614e4a7f739672391362b58203d99696b4d51a21e80f726488ffcb6d03893ec277822fd0caa00e47a
|
7
|
+
data.tar.gz: 30f219878394f83e632e222241a23323cb090323faafe86a0dccb0d430ad7cc4cea95cd942db3070e892b5765f911f6bd4f53893b66a64b992fd97124420f1a4
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
��=3�D�od��q&|}!������UE��d��"��0��}�J�ۮNg����{�P��GL���"���C<aB�{X����:��V�n`���g>�������O�'�\�H�`�O���$����G�i�<�;�8�-�m&�B9�`.+�p BNl�,�&���\��*���WfvI%���\����k�D愕ւo�d���@�B�P
|
data/LICENSE.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2021 [Illuminate Software Solutions](https://illuminate.ae).
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
<p align="center">
|
2
|
+
<img src="semantic_logger_ecs_addon.png" alt="Semantic Logger Ecs Addon Icon"/>
|
3
|
+
</p>
|
4
|
+
|
5
|
+
# Semantic Logger Ecs Addon
|
6
|
+
|
7
|
+
[](http://badge.fury.io/rb/semantic_logger_ecs_addon)
|
8
|
+
[](https://www.alchemists.io/projects/code_quality)
|
9
|
+
|
10
|
+
<!-- Tocer[start]: Auto-generated, don't remove. -->
|
11
|
+
|
12
|
+
## Table of Contents
|
13
|
+
|
14
|
+
- [Features](#features)
|
15
|
+
- [Screencasts](#screencasts)
|
16
|
+
- [Requirements](#requirements)
|
17
|
+
- [Setup](#setup)
|
18
|
+
- [Usage](#usage)
|
19
|
+
- [Development](#development)
|
20
|
+
- [Tests](#tests)
|
21
|
+
- [Versioning](#versioning)
|
22
|
+
- [Code of Conduct](#code-of-conduct)
|
23
|
+
- [Contributions](#contributions)
|
24
|
+
- [License](#license)
|
25
|
+
- [History](#history)
|
26
|
+
- [Credits](#credits)
|
27
|
+
|
28
|
+
<!-- Tocer[finish]: Auto-generated, don't remove. -->
|
29
|
+
|
30
|
+
## Features
|
31
|
+
|
32
|
+
## Screencasts
|
33
|
+
|
34
|
+
## Requirements
|
35
|
+
|
36
|
+
1. [Ruby](https://www.ruby-lang.org)
|
37
|
+
|
38
|
+
## Setup
|
39
|
+
|
40
|
+
To install, run:
|
41
|
+
|
42
|
+
gem install semantic_logger_ecs_addon
|
43
|
+
|
44
|
+
Add the following to your Gemfile:
|
45
|
+
|
46
|
+
gem "semantic_logger_ecs_addon"
|
47
|
+
|
48
|
+
## Usage
|
49
|
+
|
50
|
+
## Development
|
51
|
+
|
52
|
+
To contribute, run:
|
53
|
+
|
54
|
+
git clone https://github.com/thorion3006/semantic_logger_ecs_addon.git
|
55
|
+
cd semantic_logger_ecs_addon
|
56
|
+
bin/setup
|
57
|
+
|
58
|
+
You can also use the IRB console for direct access to all objects:
|
59
|
+
|
60
|
+
bin/console
|
61
|
+
|
62
|
+
## Tests
|
63
|
+
|
64
|
+
To test, run:
|
65
|
+
|
66
|
+
bundle exec rake
|
67
|
+
|
68
|
+
## Versioning
|
69
|
+
|
70
|
+
Read [Semantic Versioning](https://semver.org) for details. Briefly, it means:
|
71
|
+
|
72
|
+
- Major (X.y.z) - Incremented for any backwards incompatible public API changes.
|
73
|
+
- Minor (x.Y.z) - Incremented for new, backwards compatible, public API enhancements/fixes.
|
74
|
+
- Patch (x.y.Z) - Incremented for small, backwards compatible, bug fixes.
|
75
|
+
|
76
|
+
## Code of Conduct
|
77
|
+
|
78
|
+
Please note that this project is released with a [CODE OF CONDUCT](CODE_OF_CONDUCT.md). By
|
79
|
+
participating in this project you agree to abide by its terms.
|
80
|
+
|
81
|
+
## Contributions
|
82
|
+
|
83
|
+
Read [CONTRIBUTING](CONTRIBUTING.md) for details.
|
84
|
+
|
85
|
+
## License
|
86
|
+
|
87
|
+
Copyright 2021 [Illuminate Software Solutions](https://illuminate.ae).
|
88
|
+
Read [LICENSE](LICENSE.md) for details.
|
89
|
+
|
90
|
+
## History
|
91
|
+
|
92
|
+
Read [CHANGES](CHANGES.md) for details.
|
93
|
+
Built with [Gemsmith](https://www.alchemists.io/projects/gemsmith).
|
94
|
+
|
95
|
+
## Credits
|
96
|
+
|
97
|
+
Developed by [Sajeev Ramasamy]() at
|
98
|
+
[Illuminate Software Solutions](https://illuminate.ae).
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pathname"
|
4
|
+
require "zeitwerk"
|
5
|
+
|
6
|
+
loader = Zeitwerk::Loader.for_gem
|
7
|
+
loader.setup
|
8
|
+
|
9
|
+
# Main namespace.
|
10
|
+
module SemanticLoggerEcsAddon
|
11
|
+
RootPath = Pathname.getwd
|
12
|
+
BacktraceCleaner = Utils::BacktraceCleaner.new
|
13
|
+
BacktraceCleaner.add_filter { |line| line.gsub(RootPath.to_s, "") }
|
14
|
+
BacktraceCleaner.add_silencer { |line| %r(puma|ruby/gems|rubygems).match?(line) }
|
15
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "semantic_logger/formatters/base"
|
4
|
+
|
5
|
+
module SemanticLoggerEcsAddon
|
6
|
+
module Formatters
|
7
|
+
class Base < SemanticLogger::Formatters::Base
|
8
|
+
# Fields are added by populating this hash.
|
9
|
+
attr_accessor :hash, :time_key, :log_labels, :formatted_payload
|
10
|
+
|
11
|
+
# Parameters
|
12
|
+
# time_format: [String|Symbol|nil]
|
13
|
+
# See Time#strftime for the format of this string.
|
14
|
+
# :iso_8601 Outputs an ISO8601 Formatted timestamp.
|
15
|
+
# :ms Output in miliseconds since epoch.
|
16
|
+
# nil: Returns Empty string for time ( no time is output ).
|
17
|
+
# Default: '%Y-%m-%d %H:%M:%S.%<precision>N'
|
18
|
+
# log_host: [Boolean]
|
19
|
+
# Whether or not to include hostname in logs
|
20
|
+
# Default: true
|
21
|
+
# precision: [Integer]
|
22
|
+
# How many fractional digits to log times with.
|
23
|
+
# Default: PRECISION (6, except on older JRuby, where 3)
|
24
|
+
def initialize time_key: :time, **args
|
25
|
+
@time_key = time_key
|
26
|
+
@log_application = true
|
27
|
+
super(**args)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Host name
|
31
|
+
def host
|
32
|
+
log_labels[:host] = logger.host if log_host && logger.host
|
33
|
+
end
|
34
|
+
|
35
|
+
# Application name
|
36
|
+
def application
|
37
|
+
log_labels[:application] = logger.application if logger && logger.application
|
38
|
+
end
|
39
|
+
|
40
|
+
# Environment
|
41
|
+
def environment
|
42
|
+
if log_environment && logger && logger.environment
|
43
|
+
log_labels[:environment] = logger.environment
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Named Tags
|
48
|
+
def named_tags
|
49
|
+
log_labels[:named_tags] = log.named_tags if log.named_tags && !log.named_tags.empty?
|
50
|
+
end
|
51
|
+
|
52
|
+
# Date & time
|
53
|
+
def time
|
54
|
+
hash[time_key] = format_time log.time.utc
|
55
|
+
end
|
56
|
+
|
57
|
+
def labels
|
58
|
+
self.log_labels ||= {}
|
59
|
+
host
|
60
|
+
application
|
61
|
+
environment
|
62
|
+
named_tags
|
63
|
+
hash[:labels] = log_labels unless log_labels.empty?
|
64
|
+
end
|
65
|
+
|
66
|
+
# Log message
|
67
|
+
def message
|
68
|
+
hash[:message] = "#{log.name} -- #{log.cleansed_message}" if log.message
|
69
|
+
hash[:message] = "#{log.metric} -- #{log.metric_amount}" if log.metric && log.metric_amount
|
70
|
+
end
|
71
|
+
|
72
|
+
# Tags
|
73
|
+
def tags
|
74
|
+
hash[:tags] = log.tags if log.tags && !log.tags.empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Exception
|
78
|
+
def exception
|
79
|
+
return log.exception if log.exception
|
80
|
+
|
81
|
+
unless log.payload.respond_to?(:empty?) && log.payload[:exception].class.ancestors.include?(StandardError)
|
82
|
+
return
|
83
|
+
end
|
84
|
+
|
85
|
+
log.payload[:exception]
|
86
|
+
end
|
87
|
+
|
88
|
+
def inner_exception exception
|
89
|
+
if exception.respond_to?(:cause) && exception.cause
|
90
|
+
exception.cause
|
91
|
+
elsif exception.respond_to?(:continued_exception) && exception.continued_exception
|
92
|
+
exception.continued_exception
|
93
|
+
elsif exception.respond_to?(:original_exception) && exception.original_exception
|
94
|
+
exception.original_exception
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Call the block for exception and any nested exception
|
99
|
+
def each_exception exception
|
100
|
+
# With thanks to https://github.com/bugsnag/bugsnag-ruby/blob/6348306e44323eee347896843d16c690cd7c4362/lib/bugsnag/notification.rb#L81
|
101
|
+
depth = 0
|
102
|
+
exceptions = []
|
103
|
+
e = exception
|
104
|
+
while !e.nil? && !exceptions.include?(e) && exceptions.length < 5
|
105
|
+
exceptions << e
|
106
|
+
yield e, depth
|
107
|
+
|
108
|
+
depth += 1
|
109
|
+
e = inner_exception e
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def exception_hash exception
|
114
|
+
{
|
115
|
+
"error.type": exception.class.name,
|
116
|
+
"error.message": exception.message,
|
117
|
+
"error.stack_trace": BacktraceCleaner.clean(exception.backtrace)
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
def error_log?
|
122
|
+
!hash[:"error.type"].nil?
|
123
|
+
end
|
124
|
+
|
125
|
+
def initialize_rack_keys
|
126
|
+
formatted_payload[:request] ||= {}
|
127
|
+
formatted_payload[:response] ||= {}
|
128
|
+
formatted_payload[:metrics] ||= {}
|
129
|
+
end
|
130
|
+
|
131
|
+
def rack_request
|
132
|
+
formatted_payload[:request].merge! formatted_payload.extract!(
|
133
|
+
:controller,
|
134
|
+
:action,
|
135
|
+
:params,
|
136
|
+
:method,
|
137
|
+
:path,
|
138
|
+
:request_id
|
139
|
+
)
|
140
|
+
formatted_payload[:request][:body] = formatted_payload[:request].delete :params
|
141
|
+
end
|
142
|
+
|
143
|
+
def rack_response
|
144
|
+
formatted_payload[:response].merge! formatted_payload.extract!(:status, :status_message)
|
145
|
+
end
|
146
|
+
|
147
|
+
def rack_metrics
|
148
|
+
metrics_keys = formatted_payload.keys.select { |k| k.to_s.end_with?("_runtime") }
|
149
|
+
formatted_payload[:metrics].merge! formatted_payload.extract!(:allocations, *metrics_keys)
|
150
|
+
formatted_payload[:metrics][:object_allocations] =
|
151
|
+
formatted_payload[:metrics].delete :allocations
|
152
|
+
end
|
153
|
+
|
154
|
+
def rack_extract
|
155
|
+
return unless formatted_payload.key? :controller
|
156
|
+
|
157
|
+
initialize_rack_keys
|
158
|
+
rack_request
|
159
|
+
rack_response
|
160
|
+
rack_metrics
|
161
|
+
formatted_payload.delete :format
|
162
|
+
end
|
163
|
+
|
164
|
+
def format_payload
|
165
|
+
return unless log.payload.respond_to?(:empty?) && !log.payload.empty?
|
166
|
+
|
167
|
+
self.formatted_payload = Utils::Hash[**log.payload]
|
168
|
+
|
169
|
+
rack_extract
|
170
|
+
end
|
171
|
+
|
172
|
+
def request
|
173
|
+
hash[:"http.request.id"] = formatted_payload.dig :request, :request_id
|
174
|
+
hash[:"http.request.body.content"] = formatted_payload.dig :request, :body
|
175
|
+
hash[:"http.request.method"] = formatted_payload.dig :request, :method
|
176
|
+
end
|
177
|
+
|
178
|
+
def response
|
179
|
+
hash[:"http.response.body.content"] = formatted_payload.dig :response, :body
|
180
|
+
hash[:"http.response.status_code"] = formatted_payload.dig :response, :status
|
181
|
+
end
|
182
|
+
|
183
|
+
# Ruby file name and line number that logged the message.
|
184
|
+
def file_name_and_line
|
185
|
+
file, line = log.file_name_and_line
|
186
|
+
return unless file
|
187
|
+
|
188
|
+
hash[:"log.origin.file.name"] = file
|
189
|
+
hash[:"log.origin.file.line"] = line.to_i
|
190
|
+
end
|
191
|
+
|
192
|
+
def calculated_log_level
|
193
|
+
return log.level if log.level_index > 2
|
194
|
+
|
195
|
+
(formatted_payload.dig(:response, :status) || 0) >= 500 ? "error" : log.level
|
196
|
+
end
|
197
|
+
|
198
|
+
# ElasticAPM
|
199
|
+
def apm_agent_present_and_running?
|
200
|
+
return false unless defined?(::ElasticAPM)
|
201
|
+
|
202
|
+
ElasticAPM.running?
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
|
5
|
+
module SemanticLoggerEcsAddon
|
6
|
+
module Formatters
|
7
|
+
class Json < Raw
|
8
|
+
# Default JSON time format is ISO8601
|
9
|
+
def initialize time_format: :iso_8601, precision: 3, **args
|
10
|
+
super(time_format: time_format, precision: precision, **args)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns log messages in JSON format
|
14
|
+
def call log, logger
|
15
|
+
super(log, logger).to_json
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticLoggerEcsAddon
|
4
|
+
module Formatters
|
5
|
+
class Raw < Base
|
6
|
+
def initialize time_format: :none, time_key: :@timestamp, **args
|
7
|
+
@time_key = time_key
|
8
|
+
super(time_format: time_format, time_key: @time_key, **args)
|
9
|
+
end
|
10
|
+
|
11
|
+
def base
|
12
|
+
time
|
13
|
+
labels
|
14
|
+
message
|
15
|
+
tags
|
16
|
+
end
|
17
|
+
|
18
|
+
def ecs
|
19
|
+
hash[:"ecs.version"] = "1.10"
|
20
|
+
end
|
21
|
+
|
22
|
+
def error
|
23
|
+
root = hash
|
24
|
+
each_exception exception do |e, i|
|
25
|
+
if i.zero?
|
26
|
+
root.merge! exception_hash e
|
27
|
+
else
|
28
|
+
root[:"error.cause"] = exception_hash e
|
29
|
+
root = root[:"error.cause"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def event
|
35
|
+
hash[:"event.dataset"] = "#{logger.application}.log"
|
36
|
+
hash[:"event.duration"] = (log.duration || 0) * 1000000
|
37
|
+
hash[:"event.outcome"] = error_log? ? "failure" : "success"
|
38
|
+
end
|
39
|
+
|
40
|
+
def http
|
41
|
+
request
|
42
|
+
response
|
43
|
+
end
|
44
|
+
|
45
|
+
def ecs_log
|
46
|
+
hash[:"log.level"] = calculated_log_level
|
47
|
+
hash[:"log.logger"] = log.name
|
48
|
+
file_name_and_line
|
49
|
+
end
|
50
|
+
|
51
|
+
def ecs_process
|
52
|
+
hash[:"process.thread.name"] = log.thread_name
|
53
|
+
hash[:"process.pid"] = pid
|
54
|
+
end
|
55
|
+
|
56
|
+
def ecs_service
|
57
|
+
hash[:"service.name"] = logger.application
|
58
|
+
end
|
59
|
+
|
60
|
+
def ecs_source
|
61
|
+
hash[:"source.ip"] = formatted_payload.delete :remote_ip
|
62
|
+
end
|
63
|
+
|
64
|
+
def ecs_tracing
|
65
|
+
return unless apm_agent_present_and_running?
|
66
|
+
|
67
|
+
hash[:"transaction.id"] = ElasticAPM.current_transaction&.id
|
68
|
+
hash[:"trace.id"] = ElasticAPM.current_transaction&.trace_id
|
69
|
+
hash[:"span.id"] = ElasticAPM.current_span&.id
|
70
|
+
end
|
71
|
+
|
72
|
+
def ecs_url
|
73
|
+
hash[:"url.path"] = formatted_payload.dig :request, :path
|
74
|
+
end
|
75
|
+
|
76
|
+
def ecs_user
|
77
|
+
hash[:"user.email"] = formatted_payload.dig :user, :email
|
78
|
+
hash[:"user.full_name"] = formatted_payload.dig :user, :full_name
|
79
|
+
hash[:"user.id"] = formatted_payload.dig :user, :id
|
80
|
+
hash[:"user.name"] = formatted_payload.dig :user, :name
|
81
|
+
hash[:"user.domain"] = formatted_payload.dig :user, :type
|
82
|
+
end
|
83
|
+
|
84
|
+
def extras
|
85
|
+
return unless formatted_payload.respond_to?(:empty?) && !formatted_payload.empty?
|
86
|
+
|
87
|
+
hash.merge! formatted_payload.except(:request, :response, :user)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns log messages in Hash format
|
91
|
+
def call log, logger
|
92
|
+
self.hash = {}
|
93
|
+
self.log = log
|
94
|
+
self.logger = logger
|
95
|
+
format_payload
|
96
|
+
|
97
|
+
base
|
98
|
+
ecs
|
99
|
+
error
|
100
|
+
event
|
101
|
+
http
|
102
|
+
ecs_log
|
103
|
+
ecs_process
|
104
|
+
ecs_service
|
105
|
+
ecs_source
|
106
|
+
ecs_tracing
|
107
|
+
ecs_url
|
108
|
+
ecs_user
|
109
|
+
extras
|
110
|
+
|
111
|
+
hash.compact
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticLoggerEcsAddon
|
4
|
+
# Gem identity information.
|
5
|
+
module Identity
|
6
|
+
NAME = "semantic_logger_ecs_addon"
|
7
|
+
LABEL = "Semantic Logger Ecs Addon"
|
8
|
+
VERSION = "0.1.0"
|
9
|
+
VERSION_LABEL = "#{LABEL} #{VERSION}"
|
10
|
+
SUMMARY = "A semantic logger formatter that formats the logs according to Elastic Common Schema and adds APM trace data if ElasticAPM is enabled."
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticLoggerEcsAddon
|
4
|
+
module Utils
|
5
|
+
# Backtraces often include many lines that are not relevant for the context
|
6
|
+
# under review. This makes it hard to find the signal amongst the backtrace
|
7
|
+
# noise, and adds debugging time. With a BacktraceCleaner, filters and
|
8
|
+
# silencers are used to remove the noisy lines, so that only the most relevant
|
9
|
+
# lines remain.
|
10
|
+
#
|
11
|
+
# Filters are used to modify lines of data, while silencers are used to remove
|
12
|
+
# lines entirely. The typical filter use case is to remove lengthy path
|
13
|
+
# information from the start of each line, and view file paths relevant to the
|
14
|
+
# app directory instead of the file system root. The typical silencer use case
|
15
|
+
# is to exclude the output of a noisy library from the backtrace, so that you
|
16
|
+
# can focus on the rest.
|
17
|
+
#
|
18
|
+
# bc = ActiveSupport::BacktraceCleaner.new
|
19
|
+
# bc.add_filter { |line| line.gsub(Rails.root.to_s, '') } # strip the Rails.root prefix
|
20
|
+
# bc.add_silencer { |line| /puma|rubygems/.match?(line) } # skip any lines from puma or rubygems
|
21
|
+
# bc.clean(exception.backtrace) # perform the cleanup
|
22
|
+
#
|
23
|
+
# To reconfigure an existing BacktraceCleaner (like the default one in Rails)
|
24
|
+
# and show as much data as possible, you can always call
|
25
|
+
# <tt>BacktraceCleaner#remove_silencers!</tt>, which will restore the
|
26
|
+
# backtrace to a pristine state. If you need to reconfigure an existing
|
27
|
+
# BacktraceCleaner so that it does not filter or modify the paths of any lines
|
28
|
+
# of the backtrace, you can call <tt>BacktraceCleaner#remove_filters!</tt>
|
29
|
+
# These two methods will give you a completely untouched backtrace.
|
30
|
+
#
|
31
|
+
# Borrowed from ActiveSupport gem.
|
32
|
+
class BacktraceCleaner
|
33
|
+
FORMATTED_GEMS_PATTERN = %r(\A[^/]+ \([\w.]+\) )
|
34
|
+
def initialize
|
35
|
+
@filters = []
|
36
|
+
@silencers = []
|
37
|
+
add_gem_filter
|
38
|
+
add_gem_silencer
|
39
|
+
add_stdlib_silencer
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns the backtrace after all filters and silencers have been run
|
43
|
+
# against it. Filters run first, then silencers.
|
44
|
+
def clean backtrace, kind = :silent
|
45
|
+
filtered = filter_backtrace backtrace
|
46
|
+
|
47
|
+
case kind
|
48
|
+
when :silent
|
49
|
+
silence filtered
|
50
|
+
when :noise
|
51
|
+
noise filtered
|
52
|
+
else
|
53
|
+
filtered
|
54
|
+
end
|
55
|
+
end
|
56
|
+
alias filter clean
|
57
|
+
|
58
|
+
# Adds a filter from the block provided. Each line in the backtrace will be
|
59
|
+
# mapped against this filter.
|
60
|
+
#
|
61
|
+
# # Will turn "/my/rails/root/app/models/person.rb" into "/app/models/person.rb"
|
62
|
+
# backtrace_cleaner.add_filter { |line| line.gsub(Rails.root, '') }
|
63
|
+
def add_filter &block
|
64
|
+
@filters << block
|
65
|
+
end
|
66
|
+
|
67
|
+
# Adds a silencer from the block provided. If the silencer returns +true+
|
68
|
+
# for a given line, it will be excluded from the clean backtrace.
|
69
|
+
#
|
70
|
+
# # Will reject all lines that include the word "puma", like "/gems/puma/server.rb" or "/app/my_puma_server/rb"
|
71
|
+
# backtrace_cleaner.add_silencer { |line| /puma/.match?(line) }
|
72
|
+
def add_silencer &block
|
73
|
+
@silencers << block
|
74
|
+
end
|
75
|
+
|
76
|
+
# Removes all silencers, but leaves in the filters. Useful if your
|
77
|
+
# context of debugging suddenly expands as you suspect a bug in one of
|
78
|
+
# the libraries you use.
|
79
|
+
def remove_silencers!
|
80
|
+
@silencers = []
|
81
|
+
end
|
82
|
+
|
83
|
+
# Removes all filters, but leaves in the silencers. Useful if you suddenly
|
84
|
+
# need to see entire filepaths in the backtrace that you had already
|
85
|
+
# filtered out.
|
86
|
+
def remove_filters!
|
87
|
+
@filters = []
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def add_gem_filter
|
93
|
+
gems_paths = (Gem.path | [Gem.default_dir]).map { |p| Regexp.escape(p) }
|
94
|
+
return if gems_paths.empty?
|
95
|
+
|
96
|
+
gems_regexp = %r{\A(#{gems_paths.join '|'})/(bundler/)?gems/([^/]+)-([\w.]+)/(.*)}
|
97
|
+
gems_result = '\3 (\4) \5'
|
98
|
+
add_filter { |line| line.sub(gems_regexp, gems_result) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def add_gem_silencer
|
102
|
+
add_silencer { |line| FORMATTED_GEMS_PATTERN.match?(line) }
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_stdlib_silencer
|
106
|
+
add_silencer { |line| line.start_with?(RbConfig::CONFIG["rubylibdir"]) }
|
107
|
+
end
|
108
|
+
|
109
|
+
def filter_backtrace backtrace
|
110
|
+
@filters.each { |f| backtrace = backtrace.map { |line| f.call(line) } }
|
111
|
+
|
112
|
+
backtrace
|
113
|
+
end
|
114
|
+
|
115
|
+
def silence backtrace
|
116
|
+
@silencers.each { |s| backtrace = backtrace.reject { |line| s.call(line) } }
|
117
|
+
|
118
|
+
backtrace
|
119
|
+
end
|
120
|
+
|
121
|
+
def noise backtrace
|
122
|
+
backtrace.select { |line| @silencers.any? { |s| s.call line } }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module SemanticLoggerEcsAddon
|
4
|
+
module Utils
|
5
|
+
class Hash < ::Hash
|
6
|
+
# With thanks to https://github.com/rails/rails/blob/83217025a171593547d1268651b446d3533e2019/activesupport/lib/active_support/core_ext/hash/slice.rb#L24
|
7
|
+
# Removes and returns the key/value pairs matching the given keys.
|
8
|
+
#
|
9
|
+
# hash = { a: 1, b: 2, c: 3, d: 4 }
|
10
|
+
# hash.extract!(:a, :b) # => {:a=>1, :b=>2}
|
11
|
+
# hash # => {:c=>3, :d=>4}
|
12
|
+
def extract! *keys
|
13
|
+
keys.each_with_object self.class.new do |key, result|
|
14
|
+
result[key] = delete key if key? key
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: semantic_logger_ecs_addon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sajeev Ramasamy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIERDCCAqygAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDDBt0aG9y
|
14
|
+
aW9uMzAwNi9EQz1nbWFpbC9EQz1jb20wHhcNMjEwNjA4MTAyMzI4WhcNMjIwNjA4
|
15
|
+
MTAyMzI4WjAmMSQwIgYDVQQDDBt0aG9yaW9uMzAwNi9EQz1nbWFpbC9EQz1jb20w
|
16
|
+
ggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQC1entXzzkGYW4W906xYuTr
|
17
|
+
6SQWJXxjPo3GrsWk2L+KXMkY94d1MyJN9IJ0LEy6M7onkmpYSzFqBQ8W+8VRaW0y
|
18
|
+
HVLX1EQRZ9hVd0LV9j5tNx2pDUfXzoxeyNllBd64Ne7r/6D93lAErATC/+v9X6QC
|
19
|
+
cHd17QBGWU8Qb0ePkYY1UvImbePdgLRwu6F2KrUPyn3ewoAvBxsAG74XU6SbORQ2
|
20
|
+
7Hhfh9vvRAcgboXmbyPW+6Z6KN7YdBFnAwI7r/9M9cMRhWwpprTPwKNdAMR0wl+l
|
21
|
+
Bdp7Se7x8fxriTl9TTpgMScGdXIc/m2Pj48W+cnk5Wf2xlcrzXptX3UYelF8f7j3
|
22
|
+
R/Ft2OTSvG0jdYW50mGD96Om0WKM6Ggfx0Dz6pvComl2LNajTSYpHB0xj8d1L6QX
|
23
|
+
UQ2kcIERhnVMqriZauA21b2J9ZLxFf+Ddou7V2AiLMlFi9cPxgj4eHrHLiOylC29
|
24
|
+
sBztOdq1mbfofNe5ifothYpj/uMKYEdXRjlKXUITCFcCAwEAAaN9MHswCQYDVR0T
|
25
|
+
BAIwADALBgNVHQ8EBAMCBLAwHQYDVR0OBBYEFP9FcvZBAdqH9D6ECTtDk6GsVUYL
|
26
|
+
MCAGA1UdEQQZMBeBFXRob3Jpb24zMDA2QGdtYWlsLmNvbTAgBgNVHRIEGTAXgRV0
|
27
|
+
aG9yaW9uMzAwNkBnbWFpbC5jb20wDQYJKoZIhvcNAQELBQADggGBAK0XGR9ezspt
|
28
|
+
S4Dy1PZ0FeGQXjtUkXcEgY6a/BnJGie9PnHRQZx4fHeVWLH3cDTsy860rTp7tmmw
|
29
|
+
Q/5NIM05AfHabnA/HVqNj/jQt1+EpQS2DGIRNiVtD+Mk/NEyggb4nvg1AJa++IpS
|
30
|
+
XmIAlpJdGwDAsLAv8JlKVbH252QFYVNVTBaikFAQ70GOxL2PnnVPl+GrC23DCpVs
|
31
|
+
eDXVMLZlUe40RTL3Ru6mEMXASB6byp4PwQ7eCaoAUlGQwvdvW09ai0jRI4zqgsAi
|
32
|
+
sSKEjVZRRZcr6wP+zvDReTG5FSVOag1G5g0SivvltgjWqDy2EmY41Qp08jtLu+iQ
|
33
|
+
m3Fy3/MP/izFtIvuQAJaZH+1l83GFI//m8V53ZH7Gg+1VFPlXXmpGQIMR9yZSDBe
|
34
|
+
ewungQjw99puFMSK2d/NFj2L2oSvamT0vGoiH63zwx02vLziB6Jbn34/tuDEqUQ9
|
35
|
+
WEUeG6yA2jP44YUZzNP+Xbjpzr/RvJxAmFw+7SNkHkmP3csIS+0TIg==
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
date: 2021-06-27 00:00:00.000000000 Z
|
38
|
+
dependencies:
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: semantic_logger
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
42
|
+
requirements:
|
43
|
+
- - "~>"
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '4.4'
|
46
|
+
type: :runtime
|
47
|
+
prerelease: false
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - "~>"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '4.4'
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: zeitwerk
|
55
|
+
requirement: !ruby/object:Gem::Requirement
|
56
|
+
requirements:
|
57
|
+
- - "~>"
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
version: '2.4'
|
60
|
+
type: :runtime
|
61
|
+
prerelease: false
|
62
|
+
version_requirements: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: '2.4'
|
67
|
+
description:
|
68
|
+
email:
|
69
|
+
- thorion3006@gmail.com
|
70
|
+
executables: []
|
71
|
+
extensions: []
|
72
|
+
extra_rdoc_files:
|
73
|
+
- README.md
|
74
|
+
- LICENSE.md
|
75
|
+
files:
|
76
|
+
- LICENSE.md
|
77
|
+
- README.md
|
78
|
+
- lib/semantic_logger_ecs_addon.rb
|
79
|
+
- lib/semantic_logger_ecs_addon/formatters/base.rb
|
80
|
+
- lib/semantic_logger_ecs_addon/formatters/json.rb
|
81
|
+
- lib/semantic_logger_ecs_addon/formatters/raw.rb
|
82
|
+
- lib/semantic_logger_ecs_addon/identity.rb
|
83
|
+
- lib/semantic_logger_ecs_addon/utils/backtrace_cleaner.rb
|
84
|
+
- lib/semantic_logger_ecs_addon/utils/hash.rb
|
85
|
+
homepage: https://github.com/thorion3006/semantic_logger_ecs_addon
|
86
|
+
licenses:
|
87
|
+
- MIT
|
88
|
+
metadata:
|
89
|
+
bug_tracker_uri: https://github.com/thorion3006/semantic_logger_ecs_addon/issues
|
90
|
+
changelog_uri: https://github.com/thorion3006/semantic_logger_ecs_addon/blob/master/CHANGES.md
|
91
|
+
documentation_uri: https://github.com/thorion3006/semantic_logger_ecs_addon
|
92
|
+
source_code_uri: https://github.com/thorion3006/semantic_logger_ecs_addon
|
93
|
+
post_install_message:
|
94
|
+
rdoc_options: []
|
95
|
+
require_paths:
|
96
|
+
- lib
|
97
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '2.7'
|
102
|
+
- - "<"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '4'
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubygems_version: 3.2.15
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: A semantic logger formatter that formats the logs according to Elastic Common
|
115
|
+
Schema and adds APM trace data if ElasticAPM is enabled.
|
116
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|