solid_log-core 0.1.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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +176 -0
- data/Rakefile +11 -0
- data/db/log_migrate/20251222000001_create_solid_log_raw.rb +15 -0
- data/db/log_migrate/20251222000002_create_solid_log_entries.rb +29 -0
- data/db/log_migrate/20251222000004_create_solid_log_fields.rb +17 -0
- data/db/log_migrate/20251222000005_create_solid_log_tokens.rb +13 -0
- data/db/log_migrate/20251222000006_create_solid_log_facet_cache.rb +13 -0
- data/db/log_migrate/20251222000007_create_solid_log_fts_triggers.rb +41 -0
- data/db/log_structure_mysql.sql +96 -0
- data/db/log_structure_postgresql.sql +118 -0
- data/db/log_structure_sqlite.sql +123 -0
- data/lib/generators/solid_log/install/install_generator.rb +134 -0
- data/lib/generators/solid_log/install/templates/solid_log.rb.tt +133 -0
- data/lib/solid_log/adapters/adapter_factory.rb +34 -0
- data/lib/solid_log/adapters/base_adapter.rb +88 -0
- data/lib/solid_log/adapters/mysql_adapter.rb +163 -0
- data/lib/solid_log/adapters/postgresql_adapter.rb +141 -0
- data/lib/solid_log/adapters/sqlite_adapter.rb +149 -0
- data/lib/solid_log/core/client/buffer.rb +112 -0
- data/lib/solid_log/core/client/configuration.rb +31 -0
- data/lib/solid_log/core/client/http.rb +89 -0
- data/lib/solid_log/core/client/lograge_formatter.rb +99 -0
- data/lib/solid_log/core/client/retry_handler.rb +48 -0
- data/lib/solid_log/core/client.rb +138 -0
- data/lib/solid_log/core/configuration.rb +60 -0
- data/lib/solid_log/core/services/correlation_service.rb +74 -0
- data/lib/solid_log/core/services/field_analyzer.rb +108 -0
- data/lib/solid_log/core/services/health_service.rb +151 -0
- data/lib/solid_log/core/services/retention_service.rb +72 -0
- data/lib/solid_log/core/services/search_service.rb +269 -0
- data/lib/solid_log/core/version.rb +5 -0
- data/lib/solid_log/core.rb +106 -0
- data/lib/solid_log/direct_logger.rb +197 -0
- data/lib/solid_log/models/entry.rb +185 -0
- data/lib/solid_log/models/facet_cache.rb +58 -0
- data/lib/solid_log/models/field.rb +100 -0
- data/lib/solid_log/models/raw_entry.rb +33 -0
- data/lib/solid_log/models/record.rb +5 -0
- data/lib/solid_log/models/token.rb +61 -0
- data/lib/solid_log/parser.rb +179 -0
- data/lib/solid_log/silence_middleware.rb +34 -0
- data/lib/solid_log-core.rb +2 -0
- metadata +244 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: '09b479494d3ac70c18b6d0611a5ee0879ff01b90c17108dcffe1eaa951a47f00'
|
|
4
|
+
data.tar.gz: ca5f502ca1f8c7b722f1c26b31c898bf3c0e04b512fbde82d0b1e1e888006e32
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 2da7bc4a35999dc410ef3951325e1c61c7e67ef4a7f5028d386bb397e676edc50528e2b46209a4999dc94f95cf6ff6b7025db7d7010683e2dd73b207dcac9333
|
|
7
|
+
data.tar.gz: dff9a33b078a75d0019bfba2d9371b9d4804ce6bcfb52ed6be75bc2ce9a3892ba57d679885fd64ac56c4904945d101078eedfbbb709114d662b0978b57218431
|
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright Dan Loman
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# SolidLog::Core
|
|
2
|
+
|
|
3
|
+
Core models, database adapters, parser, DirectLogger, and HTTP client for the SolidLog logging system.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
`solid_log-core` provides the shared foundation for `solid_log-service` and `solid_log-ui` gems:
|
|
8
|
+
|
|
9
|
+
- **Models**: `RawEntry`, `Entry`, `Token`, `Field`, `FacetCache`
|
|
10
|
+
- **Database Adapters**: SQLite, PostgreSQL, MySQL adapters with database-specific optimizations
|
|
11
|
+
- **DirectLogger**: High-performance batched logging for parent app (50,000+ logs/sec)
|
|
12
|
+
- **Parser**: Structured log parsing with field extraction
|
|
13
|
+
- **Service Objects**: `RetentionService`, `FieldAnalyzer`, `SearchService`, `CorrelationService`
|
|
14
|
+
- **HTTP Client**: Buffered log sender with retry logic for remote ingestion
|
|
15
|
+
- **Migrations**: Database schema in `db/log_migrate/`
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
Add this line to your application's Gemfile:
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'solid_log-core'
|
|
23
|
+
|
|
24
|
+
# Also ensure you have the database adapter gem for your database:
|
|
25
|
+
gem 'sqlite3', '>= 2.1' # For SQLite
|
|
26
|
+
# OR
|
|
27
|
+
gem 'pg', '>= 1.1' # For PostgreSQL
|
|
28
|
+
# OR
|
|
29
|
+
gem 'mysql2', '>= 0.5' # For MySQL
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Dependencies:**
|
|
33
|
+
- `solid_log-core` requires only specific Rails components: `activerecord`, `activesupport`, and `activejob`
|
|
34
|
+
- It does **NOT** require the full `rails` gem
|
|
35
|
+
- Database adapter gems are optional - install only what you need
|
|
36
|
+
|
|
37
|
+
## Usage
|
|
38
|
+
|
|
39
|
+
This gem is typically used as a dependency for:
|
|
40
|
+
- **solid_log-service**: Standalone log ingestion service
|
|
41
|
+
- **solid_log-ui**: Web interface for viewing logs
|
|
42
|
+
|
|
43
|
+
### DirectLogger (Recommended for Parent App)
|
|
44
|
+
|
|
45
|
+
**DirectLogger** writes logs directly to the database for maximum performance. Use this when your Rails app has direct database access.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
# config/initializers/lograge.rb
|
|
49
|
+
Rails.application.configure do
|
|
50
|
+
config.lograge.enabled = true
|
|
51
|
+
config.lograge.formatter = Lograge::Formatters::Json.new
|
|
52
|
+
|
|
53
|
+
# Use DirectLogger for fast, batched database writes
|
|
54
|
+
config.lograge.logger = ActiveSupport::Logger.new(
|
|
55
|
+
SolidLog::DirectLogger.new(
|
|
56
|
+
batch_size: 100, # Batch size (default: 100)
|
|
57
|
+
flush_interval: 5, # Flush every 5 seconds
|
|
58
|
+
eager_flush_levels: [:error, :fatal] # Flush immediately on errors (crash safety)
|
|
59
|
+
)
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Performance:**
|
|
65
|
+
- **16,882 logs/sec** with crash safety (SQLite + WAL mode)
|
|
66
|
+
- **56,660 logs/sec** without eager flush (risky - may lose crash logs)
|
|
67
|
+
- **9x faster** than individual database inserts
|
|
68
|
+
- **67x faster** than HTTP logging
|
|
69
|
+
|
|
70
|
+
**Configuration Options:**
|
|
71
|
+
|
|
72
|
+
```ruby
|
|
73
|
+
SolidLog::DirectLogger.new(
|
|
74
|
+
batch_size: 100, # Logs per batch (default: 100)
|
|
75
|
+
flush_interval: 5, # Flush interval in seconds (default: 5)
|
|
76
|
+
eager_flush_levels: [:error, :fatal], # Flush these levels immediately (default: [:error, :fatal])
|
|
77
|
+
token_id: nil # Optional token for audit trail (default: nil or ENV['SOLIDLOG_TOKEN_ID'])
|
|
78
|
+
)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Crash Safety:**
|
|
82
|
+
|
|
83
|
+
By default, DirectLogger flushes error and fatal logs immediately to prevent losing the logs that explain WHY your app crashed:
|
|
84
|
+
|
|
85
|
+
```ruby
|
|
86
|
+
# Safe (recommended for production)
|
|
87
|
+
logger = SolidLog::DirectLogger.new(
|
|
88
|
+
eager_flush_levels: [:error, :fatal]
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
# Maximum performance (risky - may lose crash context)
|
|
92
|
+
logger = SolidLog::DirectLogger.new(
|
|
93
|
+
eager_flush_levels: []
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Maximum safety (slower)
|
|
97
|
+
logger = SolidLog::DirectLogger.new(
|
|
98
|
+
batch_size: 10,
|
|
99
|
+
flush_interval: 1,
|
|
100
|
+
eager_flush_levels: [:debug, :info, :warn, :error, :fatal]
|
|
101
|
+
)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
**Environment Variables:**
|
|
105
|
+
|
|
106
|
+
DirectLogger supports optional token configuration via environment:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
export SOLIDLOG_TOKEN_ID=123 # Optional: for audit trail tracking
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
See [BENCHMARK_RESULTS.md](BENCHMARK_RESULTS.md) for detailed performance analysis.
|
|
113
|
+
|
|
114
|
+
### HTTP Client (For External Services)
|
|
115
|
+
|
|
116
|
+
For sending logs to a remote SolidLog service:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
SolidLog::Core.configure_client do |config|
|
|
120
|
+
config.service_url = ENV['SOLIDLOG_SERVICE_URL']
|
|
121
|
+
config.token = ENV['SOLIDLOG_TOKEN']
|
|
122
|
+
config.app_name = 'web'
|
|
123
|
+
config.environment = Rails.env
|
|
124
|
+
end
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Database Setup
|
|
128
|
+
|
|
129
|
+
**Option 1: Run migrations**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
rails db:migrate
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
**Option 2: Use provided structure file**
|
|
136
|
+
|
|
137
|
+
Choose the structure file for your database type:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
# For SQLite
|
|
141
|
+
rails db:schema:load SCHEMA=db/log_structure_sqlite.sql
|
|
142
|
+
|
|
143
|
+
# For PostgreSQL
|
|
144
|
+
rails db:schema:load SCHEMA=db/log_structure_postgresql.sql
|
|
145
|
+
|
|
146
|
+
# For MySQL
|
|
147
|
+
rails db:schema:load SCHEMA=db/log_structure_mysql.sql
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
The structure files are optimized for each database with appropriate FTS implementations:
|
|
151
|
+
- **SQLite**: FTS5 virtual table
|
|
152
|
+
- **PostgreSQL**: tsvector with GIN index
|
|
153
|
+
- **MySQL**: FULLTEXT index
|
|
154
|
+
|
|
155
|
+
### Enable WAL Mode for SQLite (Highly Recommended)
|
|
156
|
+
|
|
157
|
+
WAL (Write-Ahead Logging) mode provides **243% better performance** for crash-safe logging:
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
# config/initializers/solid_log.rb
|
|
161
|
+
ActiveRecord::Base.connected_to(role: :log) do
|
|
162
|
+
ActiveRecord::Base.connection.execute("PRAGMA journal_mode=WAL")
|
|
163
|
+
ActiveRecord::Base.connection.execute("PRAGMA synchronous=NORMAL")
|
|
164
|
+
end
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Benefits:**
|
|
168
|
+
- **3.4x faster** eager flush (16,882 vs 4,923 logs/sec)
|
|
169
|
+
- Better concurrency (readers don't block writers)
|
|
170
|
+
- Recommended for all production deployments
|
|
171
|
+
|
|
172
|
+
See [BENCHMARK_RESULTS.md](BENCHMARK_RESULTS.md) for performance comparison.
|
|
173
|
+
|
|
174
|
+
## License
|
|
175
|
+
|
|
176
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateSolidLogRaw < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :solid_log_raw do |t|
|
|
4
|
+
t.datetime :received_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
|
5
|
+
t.integer :token_id # Nullable - only required for HTTP API auth, not DirectLogger
|
|
6
|
+
t.text :payload, null: false
|
|
7
|
+
t.boolean :parsed, default: false, null: false
|
|
8
|
+
t.datetime :parsed_at
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :solid_log_raw, [:parsed, :received_at], name: "idx_raw_unparsed"
|
|
12
|
+
add_index :solid_log_raw, :token_id, name: "idx_raw_token"
|
|
13
|
+
add_index :solid_log_raw, :received_at, name: "idx_raw_received"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
class CreateSolidLogEntries < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :solid_log_entries do |t|
|
|
4
|
+
t.integer :raw_id, null: false
|
|
5
|
+
t.datetime :timestamp, null: false # When the log event occurred
|
|
6
|
+
t.datetime :created_at, null: false # When the entry was parsed/created
|
|
7
|
+
t.string :level, null: false
|
|
8
|
+
t.string :app
|
|
9
|
+
t.string :env
|
|
10
|
+
t.text :message
|
|
11
|
+
t.string :request_id
|
|
12
|
+
t.string :job_id
|
|
13
|
+
t.float :duration
|
|
14
|
+
t.integer :status_code
|
|
15
|
+
t.string :controller
|
|
16
|
+
t.string :action
|
|
17
|
+
t.string :path
|
|
18
|
+
t.string :method
|
|
19
|
+
t.text :extra_fields
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
add_index :solid_log_entries, :timestamp, order: { timestamp: :desc }, name: "idx_entries_timestamp"
|
|
23
|
+
add_index :solid_log_entries, :level, name: "idx_entries_level"
|
|
24
|
+
add_index :solid_log_entries, [:app, :env, :timestamp], order: { timestamp: :desc }, name: "idx_entries_app_env_time"
|
|
25
|
+
add_index :solid_log_entries, :request_id, name: "idx_entries_request"
|
|
26
|
+
add_index :solid_log_entries, :job_id, name: "idx_entries_job"
|
|
27
|
+
add_index :solid_log_entries, :raw_id, name: "idx_entries_raw"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CreateSolidLogFields < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :solid_log_fields do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :field_type, null: false
|
|
6
|
+
t.string :filter_type, default: "multiselect", null: false
|
|
7
|
+
t.integer :usage_count, default: 0, null: false
|
|
8
|
+
t.datetime :last_seen_at
|
|
9
|
+
t.boolean :promoted, default: false, null: false
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
add_index :solid_log_fields, :name, unique: true, name: "idx_fields_name"
|
|
14
|
+
add_index :solid_log_fields, :promoted, name: "idx_fields_promoted"
|
|
15
|
+
add_index :solid_log_fields, :usage_count, name: "idx_fields_usage"
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateSolidLogTokens < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :solid_log_tokens do |t|
|
|
4
|
+
t.string :name, null: false
|
|
5
|
+
t.string :token_hash, null: false
|
|
6
|
+
t.datetime :last_used_at
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :solid_log_tokens, :token_hash, unique: true, name: "idx_tokens_hash"
|
|
11
|
+
add_index :solid_log_tokens, :name, name: "idx_tokens_name"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateSolidLogFacetCache < ActiveRecord::Migration[8.0]
|
|
2
|
+
def change
|
|
3
|
+
create_table :solid_log_facet_cache do |t|
|
|
4
|
+
t.string :key_name, null: false
|
|
5
|
+
t.text :cache_value, null: false
|
|
6
|
+
t.datetime :expires_at
|
|
7
|
+
t.timestamps
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
add_index :solid_log_facet_cache, :key_name, unique: true, name: "idx_facet_key_name"
|
|
11
|
+
add_index :solid_log_facet_cache, :expires_at, name: "idx_facet_expires"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class CreateSolidLogFtsTriggers < ActiveRecord::Migration[8.0]
|
|
2
|
+
def up
|
|
3
|
+
# SQLite FTS5 implementation
|
|
4
|
+
execute <<~SQL
|
|
5
|
+
CREATE VIRTUAL TABLE solid_log_entries_fts USING fts5(
|
|
6
|
+
message,
|
|
7
|
+
extra_fields,
|
|
8
|
+
content='solid_log_entries',
|
|
9
|
+
content_rowid='id'
|
|
10
|
+
);
|
|
11
|
+
SQL
|
|
12
|
+
|
|
13
|
+
execute <<~SQL
|
|
14
|
+
CREATE TRIGGER solid_log_entries_fts_insert AFTER INSERT ON solid_log_entries BEGIN
|
|
15
|
+
INSERT INTO solid_log_entries_fts(rowid, message, extra_fields)
|
|
16
|
+
VALUES (new.id, new.message, new.extra_fields);
|
|
17
|
+
END;
|
|
18
|
+
SQL
|
|
19
|
+
|
|
20
|
+
execute <<~SQL
|
|
21
|
+
CREATE TRIGGER solid_log_entries_fts_update AFTER UPDATE ON solid_log_entries BEGIN
|
|
22
|
+
UPDATE solid_log_entries_fts
|
|
23
|
+
SET message = new.message, extra_fields = new.extra_fields
|
|
24
|
+
WHERE rowid = new.id;
|
|
25
|
+
END;
|
|
26
|
+
SQL
|
|
27
|
+
|
|
28
|
+
execute <<~SQL
|
|
29
|
+
CREATE TRIGGER solid_log_entries_fts_delete AFTER DELETE ON solid_log_entries BEGIN
|
|
30
|
+
DELETE FROM solid_log_entries_fts WHERE rowid = old.id;
|
|
31
|
+
END;
|
|
32
|
+
SQL
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def down
|
|
36
|
+
execute "DROP TRIGGER IF EXISTS solid_log_entries_fts_delete"
|
|
37
|
+
execute "DROP TRIGGER IF EXISTS solid_log_entries_fts_update"
|
|
38
|
+
execute "DROP TRIGGER IF EXISTS solid_log_entries_fts_insert"
|
|
39
|
+
execute "DROP TABLE IF EXISTS solid_log_entries_fts"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
-- SolidLog Database Structure for MySQL
|
|
2
|
+
-- Generated from migrations in db/log_migrate/
|
|
3
|
+
|
|
4
|
+
-- solid_log_raw: Fast ingestion table for raw log payloads
|
|
5
|
+
CREATE TABLE solid_log_raw (
|
|
6
|
+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
7
|
+
received_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
token_id BIGINT NOT NULL,
|
|
9
|
+
payload TEXT NOT NULL,
|
|
10
|
+
parsed TINYINT(1) NOT NULL DEFAULT 0,
|
|
11
|
+
parsed_at DATETIME,
|
|
12
|
+
INDEX idx_raw_unparsed (parsed, received_at),
|
|
13
|
+
INDEX idx_raw_token (token_id),
|
|
14
|
+
INDEX idx_raw_received (received_at)
|
|
15
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
16
|
+
|
|
17
|
+
-- solid_log_entries: Parsed and indexed log entries for querying
|
|
18
|
+
CREATE TABLE solid_log_entries (
|
|
19
|
+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
20
|
+
raw_id BIGINT NOT NULL,
|
|
21
|
+
timestamp DATETIME NOT NULL,
|
|
22
|
+
created_at DATETIME NOT NULL,
|
|
23
|
+
level VARCHAR(255) NOT NULL,
|
|
24
|
+
app VARCHAR(255),
|
|
25
|
+
env VARCHAR(255),
|
|
26
|
+
message TEXT,
|
|
27
|
+
request_id VARCHAR(255),
|
|
28
|
+
job_id VARCHAR(255),
|
|
29
|
+
duration DOUBLE,
|
|
30
|
+
status_code INT,
|
|
31
|
+
controller VARCHAR(255),
|
|
32
|
+
action VARCHAR(255),
|
|
33
|
+
path VARCHAR(255),
|
|
34
|
+
method VARCHAR(255),
|
|
35
|
+
extra_fields TEXT,
|
|
36
|
+
INDEX idx_entries_timestamp (timestamp DESC),
|
|
37
|
+
INDEX idx_entries_level (level),
|
|
38
|
+
INDEX idx_entries_app_env_time (app, env, timestamp DESC),
|
|
39
|
+
INDEX idx_entries_request (request_id),
|
|
40
|
+
INDEX idx_entries_job (job_id),
|
|
41
|
+
INDEX idx_entries_raw (raw_id),
|
|
42
|
+
FULLTEXT INDEX idx_entries_fts (message, extra_fields)
|
|
43
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
44
|
+
|
|
45
|
+
-- solid_log_fields: Field registry for tracking dynamic JSON fields
|
|
46
|
+
CREATE TABLE solid_log_fields (
|
|
47
|
+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
48
|
+
name VARCHAR(255) NOT NULL,
|
|
49
|
+
field_type VARCHAR(255) NOT NULL,
|
|
50
|
+
filter_type VARCHAR(255) NOT NULL DEFAULT 'multiselect',
|
|
51
|
+
usage_count INT NOT NULL DEFAULT 0,
|
|
52
|
+
last_seen_at DATETIME,
|
|
53
|
+
promoted TINYINT(1) NOT NULL DEFAULT 0,
|
|
54
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
55
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
56
|
+
UNIQUE INDEX idx_fields_name (name),
|
|
57
|
+
INDEX idx_fields_promoted (promoted),
|
|
58
|
+
INDEX idx_fields_usage (usage_count)
|
|
59
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
60
|
+
|
|
61
|
+
-- solid_log_tokens: API tokens for log ingestion authentication
|
|
62
|
+
CREATE TABLE solid_log_tokens (
|
|
63
|
+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
64
|
+
name VARCHAR(255) NOT NULL,
|
|
65
|
+
token_hash VARCHAR(255) NOT NULL,
|
|
66
|
+
last_used_at DATETIME,
|
|
67
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
68
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
69
|
+
UNIQUE INDEX idx_tokens_hash (token_hash),
|
|
70
|
+
INDEX idx_tokens_name (name)
|
|
71
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
72
|
+
|
|
73
|
+
-- solid_log_facet_cache: Cache for filter options to reduce DB load
|
|
74
|
+
CREATE TABLE solid_log_facet_cache (
|
|
75
|
+
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
|
76
|
+
key_name VARCHAR(255) NOT NULL,
|
|
77
|
+
cache_value TEXT NOT NULL,
|
|
78
|
+
expires_at DATETIME,
|
|
79
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
80
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
81
|
+
UNIQUE INDEX idx_facet_key_name (key_name),
|
|
82
|
+
INDEX idx_facet_expires (expires_at)
|
|
83
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
84
|
+
|
|
85
|
+
-- Schema migrations
|
|
86
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
87
|
+
version VARCHAR(255) PRIMARY KEY
|
|
88
|
+
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
|
89
|
+
|
|
90
|
+
INSERT INTO schema_migrations (version) VALUES
|
|
91
|
+
('20251222000001'),
|
|
92
|
+
('20251222000002'),
|
|
93
|
+
('20251222000004'),
|
|
94
|
+
('20251222000005'),
|
|
95
|
+
('20251222000006'),
|
|
96
|
+
('20251222000007');
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
-- SolidLog Database Structure for PostgreSQL
|
|
2
|
+
-- Generated from migrations in db/log_migrate/
|
|
3
|
+
|
|
4
|
+
-- solid_log_raw: Fast ingestion table for raw log payloads
|
|
5
|
+
CREATE TABLE solid_log_raw (
|
|
6
|
+
id BIGSERIAL PRIMARY KEY,
|
|
7
|
+
received_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
token_id BIGINT NOT NULL,
|
|
9
|
+
payload TEXT NOT NULL,
|
|
10
|
+
parsed BOOLEAN NOT NULL DEFAULT FALSE,
|
|
11
|
+
parsed_at TIMESTAMP
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX idx_raw_unparsed ON solid_log_raw(parsed, received_at);
|
|
15
|
+
CREATE INDEX idx_raw_token ON solid_log_raw(token_id);
|
|
16
|
+
CREATE INDEX idx_raw_received ON solid_log_raw(received_at);
|
|
17
|
+
|
|
18
|
+
-- solid_log_entries: Parsed and indexed log entries for querying
|
|
19
|
+
CREATE TABLE solid_log_entries (
|
|
20
|
+
id BIGSERIAL PRIMARY KEY,
|
|
21
|
+
raw_id BIGINT NOT NULL,
|
|
22
|
+
timestamp TIMESTAMP NOT NULL,
|
|
23
|
+
created_at TIMESTAMP NOT NULL,
|
|
24
|
+
level VARCHAR(255) NOT NULL,
|
|
25
|
+
app VARCHAR(255),
|
|
26
|
+
env VARCHAR(255),
|
|
27
|
+
message TEXT,
|
|
28
|
+
request_id VARCHAR(255),
|
|
29
|
+
job_id VARCHAR(255),
|
|
30
|
+
duration DOUBLE PRECISION,
|
|
31
|
+
status_code INTEGER,
|
|
32
|
+
controller VARCHAR(255),
|
|
33
|
+
action VARCHAR(255),
|
|
34
|
+
path VARCHAR(255),
|
|
35
|
+
method VARCHAR(255),
|
|
36
|
+
extra_fields TEXT,
|
|
37
|
+
fts_vector TSVECTOR
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
CREATE INDEX idx_entries_timestamp ON solid_log_entries(timestamp DESC);
|
|
41
|
+
CREATE INDEX idx_entries_level ON solid_log_entries(level);
|
|
42
|
+
CREATE INDEX idx_entries_app_env_time ON solid_log_entries(app, env, timestamp DESC);
|
|
43
|
+
CREATE INDEX idx_entries_request ON solid_log_entries(request_id);
|
|
44
|
+
CREATE INDEX idx_entries_job ON solid_log_entries(job_id);
|
|
45
|
+
CREATE INDEX idx_entries_raw ON solid_log_entries(raw_id);
|
|
46
|
+
|
|
47
|
+
-- Full-text search index using tsvector and GIN
|
|
48
|
+
CREATE INDEX idx_entries_fts ON solid_log_entries USING GIN(fts_vector);
|
|
49
|
+
|
|
50
|
+
-- Trigger to auto-update FTS vector
|
|
51
|
+
CREATE OR REPLACE FUNCTION solid_log_entries_fts_trigger() RETURNS trigger AS $$
|
|
52
|
+
BEGIN
|
|
53
|
+
NEW.fts_vector :=
|
|
54
|
+
setweight(to_tsvector('english', COALESCE(NEW.message, '')), 'A') ||
|
|
55
|
+
setweight(to_tsvector('english', COALESCE(NEW.extra_fields, '')), 'B');
|
|
56
|
+
RETURN NEW;
|
|
57
|
+
END;
|
|
58
|
+
$$ LANGUAGE plpgsql;
|
|
59
|
+
|
|
60
|
+
CREATE TRIGGER solid_log_entries_fts_update
|
|
61
|
+
BEFORE INSERT OR UPDATE ON solid_log_entries
|
|
62
|
+
FOR EACH ROW EXECUTE FUNCTION solid_log_entries_fts_trigger();
|
|
63
|
+
|
|
64
|
+
-- solid_log_fields: Field registry for tracking dynamic JSON fields
|
|
65
|
+
CREATE TABLE solid_log_fields (
|
|
66
|
+
id BIGSERIAL PRIMARY KEY,
|
|
67
|
+
name VARCHAR(255) NOT NULL,
|
|
68
|
+
field_type VARCHAR(255) NOT NULL,
|
|
69
|
+
filter_type VARCHAR(255) NOT NULL DEFAULT 'multiselect',
|
|
70
|
+
usage_count INTEGER NOT NULL DEFAULT 0,
|
|
71
|
+
last_seen_at TIMESTAMP,
|
|
72
|
+
promoted BOOLEAN NOT NULL DEFAULT FALSE,
|
|
73
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
74
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
CREATE UNIQUE INDEX idx_fields_name ON solid_log_fields(name);
|
|
78
|
+
CREATE INDEX idx_fields_promoted ON solid_log_fields(promoted);
|
|
79
|
+
CREATE INDEX idx_fields_usage ON solid_log_fields(usage_count);
|
|
80
|
+
|
|
81
|
+
-- solid_log_tokens: API tokens for log ingestion authentication
|
|
82
|
+
CREATE TABLE solid_log_tokens (
|
|
83
|
+
id BIGSERIAL PRIMARY KEY,
|
|
84
|
+
name VARCHAR(255) NOT NULL,
|
|
85
|
+
token_hash VARCHAR(255) NOT NULL,
|
|
86
|
+
last_used_at TIMESTAMP,
|
|
87
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
88
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
CREATE UNIQUE INDEX idx_tokens_hash ON solid_log_tokens(token_hash);
|
|
92
|
+
CREATE INDEX idx_tokens_name ON solid_log_tokens(name);
|
|
93
|
+
|
|
94
|
+
-- solid_log_facet_cache: Cache for filter options to reduce DB load
|
|
95
|
+
CREATE TABLE solid_log_facet_cache (
|
|
96
|
+
id BIGSERIAL PRIMARY KEY,
|
|
97
|
+
key_name VARCHAR(255) NOT NULL,
|
|
98
|
+
cache_value TEXT NOT NULL,
|
|
99
|
+
expires_at TIMESTAMP,
|
|
100
|
+
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
101
|
+
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
CREATE UNIQUE INDEX idx_facet_key_name ON solid_log_facet_cache(key_name);
|
|
105
|
+
CREATE INDEX idx_facet_expires ON solid_log_facet_cache(expires_at);
|
|
106
|
+
|
|
107
|
+
-- Schema migrations
|
|
108
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
109
|
+
version VARCHAR(255) PRIMARY KEY
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
INSERT INTO schema_migrations (version) VALUES
|
|
113
|
+
('20251222000001'),
|
|
114
|
+
('20251222000002'),
|
|
115
|
+
('20251222000004'),
|
|
116
|
+
('20251222000005'),
|
|
117
|
+
('20251222000006'),
|
|
118
|
+
('20251222000007');
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
-- SolidLog Database Structure for SQLite
|
|
2
|
+
-- Generated from migrations in db/log_migrate/
|
|
3
|
+
|
|
4
|
+
-- solid_log_raw: Fast ingestion table for raw log payloads
|
|
5
|
+
CREATE TABLE solid_log_raw (
|
|
6
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
7
|
+
received_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
8
|
+
token_id INTEGER NOT NULL,
|
|
9
|
+
payload TEXT NOT NULL,
|
|
10
|
+
parsed BOOLEAN NOT NULL DEFAULT 0,
|
|
11
|
+
parsed_at DATETIME
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
CREATE INDEX idx_raw_unparsed ON solid_log_raw(parsed, received_at);
|
|
15
|
+
CREATE INDEX idx_raw_token ON solid_log_raw(token_id);
|
|
16
|
+
CREATE INDEX idx_raw_received ON solid_log_raw(received_at);
|
|
17
|
+
|
|
18
|
+
-- solid_log_entries: Parsed and indexed log entries for querying
|
|
19
|
+
CREATE TABLE solid_log_entries (
|
|
20
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
21
|
+
raw_id INTEGER NOT NULL,
|
|
22
|
+
timestamp DATETIME NOT NULL,
|
|
23
|
+
created_at DATETIME NOT NULL,
|
|
24
|
+
level VARCHAR(255) NOT NULL,
|
|
25
|
+
app VARCHAR(255),
|
|
26
|
+
env VARCHAR(255),
|
|
27
|
+
message TEXT,
|
|
28
|
+
request_id VARCHAR(255),
|
|
29
|
+
job_id VARCHAR(255),
|
|
30
|
+
duration REAL,
|
|
31
|
+
status_code INTEGER,
|
|
32
|
+
controller VARCHAR(255),
|
|
33
|
+
action VARCHAR(255),
|
|
34
|
+
path VARCHAR(255),
|
|
35
|
+
method VARCHAR(255),
|
|
36
|
+
extra_fields TEXT
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
CREATE INDEX idx_entries_timestamp ON solid_log_entries(timestamp DESC);
|
|
40
|
+
CREATE INDEX idx_entries_level ON solid_log_entries(level);
|
|
41
|
+
CREATE INDEX idx_entries_app_env_time ON solid_log_entries(app, env, timestamp DESC);
|
|
42
|
+
CREATE INDEX idx_entries_request ON solid_log_entries(request_id);
|
|
43
|
+
CREATE INDEX idx_entries_job ON solid_log_entries(job_id);
|
|
44
|
+
CREATE INDEX idx_entries_raw ON solid_log_entries(raw_id);
|
|
45
|
+
|
|
46
|
+
-- solid_log_fields: Field registry for tracking dynamic JSON fields
|
|
47
|
+
CREATE TABLE solid_log_fields (
|
|
48
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
49
|
+
name VARCHAR(255) NOT NULL,
|
|
50
|
+
field_type VARCHAR(255) NOT NULL,
|
|
51
|
+
filter_type VARCHAR(255) NOT NULL DEFAULT 'multiselect',
|
|
52
|
+
usage_count INTEGER NOT NULL DEFAULT 0,
|
|
53
|
+
last_seen_at DATETIME,
|
|
54
|
+
promoted BOOLEAN NOT NULL DEFAULT 0,
|
|
55
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
56
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
CREATE UNIQUE INDEX idx_fields_name ON solid_log_fields(name);
|
|
60
|
+
CREATE INDEX idx_fields_promoted ON solid_log_fields(promoted);
|
|
61
|
+
CREATE INDEX idx_fields_usage ON solid_log_fields(usage_count);
|
|
62
|
+
|
|
63
|
+
-- solid_log_tokens: API tokens for log ingestion authentication
|
|
64
|
+
CREATE TABLE solid_log_tokens (
|
|
65
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
66
|
+
name VARCHAR(255) NOT NULL,
|
|
67
|
+
token_hash VARCHAR(255) NOT NULL,
|
|
68
|
+
last_used_at DATETIME,
|
|
69
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
70
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
CREATE UNIQUE INDEX idx_tokens_hash ON solid_log_tokens(token_hash);
|
|
74
|
+
CREATE INDEX idx_tokens_name ON solid_log_tokens(name);
|
|
75
|
+
|
|
76
|
+
-- solid_log_facet_cache: Cache for filter options to reduce DB load
|
|
77
|
+
CREATE TABLE solid_log_facet_cache (
|
|
78
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
79
|
+
key_name VARCHAR(255) NOT NULL,
|
|
80
|
+
cache_value TEXT NOT NULL,
|
|
81
|
+
expires_at DATETIME,
|
|
82
|
+
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
83
|
+
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
CREATE UNIQUE INDEX idx_facet_key_name ON solid_log_facet_cache(key_name);
|
|
87
|
+
CREATE INDEX idx_facet_expires ON solid_log_facet_cache(expires_at);
|
|
88
|
+
|
|
89
|
+
-- Full-Text Search (SQLite FTS5)
|
|
90
|
+
CREATE VIRTUAL TABLE solid_log_entries_fts USING fts5(
|
|
91
|
+
message,
|
|
92
|
+
extra_fields,
|
|
93
|
+
content='solid_log_entries',
|
|
94
|
+
content_rowid='id'
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
CREATE TRIGGER solid_log_entries_fts_insert AFTER INSERT ON solid_log_entries BEGIN
|
|
98
|
+
INSERT INTO solid_log_entries_fts(rowid, message, extra_fields)
|
|
99
|
+
VALUES (new.id, new.message, new.extra_fields);
|
|
100
|
+
END;
|
|
101
|
+
|
|
102
|
+
CREATE TRIGGER solid_log_entries_fts_update AFTER UPDATE ON solid_log_entries BEGIN
|
|
103
|
+
UPDATE solid_log_entries_fts
|
|
104
|
+
SET message = new.message, extra_fields = new.extra_fields
|
|
105
|
+
WHERE rowid = new.id;
|
|
106
|
+
END;
|
|
107
|
+
|
|
108
|
+
CREATE TRIGGER solid_log_entries_fts_delete AFTER DELETE ON solid_log_entries BEGIN
|
|
109
|
+
DELETE FROM solid_log_entries_fts WHERE rowid = old.id;
|
|
110
|
+
END;
|
|
111
|
+
|
|
112
|
+
-- Schema migrations
|
|
113
|
+
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
114
|
+
version VARCHAR(255) PRIMARY KEY
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
INSERT INTO schema_migrations (version) VALUES
|
|
118
|
+
('20251222000001'),
|
|
119
|
+
('20251222000002'),
|
|
120
|
+
('20251222000004'),
|
|
121
|
+
('20251222000005'),
|
|
122
|
+
('20251222000006'),
|
|
123
|
+
('20251222000007');
|