ecs_log_rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 124f6a66b55ce717ace54e2fd6021c1b8689a676ba8b4f8539c5a323960c9fd1
4
+ data.tar.gz: 2f3b5883b2fa210de148c73e4ca9a8ef8f7e0154c6795b16a621cda4e7c89bb5
5
+ SHA512:
6
+ metadata.gz: aea14dcf42ef25a68b05c9e6c5fca1cf7aea2788b6a4e02004cbc50acfc7e8432ad44c4ad9587a7d5d0838a9a87905849fab1d323bfeb2106f83cd50c5f2d047
7
+ data.tar.gz: ce426299531612a0711a2f696759312ddb3fbbf90bd2aef58a1414ff619f053f6e8b97d08a7c6b29adf4425c515000544e83ed997732fde9be4c3382b5526397
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Francesco Coda Zabetta
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.
@@ -0,0 +1,126 @@
1
+ require "logstash-event"
2
+
3
+ class EcsFormatter
4
+ attr_reader :ecs_data, :data, :service_env, :service_name, :service_type
5
+
6
+ def initialize(service_name:, service_type:, service_env:)
7
+ @ecs_data = {}
8
+ @service_name = service_name
9
+ @service_env = service_env
10
+ @service_type = service_type
11
+ end
12
+
13
+ def call(data)
14
+ @data = data
15
+ generate_ecs
16
+ event = LogStash::Event.new(deep_compact(ecs_data))
17
+ event.to_json
18
+ end
19
+
20
+ private
21
+
22
+ def generate_ecs
23
+ add_http
24
+ add_url
25
+ add_event
26
+ add_source
27
+ add_destination
28
+ add_service
29
+ add_rails
30
+ add_error
31
+ add_custom_payload
32
+ end
33
+
34
+ def add_http
35
+ ecs_add(:http, {
36
+ request: {
37
+ method: data[:method],
38
+ referrer: data[:referrer]
39
+ },
40
+ response: {
41
+ mime_type: data[:format],
42
+ status_code: data[:status]
43
+ }
44
+ })
45
+ end
46
+
47
+ def add_url
48
+ ecs_add(:url, {
49
+ path: data[:path],
50
+ original: data[:original_url]
51
+ })
52
+ end
53
+
54
+ def add_event
55
+ ecs_add(:event, {
56
+ kind: "event",
57
+ # ECS event duration in nanoseconds
58
+ duration: nanoseconds(data[:duration])
59
+ })
60
+ end
61
+
62
+ def add_source
63
+ ecs_add(:source, {
64
+ ip: data[:remote_ip]
65
+ })
66
+ end
67
+
68
+ def add_destination
69
+ ecs_add(:destination, {
70
+ ip: data[:ip],
71
+ name: data[:host]
72
+ })
73
+ end
74
+
75
+ def add_service
76
+ ecs_add(:service, {
77
+ name: service_name,
78
+ type: service_type,
79
+ environment: service_env
80
+ })
81
+ end
82
+
83
+ def add_rails
84
+ ecs_add(:rails, {
85
+ controller: data[:controller],
86
+ action: data[:action],
87
+ params: data[:params],
88
+ view_runtime: data[:view],
89
+ db_runtime: data[:db]
90
+ })
91
+ end
92
+
93
+ def add_error
94
+ ecs_add(:error, {
95
+ type: data[:exception]&.first,
96
+ message: data[:exception]&.last,
97
+ stack_trace: data[:exception_object]&.backtrace&.join("\n")
98
+ })
99
+ end
100
+
101
+ def add_custom_payload
102
+ return if data[:ecs_custom].nil?
103
+
104
+ ecs_data.merge!(data[:ecs_custom])
105
+ end
106
+
107
+ def deep_compact(hash)
108
+ res_hash = hash.map do |key, value|
109
+ value = deep_compact(value) if value.is_a?(Hash)
110
+
111
+ value = nil if [{}, []].include?(value)
112
+ [key, value]
113
+ end
114
+ res_hash.to_h.compact
115
+ end
116
+
117
+ def ecs_add(key, value)
118
+ ecs_data.store(key, value)
119
+ end
120
+
121
+ def nanoseconds(ms)
122
+ return if ms.nil?
123
+
124
+ (ms * 1_000_000).to_i
125
+ end
126
+ end
@@ -0,0 +1,10 @@
1
+ require "active_support"
2
+ require "active_support/ordered_options"
3
+
4
+ module EcsLogRails
5
+ class OrderedOptions < ActiveSupport::OrderedOptions
6
+ def custom_payload(&block)
7
+ self.custom_payload_method = block
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,20 @@
1
+ require "rails/railtie"
2
+ require "ecs_log_rails/ordered_options"
3
+
4
+ module EcsLogRails
5
+ class Railtie < Rails::Railtie
6
+ config.ecs_log_rails = EcsLogRails::OrderedOptions.new
7
+ config.ecs_log_rails.enabled = false
8
+ config.ecs_log_rails.keep_original_rails_log = true
9
+ config.ecs_log_rails.log_level = :info
10
+ config.ecs_log_rails.log_file = File.join("log", "ecs_log_rails.log")
11
+ config.ecs_log_rails.service_env = Rails.env
12
+ config.ecs_log_rails.service_type = "rails"
13
+
14
+ config.after_initialize do |app|
15
+ app.config.ecs_log_rails.service_name ||= Rails.application.class.module_parent.name
16
+
17
+ EcsLogRails.setup(app) if app.config.ecs_log_rails.enabled
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,3 @@
1
+ module EcsLogRails
2
+ VERSION = "0.1.0".freeze
3
+ end
@@ -0,0 +1,75 @@
1
+ require "lograge"
2
+ require "ecs_log_rails/ecs_formatter"
3
+
4
+ module EcsLogRails
5
+ module_function
6
+
7
+ mattr_accessor :application
8
+
9
+ def setup(app)
10
+ self.application = app
11
+ setup_lograge
12
+ setup_custom_payload
13
+ setup_logger
14
+ setup_formatter
15
+ end
16
+
17
+ def setup_lograge
18
+ # by default keep original rails log
19
+ application.config.lograge.keep_original_rails_log = ecs_log_rails_config.keep_original_rails_log
20
+ # custom options
21
+ application.config.lograge.custom_options = ->(event) do
22
+ {
23
+ original_url: event.payload[:request]&.original_url,
24
+ remote_ip: event.payload[:request]&.remote_ip,
25
+ ip: event.payload[:request]&.ip,
26
+ host: event.payload[:request]&.host,
27
+ referrer: event.payload[:request]&.referer,
28
+ params: event.payload[:params]&.except("controller", "action"),
29
+ exception: event.payload[:exception],
30
+ exception_object: event.payload[:exception_object]
31
+ }
32
+ end
33
+ Lograge.setup(application)
34
+ end
35
+
36
+ def setup_logger
37
+ Lograge.logger = ActiveSupport::Logger.new(ecs_log_rails_config.log_file)
38
+ Lograge.log_level = ecs_log_rails_config.log_level
39
+ end
40
+
41
+ def setup_formatter
42
+ Lograge.formatter = EcsFormatter.new(
43
+ service_name: ecs_log_rails_config.service_name,
44
+ service_env: ecs_log_rails_config.service_env,
45
+ service_type: ecs_log_rails_config.service_type
46
+ )
47
+ end
48
+
49
+ def setup_custom_payload
50
+ return unless ecs_log_rails_config.custom_payload_method.respond_to?(:call)
51
+
52
+ base_classes = Array(ecs_log_rails_config.base_controller_class)
53
+ base_classes.map! { |klass| klass.try(:constantize) }
54
+ base_classes << ActionController::Base if base_classes.empty?
55
+
56
+ base_classes.each do |base_class|
57
+ extend_base_class(base_class)
58
+ end
59
+ end
60
+
61
+ def extend_base_class(klass)
62
+ append_payload_method = klass.instance_method(:append_info_to_payload)
63
+ custom_payload_method = ecs_log_rails_config.custom_payload_method
64
+
65
+ klass.send(:define_method, :append_info_to_payload) do |payload|
66
+ append_payload_method.bind_call(self, payload)
67
+ payload[:custom_payload] = {ecs_custom: custom_payload_method.call(self)}
68
+ end
69
+ end
70
+
71
+ def ecs_log_rails_config
72
+ application.config.ecs_log_rails
73
+ end
74
+ end
75
+ require "ecs_log_rails/railtie" if defined?(Rails)
metadata ADDED
@@ -0,0 +1,77 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ecs_log_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Francesco Coda Zabetta
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-05-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lograge
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: logstash-event
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: Convert rails' multi-line logging into a single line JSON formatted ECS
42
+ compliant
43
+ email: francesco.codazabetta@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - lib/ecs_log_rails.rb
50
+ - lib/ecs_log_rails/ecs_formatter.rb
51
+ - lib/ecs_log_rails/ordered_options.rb
52
+ - lib/ecs_log_rails/railtie.rb
53
+ - lib/ecs_log_rails/version.rb
54
+ homepage: https://github.com/vicvega/ecs_log_rails
55
+ licenses:
56
+ - MIT
57
+ metadata: {}
58
+ post_install_message:
59
+ rdoc_options: []
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ required_rubygems_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: '0'
72
+ requirements: []
73
+ rubygems_version: 3.4.10
74
+ signing_key:
75
+ specification_version: 4
76
+ summary: Elastic Common Schema for rails' logs
77
+ test_files: []