solid_log-service 0.1.0 → 0.2.0

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.
@@ -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
@@ -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