logdash 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 215f7ee41511ec997fa91b79b8f9e4ab4f773cb53cf76851d7c705881aae8e43
4
+ data.tar.gz: 28961a89b4436a092d064c2ff19780bba08132f255082cf465472b5ca4d87379
5
+ SHA512:
6
+ metadata.gz: e02e453c9aa4e10cab5b8788d9179c047ba38d2ca48a8174f2f95d31aa512c073d108413fa1fc144380106a4ddcb4ca3499bfb98db0fd3812a54c0e8c887acb7
7
+ data.tar.gz: 170b771a99b0e5ff5d14ab7ba8ed005665a9371acff2a0a158bfb6cad67447a8e40958a06d3915915a562072224fd69a07939c3e5ae307b962599086b2bba726
data/.rubocop.yml ADDED
@@ -0,0 +1,58 @@
1
+ require:
2
+ - rubocop-rspec
3
+
4
+ AllCops:
5
+ NewCops: enable
6
+ TargetRubyVersion: 2.6
7
+ Exclude:
8
+ - "bin/*"
9
+ - "vendor/**/*"
10
+ - "tmp/**/*"
11
+ - "logdash-*.gem"
12
+ SuggestExtensions: false
13
+
14
+ Capybara:
15
+ Enabled: false
16
+
17
+ FactoryBot:
18
+ Enabled: false
19
+
20
+ Capybara/RSpec/PredicateMatcher:
21
+ Enabled: false
22
+
23
+ Gemspec/DevelopmentDependencies:
24
+ Enabled: false
25
+
26
+ Style/Documentation:
27
+ Enabled: false
28
+
29
+ Style/FrozenStringLiteralComment:
30
+ Enabled: true
31
+
32
+ Layout/LineLength:
33
+ Max: 120
34
+
35
+ Metrics/BlockLength:
36
+ Exclude:
37
+ - "spec/**/*"
38
+ - "test/**/*"
39
+ - "ruby-sdk.gemspec"
40
+
41
+ Metrics/ModuleLength:
42
+ Max: 150
43
+
44
+ Metrics/ClassLength:
45
+ Max: 150
46
+
47
+ Metrics/CyclomaticComplexity:
48
+ Max: 10
49
+
50
+ Metrics/PerceivedComplexity:
51
+ Max: 10
52
+
53
+ Metrics/AbcSize:
54
+ Max: 26
55
+
56
+ Metrics/MethodLength:
57
+ Exclude:
58
+ - "lib/logdash/metrics.rb"
data/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
4
+
5
+ ## [1.0.0] - 2025-05-21
6
+
7
+ ### Added
8
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 logdash.io
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # logdash - Ruby SDK
2
+
3
+ Logdash is a zero-config observability platform. This package serves as a Ruby interface to use it.
4
+
5
+ ## Pre-requisites
6
+
7
+ Setup your free project in less than 2 minutes at [logdash.io](https://logdash.io/)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ ```ruby
14
+ gem 'logdash'
15
+ ```
16
+
17
+ And then execute:
18
+
19
+ ```bash
20
+ bundle install
21
+ ```
22
+
23
+ Or install it yourself as:
24
+
25
+ ```bash
26
+ gem install logdash
27
+ ```
28
+
29
+ ## Logging
30
+
31
+ ```ruby
32
+ require 'logdash'
33
+
34
+ logdash_client = Logdash.create(api_key: "YOUR_API_KEY")
35
+ logger = logdash_client[:logger]
36
+
37
+ logger.info('Application started successfully')
38
+ logger.error('An unexpected error occurred')
39
+ logger.warn('Low disk space warning')
40
+ logger.debug('Debugging information')
41
+ logger.verbose('Verbose logging')
42
+ logger.http('HTTP request details')
43
+ logger.silly('Silly level logging')
44
+ ```
45
+
46
+ ## Metrics
47
+
48
+ ```ruby
49
+ require 'logdash'
50
+
51
+ logdash_client = Logdash.create(api_key: "YOUR_API_KEY")
52
+ metrics = $logdash_client[:metrics]
53
+
54
+ metrics.set('users', 0)
55
+ metrics.mutate('users', 1)
56
+ ```
57
+
58
+ ## View
59
+
60
+ To see the logs or metrics, go to your project dashboard.
61
+
62
+ ## Configuration
63
+
64
+ | Parameter | Required | Default | Description |
65
+ | --------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------ |
66
+ | `api_key` | no | - | Api key used to authorize against logdash servers. If you don't provide one, logs will be logged into local console only |
67
+ | `host` | no | `https://api.logdash.io` | Custom API host, useful with self-hosted instances |
68
+ | `verbose` | no | `false` | Useful for debugging purposes |
69
+
70
+ ## View
71
+
72
+ To see the logs or metrics, go to your project dashboard
73
+
74
+ ![logs](docs/logs.png)
75
+ ![delta](docs/delta.png)
76
+
77
+ ## License
78
+
79
+ This project is licensed under the MIT License. See `LICENSE` for more information.
80
+
81
+ ## Contributing
82
+
83
+ Contributions are welcome! Feel free to open issues or submit pull requests.
84
+
85
+ ### Running Tests
86
+
87
+ Before submitting a PR, please run the test suite and rubocop:
88
+
89
+ ```bash
90
+ bundle exec rspec
91
+ bundle exec rubocop
92
+ ```
93
+
94
+ ## Support
95
+
96
+ If you encounter any issues, please open an issue on GitHub or let us know at [contact@logdash.io](mailto:contact@logdash.io).
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'logger'
4
+ require_relative 'metrics'
5
+ require_relative 'http_log_sync'
6
+
7
+ module Logdash
8
+ class Client
9
+ attr_reader :logger, :metrics
10
+
11
+ def initialize(api_key: nil, host: 'https://api.logdash.io', verbose: false)
12
+ @api_key = api_key
13
+ @host = host
14
+ @verbose = verbose
15
+
16
+ log_sync_instance = create_log_sync(api_key, host, verbose)
17
+ @logger = Logger.new(log_sync: log_sync_instance)
18
+
19
+ @metrics = create_metrics(api_key, host, verbose)
20
+ end
21
+
22
+ def self.create(api_key: nil, host: 'https://api.logdash.io', verbose: false)
23
+ client = new(api_key: api_key, host: host, verbose: verbose)
24
+ { logger: client.logger, metrics: client.metrics }
25
+ end
26
+
27
+ private
28
+
29
+ def create_log_sync(api_key, host, verbose)
30
+ if api_key && !api_key.empty?
31
+ HttpLogSync.new(api_key: api_key, host: host, verbose: verbose)
32
+ else
33
+ puts '[LogDash] API key not provided. Using local logger only.' if verbose
34
+ NoopLogSync.new
35
+ end
36
+ end
37
+
38
+ def create_metrics(api_key, host, verbose)
39
+ if api_key && !api_key.empty?
40
+ Metrics.new(api_key: api_key, host: host, verbose: verbose)
41
+ else
42
+ puts '[LogDash] API key not provided. Metrics will not be registered.' if verbose
43
+ NoopMetrics.new
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module Logdash
8
+ class HttpLogSync
9
+ def initialize(api_key:, host:, verbose:)
10
+ @api_key = api_key
11
+ @host = host
12
+ @verbose = verbose
13
+ @sequence_number = 0
14
+ @sequence_mutex = Mutex.new
15
+ end
16
+
17
+ def send(message, level, created_at)
18
+ return unless @api_key
19
+
20
+ payload_sequence_number = next_sequence_number
21
+
22
+ Thread.new do
23
+ send_log_message(message, level, created_at, payload_sequence_number)
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def next_sequence_number
30
+ @sequence_mutex.synchronize do
31
+ current_sequence = @sequence_number
32
+ @sequence_number += 1
33
+ current_sequence
34
+ end
35
+ end
36
+
37
+ def send_log_message(message, level, created_at, sequence_number)
38
+ uri = URI.parse("#{@host}/logs")
39
+ request = build_request(uri, message, level, created_at, sequence_number)
40
+
41
+ begin
42
+ response = http_client(uri).request(request)
43
+ puts "[LogDash BG] Sent log (seq: #{sequence_number}), status: #{response.code}" if @verbose
44
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
45
+ puts "[LogDash BG] Timeout sending log (seq: #{sequence_number}): #{e.message}" if @verbose
46
+ rescue StandardError => e
47
+ puts "[LogDash BG] Error sending log (seq: #{sequence_number}): #{e.message}" if @verbose
48
+ end
49
+ end
50
+
51
+ def build_request(uri, message, level, created_at, sequence_number)
52
+ request = Net::HTTP::Post.new(uri.request_uri)
53
+ request['Content-Type'] = 'application/json'
54
+ request['project-api-key'] = @api_key
55
+ request.body = {
56
+ message: message,
57
+ level: level,
58
+ createdAt: created_at,
59
+ sequenceNumber: sequence_number
60
+ }.to_json
61
+ request
62
+ end
63
+
64
+ def http_client(uri)
65
+ http = Net::HTTP.new(uri.host, uri.port)
66
+ http.use_ssl = (uri.scheme == 'https')
67
+ http.open_timeout = 5
68
+ http.read_timeout = 5
69
+ http
70
+ end
71
+ end
72
+
73
+ class NoopLogSync
74
+ def send(message, level, created_at); end
75
+ end
76
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module Logdash
6
+ module Types
7
+ module LogLevel
8
+ ERROR = 'error'
9
+ WARN = 'warning'
10
+ INFO = 'info'
11
+ HTTP = 'http'
12
+ VERBOSE = 'verbose'
13
+ DEBUG = 'debug'
14
+ SILLY = 'silly'
15
+ end
16
+ end
17
+
18
+ class Logger
19
+ LOG_LEVEL_COLORS = {
20
+ Logdash::Types::LogLevel::ERROR => [231, 0, 11],
21
+ Logdash::Types::LogLevel::WARN => [254, 154, 0],
22
+ Logdash::Types::LogLevel::INFO => [21, 93, 252],
23
+ Logdash::Types::LogLevel::HTTP => [0, 166, 166],
24
+ Logdash::Types::LogLevel::VERBOSE => [0, 166, 0],
25
+ Logdash::Types::LogLevel::DEBUG => [0, 166, 62],
26
+ Logdash::Types::LogLevel::SILLY => [80, 80, 80]
27
+ }.freeze
28
+
29
+ def initialize(log_sync: nil, prefix_proc: nil, on_log_proc: nil)
30
+ @log_sync = log_sync
31
+ @prefix_proc = prefix_proc || default_prefix_proc
32
+ @on_log_proc = on_log_proc
33
+ end
34
+
35
+ Logdash::Types::LogLevel.constants.each do |level_const_sym|
36
+ level_value = Logdash::Types::LogLevel.const_get(level_const_sym)
37
+ define_method(level_value) do |*data|
38
+ _log(level_value, format_data(data))
39
+ end
40
+ end
41
+
42
+ alias log info
43
+
44
+ private
45
+
46
+ def format_data(data)
47
+ data.map do |item|
48
+ item.is_a?(String) ? item : item.to_json
49
+ rescue JSON::GeneratorError
50
+ item.to_s
51
+ end.join(' ')
52
+ end
53
+
54
+ def colorize(text, level)
55
+ rgb = LOG_LEVEL_COLORS[level]
56
+ return text unless rgb
57
+
58
+ "\e[38;2;#{rgb[0]};#{rgb[1]};#{rgb[2]}m#{text}\e[0m"
59
+ end
60
+
61
+ def default_prefix_proc
62
+ lambda do |level|
63
+ timestamp = colorize("[#{Time.now.utc.iso8601}]", Logdash::Types::LogLevel::SILLY)
64
+ level_tag = colorize("[#{level.to_s.upcase}]", level)
65
+ "#{timestamp} #{level_tag} "
66
+ end
67
+ end
68
+
69
+ def _log(level, message)
70
+ prefix = @prefix_proc.call(level)
71
+ puts "#{prefix}#{message}"
72
+ @on_log_proc&.call(level, message)
73
+ @log_sync&.send(message, level, Time.now.utc)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+
7
+ module Logdash
8
+ module BaseMetrics
9
+ def set(key, value)
10
+ raise NotImplementedError, "#{self.class} has not implemented method 'set'"
11
+ end
12
+
13
+ def mutate(key, value)
14
+ raise NotImplementedError, "#{self.class} has not implemented method 'mutate'"
15
+ end
16
+ end
17
+
18
+ module MetricOperation
19
+ SET = 'set'
20
+ CHANGE = 'change'
21
+ end
22
+
23
+ class Metrics
24
+ include BaseMetrics
25
+
26
+ def initialize(api_key:, host:, verbose:)
27
+ @api_key = api_key
28
+ @host = host
29
+ @verbose = verbose
30
+ end
31
+
32
+ def set(name, value)
33
+ puts "[LogDash] Setting metric #{name} to #{value}" if @verbose
34
+ send_metric_async(name, value, MetricOperation::SET)
35
+ end
36
+
37
+ def mutate(name, value)
38
+ puts "[LogDash] Mutating metric #{name} by #{value}" if @verbose
39
+ send_metric_async(name, value, MetricOperation::CHANGE)
40
+ end
41
+
42
+ private
43
+
44
+ def send_metric_async(name, value, operation)
45
+ return unless @api_key
46
+
47
+ Thread.new do
48
+ uri = URI.parse("#{@host}/metrics")
49
+ http = Net::HTTP.new(uri.host, uri.port)
50
+ http.use_ssl = (uri.scheme == 'https')
51
+ http.open_timeout = 5
52
+ http.read_timeout = 5
53
+
54
+ request = Net::HTTP::Put.new(uri.request_uri)
55
+ request['Content-Type'] = 'application/json'
56
+ request['project-api-key'] = @api_key
57
+ request.body = {
58
+ name: name,
59
+ value: value,
60
+ operation: operation
61
+ }.to_json
62
+
63
+ response = http.request(request)
64
+ puts "[LogDash BG] Sent metric (#{name}, op: #{operation}), status: #{response.code}" if @verbose
65
+ rescue Net::OpenTimeout, Net::ReadTimeout => e
66
+ puts "[LogDash BG] Timeout sending metric (#{name}): #{e.message}" if @verbose
67
+ rescue StandardError => e
68
+ puts "[LogDash BG] Error sending metric (#{name}): #{e.message}" if @verbose
69
+ end
70
+ end
71
+ end
72
+
73
+ class NoopMetrics
74
+ include BaseMetrics
75
+
76
+ def set(key, value)
77
+ # No operation
78
+ end
79
+
80
+ def mutate(key, value)
81
+ # No operation
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Logdash
4
+ VERSION = '1.0.0'
5
+ end
data/lib/logdash.rb ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'logdash/client'
4
+ require_relative 'logdash/version'
5
+ require_relative 'logdash/logger'
6
+
7
+ module Logdash
8
+ def self.create(api_key: nil, host: 'https://api.logdash.io', verbose: false)
9
+ Client.create(api_key: api_key, host: host, verbose: verbose)
10
+ end
11
+
12
+ def self.new_client(api_key: nil, host: 'https://api.logdash.io', verbose: false)
13
+ Client.new(api_key: api_key, host: host, verbose: verbose)
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logdash
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - krzysztoff1
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2025-05-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubocop
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.62'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.62'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rubocop-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '2.25'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '2.25'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: webmock
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: A Ruby client to send logs and metrics to the LogDash.
126
+ email:
127
+ - hello@krzysztof.studio
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".rubocop.yml"
133
+ - CHANGELOG.md
134
+ - LICENSE
135
+ - README.md
136
+ - lib/logdash.rb
137
+ - lib/logdash/client.rb
138
+ - lib/logdash/http_log_sync.rb
139
+ - lib/logdash/logger.rb
140
+ - lib/logdash/metrics.rb
141
+ - lib/logdash/version.rb
142
+ homepage: https://logdash.io/
143
+ licenses:
144
+ - MIT
145
+ metadata:
146
+ homepage_uri: https://logdash.io/
147
+ source_code_uri: https://github.com/logdash-io/ruby-sdk
148
+ changelog_uri: https://github.com/logdash-io/ruby-sdk/blob/main/CHANGELOG.md
149
+ rubygems_mfa_required: 'true'
150
+ post_install_message:
151
+ rdoc_options: []
152
+ require_paths:
153
+ - lib
154
+ required_ruby_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 2.6.0
159
+ required_rubygems_version: !ruby/object:Gem::Requirement
160
+ requirements:
161
+ - - ">="
162
+ - !ruby/object:Gem::Version
163
+ version: '0'
164
+ requirements: []
165
+ rubygems_version: 3.5.20
166
+ signing_key:
167
+ specification_version: 4
168
+ summary: Ruby client for LogDash observability platform.
169
+ test_files: []