onlylogs 0.1.2
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 +7 -0
- data/README.md +311 -0
- data/Rakefile +8 -0
- data/app/assets/config/onlylogs_manifest.js +2 -0
- data/app/assets/images/onlylogs/favicon/apple-touch-icon.png +0 -0
- data/app/assets/images/onlylogs/favicon/favicon-96x96.png +0 -0
- data/app/assets/images/onlylogs/favicon/favicon.ico +0 -0
- data/app/assets/images/onlylogs/favicon/favicon.svg +3 -0
- data/app/assets/images/onlylogs/favicon/site.webmanifest.erb +21 -0
- data/app/assets/images/onlylogs/favicon/web-app-manifest-192x192.png +0 -0
- data/app/assets/images/onlylogs/favicon/web-app-manifest-512x512.png +0 -0
- data/app/assets/images/onlylogs/logo.png +0 -0
- data/app/channels/onlylogs/application_cable/channel.rb +11 -0
- data/app/channels/onlylogs/logs_channel.rb +181 -0
- data/app/controllers/onlylogs/application_controller.rb +22 -0
- data/app/controllers/onlylogs/logs_controller.rb +23 -0
- data/app/helpers/onlylogs/application_helper.rb +4 -0
- data/app/javascript/onlylogs/application.js +1 -0
- data/app/javascript/onlylogs/controllers/application.js +9 -0
- data/app/javascript/onlylogs/controllers/index.js +11 -0
- data/app/javascript/onlylogs/controllers/keyboard_shortcuts_controller.js +46 -0
- data/app/javascript/onlylogs/controllers/log_streamer_controller.js +432 -0
- data/app/javascript/onlylogs/controllers/text_selection_controller.js +90 -0
- data/app/jobs/onlylogs/application_job.rb +4 -0
- data/app/models/onlylogs/ansi_color_parser.rb +78 -0
- data/app/models/onlylogs/application_record.rb +5 -0
- data/app/models/onlylogs/batch_sender.rb +61 -0
- data/app/models/onlylogs/file.rb +151 -0
- data/app/models/onlylogs/file_path_parser.rb +118 -0
- data/app/models/onlylogs/grep.rb +54 -0
- data/app/models/onlylogs/log_line.rb +24 -0
- data/app/models/onlylogs/secure_file_path.rb +31 -0
- data/app/views/home/show.html.erb +10 -0
- data/app/views/layouts/onlylogs/application.html.erb +27 -0
- data/app/views/onlylogs/logs/index.html.erb +49 -0
- data/app/views/onlylogs/shared/_log_container.html.erb +106 -0
- data/app/views/onlylogs/shared/_log_container_styles.html.erb +228 -0
- data/config/importmap.rb +6 -0
- data/config/puma_plugins/vector.rb +94 -0
- data/config/routes.rb +4 -0
- data/config/udp_logger.rb +40 -0
- data/config/vector.toml +32 -0
- data/db/migrate/20250902112548_create_books.rb +9 -0
- data/lib/onlylogs/configuration.rb +133 -0
- data/lib/onlylogs/engine.rb +39 -0
- data/lib/onlylogs/formatter.rb +14 -0
- data/lib/onlylogs/log_silencer_middleware.rb +26 -0
- data/lib/onlylogs/logger.rb +10 -0
- data/lib/onlylogs/socket_logger.rb +71 -0
- data/lib/onlylogs/version.rb +3 -0
- data/lib/onlylogs.rb +17 -0
- data/lib/puma/plugin/onlylogs_sidecar.rb +113 -0
- data/lib/tasks/onlylogs_tasks.rake +4 -0
- metadata +110 -0
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Onlylogs
|
|
2
|
+
class Formatter < ActiveSupport::Logger::SimpleFormatter
|
|
3
|
+
include ActiveSupport::TaggedLogging::Formatter
|
|
4
|
+
|
|
5
|
+
def call(severity, time, progname, msg)
|
|
6
|
+
return nil if "Onlylogs::LogsChannel".in?(msg)
|
|
7
|
+
tags = [ time.iso8601, severity[0].upcase ]
|
|
8
|
+
push_tags tags
|
|
9
|
+
str = super
|
|
10
|
+
pop_tags tags.size
|
|
11
|
+
str
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
module Onlylogs
|
|
2
|
+
class LogSilencerMiddleware
|
|
3
|
+
def initialize(app, paths_to_silence:)
|
|
4
|
+
@app = app
|
|
5
|
+
# Ensure paths are an array for flexibility
|
|
6
|
+
@paths_to_silence = Array(paths_to_silence)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
if silence_request?(env)
|
|
11
|
+
Rails.logger.silence do
|
|
12
|
+
@app.call(env)
|
|
13
|
+
end
|
|
14
|
+
else
|
|
15
|
+
@app.call(env)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def silence_request?(env)
|
|
22
|
+
request_path = env['PATH_INFO']
|
|
23
|
+
@paths_to_silence.any? { |path| request_path.start_with?(path) }
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "socket"
|
|
4
|
+
require "thread"
|
|
5
|
+
|
|
6
|
+
# This logger sends messages to onlylogs.io via a UNIX socket connected to the onlylogs sidecar process.
|
|
7
|
+
# You need to have the onlylogs sidecar running for this to work.
|
|
8
|
+
|
|
9
|
+
module Onlylogs
|
|
10
|
+
class SocketLogger < Onlylogs::Logger
|
|
11
|
+
DEFAULT_SOCKET = "tmp/sockets/onlylogs-sidecar.sock"
|
|
12
|
+
|
|
13
|
+
def initialize(local_fallback: $stdout, socket_path: ENV.fetch("ONLYLOGS_SIDECAR_SOCKET", DEFAULT_SOCKET))
|
|
14
|
+
super(local_fallback)
|
|
15
|
+
@socket_path = socket_path
|
|
16
|
+
@socket_mutex = Mutex.new
|
|
17
|
+
@socket = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def add(severity, message = nil, progname = nil, &block)
|
|
21
|
+
|
|
22
|
+
if message.nil?
|
|
23
|
+
if block_given?
|
|
24
|
+
message = block.call
|
|
25
|
+
else
|
|
26
|
+
message = progname
|
|
27
|
+
progname = nil
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
formatted = format_message(format_severity(severity), Time.now, progname, message.to_s)
|
|
32
|
+
send_to_socket(formatted)
|
|
33
|
+
super(severity, message, progname, &block)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def send_to_socket(payload)
|
|
39
|
+
return if payload.nil? || payload.empty?
|
|
40
|
+
|
|
41
|
+
socket = ensure_socket
|
|
42
|
+
socket&.puts(payload)
|
|
43
|
+
rescue Errno::EPIPE, Errno::ECONNREFUSED, Errno::ENOENT => e
|
|
44
|
+
puts "Onlylogs::SocketLogger error: #{e.message}"
|
|
45
|
+
reconnect_socket
|
|
46
|
+
rescue => e
|
|
47
|
+
puts"Onlylogs::SocketLogger unexpected error: #{e.class}: #{e.message}"
|
|
48
|
+
reconnect_socket
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def ensure_socket
|
|
52
|
+
return @socket if @socket
|
|
53
|
+
|
|
54
|
+
@socket_mutex.synchronize do
|
|
55
|
+
@socket ||= UNIXSocket.new(@socket_path)
|
|
56
|
+
rescue => e
|
|
57
|
+
warn "Unable to connect to Onlylogs sidecar (#{@socket_path}): #{e.message}"
|
|
58
|
+
@socket = nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
@socket
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def reconnect_socket
|
|
65
|
+
@socket_mutex.synchronize do
|
|
66
|
+
@socket&.close rescue nil
|
|
67
|
+
@socket = nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
data/lib/onlylogs.rb
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
require "onlylogs/version"
|
|
2
|
+
require "onlylogs/configuration"
|
|
3
|
+
require "onlylogs/engine"
|
|
4
|
+
require "onlylogs/formatter"
|
|
5
|
+
require "onlylogs/logger"
|
|
6
|
+
require "onlylogs/socket_logger"
|
|
7
|
+
|
|
8
|
+
# require "zeitwerk"
|
|
9
|
+
#
|
|
10
|
+
# loader = Zeitwerk::Loader.new
|
|
11
|
+
# loader.inflector = Zeitwerk::GemInflector.new(__FILE__)
|
|
12
|
+
# loader.push_dir(File.expand_path("..", __dir__))
|
|
13
|
+
# loader.setup
|
|
14
|
+
|
|
15
|
+
module Onlylogs
|
|
16
|
+
mattr_accessor :importmap, default: Importmap::Map.new
|
|
17
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "puma/plugin"
|
|
4
|
+
require "rbconfig"
|
|
5
|
+
require "fileutils"
|
|
6
|
+
require "timeout"
|
|
7
|
+
|
|
8
|
+
Puma::Plugin.create do
|
|
9
|
+
def start(launcher)
|
|
10
|
+
@launcher = launcher
|
|
11
|
+
@events = launcher.events
|
|
12
|
+
@log_writer = launcher.log_writer
|
|
13
|
+
@options = launcher.config.options
|
|
14
|
+
@sidecar_pid = nil
|
|
15
|
+
|
|
16
|
+
setup_paths
|
|
17
|
+
start_sidecar
|
|
18
|
+
register_hooks
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def setup_paths
|
|
24
|
+
@app_root = @options[:directory] || Dir.pwd
|
|
25
|
+
sockets_dir = File.expand_path("tmp/sockets", @app_root)
|
|
26
|
+
FileUtils.mkdir_p(sockets_dir)
|
|
27
|
+
|
|
28
|
+
@socket_path = env_or_option("ONLYLOGS_SIDECAR_SOCKET", :onlylogs_socket,
|
|
29
|
+
File.join(sockets_dir, "onlylogs-sidecar.sock"))
|
|
30
|
+
@drain_url = ENV["ONLYLOGS_DRAIN_URL"] || @options[:onlylogs_drain_url]
|
|
31
|
+
@batch_size = env_or_option("ONLYLOGS_BATCH_SIZE", :onlylogs_batch_size, 100).to_i
|
|
32
|
+
@flush_interval = env_or_option("ONLYLOGS_FLUSH_INTERVAL", :onlylogs_flush_interval, 0.5).to_f
|
|
33
|
+
@sidecar_script = env_or_option("ONLYLOGS_SIDECAR_BIN", :onlylogs_sidecar_bin,
|
|
34
|
+
File.expand_path("../../../bin/onlylogs_sidecar", __dir__))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def register_hooks
|
|
38
|
+
events = @launcher.events
|
|
39
|
+
|
|
40
|
+
events.register(:on_restart) { restart_sidecar }
|
|
41
|
+
at_exit { stop_sidecar }
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def env_or_option(env_key, option_key, default)
|
|
45
|
+
ENV.fetch(env_key, @options.fetch(option_key, default))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def start_sidecar
|
|
49
|
+
if @drain_url.to_s.strip.empty?
|
|
50
|
+
warn "ONLYLOGS_DRAIN_URL not set; skipping Onlylogs sidecar start"
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
stop_sidecar if @sidecar_pid
|
|
55
|
+
remove_socket_file
|
|
56
|
+
|
|
57
|
+
env = {
|
|
58
|
+
"ONLYLOGS_SIDECAR_SOCKET" => @socket_path,
|
|
59
|
+
"ONLYLOGS_DRAIN_URL" => @drain_url,
|
|
60
|
+
"ONLYLOGS_BATCH_SIZE" => @batch_size.to_s,
|
|
61
|
+
"ONLYLOGS_FLUSH_INTERVAL" => @flush_interval.to_s
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
info "Starting Onlylogs sidecar (socket: #{@socket_path}, drain: #{@drain_url})"
|
|
65
|
+
@sidecar_pid = Process.spawn(env, RbConfig.ruby, @sidecar_script,
|
|
66
|
+
chdir: @app_root,
|
|
67
|
+
pgroup: true)
|
|
68
|
+
rescue Errno::ENOENT => e
|
|
69
|
+
error "Unable to start sidecar: #{e.message}"
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def restart_sidecar
|
|
73
|
+
info "Restarting Onlylogs sidecar"
|
|
74
|
+
start_sidecar
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def stop_sidecar
|
|
78
|
+
return unless @sidecar_pid
|
|
79
|
+
|
|
80
|
+
info "Stopping Onlylogs sidecar"
|
|
81
|
+
pgid = Process.getpgid(@sidecar_pid)
|
|
82
|
+
Process.kill("TERM", -pgid)
|
|
83
|
+
Timeout.timeout(5) { Process.wait(@sidecar_pid) }
|
|
84
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
85
|
+
# Already stopped
|
|
86
|
+
rescue Timeout::Error
|
|
87
|
+
warn "Sidecar did not stop in time, killing"
|
|
88
|
+
Process.kill("KILL", -pgid) rescue nil
|
|
89
|
+
ensure
|
|
90
|
+
@sidecar_pid = nil
|
|
91
|
+
remove_socket_file
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def remove_socket_file
|
|
95
|
+
FileUtils.rm_f(@socket_path)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def log(message)
|
|
99
|
+
@log_writer.log(message)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def info(message)
|
|
103
|
+
log("[OnlylogsSidecar] #{message}")
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def warn(message)
|
|
107
|
+
log("[OnlylogsSidecar][WARN] #{message}")
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def error(message)
|
|
111
|
+
log("[OnlylogsSidecar][ERROR] #{message}")
|
|
112
|
+
end
|
|
113
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: onlylogs
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Alessandro Rodi
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rails
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '8.0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '8.0'
|
|
26
|
+
description: This gem includes all the tools needed to view and stream you log files
|
|
27
|
+
directly on a web interface.
|
|
28
|
+
email:
|
|
29
|
+
- alessandro.rodi@renuo.ch
|
|
30
|
+
executables: []
|
|
31
|
+
extensions: []
|
|
32
|
+
extra_rdoc_files: []
|
|
33
|
+
files:
|
|
34
|
+
- README.md
|
|
35
|
+
- Rakefile
|
|
36
|
+
- app/assets/config/onlylogs_manifest.js
|
|
37
|
+
- app/assets/images/onlylogs/favicon/apple-touch-icon.png
|
|
38
|
+
- app/assets/images/onlylogs/favicon/favicon-96x96.png
|
|
39
|
+
- app/assets/images/onlylogs/favicon/favicon.ico
|
|
40
|
+
- app/assets/images/onlylogs/favicon/favicon.svg
|
|
41
|
+
- app/assets/images/onlylogs/favicon/site.webmanifest.erb
|
|
42
|
+
- app/assets/images/onlylogs/favicon/web-app-manifest-192x192.png
|
|
43
|
+
- app/assets/images/onlylogs/favicon/web-app-manifest-512x512.png
|
|
44
|
+
- app/assets/images/onlylogs/logo.png
|
|
45
|
+
- app/channels/onlylogs/application_cable/channel.rb
|
|
46
|
+
- app/channels/onlylogs/logs_channel.rb
|
|
47
|
+
- app/controllers/onlylogs/application_controller.rb
|
|
48
|
+
- app/controllers/onlylogs/logs_controller.rb
|
|
49
|
+
- app/helpers/onlylogs/application_helper.rb
|
|
50
|
+
- app/javascript/onlylogs/application.js
|
|
51
|
+
- app/javascript/onlylogs/controllers/application.js
|
|
52
|
+
- app/javascript/onlylogs/controllers/index.js
|
|
53
|
+
- app/javascript/onlylogs/controllers/keyboard_shortcuts_controller.js
|
|
54
|
+
- app/javascript/onlylogs/controllers/log_streamer_controller.js
|
|
55
|
+
- app/javascript/onlylogs/controllers/text_selection_controller.js
|
|
56
|
+
- app/jobs/onlylogs/application_job.rb
|
|
57
|
+
- app/models/onlylogs/ansi_color_parser.rb
|
|
58
|
+
- app/models/onlylogs/application_record.rb
|
|
59
|
+
- app/models/onlylogs/batch_sender.rb
|
|
60
|
+
- app/models/onlylogs/file.rb
|
|
61
|
+
- app/models/onlylogs/file_path_parser.rb
|
|
62
|
+
- app/models/onlylogs/grep.rb
|
|
63
|
+
- app/models/onlylogs/log_line.rb
|
|
64
|
+
- app/models/onlylogs/secure_file_path.rb
|
|
65
|
+
- app/views/home/show.html.erb
|
|
66
|
+
- app/views/layouts/onlylogs/application.html.erb
|
|
67
|
+
- app/views/onlylogs/logs/index.html.erb
|
|
68
|
+
- app/views/onlylogs/shared/_log_container.html.erb
|
|
69
|
+
- app/views/onlylogs/shared/_log_container_styles.html.erb
|
|
70
|
+
- config/importmap.rb
|
|
71
|
+
- config/puma_plugins/vector.rb
|
|
72
|
+
- config/routes.rb
|
|
73
|
+
- config/udp_logger.rb
|
|
74
|
+
- config/vector.toml
|
|
75
|
+
- db/migrate/20250902112548_create_books.rb
|
|
76
|
+
- lib/onlylogs.rb
|
|
77
|
+
- lib/onlylogs/configuration.rb
|
|
78
|
+
- lib/onlylogs/engine.rb
|
|
79
|
+
- lib/onlylogs/formatter.rb
|
|
80
|
+
- lib/onlylogs/log_silencer_middleware.rb
|
|
81
|
+
- lib/onlylogs/logger.rb
|
|
82
|
+
- lib/onlylogs/socket_logger.rb
|
|
83
|
+
- lib/onlylogs/version.rb
|
|
84
|
+
- lib/puma/plugin/onlylogs_sidecar.rb
|
|
85
|
+
- lib/tasks/onlylogs_tasks.rake
|
|
86
|
+
homepage: https://onlylogs.io
|
|
87
|
+
licenses:
|
|
88
|
+
- MIT
|
|
89
|
+
metadata:
|
|
90
|
+
homepage_uri: https://onlylogs.io
|
|
91
|
+
source_code_uri: https://github.com/renuo/onlylogs
|
|
92
|
+
changelog_uri: https://github.com/renuo/onlylogs/CHANGELOG.md
|
|
93
|
+
rdoc_options: []
|
|
94
|
+
require_paths:
|
|
95
|
+
- lib
|
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
97
|
+
requirements:
|
|
98
|
+
- - ">="
|
|
99
|
+
- !ruby/object:Gem::Version
|
|
100
|
+
version: '0'
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '0'
|
|
106
|
+
requirements: []
|
|
107
|
+
rubygems_version: 3.6.9
|
|
108
|
+
specification_version: 4
|
|
109
|
+
summary: A Rails engine to view, stream, grep log files in real-time.
|
|
110
|
+
test_files: []
|