rails_loki_exporter 1.0.3 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f11bbef4b6c0211c1df8c50080abc4b9854cad8cc25cc8f50d475ae00e38241a
4
- data.tar.gz: 6543c6c82ea8c379aabef263cd19ce18e74b3ad1751dd9c5932a12292334f43a
3
+ metadata.gz: 733316b790e527c137a95c3a5796b22d1367f53536d9afdaaca0ed8d5afe851d
4
+ data.tar.gz: b9ce3c6abaf28609f52d2783028a43635c45f3e87fdcadcebd892d08603cebe8
5
5
  SHA512:
6
- metadata.gz: dcde3cc9ce2aa6f8ae02bbdabfd28626a5b95e6be56009223051c72755aa8dd8c53b7cf0cac8103c00bf5f1faa6c373842398d993f15ec4655a36b6312ba13ac
7
- data.tar.gz: b41d24af30d8eb94e8c7fc16c93f410ae49e3db638e915748315e3eda5628424e9edfa405a57e00c5371910d17b0528361faff0c92d681aa47cea51b5a663a01
6
+ metadata.gz: 3ad48d68cda1540c115a7bcfbea1f739ec38b4b373633189d3e1c00daeff525b0e645ca6e37360cee32f0ba86c31baeb0c2f88042b9164fbd088c95fb635d081
7
+ data.tar.gz: d8c4111d0e92fa6b58141cc5eee83f36827d889d4e72304a949c1b00065ad2810ab9c457d8a9c5099c4cb7393447cc0df9f06b324368357aa16b47e76bc36fd1
data/README.md CHANGED
@@ -32,6 +32,8 @@ user_name: 'Your User number'
32
32
  password: 'Your Grafana.com API Token'
33
33
  log_file_path: 'log/#{Rails.env}.log'
34
34
  logs_type: '%w(ERROR WARN FATAL INFO DEBUG)'
35
+ interaction_interval: 5
36
+ max_buffer_size: 100
35
37
  intercept_logs: true
