rails_loki_exporter 1.0.3 → 1.0.4
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 +4 -4
- data/lib/rails_loki_exporter/intercepting_logger.rb +8 -27
- data/lib/rails_loki_exporter/loki_http_client.rb +121 -0
- data/lib/rails_loki_exporter/version.rb +1 -1
- data/lib/rails_loki_exporter.rb +1 -13
- metadata +7 -10
- data/lib/rails_loki_exporter/client.rb +0 -92
- data/lib/rails_loki_exporter/connection.rb +0 -60
- data/lib/rails_loki_exporter/my_connection.rb +0 -8
- data/lib/rails_loki_exporter/query.rb +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bce248aa49394ba911efadd30695bc1d71a19ea2e8cd9909099339a4cb2bc5eb
|
4
|
+
data.tar.gz: c744a44f2bd0ee8f01391d0a2b8da796bc84ff71fad4ac8d1cbf7b5d95c12fc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
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
|
-
|
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
|
data/lib/rails_loki_exporter.rb
CHANGED
@@ -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.
|
4
|
+
version: 1.0.4
|
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-
|
13
|
+
date: 2024-03-04 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/
|
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.
|
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
|