semantic_logger_ecs_addon 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Gem Version](https://badge.fury.io/rb/semantic_logger_ecs_addon.svg)](http://badge.fury.io/rb/semantic_logger_ecs_addon)
|
8
|
+
[![Alchemists Style Guide](https://img.shields.io/badge/code_style-alchemists-brightgreen.svg)](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
|