ez_logs_agent 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/.rspec +3 -0
- data/CHANGELOG.md +57 -0
- data/CONFIGURATION.md +752 -0
- data/FAQ.md +574 -0
- data/LICENSE.txt +21 -0
- data/QUICKSTART.md +390 -0
- data/README.md +1021 -0
- data/RELEASING.md +55 -0
- data/Rakefile +8 -0
- data/lib/ez_logs_agent/actor.rb +57 -0
- data/lib/ez_logs_agent/actor_validator.rb +51 -0
- data/lib/ez_logs_agent/buffer.rb +83 -0
- data/lib/ez_logs_agent/capturers/active_job_capturer.rb +270 -0
- data/lib/ez_logs_agent/capturers/database_capturer.rb +467 -0
- data/lib/ez_logs_agent/capturers/job_capturer.rb +238 -0
- data/lib/ez_logs_agent/configuration.rb +186 -0
- data/lib/ez_logs_agent/configuration_validator.rb +139 -0
- data/lib/ez_logs_agent/correlation.rb +40 -0
- data/lib/ez_logs_agent/event_builder.rb +281 -0
- data/lib/ez_logs_agent/flush_scheduler.rb +99 -0
- data/lib/ez_logs_agent/logger.rb +62 -0
- data/lib/ez_logs_agent/middleware/http_request.rb +1094 -0
- data/lib/ez_logs_agent/railtie.rb +353 -0
- data/lib/ez_logs_agent/resource_extractor.rb +172 -0
- data/lib/ez_logs_agent/retry_sender.rb +120 -0
- data/lib/ez_logs_agent/transport.rb +91 -0
- data/lib/ez_logs_agent/version.rb +5 -0
- data/lib/ez_logs_agent.rb +42 -0
- data/lib/generators/ez_logs_agent/install/install_generator.rb +94 -0
- data/lib/generators/ez_logs_agent/install/templates/ez_logs_agent.rb.tt +128 -0
- data/lib/tasks/ez_logs_agent.rake +110 -0
- data/script/publish-to-public.sh +113 -0
- data/sig/ez_logs_agent.rbs +4 -0
- metadata +178 -0
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "json"
|
|
5
|
+
require "uri"
|
|
6
|
+
|
|
7
|
+
module EzLogsAgent
|
|
8
|
+
# HTTP Transport Client
|
|
9
|
+
#
|
|
10
|
+
# Lowest-level component that sends events to the EzLogs server.
|
|
11
|
+
# Performs a single HTTP POST request with no retries or backoff.
|
|
12
|
+
#
|
|
13
|
+
# Responsibilities:
|
|
14
|
+
# - Serialize events to JSON
|
|
15
|
+
# - POST to server endpoint
|
|
16
|
+
# - Return :success or :failure
|
|
17
|
+
#
|
|
18
|
+
# Does NOT:
|
|
19
|
+
# - Retry failed requests
|
|
20
|
+
# - Implement backoff logic
|
|
21
|
+
# - Read from Buffer
|
|
22
|
+
# - Call Buffer.flush
|
|
23
|
+
# - Raise exceptions to host app
|
|
24
|
+
class Transport
|
|
25
|
+
class << self
|
|
26
|
+
# Send events to the server
|
|
27
|
+
#
|
|
28
|
+
# @param events [Array<Hash>] Array of event hashes
|
|
29
|
+
# @return [Symbol] :success if 2xx response, :failure otherwise
|
|
30
|
+
def send(events)
|
|
31
|
+
return :success if events.nil? || events.empty?
|
|
32
|
+
|
|
33
|
+
response = post_events(events)
|
|
34
|
+
classify_response(response)
|
|
35
|
+
rescue => error
|
|
36
|
+
Logger.error("[Transport] send failed: #{error.class} - #{error.message}")
|
|
37
|
+
:failure
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def post_events(events)
|
|
43
|
+
uri = build_uri
|
|
44
|
+
http = build_http_client(uri)
|
|
45
|
+
request = build_request(uri, events)
|
|
46
|
+
|
|
47
|
+
http.request(request)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def build_uri
|
|
51
|
+
server_url = EzLogsAgent.configuration.server_url
|
|
52
|
+
raise "server_url not configured" if server_url.nil? || server_url.empty?
|
|
53
|
+
|
|
54
|
+
URI.parse("#{server_url}/api/events")
|
|
55
|
+
rescue => error
|
|
56
|
+
raise "Invalid server_url: #{error.message}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def build_http_client(uri)
|
|
60
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
61
|
+
http.use_ssl = (uri.scheme == "https")
|
|
62
|
+
http.open_timeout = 5
|
|
63
|
+
http.read_timeout = 10
|
|
64
|
+
http
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_request(uri, events)
|
|
68
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
69
|
+
request["Content-Type"] = "application/json"
|
|
70
|
+
|
|
71
|
+
token = EzLogsAgent.configuration.project_token
|
|
72
|
+
request["Authorization"] = "Bearer #{token}" if token
|
|
73
|
+
|
|
74
|
+
request.body = JSON.generate({ events: events })
|
|
75
|
+
request
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def classify_response(response)
|
|
79
|
+
status = response.code.to_i
|
|
80
|
+
|
|
81
|
+
if status >= 200 && status < 300
|
|
82
|
+
Logger.debug("[Transport] send succeeded (#{status})")
|
|
83
|
+
:success
|
|
84
|
+
else
|
|
85
|
+
Logger.error("[Transport] send failed (#{status})")
|
|
86
|
+
:failure
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "ez_logs_agent/version"
|
|
4
|
+
require_relative "ez_logs_agent/configuration"
|
|
5
|
+
require_relative "ez_logs_agent/configuration_validator"
|
|
6
|
+
require_relative "ez_logs_agent/logger"
|
|
7
|
+
require_relative "ez_logs_agent/correlation"
|
|
8
|
+
require_relative "ez_logs_agent/actor_validator"
|
|
9
|
+
require_relative "ez_logs_agent/actor"
|
|
10
|
+
require_relative "ez_logs_agent/event_builder"
|
|
11
|
+
require_relative "ez_logs_agent/resource_extractor"
|
|
12
|
+
require_relative "ez_logs_agent/buffer"
|
|
13
|
+
require_relative "ez_logs_agent/transport"
|
|
14
|
+
require_relative "ez_logs_agent/retry_sender"
|
|
15
|
+
require_relative "ez_logs_agent/flush_scheduler"
|
|
16
|
+
require_relative "ez_logs_agent/middleware/http_request"
|
|
17
|
+
require_relative "ez_logs_agent/capturers/job_capturer"
|
|
18
|
+
require_relative "ez_logs_agent/capturers/active_job_capturer"
|
|
19
|
+
require_relative "ez_logs_agent/capturers/database_capturer"
|
|
20
|
+
|
|
21
|
+
# Load Railtie only when Rails is present
|
|
22
|
+
require_relative "ez_logs_agent/railtie" if defined?(Rails::Railtie)
|
|
23
|
+
|
|
24
|
+
module EzLogsAgent
|
|
25
|
+
class Error < StandardError; end
|
|
26
|
+
|
|
27
|
+
class << self
|
|
28
|
+
attr_writer :configuration
|
|
29
|
+
|
|
30
|
+
def configuration
|
|
31
|
+
@configuration ||= Configuration.new
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def configure
|
|
35
|
+
yield(configuration)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def reset_configuration!
|
|
39
|
+
@configuration = Configuration.new
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rails/generators/base"
|
|
4
|
+
|
|
5
|
+
module EzLogsAgent
|
|
6
|
+
module Generators
|
|
7
|
+
class InstallGenerator < Rails::Generators::Base
|
|
8
|
+
source_root File.expand_path("templates", __dir__)
|
|
9
|
+
|
|
10
|
+
desc "Creates an EzLogsAgent initializer with all configuration options"
|
|
11
|
+
|
|
12
|
+
def show_welcome_message
|
|
13
|
+
say "\n"
|
|
14
|
+
say "=" * 70, :green
|
|
15
|
+
say " EzLogs Agent Installation", :green
|
|
16
|
+
say "=" * 70, :green
|
|
17
|
+
say "\n"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def detect_environment
|
|
21
|
+
@sidekiq_detected = defined?(Sidekiq)
|
|
22
|
+
@activejob_detected = defined?(ActiveJob)
|
|
23
|
+
@activerecord_detected = defined?(ActiveRecord)
|
|
24
|
+
@devise_detected = defined?(Devise)
|
|
25
|
+
|
|
26
|
+
say "Detected Components:", :cyan
|
|
27
|
+
rails_version = defined?(Rails::VERSION::STRING) ? Rails::VERSION::STRING : "unknown"
|
|
28
|
+
say " • Rails #{rails_version}"
|
|
29
|
+
say " • Sidekiq #{Sidekiq::VERSION}", :green if @sidekiq_detected
|
|
30
|
+
say " • ActiveJob", :green if @activejob_detected && !@sidekiq_detected
|
|
31
|
+
say " • ActiveRecord", :green if @activerecord_detected
|
|
32
|
+
say " • Devise", :green if @devise_detected
|
|
33
|
+
say "\n"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def create_initializer_file
|
|
37
|
+
initializer_path = File.join(destination_root, "config/initializers/ez_logs_agent.rb")
|
|
38
|
+
|
|
39
|
+
if File.exist?(initializer_path)
|
|
40
|
+
say "⚠️ Initializer already exists at config/initializers/ez_logs_agent.rb", :yellow
|
|
41
|
+
say " Skipping creation to avoid overwriting your existing configuration.", :yellow
|
|
42
|
+
say "\n"
|
|
43
|
+
return
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
template "ez_logs_agent.rb.tt", "config/initializers/ez_logs_agent.rb"
|
|
47
|
+
say "\n"
|
|
48
|
+
say "✅ Created config/initializers/ez_logs_agent.rb", :green
|
|
49
|
+
say "\n"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def show_next_steps
|
|
53
|
+
say "=" * 70, :cyan
|
|
54
|
+
say " Next Steps", :cyan
|
|
55
|
+
say "=" * 70, :cyan
|
|
56
|
+
say "\n"
|
|
57
|
+
|
|
58
|
+
say "1. Get your API key from EzLogs:", :bold
|
|
59
|
+
say " → Log in to your EzLogs dashboard"
|
|
60
|
+
say " → Go to Settings → API Keys"
|
|
61
|
+
say " → Create a new API key for this application\n\n"
|
|
62
|
+
|
|
63
|
+
say "2. Configure the agent:", :bold
|
|
64
|
+
say " → Open config/initializers/ez_logs_agent.rb"
|
|
65
|
+
say " → Set your server_url (e.g., https://app.ezlogs.io)"
|
|
66
|
+
say " → Set your project_token (the API key from step 1)\n\n"
|
|
67
|
+
|
|
68
|
+
if @devise_detected
|
|
69
|
+
say "3. Enable actor tracking (optional but recommended):", :bold
|
|
70
|
+
say " → Uncomment the actor_from_request block"
|
|
71
|
+
say " → The Devise example is already configured for you\n\n"
|
|
72
|
+
else
|
|
73
|
+
say "3. Enable actor tracking (optional):", :bold
|
|
74
|
+
say " → Uncomment the actor_from_request block in the initializer"
|
|
75
|
+
say " → Customize it for your authentication system\n\n"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
say "4. Test the connection:", :bold
|
|
79
|
+
say " → Run: rails ez_logs_agent:test_connection"
|
|
80
|
+
say " → This verifies your configuration is correct\n\n"
|
|
81
|
+
|
|
82
|
+
say "5. Restart your Rails server:", :bold
|
|
83
|
+
say " → The agent will start capturing events automatically"
|
|
84
|
+
say " → Check your EzLogs dashboard to see activity\n\n"
|
|
85
|
+
|
|
86
|
+
say "=" * 70, :cyan
|
|
87
|
+
say "\n"
|
|
88
|
+
|
|
89
|
+
say "Need help? Check the README or visit https://docs.ezlogs.io", :cyan
|
|
90
|
+
say "\n"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# EzLogsAgent Configuration
|
|
4
|
+
#
|
|
5
|
+
# All options below show their default values.
|
|
6
|
+
# Uncomment and change only the options you need to customize.
|
|
7
|
+
|
|
8
|
+
EzLogsAgent.configure do |config|
|
|
9
|
+
# =============================================================================
|
|
10
|
+
# REQUIRED SETTINGS
|
|
11
|
+
# =============================================================================
|
|
12
|
+
|
|
13
|
+
# URL of the EzLogs server
|
|
14
|
+
# Default: "https://app.ezlogs.io" (the hosted EZLogs service)
|
|
15
|
+
# config.server_url = "https://app.ezlogs.io"
|
|
16
|
+
|
|
17
|
+
# API key for authentication (required)
|
|
18
|
+
# Get this from your EzLogs dashboard: Settings → API Keys
|
|
19
|
+
# Default: nil
|
|
20
|
+
# config.project_token = "your-api-key-here"
|
|
21
|
+
|
|
22
|
+
# =============================================================================
|
|
23
|
+
# CAPTURE SETTINGS
|
|
24
|
+
# =============================================================================
|
|
25
|
+
|
|
26
|
+
# Capture HTTP requests
|
|
27
|
+
# Default: true
|
|
28
|
+
# config.capture_http = true
|
|
29
|
+
|
|
30
|
+
# Capture background jobs (Sidekiq/ActiveJob)
|
|
31
|
+
# Default: true
|
|
32
|
+
# config.capture_jobs = true
|
|
33
|
+
|
|
34
|
+
# Capture database changes via model callbacks
|
|
35
|
+
# Default: true
|
|
36
|
+
# config.capture_database = true
|
|
37
|
+
|
|
38
|
+
# =============================================================================
|
|
39
|
+
# EXCLUSIONS
|
|
40
|
+
# =============================================================================
|
|
41
|
+
|
|
42
|
+
# Additional paths to exclude from HTTP capture (added to built-in defaults)
|
|
43
|
+
# Built-in: /rails/active_storage*, /assets*, /packs*, /vite*, /health*, /up, /favicon.ico
|
|
44
|
+
# config.excluded_paths = ["/admin*", "/internal*"]
|
|
45
|
+
|
|
46
|
+
# Additional tables to exclude from database capture (added to built-in defaults)
|
|
47
|
+
# Built-in: schema_migrations, ar_internal_metadata, sessions, session, active_storage_*, solid_queue_*, etc.
|
|
48
|
+
# config.excluded_tables = ["audit_logs", "versions"]
|
|
49
|
+
|
|
50
|
+
# Additional job classes to exclude from background job capture (added to built-in defaults)
|
|
51
|
+
# Built-in: SidekiqAlive::Worker, SolidQueue::CleanupJob, SolidQueue::RecurringJob
|
|
52
|
+
# config.excluded_job_classes = ["MyApp::HealthCheckJob", "MyApp::MetricsJob"]
|
|
53
|
+
|
|
54
|
+
# Additional GraphQL operations to exclude from capture (added to built-in defaults)
|
|
55
|
+
# Built-in: IntrospectionQuery, __* (all introspection fields like __schema, __type)
|
|
56
|
+
# Supports exact match and prefix match (patterns ending with *)
|
|
57
|
+
# config.excluded_graphql_operations = ["GetCurrentUser", "Internal*"]
|
|
58
|
+
|
|
59
|
+
# =============================================================================
|
|
60
|
+
# PERFORMANCE & RELIABILITY
|
|
61
|
+
# =============================================================================
|
|
62
|
+
|
|
63
|
+
# Max events in memory before oldest are dropped
|
|
64
|
+
# Default: 10000 (optimized for high-volume workloads)
|
|
65
|
+
# config.buffer_size = 10000
|
|
66
|
+
|
|
67
|
+
# Retry attempts for failed sends
|
|
68
|
+
# Default: 3
|
|
69
|
+
# config.retry_attempts = 3
|
|
70
|
+
|
|
71
|
+
# Seconds between automatic flushes
|
|
72
|
+
# Default: 3 (more frequent sends for better throughput)
|
|
73
|
+
# config.send_interval = 3
|
|
74
|
+
|
|
75
|
+
# =============================================================================
|
|
76
|
+
# LOGGING
|
|
77
|
+
# =============================================================================
|
|
78
|
+
|
|
79
|
+
# Agent log level (:debug, :info, :warn, :error)
|
|
80
|
+
# Default: :warn
|
|
81
|
+
# Set to :error in production to minimize output
|
|
82
|
+
# config.log_level = :warn
|
|
83
|
+
|
|
84
|
+
# =============================================================================
|
|
85
|
+
# DISPLAY NAMES (What Resource Was Affected?)
|
|
86
|
+
# =============================================================================
|
|
87
|
+
|
|
88
|
+
# Configure display name resolution per model
|
|
89
|
+
# When a database callback fires, the agent resolves a human-readable name
|
|
90
|
+
# for the affected record using the configured field.
|
|
91
|
+
#
|
|
92
|
+
# Fallback chain: configured field → name → title → number → nil
|
|
93
|
+
#
|
|
94
|
+
# Example:
|
|
95
|
+
# config.display_name_for = {
|
|
96
|
+
# "User" => :email, # Use email field for User models
|
|
97
|
+
# "Product" => :name, # Use name field for Product models
|
|
98
|
+
# "Order" => :number # Use number field for Order models
|
|
99
|
+
# }
|
|
100
|
+
#
|
|
101
|
+
# This enables action titles like:
|
|
102
|
+
# - "User updated 'john@example.com'" instead of just "User updated"
|
|
103
|
+
# - "Product created 'Premium Widget'" instead of just "Product created"
|
|
104
|
+
#
|
|
105
|
+
# IMPORTANT: Only use direct attributes, not associations (would trigger DB queries)
|
|
106
|
+
|
|
107
|
+
# =============================================================================
|
|
108
|
+
# ACTOR CONTEXT (Who Triggered This?)
|
|
109
|
+
# =============================================================================
|
|
110
|
+
|
|
111
|
+
# Optional: Extract actor (user identity) from HTTP requests
|
|
112
|
+
# This enables "who triggered this action?" tracking in EzLogs
|
|
113
|
+
#
|
|
114
|
+
# The hook receives (env, controller) and should return:
|
|
115
|
+
# { id: String, label: String } or nil if actor cannot be determined
|
|
116
|
+
#
|
|
117
|
+
# Example for Devise:
|
|
118
|
+
# config.actor_from_request = ->(env, controller) {
|
|
119
|
+
# return nil unless controller.respond_to?(:current_user)
|
|
120
|
+
# user = controller.current_user
|
|
121
|
+
# return nil unless user
|
|
122
|
+
#
|
|
123
|
+
# {
|
|
124
|
+
# id: user.id.to_s,
|
|
125
|
+
# label: user.email
|
|
126
|
+
# }
|
|
127
|
+
# }
|
|
128
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
namespace :ez_logs_agent do
|
|
4
|
+
desc "Test connection to EzLogs server"
|
|
5
|
+
task test_connection: :environment do
|
|
6
|
+
require "ez_logs_agent"
|
|
7
|
+
|
|
8
|
+
puts "\n🔍 EzLogs Agent Connection Test\n\n"
|
|
9
|
+
|
|
10
|
+
# Step 1: Validate configuration
|
|
11
|
+
puts "Step 1: Validating configuration..."
|
|
12
|
+
result = EzLogsAgent::ConfigurationValidator.validate(EzLogsAgent.configuration)
|
|
13
|
+
|
|
14
|
+
if result.errors.any?
|
|
15
|
+
puts "❌ Configuration validation failed:\n"
|
|
16
|
+
result.errors.each do |error|
|
|
17
|
+
puts " - #{error}"
|
|
18
|
+
end
|
|
19
|
+
puts "\n"
|
|
20
|
+
exit 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
if result.warnings.any?
|
|
24
|
+
puts "⚠️ Configuration warnings:\n"
|
|
25
|
+
result.warnings.each do |warning|
|
|
26
|
+
puts " - #{warning}"
|
|
27
|
+
end
|
|
28
|
+
puts "\n"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
puts "✅ Configuration is valid\n\n"
|
|
32
|
+
|
|
33
|
+
# Step 2: Show configuration summary
|
|
34
|
+
config = EzLogsAgent.configuration
|
|
35
|
+
puts "Configuration Summary:"
|
|
36
|
+
puts " Server URL: #{config.server_url}"
|
|
37
|
+
puts " Project Token: #{config.project_token ? '***' + config.project_token[-4..-1] : '(not set)'}"
|
|
38
|
+
puts " Capture HTTP: #{config.capture_http}"
|
|
39
|
+
puts " Capture Jobs: #{config.capture_jobs}"
|
|
40
|
+
puts " Capture Database: #{config.capture_database}"
|
|
41
|
+
puts "\n"
|
|
42
|
+
|
|
43
|
+
# Step 2: Test connection
|
|
44
|
+
puts "Step 2: Testing connection to server..."
|
|
45
|
+
|
|
46
|
+
begin
|
|
47
|
+
uri = URI.parse(config.server_url)
|
|
48
|
+
uri.path = "/api/events" if uri.path.empty?
|
|
49
|
+
|
|
50
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
51
|
+
http.use_ssl = uri.scheme == "https"
|
|
52
|
+
http.open_timeout = 5
|
|
53
|
+
http.read_timeout = 10
|
|
54
|
+
|
|
55
|
+
# Send a test event
|
|
56
|
+
test_event = {
|
|
57
|
+
kind: "test",
|
|
58
|
+
correlation_id: "test_connection_#{Time.now.to_i}",
|
|
59
|
+
occurred_at: Time.now.utc.iso8601(3),
|
|
60
|
+
context: {
|
|
61
|
+
message: "EzLogs Agent connection test",
|
|
62
|
+
source: "rake ez_logs_agent:test_connection"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
request = Net::HTTP::Post.new(uri.path)
|
|
67
|
+
request["Content-Type"] = "application/json"
|
|
68
|
+
request["Authorization"] = "Bearer #{config.project_token}" if config.project_token
|
|
69
|
+
request.body = { events: [test_event] }.to_json
|
|
70
|
+
|
|
71
|
+
response = http.request(request)
|
|
72
|
+
|
|
73
|
+
case response.code.to_i
|
|
74
|
+
when 200..299
|
|
75
|
+
puts "✅ Connection successful (HTTP #{response.code})"
|
|
76
|
+
puts " Test event accepted by server\n\n"
|
|
77
|
+
puts "✅ All checks passed! EzLogs Agent is configured correctly.\n\n"
|
|
78
|
+
exit 0
|
|
79
|
+
when 401
|
|
80
|
+
puts "❌ Authentication failed (HTTP 401)"
|
|
81
|
+
puts " Check your project_token in config/initializers/ez_logs_agent.rb\n\n"
|
|
82
|
+
exit 1
|
|
83
|
+
when 404
|
|
84
|
+
puts "❌ Server endpoint not found (HTTP 404)"
|
|
85
|
+
puts " Check your server_url: #{config.server_url}\n\n"
|
|
86
|
+
exit 1
|
|
87
|
+
else
|
|
88
|
+
puts "❌ Server responded with HTTP #{response.code}"
|
|
89
|
+
puts " Response: #{response.body[0..200]}\n\n"
|
|
90
|
+
exit 1
|
|
91
|
+
end
|
|
92
|
+
rescue SocketError => e
|
|
93
|
+
puts "❌ Could not resolve host: #{e.message}"
|
|
94
|
+
puts " Check your server_url: #{config.server_url}\n\n"
|
|
95
|
+
exit 1
|
|
96
|
+
rescue Errno::ECONNREFUSED => e
|
|
97
|
+
puts "❌ Connection refused: #{e.message}"
|
|
98
|
+
puts " Is the EzLogs server running at #{config.server_url}?\n\n"
|
|
99
|
+
exit 1
|
|
100
|
+
rescue Timeout::Error => e
|
|
101
|
+
puts "❌ Connection timeout: #{e.message}"
|
|
102
|
+
puts " The server is not responding. Check firewall rules.\n\n"
|
|
103
|
+
exit 1
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
puts "❌ Connection failed: #{e.class} - #{e.message}"
|
|
106
|
+
puts " #{e.backtrace.first}\n\n"
|
|
107
|
+
exit 1
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# publish-to-public.sh — sync the Ruby agent from the private monorepo into
|
|
4
|
+
# the public `dezsirazvan/ez_logs_agent` repo, so RubyGems metadata links,
|
|
5
|
+
# GitHub badges, and external contributors all resolve.
|
|
6
|
+
#
|
|
7
|
+
# Architecture:
|
|
8
|
+
# - This repo (ez_logs) is PRIVATE and holds the source of truth for
|
|
9
|
+
# server code, demo apps, and the agent itself.
|
|
10
|
+
# - A separate PUBLIC repo (ez_logs_agent) holds ONLY the agent so that
|
|
11
|
+
# RubyGems homepage/source/changelog URLs resolve and contributors
|
|
12
|
+
# can open issues + PRs.
|
|
13
|
+
# - This script is the seam: rsync this dir into a temp clone of the
|
|
14
|
+
# public repo, commit, push.
|
|
15
|
+
#
|
|
16
|
+
# Usage:
|
|
17
|
+
# ./script/publish-to-public.sh # sync + push
|
|
18
|
+
# ./script/publish-to-public.sh --dry-run # sync only, skip push
|
|
19
|
+
#
|
|
20
|
+
# Configuration:
|
|
21
|
+
# PUBLIC_REPO — git URL. Default: https://github.com/dezsirazvan/ez_logs_agent.git
|
|
22
|
+
# PUBLIC_BRANCH — Default: master
|
|
23
|
+
#
|
|
24
|
+
# What gets synced:
|
|
25
|
+
# Everything in ez_logs_agent/ EXCEPT: tmp/, vendor/, .bundle/, *.gem,
|
|
26
|
+
# coverage/, .rspec_status, log/, this script's clone dir.
|
|
27
|
+
# (Tests, sig/, CHANGELOG.md, LICENSE.txt, all source — all included.)
|
|
28
|
+
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
|
32
|
+
AGENT_DIR="$( cd "$SCRIPT_DIR/.." && pwd )"
|
|
33
|
+
REPO_ROOT="$( cd "$AGENT_DIR/.." && pwd )"
|
|
34
|
+
|
|
35
|
+
PUBLIC_REPO="${PUBLIC_REPO:-https://github.com/dezsirazvan/ez_logs_agent.git}"
|
|
36
|
+
PUBLIC_BRANCH="${PUBLIC_BRANCH:-master}"
|
|
37
|
+
CLONE_DIR="$REPO_ROOT/.ez-logs-agent-publish"
|
|
38
|
+
|
|
39
|
+
DRY_RUN=false
|
|
40
|
+
for arg in "$@"; do
|
|
41
|
+
case "$arg" in
|
|
42
|
+
--dry-run) DRY_RUN=true ;;
|
|
43
|
+
-h|--help)
|
|
44
|
+
sed -n '2,/^$/p' "$0" | sed 's/^# \{0,1\}//'
|
|
45
|
+
exit 0
|
|
46
|
+
;;
|
|
47
|
+
esac
|
|
48
|
+
done
|
|
49
|
+
|
|
50
|
+
echo "==> Source: $AGENT_DIR"
|
|
51
|
+
echo "==> Target: $PUBLIC_REPO ($PUBLIC_BRANCH)"
|
|
52
|
+
echo "==> Clone: $CLONE_DIR"
|
|
53
|
+
$DRY_RUN && echo "==> Mode: DRY RUN (no push)"
|
|
54
|
+
|
|
55
|
+
# ---------- Clone (or refresh) the public repo --------------------------
|
|
56
|
+
if [ -d "$CLONE_DIR/.git" ]; then
|
|
57
|
+
echo "==> Refreshing existing clone..."
|
|
58
|
+
git -C "$CLONE_DIR" fetch origin
|
|
59
|
+
git -C "$CLONE_DIR" reset --hard "origin/$PUBLIC_BRANCH" 2>/dev/null || true
|
|
60
|
+
else
|
|
61
|
+
echo "==> Cloning public repo..."
|
|
62
|
+
rm -rf "$CLONE_DIR"
|
|
63
|
+
git clone "$PUBLIC_REPO" "$CLONE_DIR"
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Initial-commit case: empty repo with no master branch yet.
|
|
67
|
+
if ! git -C "$CLONE_DIR" rev-parse --verify "$PUBLIC_BRANCH" >/dev/null 2>&1; then
|
|
68
|
+
echo "==> Public repo is empty; will create $PUBLIC_BRANCH on first push."
|
|
69
|
+
git -C "$CLONE_DIR" checkout -b "$PUBLIC_BRANCH"
|
|
70
|
+
fi
|
|
71
|
+
|
|
72
|
+
# ---------- Wipe the clone (except .git) and sync source ----------------
|
|
73
|
+
echo "==> Syncing source into clone..."
|
|
74
|
+
find "$CLONE_DIR" -mindepth 1 -maxdepth 1 ! -name '.git' -exec rm -rf {} +
|
|
75
|
+
|
|
76
|
+
rsync -a \
|
|
77
|
+
--exclude='/tmp/' \
|
|
78
|
+
--exclude='/vendor/' \
|
|
79
|
+
--exclude='/.bundle/' \
|
|
80
|
+
--exclude='/coverage/' \
|
|
81
|
+
--exclude='/log/' \
|
|
82
|
+
--exclude='/script/publish-to-public.sh' \
|
|
83
|
+
--exclude='.rspec_status' \
|
|
84
|
+
--exclude='*.gem' \
|
|
85
|
+
--exclude='.DS_Store' \
|
|
86
|
+
"$AGENT_DIR/" "$CLONE_DIR/"
|
|
87
|
+
|
|
88
|
+
# ---------- Commit + push ----------------------------------------------
|
|
89
|
+
cd "$CLONE_DIR"
|
|
90
|
+
git add -A
|
|
91
|
+
if git diff --cached --quiet; then
|
|
92
|
+
echo "==> Working tree matches origin — nothing new to commit."
|
|
93
|
+
else
|
|
94
|
+
COMMIT_MSG="Sync from ez_logs monorepo @ $(git -C "$AGENT_DIR" rev-parse --short HEAD)"
|
|
95
|
+
git commit -m "$COMMIT_MSG"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
# Are we ahead of origin? (covers commit-now, push-later case across runs.)
|
|
99
|
+
LOCAL_HEAD="$(git rev-parse HEAD)"
|
|
100
|
+
REMOTE_HEAD="$(git rev-parse "origin/$PUBLIC_BRANCH" 2>/dev/null || echo "")"
|
|
101
|
+
if [ "$LOCAL_HEAD" = "$REMOTE_HEAD" ]; then
|
|
102
|
+
echo "==> Already in sync with origin/$PUBLIC_BRANCH. Nothing to push."
|
|
103
|
+
exit 0
|
|
104
|
+
fi
|
|
105
|
+
|
|
106
|
+
if $DRY_RUN; then
|
|
107
|
+
echo "==> Dry run — skipping push. Diff staged in $CLONE_DIR."
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
echo "==> Pushing to $PUBLIC_REPO..."
|
|
112
|
+
git push -u origin "$PUBLIC_BRANCH"
|
|
113
|
+
echo "==> Done. https://github.com/dezsirazvan/ez_logs_agent"
|