logsnag-ruby 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/.rubocop.yml +36 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +12 -0
- data/lib/logsnag/configuration.rb +21 -0
- data/lib/logsnag/event_base.rb +30 -0
- data/lib/logsnag/identify.rb +46 -0
- data/lib/logsnag/insight.rb +66 -0
- data/lib/logsnag/log.rb +58 -0
- data/lib/logsnag/result.rb +13 -0
- data/lib/logsnag/validator.rb +49 -0
- data/lib/logsnag/version.rb +5 -0
- data/lib/logsnag-ruby.rb +5 -0
- data/lib/logsnag.rb +173 -0
- data/logsnag.gemspec +36 -0
- metadata +80 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 290549e28821123d90a3a090d8fbfbddfb4121966467d67e166009956586ad9c
|
4
|
+
data.tar.gz: 163c0b7bc7ff995f1aa932cfc46348918e53eb618e1f6be4bbf913442eb38837
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 755f3063453f5555b7f5d6c02265d6e28d029055979a9a1f31104abc26cd447ec27415049f90cb6313c52934b29ba6904805847540d5cd443547722e770521e5
|
7
|
+
data.tar.gz: 716e831b19d6d79d4810647a2c5e3591b860aba9b7d1e47e2b43817d3055455f65e87adc52de4aea8129663f720b200c226a3b1aca08c34448e8355938637845
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-rspec
|
3
|
+
|
4
|
+
AllCops:
|
5
|
+
NewCops: enable
|
6
|
+
SuggestExtensions: false
|
7
|
+
TargetRubyVersion: 2.6
|
8
|
+
|
9
|
+
GemSpec/RequireMFA:
|
10
|
+
Enabled: false
|
11
|
+
|
12
|
+
Layout/LineLength:
|
13
|
+
Enabled: false
|
14
|
+
|
15
|
+
Lint/NonDeterministicRequireOrder:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Metrics/MethodLength:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
RSpec/ExampleLength:
|
22
|
+
Enabled: false
|
23
|
+
|
24
|
+
RSpec/MultipleExpectations:
|
25
|
+
Enabled: false
|
26
|
+
|
27
|
+
RSpec/NestedGroups:
|
28
|
+
Enabled: false
|
29
|
+
|
30
|
+
Style/StringLiterals:
|
31
|
+
Enabled: true
|
32
|
+
EnforcedStyle: double_quotes
|
33
|
+
|
34
|
+
Style/StringLiteralsInInterpolation:
|
35
|
+
Enabled: true
|
36
|
+
EnforcedStyle: double_quotes
|
data/.tool-versions
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby 3.1.4
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Damon Bauer
|
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
|
13
|
+
all 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
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# logsnag-ruby
|
2
|
+
|
3
|
+
Interact with the [LogSnag](https://logsnag.com) API to send logs, identify users, and send insights.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'logsnag-ruby'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
```bash
|
16
|
+
bundle install
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
|
21
|
+
```bash
|
22
|
+
gem install logsnag-ruby
|
23
|
+
```
|
24
|
+
|
25
|
+
## Configuration
|
26
|
+
|
27
|
+
Before you can send logs to LogSnag, you need to configure the gem with your API token and project name.
|
28
|
+
This is typically done in an initializer in your application.
|
29
|
+
For example:
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# config/initializers/logsnag.rb
|
33
|
+
|
34
|
+
LogSnag.configure do |config|
|
35
|
+
config.api_token = "your_api_token"
|
36
|
+
config.project = "your_project_name"
|
37
|
+
end
|
38
|
+
```
|
39
|
+
|
40
|
+
## Usage
|
41
|
+
|
42
|
+
The `logsnag-ruby` gem provides several methods to interact with the LogSnag API.
|
43
|
+
|
44
|
+
Each method will automatically add the `project` and `api_token` parameters to the request.
|
45
|
+
|
46
|
+
### Logging Events
|
47
|
+
|
48
|
+
To send an event log:
|
49
|
+
|
50
|
+
```ruby
|
51
|
+
LogSnag.log({
|
52
|
+
channel: "server",
|
53
|
+
event: "server_start",
|
54
|
+
# ... other optional parameters ...
|
55
|
+
})
|
56
|
+
```
|
57
|
+
|
58
|
+
**Arguments:**
|
59
|
+
|
60
|
+
- `data`: A hash containing the event data.
|
61
|
+
- **Required keys:**
|
62
|
+
- `channel` [String]: The channel within the project to which the log belongs.
|
63
|
+
- `event` [String]: The name of the event.
|
64
|
+
- Optional keys:
|
65
|
+
- `user_id` [String]: The user ID of the user related to the event.
|
66
|
+
- `description` [String]: The description of the event.
|
67
|
+
- `icon` [String]: The icon to be displayed with the event.
|
68
|
+
- `notify` [Boolean]: Whether to send a push notification for the event.
|
69
|
+
- `tags` [Hash]: The tags associated with the event. See [the LogSnag docs](https://docs.logsnag.com/api-reference/log#tags) for more information regarding the format of the `tags` hash.
|
70
|
+
- `parser` [String]: The parser to be used for the event. One of "text" or "markdown".
|
71
|
+
- `timestamp` [Numeric]: The timestamp of the event (in Unix seconds).
|
72
|
+
|
73
|
+
**Returns:**
|
74
|
+
|
75
|
+
- `LogSnag::Result`: A result object with the following methods:
|
76
|
+
- `success?`: Returns `true` if the request was successful.
|
77
|
+
- `error?`: Returns `true` if the request failed.
|
78
|
+
- `data`: The parsed response data from the server.
|
79
|
+
- `error_message`: The error message if the request failed.
|
80
|
+
- `status_code`: The HTTP status code of the response.
|
81
|
+
|
82
|
+
### Identifying Users
|
83
|
+
|
84
|
+
To add or update properties to a user profile:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
LogSnag.identify({
|
88
|
+
user_id: "user_123",
|
89
|
+
properties: {
|
90
|
+
email: "user@example.com",
|
91
|
+
plan: "premium"
|
92
|
+
}
|
93
|
+
})
|
94
|
+
```
|
95
|
+
|
96
|
+
**Arguments:**
|
97
|
+
|
98
|
+
- `data`: A hash containing the identification data.
|
99
|
+
- **Required keys:**
|
100
|
+
- `user_id` [String]: The user ID of the user to be identified.
|
101
|
+
- `properties` [Hash]: The properties of the user to be identified. See [the LogSnag docs](https://docs.logsnag.com/api-reference/identify#properties-schema) for more information regarding the format of the `properties` hash.
|
102
|
+
|
103
|
+
**Returns:**
|
104
|
+
|
105
|
+
- `LogSnag::Result`: A result object with the following methods:
|
106
|
+
- `success?`: Returns `true` if the request was successful.
|
107
|
+
- `error?`: Returns `true` if the request failed.
|
108
|
+
- `data`: The parsed response data from the server.
|
109
|
+
- `error_message`: The error message if the request failed.
|
110
|
+
- `status_code`: The HTTP status code of the response.
|
111
|
+
|
112
|
+
### Sending Insights
|
113
|
+
|
114
|
+
To send an insight log:
|
115
|
+
|
116
|
+
```ruby
|
117
|
+
LogSnag.insight({
|
118
|
+
title: "New Signups",
|
119
|
+
value: 42,
|
120
|
+
# ... other optional parameters ...
|
121
|
+
})
|
122
|
+
```
|
123
|
+
|
124
|
+
**Arguments:**
|
125
|
+
|
126
|
+
- `data`: A hash containing the insight data.
|
127
|
+
- **Required keys:**
|
128
|
+
- `title` [String]: The title of the insight.
|
129
|
+
- `value` [String, Numeric]: The numerical value of the insight.
|
130
|
+
- Optional keys:
|
131
|
+
- `icon` [String]: The icon to be displayed with the insight.
|
132
|
+
|
133
|
+
**Returns:**
|
134
|
+
|
135
|
+
- `LogSnag::Result`: A result object with the following methods:
|
136
|
+
- `success?`: Returns `true` if the request was successful.
|
137
|
+
- `error?`: Returns `true` if the request failed.
|
138
|
+
- `data`: The parsed response data from the server.
|
139
|
+
- `error_message`: The error message if the request failed.
|
140
|
+
- `status_code`: The HTTP status code of the response.
|
141
|
+
|
142
|
+
### Mutating Insights
|
143
|
+
|
144
|
+
To mutate (increment) an existing numerical insight:
|
145
|
+
|
146
|
+
```ruby
|
147
|
+
LogSnag.mutate_insight({
|
148
|
+
title: "Total Users",
|
149
|
+
value: 5
|
150
|
+
})
|
151
|
+
```
|
152
|
+
|
153
|
+
**Arguments:**
|
154
|
+
|
155
|
+
- `data`: A hash containing the insight mutation data.
|
156
|
+
- **Required keys:**
|
157
|
+
- `title` [String]: The title of the insight.
|
158
|
+
- `value` [Numeric]: The amount to increment the insight by.
|
159
|
+
- Optional keys:
|
160
|
+
- `icon` [String]: The icon to be displayed with the insight.
|
161
|
+
|
162
|
+
**Returns:**
|
163
|
+
|
164
|
+
- `LogSnag::Result`: A result object with the following methods:
|
165
|
+
- `success?`: Returns `true` if the request was successful.
|
166
|
+
- `error?`: Returns `true` if the request failed.
|
167
|
+
- `data`: The parsed response data from the server.
|
168
|
+
- `error_message`: The error message if the request failed.
|
169
|
+
- `status_code`: The HTTP status code of the response.
|
170
|
+
|
171
|
+
## Contributing
|
172
|
+
|
173
|
+
Bug reports and pull requests are welcome on GitHub
|
174
|
+
at [https://github.com/damonbauer/logsnag-ruby](https://github.com/damonbauer/logsnag-ruby).
|
175
|
+
|
176
|
+
## License
|
177
|
+
|
178
|
+
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,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LogSnag
|
4
|
+
# Configuration class for storing API token, project, and logger
|
5
|
+
class Configuration
|
6
|
+
attr_accessor :logger
|
7
|
+
attr_reader :api_token, :project
|
8
|
+
|
9
|
+
def api_token=(token)
|
10
|
+
raise ArgumentError, "API token cannot be nil" if token.nil? || token.empty?
|
11
|
+
|
12
|
+
@api_token = token
|
13
|
+
end
|
14
|
+
|
15
|
+
def project=(project_name)
|
16
|
+
raise ArgumentError, "Project cannot be nil" if project_name.nil? || project_name.empty?
|
17
|
+
|
18
|
+
@project = project_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LogSnag
|
4
|
+
# Provides a base class for LogSnag events.
|
5
|
+
# Ensures that the data hash is valid and that the configuration object is present.
|
6
|
+
# @abstract Subclass and override {#data} and {#config} to implement.
|
7
|
+
# @attr_reader data [Hash] The data to be sent to LogSnag.
|
8
|
+
# @attr_reader config [LogSnag::Configuration] The configuration object.
|
9
|
+
# @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
|
10
|
+
# @return [LogSnag::EventBase] The new EventBase object.
|
11
|
+
class EventBase
|
12
|
+
attr_reader :data, :config
|
13
|
+
|
14
|
+
def initialize(data, config)
|
15
|
+
@data = data
|
16
|
+
@config = config
|
17
|
+
|
18
|
+
raise ArgumentError, "`data` must be a Hash" unless data.is_a?(Hash)
|
19
|
+
|
20
|
+
return if config.is_a?(LogSnag::Configuration)
|
21
|
+
|
22
|
+
raise ArgumentError,
|
23
|
+
"LogSnag::Configuration not found. Did you call LogSnag.configure?"
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_json(*_args)
|
27
|
+
data.to_json
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "event_base"
|
4
|
+
require_relative "validator"
|
5
|
+
|
6
|
+
module LogSnag
|
7
|
+
# Constructs and validates an "identify" object
|
8
|
+
class Identify < EventBase
|
9
|
+
REQUIRED_KEYS = %i[project user_id properties].freeze
|
10
|
+
|
11
|
+
attr_reader :data, :config
|
12
|
+
|
13
|
+
# Creates a new Identify object.
|
14
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
15
|
+
# The data hash must include the following keys:
|
16
|
+
# - :user_id [String] The user ID of the user to be identified.
|
17
|
+
# - :properties [Hash] The properties of the user to be identified.
|
18
|
+
# @param config [LogSnag::Configuration] The configuration object.
|
19
|
+
# @see https://docs.logsnag.com/api-reference/identify
|
20
|
+
# @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
|
21
|
+
# @return [LogSnag::Insight] The new Identify object.
|
22
|
+
def initialize(data, config)
|
23
|
+
super(data, config)
|
24
|
+
|
25
|
+
append_required_fields!
|
26
|
+
compact_properties!
|
27
|
+
validate_data
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def append_required_fields!
|
33
|
+
data[:project] = config.project
|
34
|
+
data
|
35
|
+
end
|
36
|
+
|
37
|
+
def compact_properties!
|
38
|
+
Validator.compact_hash!(data, :properties)
|
39
|
+
end
|
40
|
+
|
41
|
+
def validate_data
|
42
|
+
Validator.validate_required_keys(REQUIRED_KEYS, data)
|
43
|
+
Validator.validate_hash(data[:properties])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "event_base"
|
4
|
+
require_relative "validator"
|
5
|
+
|
6
|
+
module LogSnag
|
7
|
+
# Constructs and validates an "insight" object
|
8
|
+
class Insight < EventBase
|
9
|
+
REQUIRED_KEYS = %i[project title value].freeze
|
10
|
+
|
11
|
+
attr_reader :data, :config
|
12
|
+
|
13
|
+
# Creates a new Insight object.
|
14
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
15
|
+
# The data hash MUST include the following keys:
|
16
|
+
# - :title [String] The title of the insight.
|
17
|
+
# - :value [String,Numeric] The numerical value of the insight.
|
18
|
+
# The data hash MAY include the following keys:
|
19
|
+
# - :icon [String] The icon to be displayed with the insight.
|
20
|
+
# @param config [LogSnag::Configuration] The configuration object.
|
21
|
+
# @param mutate [Boolean] Whether or not to mutate the data hash.
|
22
|
+
# @see https://docs.logsnag.com/api-reference/insight
|
23
|
+
# @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
|
24
|
+
# @return [LogSnag::Insight] The new Insight object.
|
25
|
+
def initialize(data, config, mutate: false)
|
26
|
+
super(data, config)
|
27
|
+
|
28
|
+
append_required_fields!
|
29
|
+
|
30
|
+
if mutate
|
31
|
+
validate_mutation_data
|
32
|
+
else
|
33
|
+
validate_data
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def append_required_fields!
|
40
|
+
data[:project] = config.project
|
41
|
+
data
|
42
|
+
end
|
43
|
+
|
44
|
+
def validate_mutation_data
|
45
|
+
Validator.validate_required_keys(REQUIRED_KEYS, data)
|
46
|
+
validate_mutation_values
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_data
|
50
|
+
Validator.validate_required_keys(REQUIRED_KEYS, data)
|
51
|
+
validate_values
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_mutation_values
|
55
|
+
return if [Numeric].any? { |type| data[:value].is_a?(type) }
|
56
|
+
|
57
|
+
raise ArgumentError, "Invalid value for Insight mutation: '#{data[:value]}'. Value must be a number."
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_values
|
61
|
+
return if [String, Numeric].any? { |type| data[:value].is_a?(type) }
|
62
|
+
|
63
|
+
raise ArgumentError, "Invalid value for Insight: '#{data[:value]}'. Values must be a string or number."
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/logsnag/log.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "event_base"
|
4
|
+
require_relative "validator"
|
5
|
+
|
6
|
+
module LogSnag
|
7
|
+
# Constructs and validates an event log
|
8
|
+
class Log < EventBase
|
9
|
+
REQUIRED_KEYS = %i[project channel event].freeze
|
10
|
+
ALLOWED_KEYS = REQUIRED_KEYS + %i[
|
11
|
+
user_id description icon notify tags parser timestamp
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
# attr_reader :data, :config
|
15
|
+
|
16
|
+
# Creates a new Log object.
|
17
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
18
|
+
# The data hash must include the following keys:
|
19
|
+
# - :channel [String] The channel within the project to which the log belongs.
|
20
|
+
# - :event [String] The name of the event.
|
21
|
+
# The data hash may include the following keys:
|
22
|
+
# - :user_id [String] The user ID of the user to be identified.
|
23
|
+
# - :description [String] The description of the event.
|
24
|
+
# - :icon [String] The icon to be displayed with the event.
|
25
|
+
# - :notify [Boolean] Whether or not to send a push notification for the event.
|
26
|
+
# - :tags [Hash] The tags to be associated with the event.
|
27
|
+
# - :parser [String] The parser to be used for the event. One of "text" or "markdown".
|
28
|
+
# - :timestamp [Numeric] The timestamp of the event (in Unix seconds).
|
29
|
+
# @param config [LogSnag::Configuration] The configuration object.
|
30
|
+
# @see https://docs.logsnag.com/api-reference/log
|
31
|
+
# @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
|
32
|
+
# @return [LogSnag::Insight] The new Identify object.
|
33
|
+
def initialize(data, config)
|
34
|
+
super(data, config)
|
35
|
+
|
36
|
+
append_required_fields!
|
37
|
+
compact_tags!
|
38
|
+
validate_data
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def append_required_fields!
|
44
|
+
data[:project] = config.project
|
45
|
+
data
|
46
|
+
end
|
47
|
+
|
48
|
+
def compact_tags!
|
49
|
+
Validator.compact_hash!(data, :tags)
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_data
|
53
|
+
Validator.validate_required_keys(REQUIRED_KEYS, data)
|
54
|
+
Validator.validate_allowed_keys(ALLOWED_KEYS, data)
|
55
|
+
Validator.validate_hash(data[:tags])
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module LogSnag
|
4
|
+
# Provides helpers for validating data.
|
5
|
+
class Validator
|
6
|
+
# Ensure that the `required_keys` are present in the `data` hash.
|
7
|
+
# @param required_keys [Array<Symbol>] The keys that must be present in the hash.
|
8
|
+
# @param data [Hash] The hash to be validated.
|
9
|
+
# @raise [ArgumentError] If the hash is missing required keys.
|
10
|
+
# @return [void]
|
11
|
+
def self.validate_required_keys(required_keys, data)
|
12
|
+
missing_keys = required_keys - data.keys
|
13
|
+
raise ArgumentError, "Missing required keys: #{missing_keys}" unless missing_keys.empty?
|
14
|
+
end
|
15
|
+
|
16
|
+
# Ensure that the `data` hash only contains keys in the `allowed_keys` array.
|
17
|
+
# @param allowed_keys [Array<Symbol>] The keys that are allowed in the hash.
|
18
|
+
# @param data [Hash] The hash to be validated.
|
19
|
+
# @raise [ArgumentError] If the hash contains invalid keys.
|
20
|
+
# @return [void]
|
21
|
+
def self.validate_allowed_keys(allowed_keys, data)
|
22
|
+
invalid_keys = data.keys - allowed_keys
|
23
|
+
raise ArgumentError, "Found invalid keys: #{invalid_keys}" unless invalid_keys.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
# Removes any nil values from the hash at the specified path.
|
27
|
+
# @param hash [Hash] The hash from which to remove nil values.
|
28
|
+
# @param path [Symbol] The path to the hash to be compacted.
|
29
|
+
# @return [Hash] The hash with nil values removed.
|
30
|
+
def self.compact_hash!(hash, path)
|
31
|
+
hash[path] = hash[path].compact if hash[path]
|
32
|
+
hash
|
33
|
+
end
|
34
|
+
|
35
|
+
# Ensures that the keys and values in the hash conform to the specified rules.
|
36
|
+
# @param hash [Hash] The hash to be validated.
|
37
|
+
# @raise [ArgumentError] If the hash contains invalid keys or values.
|
38
|
+
# @return [void]
|
39
|
+
def self.validate_hash(hash)
|
40
|
+
return unless hash
|
41
|
+
|
42
|
+
hash.each do |key, value|
|
43
|
+
raise ArgumentError, "Invalid key: '#{key}'. Keys must be lowercase and may include dashes." unless key.to_s.match?(/\A[a-z]+(-[a-z]+)*\z/)
|
44
|
+
|
45
|
+
raise ArgumentError, "Invalid value for '#{key}': '#{value}'. Values must be a string, boolean, or number." unless [String, TrueClass, FalseClass, Numeric].any? { |type| value.is_a?(type) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/logsnag-ruby.rb
ADDED
data/lib/logsnag.rb
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "httparty"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
require_relative "logsnag/version"
|
7
|
+
require_relative "logsnag/configuration"
|
8
|
+
require_relative "logsnag/log"
|
9
|
+
require_relative "logsnag/identify"
|
10
|
+
require_relative "logsnag/insight"
|
11
|
+
require_relative "logsnag/result"
|
12
|
+
|
13
|
+
# The main module for the LogSnag gem, containing methods for sending logs to LogSnag.
|
14
|
+
module LogSnag
|
15
|
+
include HTTParty
|
16
|
+
|
17
|
+
base_uri "https://api.logsnag.com/v1"
|
18
|
+
PATHS = {
|
19
|
+
LOG: "/log",
|
20
|
+
IDENTIFY: "/identify",
|
21
|
+
INSIGHT: "/insight"
|
22
|
+
}.freeze
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_accessor :config
|
26
|
+
|
27
|
+
# Configures LogSnag with the provided configuration options.
|
28
|
+
# @yield [LogSnag::Configuration] The configuration object.
|
29
|
+
def configure
|
30
|
+
self.config ||= LogSnag::Configuration.new
|
31
|
+
yield(config)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Sends an event log to LogSnag.
|
35
|
+
#
|
36
|
+
# Logs are the core of LogSnag. They are used to track events in your application. These events could be
|
37
|
+
# anything from user actions to server events, such as a database running out of space or a server crash.
|
38
|
+
#
|
39
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
40
|
+
# The data hash MUST include the following keys:
|
41
|
+
# - :channel [String] The channel within the project to which the log belongs.
|
42
|
+
# - :event [String] The name of the event.
|
43
|
+
# The data hash MAY include the following keys:
|
44
|
+
# - :user_id [String] The user ID of the user to be identified.
|
45
|
+
# - :description [String] The description of the event.
|
46
|
+
# - :icon [String] The icon to be displayed with the event.
|
47
|
+
# - :notify [Boolean] Whether or not to send a push notification for the event.
|
48
|
+
# - :tags [Hash] The tags to be associated with the event.
|
49
|
+
# - :parser [String] The parser to be used for the event. One of "text" or "markdown".
|
50
|
+
# - :timestamp [Numeric] The timestamp of the event (in Unix seconds).
|
51
|
+
# @see https://docs.logsnag.com/api-reference/log
|
52
|
+
# @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
|
53
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
54
|
+
def log(data = {})
|
55
|
+
event = LogSnag::Log.new(data, config)
|
56
|
+
execute_post(PATHS[:LOG], event)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sends an identify log to LogSnag.
|
60
|
+
#
|
61
|
+
# The identify endpoint lets you add key-value properties to a user profile.
|
62
|
+
# This endpoint is optional and useful for getting a complete picture of a user just by looking at their profile,
|
63
|
+
# and additionally, these properties can be used for filtering and searching.
|
64
|
+
#
|
65
|
+
# For example, you may add a user's email address, their plan, last payment date, etc., to their profile and
|
66
|
+
# then use these properties to filter and search for users, such as searching for all users on a specific plan.
|
67
|
+
#
|
68
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
69
|
+
# The data hash MUST include the following keys:
|
70
|
+
# - :user_id [String] The user ID of the user to be identified.
|
71
|
+
# - :properties [Hash] The properties of the user to be identified.
|
72
|
+
# @see https://docs.logsnag.com/api-reference/identify
|
73
|
+
# @raise [ArgumentError] If the hash is missing required keys or contains invalid keys.
|
74
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
75
|
+
def identify(data = {})
|
76
|
+
event = LogSnag::Identify.new(data, config)
|
77
|
+
execute_post(PATHS[:IDENTIFY], event)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Sends an insight log to LogSnag.
|
81
|
+
#
|
82
|
+
# Insights are real-time widgets that you can add to each of your projects.
|
83
|
+
# They are use-case agnostic and can be used to display any information that you want in real-time.
|
84
|
+
#
|
85
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
86
|
+
# The data hash MUST include the following keys:
|
87
|
+
# - :title [String] The title of the insight.
|
88
|
+
# - :value [String,Numeric] The numerical value of the insight.
|
89
|
+
# @see https://docs.logsnag.com/api-reference/insight
|
90
|
+
# @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
|
91
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
92
|
+
def insight(data = {})
|
93
|
+
event = LogSnag::Insight.new(data, config)
|
94
|
+
execute_post(PATHS[:INSIGHT], event)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Mutate an insight to LogSnag.
|
98
|
+
# This endpoint allows you to change (mutate) existing numerical insights.
|
99
|
+
# @param data [Hash] The data to be sent to LogSnag.
|
100
|
+
# The data hash MUST include the following keys:
|
101
|
+
# - :title [String] The title of the insight.
|
102
|
+
# - :value [Numeric] The numerical value of the insight.
|
103
|
+
# The data hash MAY include the following keys:
|
104
|
+
# - :icon [String] The icon to be displayed with the event.
|
105
|
+
# @see https://docs.logsnag.com/api-reference/insight-mutate
|
106
|
+
# @raise [ArgumentError] If the hash is missing required keys, has invalid keys, or has values of incorrect types.
|
107
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
108
|
+
def mutate_insight(data = {})
|
109
|
+
event = LogSnag::Insight.new(data, config, mutate: true)
|
110
|
+
execute_patch(PATHS[:INSIGHT], event)
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Executes a POST request to the specified path with the provided body.
|
116
|
+
# @param path [String] The path to which the request should be made.
|
117
|
+
# @param body [LogSnag::Log,LogSnag::Identify,LogSnag::Insight] The body of the request.
|
118
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
119
|
+
def execute_post(path, body)
|
120
|
+
response = post(
|
121
|
+
path,
|
122
|
+
body: body.to_json,
|
123
|
+
headers: headers
|
124
|
+
)
|
125
|
+
|
126
|
+
handle_response(response)
|
127
|
+
rescue HTTParty::Error => e
|
128
|
+
Result.new(success: false, error_message: e.message, status_code: nil)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Executes a PATCH request to the specified path with the provided body.
|
132
|
+
# @param path [String] The path to which the request should be made.
|
133
|
+
# @param body [LogSnag::Insight] The body of the request.
|
134
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
135
|
+
def execute_patch(path, body)
|
136
|
+
response = patch(
|
137
|
+
path,
|
138
|
+
body: {
|
139
|
+
**body.data,
|
140
|
+
value: {
|
141
|
+
"$inc": body.data[:value]
|
142
|
+
}
|
143
|
+
}.to_json,
|
144
|
+
headers: headers
|
145
|
+
)
|
146
|
+
|
147
|
+
handle_response(response)
|
148
|
+
rescue HTTParty::Error => e
|
149
|
+
Result.new(success: false, error_message: e.message, status_code: nil)
|
150
|
+
end
|
151
|
+
|
152
|
+
# Handles the response from the HTTP request.
|
153
|
+
# @param response [HTTParty::Response] The response from the HTTP request.
|
154
|
+
# @return [LogSnag::Result] The formatted result from the request.
|
155
|
+
def handle_response(response)
|
156
|
+
if response.success?
|
157
|
+
Result.new(success: true, data: response.parsed_response, status_code: response.code)
|
158
|
+
else
|
159
|
+
error_message = response.parsed_response["message"] || response.body
|
160
|
+
Result.new(success: false, error_message: error_message, status_code: response.code)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns the headers to be used in the HTTP request.
|
165
|
+
# @return [Hash] The headers to be used in the HTTP request.
|
166
|
+
def headers
|
167
|
+
{
|
168
|
+
"Content-Type" => "application/json",
|
169
|
+
"Authorization" => "Bearer #{config.api_token}"
|
170
|
+
}
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
data/logsnag.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/logsnag/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "logsnag-ruby"
|
7
|
+
spec.version = LogSnag::VERSION
|
8
|
+
spec.authors = ["Damon Bauer"]
|
9
|
+
spec.email = ["damonbauer@protonmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "Unofficial LogSnag API client, written in Ruby."
|
12
|
+
spec.homepage = "https://github.com/damonbauer/logsnag-ruby"
|
13
|
+
spec.license = "MIT"
|
14
|
+
spec.required_ruby_version = ">= 2.6.0"
|
15
|
+
|
16
|
+
spec.metadata["allowed_push_host"] = "https://rubygems.org"
|
17
|
+
spec.metadata["rubygems_mfa_required"] = "false"
|
18
|
+
|
19
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
20
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
21
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/master/CHANGELOG.md"
|
22
|
+
|
23
|
+
spec.add_dependency "httparty", "~> 0.21.0"
|
24
|
+
|
25
|
+
# Specify which files should be added to the gem when it is released.
|
26
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
27
|
+
spec.files = Dir.chdir(__dir__) do
|
28
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
29
|
+
(File.expand_path(f) == __FILE__) ||
|
30
|
+
f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor Gemfile])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
spec.bindir = "exe"
|
34
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
35
|
+
spec.require_paths = ["lib"]
|
36
|
+
end
|
metadata
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logsnag-ruby
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Damon Bauer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-11-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.21.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.21.0
|
27
|
+
description:
|
28
|
+
email:
|
29
|
+
- damonbauer@protonmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- ".rspec"
|
35
|
+
- ".rubocop.yml"
|
36
|
+
- ".tool-versions"
|
37
|
+
- CHANGELOG.md
|
38
|
+
- LICENSE.txt
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- lib/logsnag-ruby.rb
|
42
|
+
- lib/logsnag.rb
|
43
|
+
- lib/logsnag/configuration.rb
|
44
|
+
- lib/logsnag/event_base.rb
|
45
|
+
- lib/logsnag/identify.rb
|
46
|
+
- lib/logsnag/insight.rb
|
47
|
+
- lib/logsnag/log.rb
|
48
|
+
- lib/logsnag/result.rb
|
49
|
+
- lib/logsnag/validator.rb
|
50
|
+
- lib/logsnag/version.rb
|
51
|
+
- logsnag.gemspec
|
52
|
+
homepage: https://github.com/damonbauer/logsnag-ruby
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata:
|
56
|
+
allowed_push_host: https://rubygems.org
|
57
|
+
rubygems_mfa_required: 'false'
|
58
|
+
homepage_uri: https://github.com/damonbauer/logsnag-ruby
|
59
|
+
source_code_uri: https://github.com/damonbauer/logsnag-ruby
|
60
|
+
changelog_uri: https://github.com/damonbauer/logsnag-ruby/blob/master/CHANGELOG.md
|
61
|
+
post_install_message:
|
62
|
+
rdoc_options: []
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 2.6.0
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
requirements: []
|
76
|
+
rubygems_version: 3.3.26
|
77
|
+
signing_key:
|
78
|
+
specification_version: 4
|
79
|
+
summary: Unofficial LogSnag API client, written in Ruby.
|
80
|
+
test_files: []
|