36
38
  ```
37
39
  - Add block for **Rails Loki Exporter** in your `application.rb` file:
@@ -8,7 +8,7 @@ module RailsLokiExporter
8
8
  class InterceptingLogger < ActiveSupport::Logger
9
9
  attr_accessor :client
10
10
 
11
- SEVERITY_NAMES = %w(DEBUG INFO WARN ERROR FATAL).freeze
11
+ SEVERITY_NAMES = %w(DEBUG INFO WARN ERROR FATAL ANY).freeze
12
12
 
13
13
  def initialize(intercept_logs: false)
14
14
  @intercept_logs = intercept_logs
@@ -23,37 +23,18 @@ module RailsLokiExporter
23
23
  if log_message.nil?
24
24
  if block_given?
25
25
  log_message = yield
26
- end
27
- end
28
-
29
- if @intercept_logs
30
- if log_message.nil?
31
- puts caller
32
26
  else
33
- client.send_log(@log) if client
27
+ log_message = progname
28
+ progname = @progname
34
29
  end
35
30
  end
36
- super(severity, message, progname, &block)
37
- end
38
-
39
- def debug(log_message = "")
40
- client.send_log("#{log_message}") if client
41
- end
42
31
 
43
- def info(log_message = "")
44
- client.send_log("#{log_message}") if client
45
- end
46
-
47
- def fatal(log_message = "")
48
- client.send_log("#{log_message}") if client
49
- end
50
-
51
- def warn(log_message = "")
52
- client.send_log("#{log_message}") if client
53
- end
32
+ if @intercept_logs && !log_message.nil?
33
+ formatted_message = format_message(severity_name, Time.now, progname, log_message)
34
+ client.send_log(formatted_message) if client
35
+ end
54
36
 
55
- def error(log_message = "")
56
- client.send_log("#{log_message}") if client
37
+ super(severity, message, progname, &block)
57
38
  end
58
39
 
59
40
  def broadcast_to(console)
@@ -0,0 +1,121 @@
1
+ require 'socket'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'uri'
5
+
6
+ module RailsLokiExporter
7
+ LOGS_TYPE = %w(ERROR WARN FATAL INFO DEBUG).freeze
8
+
9
+ class LokiHttpClient
10
+
11
+ def initialize(config)
12
+ uri = URI.parse(config['base_url'])
13
+ @base_url = uri
14
+ @log_file_path = config['log_file_path']
15
+ @logs_type = config['logs_type']
16
+ @intercept_logs = config['intercept_logs']
17
+ @job_name = config['job_name'] || "#{$0}_#{Process.pid}"
18
+ @host_name = config['host_name'] || Socket.gethostname
19
+ @user_name = config['user_name']
20
+ @password = config['password']
21
+ @auth_enabled = config['auth_enabled']
22
+ @log_buffer = []
23
+ @last_interaction_time = nil
24
+ @interaction_interval = (config['interaction_interval'] || '5').to_i # in seconds, adjust as needed
25
+ @max_buffer_size = (config['max_buffer_size'] || '100').to_i # set the maximum number of logs to buffer
26
+
27
+ http = Net::HTTP.new(@base_url.to_s, @base_url.port)
28
+ http.use_ssl = @base_url.scheme == 'https'
29
+ http.read_timeout = 30
30
+ http.open_timeout = 30
31
+ http
32
+ end
33
+
34
+ def send_log(log_message)
35
+ @log_buffer << log_message
36
+ if @log_buffer.size >= @max_buffer_size || can_send_log?
37
+ send_buffered_logs
38
+ @last_interaction_time = Time.now
39
+ else
40
+ # @logger.info('Log buffered. Waiting for more logs or interaction interval.')
41
+ end
42
+ end
43
+
44
+ private
45
+ def send_buffered_logs
46
+ return if @log_buffer.empty?
47
+
48
+ curr_datetime = Time.now.to_i * 1_000_000_000
49
+ msg = "On server #{@host_name} detected error"
50
+ payload = {
51
+ 'streams' => [
52
+ { 'stream' => {
53
+ 'job' => @job_name,
54
+ 'host' => @host_name },
55
+ 'values' => @log_buffer.map { |log| [curr_datetime.to_s, log] }}
56
+ ]
57
+ }
58
+
59
+ json_payload = JSON.generate(payload)
60
+ uri = '/loki/api/v1/push'
61
+ post(uri, json_payload)
62
+
63
+ @log_buffer.clear
64
+ end
65
+
66
+ def can_send_log?
67
+ return true if @last_interaction_time.nil?
68
+
69
+ elapsed_time = Time.now - @last_interaction_time
70
+ elapsed_time >= @interaction_interval
71
+ end
72
+
73
+ def match_logs_type?(log_line)
74
+ return false if log_line.nil?
75
+
76
+ type_match = log_line.match(/(ERROR|WARN|FATAL|INFO|DEBUG)/)
77
+ type = type_match ? type_match.to_s : 'UNMATCHED'
78
+ type == 'UNMATCHED' || @logs_type.include?(type)
79
+ end
80
+
81
+ def post(url_loki, body)
82
+ url = @base_url.to_s + url_loki
83
+ username = @user_name
84
+ password = @password
85
+ send_authenticated_post(url, body, username, password)
86
+ end
87
+
88
+ def send_authenticated_post(url, body, username, password)
89
+ uri = URI.parse(url)
90
+ request = Net::HTTP::Post.new(uri.path)
91
+ request['Content-Type'] = 'application/json'
92
+ request.body = body
93
+
94
+ if username && password && @auth_enabled
95
+ request.basic_auth(username, password)
96
+ else
97
+ raise "Username or password is nil."
98
+ end
99
+ response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
100
+ http.request(request)
101
+ end
102
+
103
+ case response
104
+ when Net::HTTPSuccess
105
+ response.body ? JSON.parse(response.body) : nil
106
+ when Net::HTTPNoContent
107
+ puts "Request successful, but no content was returned."
108
+ nil
109
+ else
110
+ raise "Failed to make POST request. Response code: #{response.code}, Response body: #{response.body}"
111
+ end
112
+ rescue StandardError => e
113
+ puts "Error: #{e.message}"
114
+ end
115
+
116
+ def base64_encode_credentials(user_name, password)
117
+ credentials = "#{user_name}:#{password}"
118
+ Base64.strict_encode64(credentials)
119
+ end
120
+ end
121
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsLokiExporter
2
- VERSION = '1.0.3'
2
+ VERSION = '1.0.5'
3
3
  end
@@ -7,23 +7,12 @@ module RailsLokiExporter
7
7
  class << self
8
8
  def create_logger(config_file_path)
9
9
  config = load_config(config_file_path)
10
-
11
- connection_instance = MyConnection.new(
12
- config['base_url'],
13
- config['user_name'],
14
- config['password'],
15
- config['auth_enabled'],
16
- config['host_name'],
17
- config['job_name']
18
- )
19
-
20
- client = Client.new(config)
10
+ client = LokiHttpClient.new(config)
21
11
  logger = InterceptingLogger.new(intercept_logs: config['intercept_logs'])
22
12
  if config['enable_log_subscriber']
23
13
  CustomLogSubscriber.client = client
24
14
  end
25
15
  logger.client = client
26
- client.connection = connection_instance
27
16
  logger
28
17
  end
29
18
 
@@ -35,7 +24,6 @@ module RailsLokiExporter
35
24
  if File.exist?(expanded_path)
36
25
  config_erb = ERB.new(File.read(expanded_path)).result
37
26
  config = YAML.safe_load(config_erb, aliases: true)
38
- puts config.to_json
39
27
  return config
40
28
  else
41
29
  puts "Config file not found: #{expanded_path}"
metadata CHANGED
@@ -1,16 +1,16 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_loki_exporter
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 1.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - planning.how
8
8
  - Oleg Ten
9
9
  - Assiya Kalykova
10
- autorequire:
10
+ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2024-02-03 00:00:00.000000000 Z
13
+ date: 2024-04-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: zeitwerk
@@ -52,19 +52,16 @@ files:
52
52
  - Gemfile
53
53
  - README.md
54
54
  - lib/rails_loki_exporter.rb
55
- - lib/rails_loki_exporter/client.rb
56
- - lib/rails_loki_exporter/connection.rb
57
55
  - lib/rails_loki_exporter/custom_log_subscriber.rb
58
56
  - lib/rails_loki_exporter/intercepting_logger.rb
59
- - lib/rails_loki_exporter/my_connection.rb
60
- - lib/rails_loki_exporter/query.rb
57
+ - lib/rails_loki_exporter/loki_http_client.rb
61
58
  - lib/rails_loki_exporter/version.rb
62
59
  - rails_loki_exporter.gemspec
63
60
  homepage: https://github.com/planninghow/rails-loki-exporter
64
61
  licenses:
65
62
  - MIT
66
63
  metadata: {}
67
- post_install_message:
64
+ post_install_message:
68
65
  rdoc_options: []
69
66
  require_paths:
70
67
  - lib
@@ -79,8 +76,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
79
76
  - !ruby/object:Gem::Version
80
77
  version: '0'
81
78
  requirements: []
82
- rubygems_version: 3.4.22
83
- signing_key:
79
+ rubygems_version: 3.1.6
80
+ signing_key:
84
81
  specification_version: 4
85
82
  summary: Rails Loki exporter
86
83
  test_files: []
@@ -1,92 +0,0 @@
1
- require 'socket'
2
- module RailsLokiExporter
3
- LOGS_TYPE = %w(ERROR WARN FATAL INFO DEBUG).freeze
4
-
5
- class Client
6
- include RailsLokiExporter::Connection
7
-
8
- attr_accessor :job_name
9
- attr_accessor :host_name
10
- attr_accessor :max_buffer_size
11
- attr_accessor :interaction_interval
12
- attr_accessor :connection
13
-
14
- def initialize(config)
15
- @base_url = config['base_url']
16
- @log_file_path = config['log_file_path']
17
- @logs_type = config['logs_type']
18
- @intercept_logs = config['intercept_logs']
19
- @job_name = config['job_name'] || "#{$0}_#{Process.pid}"
20
- @host_name = config['host_name'] || Socket.gethostname
21
- @log_buffer = []
22
- @last_interaction_time = nil
23
- @interaction_interval = 1 # in seconds, adjust as needed
24
- @max_buffer_size = 10 # set the maximum number of logs to buffer
25
- @connection = connection
26
- end
27
-
28
- def send_all_logs
29
- File.open(@log_file_path, 'r') do |file|
30
- file.each_line do |line|
31
- send_log(line)
32
- end
33
- end
34
- end
35
-
36
- def send_log(log_message)
37
- @log_buffer << log_message
38
- if @log_buffer.size >= @max_buffer_size || can_send_log?
39
- send_buffered_logs
40
- @last_interaction_time = Time.now
41
- else
42
- # @logger.info('Log buffered. Waiting for more logs or interaction interval.')
43
- end
44
- end
45
-
46
- private
47
- def send_buffered_logs
48
- return if @log_buffer.empty?
49
-
50
- curr_datetime = Time.now.to_i * 1_000_000_000
51
- msg = "On server #{@host_name} detected error"
52
- payload = {
53
- 'streams' => [
54
- {
55
- 'stream' => {
56
- 'job' => @job_name,
57
- 'host' => @host_name
58
- },
59
- 'values' => @log_buffer.map { |log| [curr_datetime.to_s, log] },
60
- 'entries' => @log_buffer.map do |_|
61
- {
62
- 'ts' => curr_datetime,
63
- 'line' => "[WARN] " + msg
64
- }
65
- end
66
- }
67
- ]
68
- }
69
-
70
- json_payload = JSON.generate(payload)
71
- uri = '/loki/api/v1/push'
72
- @connection.post(uri, json_payload)
73
-
74
- @log_buffer.clear
75
- end
76
-
77
- def can_send_log?
78
- return true if @last_interaction_time.nil?
79
-
80
- elapsed_time = Time.now - @last_interaction_time
81
- elapsed_time >= @interaction_interval
82
- end
83
-
84
- def match_logs_type?(log_line)
85
- return false if log_line.nil?
86
-
87
- type_match = log_line.match(/(ERROR|WARN|FATAL|INFO|DEBUG)/)
88
- type = type_match ? type_match.to_s : 'UNMATCHED'
89
- type == 'UNMATCHED' || @logs_type.include?(type)
90
- end
91
- end
92
- end
@@ -1,60 +0,0 @@
1
- require 'json'
2
- require 'net/http'
3
- require 'uri'
4
-
5
- module RailsLokiExporter
6
- module Connection
7
- def initialize(base_url, user_name, password, auth_enabled, host_name, job_name)
8
- uri = URI.parse(base_url)
9
- @base_url = uri
10
- @user_name = user_name
11
- @password = password
12
- @auth_enabled = auth_enabled
13
- end
14
- def connection
15
- http = Net::HTTP.new(@base_url.to_s, @base_url.port)
16
- http.use_ssl = @base_url.scheme == 'https'
17
- http.read_timeout = 30 # Adjust as needed
18
- http.open_timeout = 30 # Adjust as needed
19
- http
20
- end
21
- def post(url_loki, body)
22
- url = @base_url.to_s + url_loki
23
- username = @user_name
24
- password = @password
25
- send_authenticated_post(url, body, username, password)
26
- end
27
- def send_authenticated_post(url, body, username, password)
28
- uri = URI.parse(url)
29
- request = Net::HTTP::Post.new(uri.path)
30
- request['Content-Type'] = 'application/json'
31
- request.body = body
32
-
33
- if username && password && @auth_enabled
34
- request.basic_auth(username, password)
35
- else
36
- raise "Username or password is nil."
37
- end
38
-
39
- response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
40
- http.request(request)
41
- end
42
-
43
- case response
44
- when Net::HTTPSuccess
45
- response.body ? JSON.parse(response.body) : nil
46
- when Net::HTTPNoContent
47
- puts "Request successful, but no content was returned."
48
- nil
49
- else
50
- raise "Failed to make POST request. Response code: #{response.code}, Response body: #{response.body}"
51
- end
52
- rescue StandardError => e
53
- puts "Error: #{e.message}"
54
- end
55
- def base64_encode_credentials(user_name, password)
56
- credentials = "#{user_name}:#{password}"
57
- Base64.strict_encode64(credentials)
58
- end
59
- end
60
- end
@@ -1,8 +0,0 @@
1
- module RailsLokiExporter
2
- class MyConnection
3
- include Connection
4
- def self.create(base_url, user_name, password, auth_enabled, host_name, job_name)
5
- new(base_url, user_name, password, auth_enabled, host_name, job_name).connection
6
- end
7
- end
8
- end
@@ -1,8 +0,0 @@
1
- module RailsLokiExporter
2
- class Query
3
- attr_reader :streams_data
4
- def initialize(response)
5
- @streams_data = response['streams']
6
- end
7
- end
8
- end