rails_loki_exporter 1.0.2 → 1.0.4

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: bf716f8af36e10dc3e446c2dc7e044835927161c00cca433f666ef4c34afcd50
4
- data.tar.gz: fd8d98a489d8123d88e5e28b048d33fedd5d069e3d88e884ab0e1ef5a4e8ff5c
3
+ metadata.gz: bce248aa49394ba911efadd30695bc1d71a19ea2e8cd9909099339a4cb2bc5eb
4
+ data.tar.gz: c744a44f2bd0ee8f01391d0a2b8da796bc84ff71fad4ac8d1cbf7b5d95c12fc3
5
5
  SHA512:
6
- metadata.gz: b0e41dfe931cefead14aa113b5c35268757a43d517ba296ba48b410612b3f03721808d67896f64ca187cb5d36f9797c16152b2e9f50bdeef814913b4f6163cf0
7
- data.tar.gz: 2a949bbe2ef8a94c40c5096992dea844598e043a20dba564f2aa43e86030cd2a0ede0ce34cbcfe1e2def25701a81c74bdedc0e6fee1f9a7df9f3c9a4e7c02ad8
6
+ metadata.gz: 40491ac4ac77da177c05f445ad2e31332535f4784654aaeebf78d81eb1ec15e45f5ad8db6306a955b47e67d887574f22cefcd7efac8759bcd4b050f05ecb3c90
7
+ data.tar.gz: 5a5d1ec7f81b184cee101091a3330c6f7c6d31d300681f6028d28e113bb732aa8cf2690aaf99dae689218f9c473bea2c5585299a11f48fe12fa42541bddf7bd6
@@ -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 = 1 # in seconds, adjust as needed
25
+ @max_buffer_size = 10 # 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.2'
2
+ VERSION = '1.0.4'
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}"
@@ -3,11 +3,11 @@ require File.expand_path('lib/rails_loki_exporter/version', __dir__)
3
3
  Gem::Specification.new do |spec|
4
4
  spec.name = 'rails_loki_exporter'
5
5
  spec.version = RailsLokiExporter::VERSION
6
- spec.authors = ['Oleg Ten', 'Assiya Kalykova']
7
- spec.email = ['tennet0505@gmail.com']
8
- spec.summary = 'Ruby for Grafana Loki'
9
- spec.description = 'Attempt to make gem'
10
- spec.homepage = 'https://rubygems.org/gems/hello_app_ak_gem'
6
+ spec.authors = ['planning.how', 'Oleg Ten', 'Assiya Kalykova']
7
+ spec.email = ['info@planning.how']
8
+ spec.summary = 'Rails Loki exporter'
9
+ spec.description = 'Export logs for your Rails application to Loki instance and access them through Grafana dashboard.'
10
+ spec.homepage = 'https://github.com/planninghow/rails-loki-exporter'
11
11
  spec.license = 'MIT'
12
12
  spec.platform = Gem::Platform::RUBY
13
13
  spec.required_ruby_version = '>= 2.7.0'
metadata CHANGED
@@ -1,15 +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.2
4
+ version: 1.0.4
5
5
  platform: ruby
6
6
  authors:
7
+ - planning.how
7
8
  - Oleg Ten
8
9
  - Assiya Kalykova
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2024-02-02 00:00:00.000000000 Z
13
+ date: 2024-03-04 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: zeitwerk
@@ -39,9 +40,10 @@ dependencies:
39
40
  - - ">="
40
41
  - !ruby/object:Gem::Version
41
42
  version: '0'
42
- description: Attempt to make gem
43
+ description: Export logs for your Rails application to Loki instance and access them
44
+ through Grafana dashboard.
43
45
  email:
44
- - tennet0505@gmail.com
46
+ - info@planning.how
45
47
  executables: []
46
48
  extensions: []
47
49
  extra_rdoc_files:
@@ -50,15 +52,12 @@ files:
50
52
  - Gemfile
51
53
  - README.md
52
54
  - lib/rails_loki_exporter.rb
53
- - lib/rails_loki_exporter/client.rb
54
- - lib/rails_loki_exporter/connection.rb
55
55
  - lib/rails_loki_exporter/custom_log_subscriber.rb
56
56
  - lib/rails_loki_exporter/intercepting_logger.rb
57
- - lib/rails_loki_exporter/my_connection.rb
58
- - lib/rails_loki_exporter/query.rb
57
+ - lib/rails_loki_exporter/loki_http_client.rb
59
58
  - lib/rails_loki_exporter/version.rb
60
59
  - rails_loki_exporter.gemspec
61
- homepage: https://rubygems.org/gems/hello_app_ak_gem
60
+ homepage: https://github.com/planninghow/rails-loki-exporter
62
61
  licenses:
63
62
  - MIT
64
63
  metadata: {}
@@ -80,5 +79,5 @@ requirements: []
80
79
  rubygems_version: 3.1.6
81
80
  signing_key:
82
81
  specification_version: 4
83
- summary: Ruby for Grafana Loki
82
+ summary: Rails Loki exporter
84
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