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
metadata
CHANGED
|
@@ -1,143 +1,163 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_log-service
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Dan Loman
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: solid_log-core
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: 0.2.1
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: 0.2.1
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rack
|
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
|
16
30
|
requirements:
|
|
17
31
|
- - "~>"
|
|
18
32
|
- !ruby/object:Gem::Version
|
|
19
|
-
version:
|
|
33
|
+
version: '3.0'
|
|
20
34
|
type: :runtime
|
|
21
35
|
prerelease: false
|
|
22
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
37
|
requirements:
|
|
24
38
|
- - "~>"
|
|
25
39
|
- !ruby/object:Gem::Version
|
|
26
|
-
version:
|
|
40
|
+
version: '3.0'
|
|
27
41
|
- !ruby/object:Gem::Dependency
|
|
28
|
-
name:
|
|
42
|
+
name: puma
|
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
|
30
44
|
requirements:
|
|
31
45
|
- - ">="
|
|
32
46
|
- !ruby/object:Gem::Version
|
|
33
|
-
version:
|
|
47
|
+
version: '6'
|
|
48
|
+
- - "<"
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: '8'
|
|
34
51
|
type: :runtime
|
|
35
52
|
prerelease: false
|
|
36
53
|
version_requirements: !ruby/object:Gem::Requirement
|
|
37
54
|
requirements:
|
|
38
55
|
- - ">="
|
|
39
56
|
- !ruby/object:Gem::Version
|
|
40
|
-
version:
|
|
57
|
+
version: '6'
|
|
58
|
+
- - "<"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '8'
|
|
41
61
|
- !ruby/object:Gem::Dependency
|
|
42
|
-
name:
|
|
62
|
+
name: activesupport
|
|
43
63
|
requirement: !ruby/object:Gem::Requirement
|
|
44
64
|
requirements:
|
|
45
65
|
- - "~>"
|
|
46
66
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: '
|
|
67
|
+
version: '8.0'
|
|
48
68
|
type: :runtime
|
|
49
69
|
prerelease: false
|
|
50
70
|
version_requirements: !ruby/object:Gem::Requirement
|
|
51
71
|
requirements:
|
|
52
72
|
- - "~>"
|
|
53
73
|
- !ruby/object:Gem::Version
|
|
54
|
-
version: '
|
|
74
|
+
version: '8.0'
|
|
55
75
|
- !ruby/object:Gem::Dependency
|
|
56
|
-
name:
|
|
76
|
+
name: activerecord
|
|
57
77
|
requirement: !ruby/object:Gem::Requirement
|
|
58
78
|
requirements:
|
|
59
|
-
- - "
|
|
79
|
+
- - "~>"
|
|
60
80
|
- !ruby/object:Gem::Version
|
|
61
|
-
version: '
|
|
62
|
-
type: :
|
|
81
|
+
version: '8.0'
|
|
82
|
+
type: :runtime
|
|
63
83
|
prerelease: false
|
|
64
84
|
version_requirements: !ruby/object:Gem::Requirement
|
|
65
85
|
requirements:
|
|
66
|
-
- - "
|
|
86
|
+
- - "~>"
|
|
67
87
|
- !ruby/object:Gem::Version
|
|
68
|
-
version: '
|
|
88
|
+
version: '8.0'
|
|
69
89
|
- !ruby/object:Gem::Dependency
|
|
70
|
-
name:
|
|
90
|
+
name: actioncable
|
|
71
91
|
requirement: !ruby/object:Gem::Requirement
|
|
72
92
|
requirements:
|
|
73
|
-
- - "
|
|
93
|
+
- - "~>"
|
|
74
94
|
- !ruby/object:Gem::Version
|
|
75
|
-
version: '
|
|
76
|
-
type: :
|
|
95
|
+
version: '8.0'
|
|
96
|
+
type: :runtime
|
|
77
97
|
prerelease: false
|
|
78
98
|
version_requirements: !ruby/object:Gem::Requirement
|
|
79
99
|
requirements:
|
|
80
|
-
- - "
|
|
100
|
+
- - "~>"
|
|
81
101
|
- !ruby/object:Gem::Version
|
|
82
|
-
version: '
|
|
102
|
+
version: '8.0'
|
|
83
103
|
- !ruby/object:Gem::Dependency
|
|
84
|
-
name:
|
|
104
|
+
name: rack-cors
|
|
85
105
|
requirement: !ruby/object:Gem::Requirement
|
|
86
106
|
requirements:
|
|
87
107
|
- - "~>"
|
|
88
108
|
- !ruby/object:Gem::Version
|
|
89
|
-
version: '
|
|
90
|
-
type: :
|
|
109
|
+
version: '2.0'
|
|
110
|
+
type: :runtime
|
|
91
111
|
prerelease: false
|
|
92
112
|
version_requirements: !ruby/object:Gem::Requirement
|
|
93
113
|
requirements:
|
|
94
114
|
- - "~>"
|
|
95
115
|
- !ruby/object:Gem::Version
|
|
96
|
-
version: '
|
|
116
|
+
version: '2.0'
|
|
97
117
|
- !ruby/object:Gem::Dependency
|
|
98
|
-
name:
|
|
118
|
+
name: sqlite3
|
|
99
119
|
requirement: !ruby/object:Gem::Requirement
|
|
100
120
|
requirements:
|
|
101
|
-
- - "
|
|
121
|
+
- - ">="
|
|
102
122
|
- !ruby/object:Gem::Version
|
|
103
|
-
version: '
|
|
123
|
+
version: '2.1'
|
|
104
124
|
type: :development
|
|
105
125
|
prerelease: false
|
|
106
126
|
version_requirements: !ruby/object:Gem::Requirement
|
|
107
127
|
requirements:
|
|
108
|
-
- - "
|
|
128
|
+
- - ">="
|
|
109
129
|
- !ruby/object:Gem::Version
|
|
110
|
-
version: '
|
|
130
|
+
version: '2.1'
|
|
111
131
|
- !ruby/object:Gem::Dependency
|
|
112
|
-
name:
|
|
132
|
+
name: minitest
|
|
113
133
|
requirement: !ruby/object:Gem::Requirement
|
|
114
134
|
requirements:
|
|
115
135
|
- - ">="
|
|
116
136
|
- !ruby/object:Gem::Version
|
|
117
|
-
version: '0'
|
|
137
|
+
version: '5.0'
|
|
118
138
|
type: :development
|
|
119
139
|
prerelease: false
|
|
120
140
|
version_requirements: !ruby/object:Gem::Requirement
|
|
121
141
|
requirements:
|
|
122
142
|
- - ">="
|
|
123
143
|
- !ruby/object:Gem::Version
|
|
124
|
-
version: '0'
|
|
144
|
+
version: '5.0'
|
|
125
145
|
- !ruby/object:Gem::Dependency
|
|
126
|
-
name:
|
|
146
|
+
name: rake
|
|
127
147
|
requirement: !ruby/object:Gem::Requirement
|
|
128
148
|
requirements:
|
|
129
|
-
- - "
|
|
149
|
+
- - "~>"
|
|
130
150
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '0'
|
|
151
|
+
version: '13.0'
|
|
132
152
|
type: :development
|
|
133
153
|
prerelease: false
|
|
134
154
|
version_requirements: !ruby/object:Gem::Requirement
|
|
135
155
|
requirements:
|
|
136
|
-
- - "
|
|
156
|
+
- - "~>"
|
|
137
157
|
- !ruby/object:Gem::Version
|
|
138
|
-
version: '0'
|
|
158
|
+
version: '13.0'
|
|
139
159
|
- !ruby/object:Gem::Dependency
|
|
140
|
-
name:
|
|
160
|
+
name: debug
|
|
141
161
|
requirement: !ruby/object:Gem::Requirement
|
|
142
162
|
requirements:
|
|
143
163
|
- - ">="
|
|
@@ -151,7 +171,7 @@ dependencies:
|
|
|
151
171
|
- !ruby/object:Gem::Version
|
|
152
172
|
version: '0'
|
|
153
173
|
- !ruby/object:Gem::Dependency
|
|
154
|
-
name:
|
|
174
|
+
name: rubocop
|
|
155
175
|
requirement: !ruby/object:Gem::Requirement
|
|
156
176
|
requirements:
|
|
157
177
|
- - ">="
|
|
@@ -165,7 +185,7 @@ dependencies:
|
|
|
165
185
|
- !ruby/object:Gem::Version
|
|
166
186
|
version: '0'
|
|
167
187
|
- !ruby/object:Gem::Dependency
|
|
168
|
-
name:
|
|
188
|
+
name: rubocop-rails-omakase
|
|
169
189
|
requirement: !ruby/object:Gem::Requirement
|
|
170
190
|
requirements:
|
|
171
191
|
- - ">="
|
|
@@ -183,36 +203,20 @@ description: Provides HTTP API for log ingestion, background processing with bui
|
|
|
183
203
|
Rails apps.
|
|
184
204
|
email:
|
|
185
205
|
- daniel.h.loman@gmail.com
|
|
186
|
-
executables:
|
|
187
|
-
- solid_log_service
|
|
206
|
+
executables: []
|
|
188
207
|
extensions: []
|
|
189
208
|
extra_rdoc_files: []
|
|
190
209
|
files:
|
|
191
210
|
- MIT-LICENSE
|
|
192
211
|
- README.md
|
|
193
212
|
- Rakefile
|
|
194
|
-
- app/controllers/solid_log/api/base_controller.rb
|
|
195
|
-
- app/controllers/solid_log/api/v1/entries_controller.rb
|
|
196
|
-
- app/controllers/solid_log/api/v1/facets_controller.rb
|
|
197
|
-
- app/controllers/solid_log/api/v1/health_controller.rb
|
|
198
|
-
- app/controllers/solid_log/api/v1/ingest_controller.rb
|
|
199
|
-
- app/controllers/solid_log/api/v1/search_controller.rb
|
|
200
|
-
- app/controllers/solid_log/api/v1/timelines_controller.rb
|
|
201
|
-
- app/jobs/solid_log/application_job.rb
|
|
202
|
-
- app/jobs/solid_log/cache_cleanup_job.rb
|
|
203
|
-
- app/jobs/solid_log/field_analysis_job.rb
|
|
204
|
-
- app/jobs/solid_log/parser_job.rb
|
|
205
|
-
- app/jobs/solid_log/retention_job.rb
|
|
206
|
-
- bin/solid_log_service
|
|
207
213
|
- config.ru
|
|
208
214
|
- config/cable.yml
|
|
209
|
-
- config/routes.rb
|
|
210
215
|
- lib/solid_log-service.rb
|
|
211
216
|
- lib/solid_log/service.rb
|
|
212
|
-
- lib/solid_log/service/application.rb
|
|
213
217
|
- lib/solid_log/service/configuration.rb
|
|
214
|
-
- lib/solid_log/service/engine.rb
|
|
215
218
|
- lib/solid_log/service/job_processor.rb
|
|
219
|
+
- lib/solid_log/service/rack_app.rb
|
|
216
220
|
- lib/solid_log/service/scheduler.rb
|
|
217
221
|
- lib/solid_log/service/version.rb
|
|
218
222
|
homepage: https://github.com/namolnad/solid_log
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
class BaseController < ActionController::API
|
|
4
|
-
before_action :authenticate_token!
|
|
5
|
-
|
|
6
|
-
rescue_from ActionDispatch::Http::Parameters::ParseError do |exception|
|
|
7
|
-
render json: {
|
|
8
|
-
error: "Invalid JSON",
|
|
9
|
-
message: exception.message
|
|
10
|
-
}, status: :unprocessable_entity
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
rescue_from ActionController::BadRequest do |exception|
|
|
14
|
-
render json: {
|
|
15
|
-
error: "Invalid JSON",
|
|
16
|
-
message: exception.message
|
|
17
|
-
}, status: :unprocessable_entity
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
rescue_from StandardError do |exception|
|
|
21
|
-
# Check if it's a parameter parsing error based on message
|
|
22
|
-
if exception.message.include?("parsing request parameters")
|
|
23
|
-
render json: {
|
|
24
|
-
error: "Invalid JSON",
|
|
25
|
-
message: exception.message
|
|
26
|
-
}, status: :unprocessable_entity
|
|
27
|
-
else
|
|
28
|
-
Rails.logger.error "SolidLog API Error: #{exception.message}"
|
|
29
|
-
Rails.logger.error exception.backtrace.join("\n")
|
|
30
|
-
|
|
31
|
-
render json: {
|
|
32
|
-
error: "Internal server error",
|
|
33
|
-
message: exception.message
|
|
34
|
-
}, status: :internal_server_error
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
rescue_from ActiveRecord::RecordInvalid do |exception|
|
|
39
|
-
render json: {
|
|
40
|
-
error: "Validation error",
|
|
41
|
-
details: exception.record.errors.full_messages
|
|
42
|
-
}, status: :unprocessable_entity
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
private
|
|
46
|
-
|
|
47
|
-
def authenticate_token!
|
|
48
|
-
token_value = extract_bearer_token
|
|
49
|
-
|
|
50
|
-
unless token_value
|
|
51
|
-
render json: { error: "Missing or invalid Authorization header" }, status: :unauthorized
|
|
52
|
-
return
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
@current_token = SolidLog::Token.authenticate(token_value)
|
|
56
|
-
|
|
57
|
-
unless @current_token
|
|
58
|
-
render json: { error: "Invalid token" }, status: :unauthorized
|
|
59
|
-
return
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
# Touch last_used_at timestamp
|
|
63
|
-
@current_token.touch_last_used!
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
def current_token
|
|
67
|
-
@current_token
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def extract_bearer_token
|
|
71
|
-
header = request.headers["Authorization"]
|
|
72
|
-
return nil unless header
|
|
73
|
-
|
|
74
|
-
# Expected format: "Bearer <token>"
|
|
75
|
-
matches = header.match(/^Bearer (.+)$/i)
|
|
76
|
-
matches[1] if matches
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class EntriesController < Api::BaseController
|
|
5
|
-
# GET /api/v1/entries
|
|
6
|
-
def index
|
|
7
|
-
search_service = SolidLog::SearchService.new(filter_params)
|
|
8
|
-
entries = search_service.search
|
|
9
|
-
|
|
10
|
-
render json: {
|
|
11
|
-
entries: entries.as_json(methods: [:extra_fields_hash]),
|
|
12
|
-
total: entries.count,
|
|
13
|
-
limit: params[:limit]&.to_i || 100
|
|
14
|
-
}
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# GET /api/v1/entries/:id
|
|
18
|
-
def show
|
|
19
|
-
entry = Entry.find(params[:id])
|
|
20
|
-
|
|
21
|
-
render json: {
|
|
22
|
-
entry: entry.as_json(methods: [:extra_fields_hash])
|
|
23
|
-
}
|
|
24
|
-
rescue ActiveRecord::RecordNotFound
|
|
25
|
-
render json: { error: "Entry not found" }, status: :not_found
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def filter_params
|
|
31
|
-
# Extract filters from params[:filters] to avoid Rails routing params collision
|
|
32
|
-
search_params = {}
|
|
33
|
-
filters = params[:filters] || {}
|
|
34
|
-
|
|
35
|
-
search_params[:levels] = [filters[:level]].compact if filters[:level].present?
|
|
36
|
-
search_params[:app] = filters[:app] if filters[:app].present?
|
|
37
|
-
search_params[:env] = filters[:env] if filters[:env].present?
|
|
38
|
-
search_params[:controller] = filters[:controller] if filters[:controller].present?
|
|
39
|
-
search_params[:action] = filters[:action] if filters[:action].present?
|
|
40
|
-
search_params[:path] = filters[:path] if filters[:path].present?
|
|
41
|
-
search_params[:method] = filters[:method] if filters[:method].present?
|
|
42
|
-
search_params[:status_code] = filters[:status_code] if filters[:status_code].present?
|
|
43
|
-
search_params[:start_time] = filters[:start_time] if filters[:start_time].present?
|
|
44
|
-
search_params[:end_time] = filters[:end_time] if filters[:end_time].present?
|
|
45
|
-
search_params[:min_duration] = filters[:min_duration] if filters[:min_duration].present?
|
|
46
|
-
search_params[:max_duration] = filters[:max_duration] if filters[:max_duration].present?
|
|
47
|
-
search_params[:query] = params[:q] if params[:q].present?
|
|
48
|
-
search_params[:limit] = params[:limit] if params[:limit].present?
|
|
49
|
-
|
|
50
|
-
search_params
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
end
|
|
55
|
-
end
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class FacetsController < Api::BaseController
|
|
5
|
-
# GET /api/v1/facets
|
|
6
|
-
def index
|
|
7
|
-
field = params[:field]
|
|
8
|
-
|
|
9
|
-
if field.blank?
|
|
10
|
-
return render json: { error: "Field parameter required" }, status: :bad_request
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Use Entry model directly for facets
|
|
14
|
-
limit = params[:limit]&.to_i || 100
|
|
15
|
-
facets = SolidLog::Entry.facets_for(field, limit: limit)
|
|
16
|
-
|
|
17
|
-
render json: {
|
|
18
|
-
field: field,
|
|
19
|
-
values: facets,
|
|
20
|
-
total: facets.size
|
|
21
|
-
}
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
# GET /api/v1/facets/all
|
|
25
|
-
def all
|
|
26
|
-
facets = {
|
|
27
|
-
level: SolidLog::Entry.facets_for("level"),
|
|
28
|
-
app: SolidLog::Entry.facets_for("app"),
|
|
29
|
-
env: SolidLog::Entry.facets_for("env"),
|
|
30
|
-
controller: SolidLog::Entry.facets_for("controller", limit: 50),
|
|
31
|
-
action: SolidLog::Entry.facets_for("action", limit: 50),
|
|
32
|
-
method: SolidLog::Entry.facets_for("method"),
|
|
33
|
-
status_code: SolidLog::Entry.facets_for("status_code")
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
render json: { facets: facets }
|
|
37
|
-
end
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class HealthController < Api::BaseController
|
|
5
|
-
skip_before_action :authenticate_token!, only: [:show]
|
|
6
|
-
|
|
7
|
-
# GET /api/v1/health
|
|
8
|
-
def show
|
|
9
|
-
metrics = SolidLog::HealthService.metrics
|
|
10
|
-
|
|
11
|
-
status = case metrics[:parsing][:health_status]
|
|
12
|
-
when "critical"
|
|
13
|
-
:service_unavailable
|
|
14
|
-
when "warning", "degraded"
|
|
15
|
-
:ok # Still functional
|
|
16
|
-
else
|
|
17
|
-
:ok
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
render json: {
|
|
21
|
-
status: metrics[:parsing][:health_status],
|
|
22
|
-
timestamp: Time.current.iso8601,
|
|
23
|
-
metrics: metrics
|
|
24
|
-
}, status: status
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class IngestController < Api::BaseController
|
|
5
|
-
# POST /api/v1/ingest
|
|
6
|
-
# Accepts single log entry (hash) or batch (array of hashes)
|
|
7
|
-
def create
|
|
8
|
-
payload = params[:_json] || parse_ndjson_body
|
|
9
|
-
|
|
10
|
-
if payload.blank?
|
|
11
|
-
render json: { error: "Empty payload" }, status: :bad_request
|
|
12
|
-
return
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
entries = Array.wrap(payload)
|
|
16
|
-
|
|
17
|
-
if entries.size > max_batch_size
|
|
18
|
-
render json: {
|
|
19
|
-
error: "Batch too large",
|
|
20
|
-
max_size: max_batch_size,
|
|
21
|
-
received: entries.size
|
|
22
|
-
}, status: :payload_too_large
|
|
23
|
-
return
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# Create raw entries
|
|
27
|
-
raw_entries = entries.map do |entry|
|
|
28
|
-
{
|
|
29
|
-
token_id: current_token.id,
|
|
30
|
-
payload: entry.to_json,
|
|
31
|
-
received_at: Time.current,
|
|
32
|
-
parsed: false
|
|
33
|
-
}
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Bulk insert
|
|
37
|
-
SolidLog.without_logging do
|
|
38
|
-
SolidLog::RawEntry.insert_all(raw_entries)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
render json: {
|
|
42
|
-
status: "accepted",
|
|
43
|
-
count: entries.size,
|
|
44
|
-
message: "Log entries queued for processing"
|
|
45
|
-
}, status: :accepted
|
|
46
|
-
rescue JSON::ParserError => e
|
|
47
|
-
render json: {
|
|
48
|
-
error: "Invalid JSON",
|
|
49
|
-
message: e.message
|
|
50
|
-
}, status: :bad_request
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
private
|
|
54
|
-
|
|
55
|
-
def max_batch_size
|
|
56
|
-
SolidLog.configuration.max_batch_size
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Parse NDJSON (newline-delimited JSON) from request body
|
|
60
|
-
def parse_ndjson_body
|
|
61
|
-
return [] unless request.body
|
|
62
|
-
|
|
63
|
-
body = request.body.read
|
|
64
|
-
return [] if body.blank?
|
|
65
|
-
|
|
66
|
-
# Check if it's NDJSON (multiple lines) or regular JSON
|
|
67
|
-
if body.include?("\n")
|
|
68
|
-
# NDJSON format
|
|
69
|
-
body.lines.map do |line|
|
|
70
|
-
JSON.parse(line.strip) unless line.strip.empty?
|
|
71
|
-
end.compact
|
|
72
|
-
else
|
|
73
|
-
# Regular JSON (single entry or array)
|
|
74
|
-
JSON.parse(body)
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
end
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class SearchController < Api::BaseController
|
|
5
|
-
# POST /api/v1/search
|
|
6
|
-
def create
|
|
7
|
-
query = params[:q] || params[:query]
|
|
8
|
-
|
|
9
|
-
if query.blank?
|
|
10
|
-
return render json: { error: "Query parameter required" }, status: :bad_request
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
search_params = {
|
|
14
|
-
query: query,
|
|
15
|
-
limit: params[:limit]
|
|
16
|
-
}.compact
|
|
17
|
-
|
|
18
|
-
search_service = SolidLog::SearchService.new(search_params)
|
|
19
|
-
entries = search_service.search
|
|
20
|
-
|
|
21
|
-
render json: {
|
|
22
|
-
query: query,
|
|
23
|
-
entries: entries.as_json(methods: [:extra_fields_hash]),
|
|
24
|
-
total: entries.count,
|
|
25
|
-
limit: params[:limit]&.to_i || 100
|
|
26
|
-
}
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
module Api
|
|
3
|
-
module V1
|
|
4
|
-
class TimelinesController < Api::BaseController
|
|
5
|
-
# GET /api/v1/timelines/request/:request_id
|
|
6
|
-
def show_request
|
|
7
|
-
request_id = params[:request_id]
|
|
8
|
-
|
|
9
|
-
if request_id.blank?
|
|
10
|
-
return render json: { error: "Request ID required" }, status: :bad_request
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
entries = SolidLog::CorrelationService.request_timeline(request_id)
|
|
14
|
-
stats = SolidLog::CorrelationService.request_stats(request_id)
|
|
15
|
-
|
|
16
|
-
render json: {
|
|
17
|
-
request_id: request_id,
|
|
18
|
-
entries: entries.as_json(methods: [:extra_fields_hash]),
|
|
19
|
-
stats: stats
|
|
20
|
-
}
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# GET /api/v1/timelines/job/:job_id
|
|
24
|
-
def show_job
|
|
25
|
-
job_id = params[:job_id]
|
|
26
|
-
|
|
27
|
-
if job_id.blank?
|
|
28
|
-
return render json: { error: "Job ID required" }, status: :bad_request
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
entries = SolidLog::CorrelationService.job_timeline(job_id)
|
|
32
|
-
stats = SolidLog::CorrelationService.job_stats(job_id)
|
|
33
|
-
|
|
34
|
-
render json: {
|
|
35
|
-
job_id: job_id,
|
|
36
|
-
entries: entries.as_json(methods: [:extra_fields_hash]),
|
|
37
|
-
stats: stats
|
|
38
|
-
}
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
class CacheCleanupJob < ApplicationJob
|
|
3
|
-
queue_as :default
|
|
4
|
-
|
|
5
|
-
def perform
|
|
6
|
-
SolidLog.without_logging do
|
|
7
|
-
expired_count = FacetCache.expired.count
|
|
8
|
-
|
|
9
|
-
if expired_count > 0
|
|
10
|
-
FacetCache.cleanup_expired!
|
|
11
|
-
Rails.logger.info "SolidLog::CacheCleanupJob: Cleaned up #{expired_count} expired cache entries"
|
|
12
|
-
end
|
|
13
|
-
end
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
end
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
module SolidLog
|
|
2
|
-
class FieldAnalysisJob < ApplicationJob
|
|
3
|
-
queue_as :default
|
|
4
|
-
|
|
5
|
-
def perform(auto_promote: false)
|
|
6
|
-
SolidLog.without_logging do
|
|
7
|
-
recommendations = FieldAnalyzer.analyze
|
|
8
|
-
|
|
9
|
-
if recommendations.any?
|
|
10
|
-
Rails.logger.info "SolidLog::FieldAnalysisJob: Found #{recommendations.size} fields for potential promotion"
|
|
11
|
-
|
|
12
|
-
recommendations.take(10).each do |rec|
|
|
13
|
-
Rails.logger.info " - #{rec[:field].name} (#{rec[:field].usage_count} uses, priority: #{rec[:priority]})"
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
if auto_promote
|
|
17
|
-
promoted_count = FieldAnalyzer.auto_promote_candidates
|
|
18
|
-
Rails.logger.info "SolidLog::FieldAnalysisJob: Auto-promoted #{promoted_count} fields"
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
Rails.logger.info "SolidLog::FieldAnalysisJob: No fields meet promotion threshold"
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|