solid_log-service 0.1.0 → 0.2.1
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/config/cable.yml +1 -4
- data/config.ru +53 -4
- data/lib/solid_log/service/job_processor.rb +14 -14
- data/lib/solid_log/service/rack_app.rb +382 -0
- data/lib/solid_log/service/scheduler.rb +22 -17
- data/lib/solid_log/service/version.rb +1 -1
- data/lib/solid_log/service.rb +18 -1
- metadata +64 -60
- data/app/controllers/solid_log/api/base_controller.rb +0 -80
- data/app/controllers/solid_log/api/v1/entries_controller.rb +0 -55
- data/app/controllers/solid_log/api/v1/facets_controller.rb +0 -41
- data/app/controllers/solid_log/api/v1/health_controller.rb +0 -29
- data/app/controllers/solid_log/api/v1/ingest_controller.rb +0 -80
- data/app/controllers/solid_log/api/v1/search_controller.rb +0 -31
- data/app/controllers/solid_log/api/v1/timelines_controller.rb +0 -43
- data/app/jobs/solid_log/application_job.rb +0 -4
- data/app/jobs/solid_log/cache_cleanup_job.rb +0 -16
- data/app/jobs/solid_log/field_analysis_job.rb +0 -26
- data/app/jobs/solid_log/parser_job.rb +0 -130
- data/app/jobs/solid_log/retention_job.rb +0 -24
- data/bin/solid_log_service +0 -75
- data/config/routes.rb +0 -26
- data/lib/solid_log/service/application.rb +0 -72
- data/lib/solid_log/service/engine.rb +0 -25
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
require "action_cable" if defined?(Rails)
|
|
2
|
-
|
|
3
|
-
module SolidLog
|
|
4
|
-
class ParserJob < ApplicationJob
|
|
5
|
-
queue_as :default
|
|
6
|
-
|
|
7
|
-
# Process a batch of unparsed raw entries
|
|
8
|
-
def perform(batch_size: nil)
|
|
9
|
-
batch_size ||= SolidLog.configuration.parser_batch_size
|
|
10
|
-
|
|
11
|
-
SolidLog.without_logging do
|
|
12
|
-
# Claim a batch of unparsed entries
|
|
13
|
-
raw_entries = RawEntry.claim_batch(batch_size: batch_size)
|
|
14
|
-
|
|
15
|
-
return if raw_entries.empty?
|
|
16
|
-
|
|
17
|
-
Rails.logger.info "SolidLog::ParserJob: Processing #{raw_entries.size} raw entries"
|
|
18
|
-
|
|
19
|
-
# Process each entry
|
|
20
|
-
entries_to_insert = []
|
|
21
|
-
fields_to_track = {}
|
|
22
|
-
|
|
23
|
-
raw_entries.each do |raw_entry|
|
|
24
|
-
begin
|
|
25
|
-
# Parse the raw payload
|
|
26
|
-
parsed = Parser.parse(raw_entry.payload)
|
|
27
|
-
|
|
28
|
-
# Extract dynamic fields for field registry
|
|
29
|
-
extra_fields = parsed.delete(:extra_fields) || {}
|
|
30
|
-
track_fields(fields_to_track, extra_fields)
|
|
31
|
-
|
|
32
|
-
# Prepare entry for insertion
|
|
33
|
-
entry_data = {
|
|
34
|
-
raw_id: raw_entry.id,
|
|
35
|
-
timestamp: parsed[:timestamp],
|
|
36
|
-
created_at: Time.current, # When entry was parsed/created
|
|
37
|
-
level: parsed[:level],
|
|
38
|
-
app: parsed[:app],
|
|
39
|
-
env: parsed[:env],
|
|
40
|
-
message: parsed[:message],
|
|
41
|
-
request_id: parsed[:request_id],
|
|
42
|
-
job_id: parsed[:job_id],
|
|
43
|
-
duration: parsed[:duration],
|
|
44
|
-
status_code: parsed[:status_code],
|
|
45
|
-
controller: parsed[:controller],
|
|
46
|
-
action: parsed[:action],
|
|
47
|
-
path: parsed[:path],
|
|
48
|
-
method: parsed[:method],
|
|
49
|
-
extra_fields: extra_fields.to_json
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
entries_to_insert << entry_data
|
|
53
|
-
rescue StandardError => e
|
|
54
|
-
Rails.logger.error "SolidLog::ParserJob: Failed to parse entry #{raw_entry.id}: #{e.message}"
|
|
55
|
-
Rails.logger.error e.backtrace.join("\n")
|
|
56
|
-
# Leave entry unparsed so it can be retried or investigated
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Bulk insert parsed entries
|
|
61
|
-
if entries_to_insert.any?
|
|
62
|
-
raw_ids = entries_to_insert.map { |e| e[:raw_id] }
|
|
63
|
-
|
|
64
|
-
Entry.insert_all(entries_to_insert)
|
|
65
|
-
Rails.logger.info "SolidLog::ParserJob: Inserted #{entries_to_insert.size} entries"
|
|
66
|
-
|
|
67
|
-
# Broadcast new entry IDs for live tail
|
|
68
|
-
begin
|
|
69
|
-
new_entry_ids = Entry.where(raw_id: raw_ids).pluck(:id)
|
|
70
|
-
if new_entry_ids.any?
|
|
71
|
-
ActionCable.server.broadcast(
|
|
72
|
-
"solid_log_new_entries",
|
|
73
|
-
{ entry_ids: new_entry_ids }
|
|
74
|
-
)
|
|
75
|
-
end
|
|
76
|
-
rescue NameError => e
|
|
77
|
-
Rails.logger.error "SolidLog::ParserJob: ActionCable not available: #{e.message}"
|
|
78
|
-
rescue => e
|
|
79
|
-
Rails.logger.error "SolidLog::ParserJob: Failed to broadcast: #{e.class} - #{e.message}"
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# Update field registry
|
|
84
|
-
update_field_registry(fields_to_track)
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private
|
|
89
|
-
|
|
90
|
-
# Track field occurrences for the registry
|
|
91
|
-
def track_fields(fields_hash, extra_fields)
|
|
92
|
-
extra_fields.each do |key, value|
|
|
93
|
-
fields_hash[key] ||= { values: [], count: 0 }
|
|
94
|
-
fields_hash[key][:count] += 1
|
|
95
|
-
fields_hash[key][:type] ||= infer_field_type(value)
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Update the field registry with tracked fields
|
|
100
|
-
def update_field_registry(fields_hash)
|
|
101
|
-
fields_hash.each do |name, data|
|
|
102
|
-
field = Field.find_or_initialize_by(name: name)
|
|
103
|
-
field.field_type ||= data[:type]
|
|
104
|
-
field.usage_count += data[:count]
|
|
105
|
-
field.last_seen_at = Time.current
|
|
106
|
-
field.save!
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
# Infer field type from value
|
|
111
|
-
def infer_field_type(value)
|
|
112
|
-
case value
|
|
113
|
-
when String
|
|
114
|
-
"string"
|
|
115
|
-
when Numeric
|
|
116
|
-
"number"
|
|
117
|
-
when TrueClass, FalseClass
|
|
118
|
-
"boolean"
|
|
119
|
-
when Time, DateTime, Date
|
|
120
|
-
"datetime"
|
|
121
|
-
when Array
|
|
122
|
-
"array"
|
|
123
|
-
when Hash
|
|
124
|
-
"object"
|
|
125
|
-
else
|
|
126
|
-
"string"
|
|
127
|
-
end
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
end
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
class RetentionJob < ApplicationJob
|
|
3
|
-
queue_as :default
|
|
4
|
-
|
|
5
|
-
def perform(retention_days: 30, error_retention_days: 90, vacuum: false)
|
|
6
|
-
SolidLog.without_logging do
|
|
7
|
-
Rails.logger.info "SolidLog::RetentionJob: Starting cleanup (retention: #{retention_days} days, errors: #{error_retention_days} days)"
|
|
8
|
-
|
|
9
|
-
stats = RetentionService.cleanup(
|
|
10
|
-
retention_days: retention_days,
|
|
11
|
-
error_retention_days: error_retention_days
|
|
12
|
-
)
|
|
13
|
-
|
|
14
|
-
Rails.logger.info "SolidLog::RetentionJob: Deleted #{stats[:entries_deleted]} entries, #{stats[:raw_deleted]} raw entries, cleared #{stats[:cache_cleared]} cache entries"
|
|
15
|
-
|
|
16
|
-
if vacuum
|
|
17
|
-
Rails.logger.info "SolidLog::RetentionJob: Running VACUUM..."
|
|
18
|
-
RetentionService.vacuum_database
|
|
19
|
-
Rails.logger.info "SolidLog::RetentionJob: VACUUM complete"
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
data/bin/solid_log_service
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'bundler/setup'
|
|
4
|
-
require 'rack'
|
|
5
|
-
require_relative '../lib/solid_log/service'
|
|
6
|
-
|
|
7
|
-
# Parse command line options
|
|
8
|
-
options = {
|
|
9
|
-
port: ENV['PORT'] || SolidLog::Service.configuration.port || 3001,
|
|
10
|
-
bind: ENV['BIND'] || SolidLog::Service.configuration.bind || '0.0.0.0',
|
|
11
|
-
environment: ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'production'
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
ARGV.each do |arg|
|
|
15
|
-
case arg
|
|
16
|
-
when /^--port=(\d+)$/
|
|
17
|
-
options[:port] = $1.to_i
|
|
18
|
-
when /^--bind=(.+)$/
|
|
19
|
-
options[:bind] = $1
|
|
20
|
-
when /^--environment=(.+)$/
|
|
21
|
-
options[:environment] = $1
|
|
22
|
-
when '--help', '-h'
|
|
23
|
-
puts <<~HELP
|
|
24
|
-
SolidLog Service - Standalone log ingestion and processing service
|
|
25
|
-
|
|
26
|
-
Usage: solid_log_service [options]
|
|
27
|
-
|
|
28
|
-
Options:
|
|
29
|
-
--port=PORT Port to bind to (default: 3001)
|
|
30
|
-
--bind=ADDRESS Address to bind to (default: 0.0.0.0)
|
|
31
|
-
--environment=ENV Environment (default: production)
|
|
32
|
-
--help, -h Show this help message
|
|
33
|
-
|
|
34
|
-
Environment Variables:
|
|
35
|
-
PORT Port to bind to
|
|
36
|
-
BIND Address to bind to
|
|
37
|
-
RAILS_ENV Rails environment
|
|
38
|
-
DATABASE_URL Database connection string
|
|
39
|
-
LOG_LEVEL Log level (debug, info, warn, error)
|
|
40
|
-
|
|
41
|
-
Examples:
|
|
42
|
-
solid_log_service
|
|
43
|
-
solid_log_service --port=8080 --bind=127.0.0.1
|
|
44
|
-
PORT=8080 solid_log_service
|
|
45
|
-
|
|
46
|
-
Configuration:
|
|
47
|
-
Create config/solid_log_service.rb to configure the service.
|
|
48
|
-
See README.md for configuration options.
|
|
49
|
-
HELP
|
|
50
|
-
exit 0
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Set environment
|
|
55
|
-
ENV['RAILS_ENV'] = options[:environment]
|
|
56
|
-
ENV['RACK_ENV'] = options[:environment]
|
|
57
|
-
|
|
58
|
-
puts "Starting SolidLog Service..."
|
|
59
|
-
puts " Environment: #{options[:environment]}"
|
|
60
|
-
puts " Binding: #{options[:bind]}:#{options[:port]}"
|
|
61
|
-
|
|
62
|
-
# Load the application
|
|
63
|
-
require_relative '../config.ru'
|
|
64
|
-
|
|
65
|
-
# Run with Puma
|
|
66
|
-
require 'puma/cli'
|
|
67
|
-
|
|
68
|
-
puma_args = [
|
|
69
|
-
'--bind', "tcp://#{options[:bind]}:#{options[:port]}",
|
|
70
|
-
'--environment', options[:environment],
|
|
71
|
-
'--workers', ENV.fetch('WEB_CONCURRENCY', '2'),
|
|
72
|
-
'--threads', "#{ENV.fetch('RAILS_MIN_THREADS', '5')}:#{ENV.fetch('RAILS_MAX_THREADS', '5')}"
|
|
73
|
-
]
|
|
74
|
-
|
|
75
|
-
Puma::CLI.new(puma_args).run
|
data/config/routes.rb
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
SolidLog::Service::Engine.routes.draw do
|
|
2
|
-
namespace :api do
|
|
3
|
-
namespace :v1 do
|
|
4
|
-
# Ingestion
|
|
5
|
-
post "ingest", to: "ingest#create"
|
|
6
|
-
|
|
7
|
-
# Queries
|
|
8
|
-
resources :entries, only: [:index, :show]
|
|
9
|
-
post "search", to: "search#create"
|
|
10
|
-
|
|
11
|
-
# Facets
|
|
12
|
-
get "facets", to: "facets#index"
|
|
13
|
-
get "facets/all", to: "facets#all", as: :all_facets
|
|
14
|
-
|
|
15
|
-
# Timelines
|
|
16
|
-
get "timelines/request/:request_id", to: "timelines#show_request", as: :timeline_request
|
|
17
|
-
get "timelines/job/:job_id", to: "timelines#show_job", as: :timeline_job
|
|
18
|
-
|
|
19
|
-
# Health
|
|
20
|
-
get "health", to: "health#show"
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# Root health check
|
|
25
|
-
get "/health", to: "api/v1/health#show"
|
|
26
|
-
end
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
require "rails"
|
|
2
|
-
require "action_controller/railtie"
|
|
3
|
-
require "active_record/railtie"
|
|
4
|
-
require "active_job/railtie"
|
|
5
|
-
require "action_cable/engine"
|
|
6
|
-
|
|
7
|
-
module SolidLog
|
|
8
|
-
module Service
|
|
9
|
-
class Application < Rails::Application
|
|
10
|
-
config.load_defaults 8.0
|
|
11
|
-
config.api_only = true
|
|
12
|
-
|
|
13
|
-
# Enable caching with memory store
|
|
14
|
-
config.cache_store = :memory_store
|
|
15
|
-
|
|
16
|
-
# Load service configuration
|
|
17
|
-
config.before_initialize do
|
|
18
|
-
# Load configuration file if it exists
|
|
19
|
-
config_file = Rails.root.join("config", "solid_log_service.rb")
|
|
20
|
-
require config_file if File.exist?(config_file)
|
|
21
|
-
|
|
22
|
-
# Load Action Cable configuration
|
|
23
|
-
cable_config_path = Rails.root.join("config", "cable.yml")
|
|
24
|
-
if File.exist?(cable_config_path)
|
|
25
|
-
config.action_cable.cable = YAML.load_file(cable_config_path, aliases: true)[Rails.env]
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Set up database connection
|
|
30
|
-
config.before_initialize do
|
|
31
|
-
db_config = {
|
|
32
|
-
adapter: ENV["DB_ADAPTER"] || "sqlite3",
|
|
33
|
-
database: ENV["DATABASE_URL"] || Rails.root.join("storage", "production_log.sqlite3").to_s,
|
|
34
|
-
pool: ENV.fetch("RAILS_MAX_THREADS", 5)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
ActiveRecord::Base.establish_connection(db_config)
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Start job processor after initialization (but not in console mode)
|
|
41
|
-
config.after_initialize do
|
|
42
|
-
unless defined?(Rails::Console)
|
|
43
|
-
SolidLog::Service.start!
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Stop job processor on shutdown
|
|
48
|
-
at_exit do
|
|
49
|
-
SolidLog::Service.stop!
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# Eager load controllers and jobs
|
|
53
|
-
config.eager_load_paths << Rails.root.join("app", "controllers")
|
|
54
|
-
config.eager_load_paths << Rails.root.join("app", "jobs")
|
|
55
|
-
|
|
56
|
-
# CORS configuration
|
|
57
|
-
config.middleware.insert_before 0, Rack::Cors do
|
|
58
|
-
allow do
|
|
59
|
-
origins { |source, env| SolidLog::Service.configuration.cors_origins.include?(source) || SolidLog::Service.configuration.cors_origins.include?("*") }
|
|
60
|
-
resource "*",
|
|
61
|
-
headers: :any,
|
|
62
|
-
methods: [:get, :post, :put, :patch, :delete, :options, :head],
|
|
63
|
-
credentials: false
|
|
64
|
-
end
|
|
65
|
-
end if defined?(Rack::Cors)
|
|
66
|
-
|
|
67
|
-
# Logging
|
|
68
|
-
config.logger = ActiveSupport::Logger.new(STDOUT)
|
|
69
|
-
config.log_level = ENV.fetch("LOG_LEVEL", "info").to_sym
|
|
70
|
-
end
|
|
71
|
-
end
|
|
72
|
-
end
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
require "rails/engine"
|
|
2
|
-
|
|
3
|
-
module SolidLog
|
|
4
|
-
module Service
|
|
5
|
-
class Engine < ::Rails::Engine
|
|
6
|
-
isolate_namespace SolidLog
|
|
7
|
-
|
|
8
|
-
config.generators do |g|
|
|
9
|
-
g.test_framework :minitest
|
|
10
|
-
g.fixture_replacement :factory_bot
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Ensure controllers and jobs are autoloaded
|
|
14
|
-
config.autoload_paths << root.join("app/controllers")
|
|
15
|
-
config.autoload_paths << root.join("app/jobs")
|
|
16
|
-
|
|
17
|
-
# Add SilenceMiddleware to prevent recursive logging
|
|
18
|
-
# This intercepts all requests to the service and sets Thread.current[:solid_log_silenced]
|
|
19
|
-
# so the service doesn't log its own API requests, parser jobs, etc.
|
|
20
|
-
initializer "solid_log_service.add_middleware" do |app|
|
|
21
|
-
app.middleware.use SolidLog::SilenceMiddleware
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|