chili_logger 0.0.1 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 10b5fc2b3c38931ea3e011926d0b53bde8c815afa458355551141c4e0848c210
4
- data.tar.gz: ba11f3e2061537c5ac53b2082154678c534409ee6114b6e7eabbd538231db8e6
3
+ metadata.gz: fdf566e52a267b06ca8561b8c17192e7cce0ef988418f566ffdf684fd98653d0
4
+ data.tar.gz: 021bf795a6b256054f622fad78ba9b9372b90adfeea409bd3824918413a32bd1
5
5
  SHA512:
6
- metadata.gz: b89430120e833f374bcd1e5e4613184f450228ba787d7e0a0bd0a4ae7932bd73cdaac480bc06a8b5b3a594c9f6721b5ef27ab6911e625441b7c3d90cf13ec499
7
- data.tar.gz: 4498034a82c5ced13a1a342e0dc449ebf6872806a50544eaa85ecc4d251f7d3c85e097b1e528d6ff850f4fbe806b5481342f2cb23de3be0f57cd170006ab8fe9
6
+ metadata.gz: 276a50ea4d77dec85187a596d9724d950358cb02e7799ce7a57ca51af2f56cee356b3985ec813bc6bf7fe36d152862301260e560f8eed45d8cf79e37e40f4253
7
+ data.tar.gz: 14e33d99349ced59521f52eca10426a636836af432139a53ab2cd452b581fb459c6c277084fc24775e193d226fad576c1a289ac711c6062ec49690cbb010d927
@@ -1,4 +1,92 @@
1
1
  continue
2
+ options
3
+ continue
4
+ options
5
+ continue
6
+ @desc_uniformizer.desc([])
7
+ @desc_uniformizer.desc(options[:desc])
8
+ @desc_uniformizer.desc
9
+ @desc_uniformizer.desc(options[:desc])
10
+ options
11
+ continue
12
+ options
13
+ continue
14
+ main_content.merge(uniformized_content)
15
+ main_content
16
+ continue
17
+ main_content
18
+ uniformized_content
19
+ continue
20
+ desc
21
+ continue
22
+ uniformized_content
23
+ continue
24
+ uniformized_content
25
+ quit
26
+ val.first.is_a?(Hash)
27
+ val.first.is_a?(Hash) ? {} : record
28
+ modified_record(val.first)
29
+ val
30
+ uniformized_val
31
+ quit
32
+ continue
33
+ error.inspect.is_a?(String)
34
+ error.inspect
35
+ error.class.ancestors.include?(Exception)
36
+ error
37
+ new_error
38
+ errors
39
+ all_errors
40
+ continue
41
+ new_error.is_a?(String)
42
+ continue
43
+ new_error.is_a?(String)
44
+ new_error.class
45
+ new_error
46
+ continue
47
+ new_error
48
+ continue
49
+ new_error
50
+ continue
51
+ error
52
+ new_error
53
+ quit
54
+ main_content[:errors] ||= @default.log_errors
55
+ errors
56
+ continue
57
+ user[:cognito_id]
58
+ new_user[:cognito_id]
59
+ continuenew_user[:cognito_id]
60
+ continue
61
+ new_user
62
+ continue
63
+ user
64
+ Thread.current[:current_log_user]
65
+ Thread.current[:current_log_user] ||= default_user
66
+ continue
67
+ Thread.current[:current_log_user] ||= default_user
68
+ continue
69
+ Thread.current[:current_log_user] ||= default_user
70
+ continue
71
+ Thread.current[:current_log_user] ||= default_user
72
+ user
73
+ continue
74
+ log_user
75
+ continue
76
+ Thread.current[:current_log_user] ||= default_user
77
+ Thread.current[:current_log_user]
78
+ user
79
+ continue
80
+ e.class
81
+ e.is_a?(Bunny:TCPConnectionFailed)
82
+ e
83
+ @sqs.send_message('queue_message')
84
+ @sqs.get_queue_url(queue_name: 'mockeesd').queue_url
85
+ @sqs.get_queue_url(queue_name: 'mock').queue_url
86
+ @sqs.get_queue_url(queue_name: queue_name).queue_url
87
+ @sqs
88
+ response
89
+ continue
2
90
  cloud_metadata[@cloud_provider]
3
91
  @cloud_provider
4
92
  continue
data/.gitignore CHANGED
@@ -7,5 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ local_tests_cheatsheet.rb
11
+
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/Gemfile CHANGED
@@ -3,11 +3,14 @@ source 'https://rubygems.org'
3
3
  # Specify your gem's dependencies in chili_logger.gemspec
4
4
  gemspec
5
5
 
6
- gem "activerecord", "~> 5.2"
6
+ gem 'activerecord', '~> 5.2'
7
+ gem 'aws-sdk', '2.9.2'
7
8
  gem 'bunny'
8
9
  gem 'byebug'
9
10
  gem 'rake', '~> 12.0'
10
11
  gem 'rspec', '~> 3.0'
11
12
  gem 'rubocop', '~> 0.60.0', require: false
12
13
 
13
- gem "httparty", "~> 0.18.1"
14
+ gem 'httparty', '~> 0.18.1'
15
+
16
+ gem "simplecov", "~> 0.17.1"
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- chili_logger (0.0.0)
4
+ chili_logger (0.0.8)
5
+ aws-sdk (~> 2.9, >= 2.9.0)
5
6
  bunny
6
7
  httparty
7
8
 
@@ -21,29 +22,42 @@ GEM
21
22
  tzinfo (~> 1.1)
22
23
  amq-protocol (2.3.1)
23
24
  arel (9.0.0)
24
- ast (2.4.0)
25
+ ast (2.4.1)
26
+ aws-eventstream (1.1.0)
27
+ aws-sdk (2.9.2)
28
+ aws-sdk-resources (= 2.9.2)
29
+ aws-sdk-core (2.9.2)
30
+ aws-sigv4 (~> 1.0)
31
+ jmespath (~> 1.0)
32
+ aws-sdk-resources (2.9.2)
33
+ aws-sdk-core (= 2.9.2)
34
+ aws-sigv4 (1.1.4)
35
+ aws-eventstream (~> 1.0, >= 1.0.2)
25
36
  bunny (2.15.0)
26
37
  amq-protocol (~> 2.3, >= 2.3.1)
27
38
  byebug (11.0.1)
28
39
  concurrent-ruby (1.1.6)
29
- diff-lcs (1.3)
40
+ diff-lcs (1.4.2)
41
+ docile (1.3.2)
30
42
  httparty (0.18.1)
31
43
  mime-types (~> 3.0)
32
44
  multi_xml (>= 0.5.2)
33
45
  i18n (1.8.3)
34
46
  concurrent-ruby (~> 1.0)
35
47
  jaro_winkler (1.5.4)
48
+ jmespath (1.4.0)
49
+ json (2.3.1)
36
50
  mime-types (3.3.1)
37
51
  mime-types-data (~> 3.2015)
38
52
  mime-types-data (3.2020.0512)
39
53
  minitest (5.14.1)
40
54
  multi_xml (0.6.0)
41
- parallel (1.19.1)
42
- parser (2.7.1.3)
43
- ast (~> 2.4.0)
55
+ parallel (1.19.2)
56
+ parser (2.7.1.4)
57
+ ast (~> 2.4.1)
44
58
  powerpack (0.1.2)
45
59
  rainbow (3.0.0)
46
- rake (12.3.1)
60
+ rake (12.3.3)
47
61
  rspec (3.9.0)
48
62
  rspec-core (~> 3.9.0)
49
63
  rspec-expectations (~> 3.9.0)
@@ -66,6 +80,11 @@ GEM
66
80
  ruby-progressbar (~> 1.7)
67
81
  unicode-display_width (~> 1.4.0)
68
82
  ruby-progressbar (1.10.1)
83
+ simplecov (0.17.1)
84
+ docile (~> 1.1)
85
+ json (>= 1.8, < 3)
86
+ simplecov-html (~> 0.10.0)
87
+ simplecov-html (0.10.2)
69
88
  thread_safe (0.3.6)
70
89
  tzinfo (1.2.7)
71
90
  thread_safe (~> 0.1)
@@ -76,6 +95,7 @@ PLATFORMS
76
95
 
77
96
  DEPENDENCIES
78
97
  activerecord (~> 5.2)
98
+ aws-sdk (= 2.9.2)
79
99
  bunny
80
100
  byebug
81
101
  chili_logger!
@@ -83,6 +103,7 @@ DEPENDENCIES
83
103
  rake (~> 12.0)
84
104
  rspec (~> 3.0)
85
105
  rubocop (~> 0.60.0)
106
+ simplecov (~> 0.17.1)
86
107
 
87
108
  BUNDLED WITH
88
- 2.1.4
109
+ 1.17.3
data/README.md CHANGED
@@ -1,27 +1,257 @@
1
1
  # ChiliLogger
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/chili_logger`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ ChiliLogger is a gem developed by Chiligum Creatives for internal use. It is used to monitor our ruby applications and generate logs with some uniformization.
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ ## Table of Contents
6
+ * [READ BEFORE CONTRIBUTING](##read-before-contributing)
7
+ * [How it Works](##how-it-works)
8
+ * [Overview](###overview)
9
+ * [Anatomy of a Chiligum Log](###anatomy-of-a-chiligum-log)
10
+ * [Example Log](####example-log)
11
+ * [Environment](####environment)
12
+ * [Layer](####layer)
13
+ * [Type](####type)
14
+ * [Service](####service)
15
+ * [Action](####action)
16
+ * [Description](####description)
17
+ * [User](####user)
18
+ * [Main Content](####main-content)
19
+ * [Ops Metadata](####ops-metadata)
20
+ * [Timestamp](####timestamp)
21
+ * [A Primer on RabbitMQ and ElasticSearch](###a-primer-on-rabbitmq-and-elasticsearch)
22
+ * [RabbitMQ](####rabbitmq)
23
+ * [ElasticSearch](####elasticsearch)
24
+ * [Putting It All Together](####putting-it-all-together)
25
+ * [Installation](##installation)
26
+ * [Basic Usage](##basic-usage)
27
+ * [Advanced Usage](##advanced-usage)
28
+ * [Code Example](####advanced-code-example)
29
+ * [Error Logging](####error-logging)
30
+ * [Customizing a Started Log](####customizing-a-started-log)
31
+ * [Accessing the current_log](####accessing-the-current_log)
32
+ * [user](####user)
33
+ * [overwrite_user](####overwrite_user)
34
+ * [desc](####desc)
35
+ * [overwrite_type](####overwrite_type)
36
+ * [overwrite_service](####overwrite_service)
37
+ * [overwrite_action](####overwrite_action)
38
+ * [overwrite_desc](####overwrite_desc)
39
+ * [main_content](####main_content)
40
+ * [overwrite_main_content](####overwrite_main_content)
41
+ * [add_to_main_content](####add_to_main_content)
42
+ * [modified_records](####modified_records)
43
+ * [overwrite_modified_records](####overwrite_modified_records)
44
+ * [add_modified_record](####add_modified_record)
45
+ * [clear_log_info](####clear_log_info)
46
+ * [Copy-Paste Snippets For Quick Logging](##copy-paste-examples-for-quick-logging)
47
+ * [Papertrail Optional Use](###papertrail-optional-use)
48
+ * [Logging Transactions](###logging-transactions)
49
+ * [Logging Rake Tasks](###logging-rake-tasks)
50
+ * [A Note About ActiveRecords](###a-note-about-activerecords)
51
+ * [Coverage](##Coverage)
6
52
 
7
- ## Installation
53
+ ## READ BEFORE CONTRIBUTING
54
+ The main thing you need to know before changing Chililogger's source code is this: we send our logs in JSON format and [Logstash parses and converts them to Elasticsearch objects](####elasticsearch), requiring all fields to have consistent primitive types. So suppose you send these logs:
55
+ ```json
56
+ // first log
57
+ {
58
+ "user": {
59
+ "name": "Joe Doe"
60
+ }
61
+ }
8
62
 
9
- Add this line to your application's Gemfile:
63
+ // second log
64
+ {
65
+ "user": "Mary Jane"
66
+ }
67
+
68
+ // third log
69
+ {
70
+ "user": {
71
+ "name": {
72
+ "first": "Francis",
73
+ "last": "Coppola"
74
+ }
75
+ }
76
+ }
77
+
78
+ // fourth log
79
+ {
80
+ "client": {
81
+ "name": "Angela"
82
+ }
83
+ }
84
+ ```
85
+ After receiving the first log, `Logstash` will parse it and define that the `user` field is always an object and that it **may** have a field called `name`, which is a string. Notice that it is not mandatory for logs to have a `user` and a `user.name`. But, if a log does have these fields, then they **must** be an object and a string, respectively.
86
+
87
+ When Logstash receives the second log, it will see an inconsistency, because this time `user` is a string, not an object. Because of that, Logstash will ignore this log - in other words, **this log will not be saved to Elasticsearch**. When the third log arrives, the same will happen. It will see that `user` is consistent with what it expected, but that the field `user.name` is now an object, intead of being a string as was expected. This log will also be ignored by Logstash. The fourth log would be accepted though, since `client.name` hasn't been set yet.
88
+
89
+ So if you are going to change anything in ChiliLogger, you have to keep this in mind: **the primitive types of fields in an Elasticsearch index must always be consistent**. If your changes generate logs with fields that already existed in the Elasticsearch index, but now have a different primitive type, Logstash will ignore these logs. This is important because Logstash just ignores incorrect logs without raising errors - so it may take a long time before the problem is noticed. If you need to change the type of a previously set field, you will need to create a new index/Logstash and make sure the logs are sent to this new Logstash.
90
+
91
+ ## How it works
92
+
93
+ ### Overview
94
+
95
+ ![ChiliLogger usual Flow](assets/images/chili-logger-flow.png)
96
+
97
+ For the time being, only `RabbitMQ` is supported as the main message broker. Only `AWS SQS` is available as a fallback broker.
98
+
99
+ If both the main message broker and the fallback message broker are down, ChiliLogger wil discard the log to keep the application running without raising any error. That's because raising errors would make the app crash and make it impossible to be used while the brokers are down.
100
+
101
+
102
+ ### Anatomy of a Chiligum Log
103
+
104
+ The ChiliLogger gem was created to guarantee some level of uniformization in logs generated by all our ruby applications. So, to better understand its functioning and usage, it is helpful to first understand what kind of logs it is trying to generate. An example log would look like the following:
105
+
106
+ #### Example Log
107
+ ```json
108
+ {
109
+ "env": "development",
110
+ "layer": "creatives",
111
+ "type": "transaction",
112
+ "service": "videos",
113
+ "action": "create",
114
+ "desc": "development.creatives.transaction.videos.create",
115
+ "user": {
116
+ "cognito_id": "88",
117
+ "email": 'exemple@test.com',
118
+ "company_cognito_id": "75",
119
+ "company_name": "Chiligum Creatives",
120
+ "admin_ghost_user": "not_specified"
121
+ },
122
+ "transaction": {
123
+ "modified_records": {
124
+ "videos": [{ "id": 42, "title": 'Lateralus' }]
125
+ },
126
+ "errors": [],
127
+ "backtrace": [
128
+ "app/views/dashboard/controllers/videos_controller.rb:17 in block 'create'"
129
+ ],
130
+ },
131
+ "ops_metadata": {
132
+ "server_url": "https://app.chiligumvideos.com"
133
+ },
134
+ "timestamp": "2020-06-30T18:08:59-03:00"
135
+ }
136
+
137
+ ```
138
+ Below is a short explanation of each one of the log attributes.
139
+
140
+ #### Environment
141
+ The `env` attribute is used for tagging the log and tells in which environment the app was running when the log was created. We are mainly concerned with three possible environments: `production`, `staging`, and `development`. Needless to say, logs from a production environment are the most important to us, but staging and development logs can be useful for debugging/testing.
142
+
143
+ ---
10
144
 
145
+ #### Layer
146
+ The `layer` attribute is also used for tagging and tells us in which layer of the Chiligum platform the log was created. Usually, the layer will correspond to the name of the application/service where ChiliLogger was installed. For instance, it could be 'creatives', 'api', 'render', etc...
147
+
148
+ ---
149
+
150
+ #### Type
151
+ Also a tagging attribute, tells what type of log it is. Chiligum has 3 main types of logs:
152
+ - `transaction` is used for logging transactions made by users, such as creating a campaign, uploading a file to the gallery, etc...
153
+ - `uncaught_error` is used for logging internal server errors and to help us find problematic points in our applications;
154
+ - `monitoring` is used to monitor the health of our servers, providing information such as CPU usage, uptime, etc...
155
+
156
+ ChiliLogger is concerned only with `transaction` and `uncaught_errors`, since they are the only ones that can be generated in the application itself. Monitoring logs are created by other monitoring services, like ElasticSearch Beats or AWS Cloudwatch.
157
+
158
+ ---
159
+
160
+ #### Service
161
+ The `service` tagging attribute is used to tell what group of features the user was trying to interact with. Services can be things like 'videos', 'campaigns', 'banners', 'gallery' and will usually have the similar names to the Controllers in an application.
162
+
163
+ ---
164
+
165
+ #### Action
166
+ The `action` tagging attribute describes what action exactly the user was trying to perform in our platform. Usual values for it are 'create', 'update, and 'delete'. Usually, action tags will have similar names to Controllers' methods.
167
+
168
+ ---
169
+
170
+ #### Description
171
+ The `desc` is the last of the tagging attributes and is used to combine all of the previous attributes in a single tag, allowing any person that is examining a log to quickly identify what the log is about. It is structured in the following manner:
11
172
  ```ruby
12
- gem 'chili_logger'
173
+ "#{env}.#{layer}.#{type}.#{service}.#{action}"
13
174
  ```
175
+ Notice that the tags are separated by a `.` period. That's because the `desc` tag is also used to create the [routing_key for topic exchanges](https://www.rabbitmq.com/tutorials/amqp-concepts.html#exchange-topic) when ChiliLogger is using RabbitMQ as its MessageBroker.
14
176
 
15
- And then execute:
177
+ ---
16
178
 
17
- $ bundle install
179
+ #### User
180
+ The `user` stores the main infos about the user whose request generated the log. The user is an object that accepts five attributes:
181
+ - `cognito_id`, the user's global ID, the one the identifies it in Cognito's DB;
182
+ - `email`, so we can easily know who the user is;
183
+ - `company_cognito_id`, the global ID of the user's company;
184
+ - `company_name`;
185
+ - `ghost_user_cognito_id`, for cases when admin users can log as other users. In these cases, the admin's global ID should be stored in the ghost_user_cognito_id;
186
+
187
+ ---
188
+
189
+ #### Main Content
190
+ ##### (Transaction | Error)
191
+ The `main_content` will have all other data relevant to the log. The main content of a log will be stored in a key with the same name as the log `type`. So a transaction log, as in the example above, willhave its main_content stored in the `transaction` field. In an error log, it would be in an `error` field.
192
+ This is a very customizable attribute and its content will be very context relative. But as often as possible, we want it to store `errors`, `backtrace` and the records that were modified (`modified_records`) during the transaction, so we can later audit them, if needed. The `modified_records` are also used for creating analytics reports.
193
+
194
+ ---
195
+
196
+ #### Ops Metadata
197
+ Based on the configuration data(`:server_url`, `:cloud_provider`) that is passed to ChiliLogger when it is initialized, the log will also have metadata about the server itself where the application is running.
198
+
199
+ ---
200
+
201
+ #### Timestamp
202
+ ChiliLogger automatically adds a `timestamp` to the log.
203
+
204
+ ### A Primer on RabbitMQ and ElasticSearch
205
+ Since Chiligum's logging system is entirely based on RabbitMQ and ElasticSearch, it's useful to have at least a high level understanding of how they work and how they are being used together.
206
+
207
+ #### RabbitMQ
208
+ RabbitMQis a popular message broker that can handle a high traffic of messages. Its role in our logging system is to simply receive all incoming logs and put them in appropriate queues, so other services can have access to these logs and process and/or store them in a central database.
209
+
210
+ RabbitMQ has 4 main entities:
211
+ * `publishers`, which create the messages (in our case, it's ChiliLogger creating the logs);
212
+ * `exchanges`, where publishers send the messages to;
213
+ * `queues`, which receive just the messages from the exchange that are relevant to them.
214
+ * `consumers`, which get messages from specific queues (in our case, that's Logstash/Elasticsearch)
215
+
216
+ ![explanation for RabbitMQ basic functioning](assets/images/rabbit-basic-functioning.png)
217
+
218
+ All messages are published to an exchange with a `routing_key`, which allows the exchange to know which queues should receive that message. RabbitMQ has a couple of possible exchange types and each one of them has a different way of distributing the messages among the queues. At Chiligum, we use only the `topic` type, the most flexible one and the one that can simulate all other types if needed.
219
+
220
+ In topic exchanges, the routing_key will be composed of keywords separated by dots ("."). Queues, on the other hand, will define which keywords they are interested at by defining their `binding_keys`. So a message with `production.creatives.videos` as routing_key would bedelivered to any queue with a matching binding_key of `production.creatives.videos`. Queues can use wildcards(`*`, `#`) in their binding keys. It's easier to understand it with the exemple from the RabbitMQ tutorials itself:
18
221
 
19
- Or install it yourself as:
222
+ > `*` can substitute for exactly one word. `#` can substitute for zero or more words.
223
+ >
224
+ > In this example, we're going to send messages which all describe animals. The messages will be sent with a routing key that consists of three words (two dots). The first word in the routing key will describe a celerity, second a colour and third a species: "`<celerity>.<colour>.<species>`".
20
225
 
21
- $ gem install chili_logger
226
+ ![explanation for RabbitMQ topic excahnges](assets/images/rabbit-topic-explanation.webp)
227
+ >
228
+ > We created three bindings: Q1 is bound with binding key "`*.orange.*`" and Q2 with "`*.*.rabbit`" and "`lazy.#`". These bindings can be summarised as:
229
+ >
230
+ > Q1 is interested in all the orange animals. Q2 wants to hear everything about rabbits, and everything about lazy animals.
231
+ >
232
+ > A message with a routing key set to "`quick.orange.rabbit`" will be delivered to both queues. Message "`lazy.orange.elephant`" also will go to both of them. On the other hand "quick.orange.fox" will only go to the first queue, and "`lazy.brown.fox`" only to the second. "`lazy.pink.rabbit`" will be delivered to the second queue only once, even though it matches two bindings. "`quick.brown.fox`" doesn't match any binding so it will be discarded.
22
233
 
23
234
 
24
- Create a initializer file and configure ChiliLogger passing all the relevant informations:
235
+
236
+ #### ElasticSearch
237
+ ElasticSearch is part of the so-called Elastic Stack. It is an open source database that offers powerful, fast querying of data. It is usually used with Logstash, also a tool of the Elastic Stack. Logstash can fetch or receive data from many different sources and can parse this data so it is in compliance with ElasticSearch data structures. At Chiligum, we create logs in JSON format and it is Logstash who converts it to Elasticsearch objects. Logstash can also work with RabbitMQ, creating queues and receiving data form them.
238
+
239
+ #### Putting It All Together
240
+ By now, perhaps it has already become clear how RabbitMQ and ElasticSearch are being used for Chiligum's logging system. `ChiliLogger` will create logs and publish them to a topic exchange in `RabbitMQ` with the appropriate routing_keys. RabbitMQ will distribute these logs to the queues created by `Logstash` instances. The distribution of messages will be according to the queues' binding_keys. Logstash instances will keep polling the queues, fetching messages, parsing them to Elasticsearch objects and storing them in `Elasticsearch indexes`. An Elasticsearch index is analogous to a SQS table, it's basically a collection of stored data.
241
+
242
+ ![video showing Chiligum's logging system in action](assets/images/chiligum-logging-system.gif)
243
+
244
+ ## Installation
245
+
246
+ Add this line to your application's Gemfile:
247
+
248
+ ```ruby
249
+ gem 'chili_logger'
250
+ ```
251
+
252
+
253
+ Create a initializer file and configure ChiliLogger passing all the relevant information. It is recommended to deactivate ChiliLogger when running tests:
254
+
25
255
  ```ruby
26
256
  # config/initializers/chili_logger.rb
27
257
  require 'chili_logger'
@@ -30,42 +260,507 @@ is_deactivated = Rails.env.test?
30
260
 
31
261
  ChiliLogger.instance.config({
32
262
  deactivated: is_deactivated,
33
- log_env: 'development',
34
- log_layer: 'creatives',
35
- server_url: ENV['SERVER_URL'],
36
- cloud_provider: :aws,
37
- msg_broker_name: :rabbitmq,
263
+ log_env: Rails.env, # sets the log's env attribute
264
+ log_layer: ENV['APPLICATION_NAME'], # sets the log's layer attribute
265
+ server_url: ENV['SERVER_URL'], # used for log's ops_metadata attribute
266
+ cloud_provider: ENV['CLOUD_PROVIDER'], # used for log's ops_metadata attribute
267
+ msg_broker_name: :rabbitmq, # ChiliLogger's Message Broker
38
268
  msg_broker_config: {
39
269
  user: ENV['RABBIT_USER'],
40
270
  password: ENV['RABBIT_PASSWORD'],
41
271
  ip: ENV['RABBIT_IP'],
42
272
  port: ENV['RABBIT_PORT'],
43
273
  exchange_name: ENV['RABBIT_EXCHANGE'],
44
- routing_key: ENV['RABBIT_ROUTING_KEY']
274
+ },
275
+ fallback_broker: :aws_sqs, # Chiligum's fallback option in case of Message Broker being down
276
+ fallback_broker_config: {
277
+ region: ENV['CHILI_LOGGER_SQS_HANDLER_REGION'],
278
+ access_key_id: ENV['CHILI_LOGGER_SQS_HANDLER_KEY_ID'],
279
+ secret_access_key: ENV['CHILI_LOGGER_SQS_HANDLER_SECRET_KEY'],
280
+ queue_name: ENV['CHILI_LOGGER_SQS_HANDLER_QUEUE_NAME']
45
281
  }
46
282
  })
47
283
  ```
284
+ The attributes `log_env` and `log_layer` set the fields ['env'](###environment) and ['layer'](###layer) in all logs that will be published by ChiliLogger.
48
285
 
286
+ When configuring ChiliLogger, you MUST set `msg_broker_name` and `msg_broker_config`. All logs are published to the configured message broker and, without it, ChiliLogger can't work. For the time being, only `:rabbitmq` is supported.
49
287
 
288
+ You ALSO MUST set `fallback_broker` and `fallback_broker_config`. This is the fallback option if the Message Broker is not available and, without it, ChiliLogger would break the application every time the Message Broker was down. For the time being, only `:aws_sqs` is supported.
50
289
 
290
+ Please note that if ChiliLogger tries to publish a log and both the configured Message Broker and the Error Handler are simultaneously down, the log will be discarded. This behavior makes sure logging problems never cause the app to break.
51
291
 
52
292
 
293
+ ## Basic Usage
294
+ The easiest way to use ChiliLogger is with the `publish_instant_log` method. It requires a hash with the following optional attributes:
295
+ ```ruby
296
+ {
297
+ desc: { # Hash
298
+ type: 'transaction', # String,
299
+ service: 'videos', # String
300
+ action: 'create', # String
301
+ },
302
+ user: {
303
+ cognito_id: 100, # String || Fixnum || Bignum -> ChiliLogger converts cognito_id fields with these types to Strings
304
+ email: 'example@chiligumvideos.com', # String
305
+ company_cognito_id: 88, # String || Fixnum || Bignum -> ChiliLogger converts company_cognito_id fields with these types to Strings
306
+ company_name: 'Chiligum', # String
307
+ admin_ghost_user: 420, # String || Fixnum || Bignum -> ChiliLogger converts admin_ghost_user with these types to Strings
308
+ },
309
+ main_content: { # Hash
310
+ modified_records: { # Hash
311
+ "#{tablename}": [ # Array of Hashes
312
+ { id: 42, title: 'Example video 1' },
313
+ { id: 42, title: 'Example video 2' },
314
+ ],
315
+ "#{another_tablename}": [ # Array of Hashes
316
+ { id: 42, title: 'Example banner 1' },
317
+ { id: 42, title: 'Example banner 2' },
318
+ ],
319
+ },
320
+ errors: [ # Array of Strings or Exception descendents -> ChiliLogger converts Exception descendents to Strings
321
+ 'first error',
322
+ StandardError.new('second error')
323
+ ]
324
+ },
325
+ }
326
+ ```
327
+ `desc` is used for setting the log's [type](###type), [service](###service), and [action](###action). env and layer were already set when ChiliLogger was [initialized and configured](##installation). Only `type`, `service`, and `action` are accepted.
53
328
 
54
- ## Usage
329
+ `user` will set the user data. Only `cognito_id`, `email`, `company_cognito_id`, `company_name`, and `admin_ghost_user` are accepted.
55
330
 
56
- TODO: Write usage instructions here
331
+ `main_content` will set the main information the log is concerned about. It only accepts `modified_records` or `errors`.
57
332
 
58
- ## Development
333
+ These fields must be set with the appropriate primitive types, as specified above. Not doing so [may create invalid logs](##read-before-contributing). ChiliLogger tries its best to enforce that all fields in a log will have consistent primitive types.
59
334
 
60
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
335
+ ```ruby
336
+ desc = { type: 'transaction', service: 'videos', action: 'create' }
337
+ agent = { user: { id: 88, email: 'axemple@test.com'}, company: {id: 75, name: 'Chiligum' }}
338
+ main_content = {
339
+ modified_records: {
340
+ videos: [{ id: 42, title: 'Lateralus' }]
341
+ }
342
+ }
61
343
 
62
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
344
+ ChiliLogger.instance.publish_instant_log(desc, agent, main_content)
345
+ # publishes a json like this:
346
+ {
347
+ "env": "development",
348
+ "layer": "creatives",
349
+ "type": "transaction",
350
+ "service": "videos",
351
+ "action": "create",
352
+ "desc": "development.creatives.transaction.videos.create",
353
+ "agent": {
354
+ "user": {
355
+ "id": 88,
356
+ "email": 'exemple@test.com'
357
+ },
358
+ "company": {
359
+ "id": 75,
360
+ "name": 'Chiligum'
361
+ }
362
+ },
363
+ "transaction": {
364
+ "modified_records": {
365
+ "videos": [{ "id": 42, "title": 'Lateralus' }]
366
+ },
367
+ "backtrace": [
368
+ "app/views/dashboard/controllers/videos_controller.rb:17 in block 'create'"
369
+ ]
370
+ },
371
+ "ops_metadata": {
372
+ "server_url": "https://app.chiligumvideos.com"
373
+ },
374
+ "timestamp": "2020-06-30T18:08:59-03:00",
375
+ }
376
+ ```
377
+
378
+ Passing `desc`, `agent`, and `main_content` is optional, since all of them have default values. This is so ChiliLogger is resiliant and doesn't break if any of these arguments is forgotten. But notice that their default values are not very descriptive and it results in low-quality (almost useless) logs:
379
+
380
+ ```ruby
381
+ ChiliLogger.instance.publish_instant_log
382
+ # publishes a json like this:
383
+ {
384
+ "action": "not_specified",
385
+ "layer": "creatives",
386
+ "env": "development",
387
+ "type": "not_specified",
388
+ "service": "not_specified",
389
+ "desc": "development.creatives.not_specified.not_specified.not_specified",
390
+ "agent": "not_specified",
391
+ "not_specified": {},
392
+ "backtrace": [
393
+ "app/views/dashboard/controllers/videos_controller.rb:17 in block 'create'"
394
+ ]
395
+ },
396
+ "ops_metadata": {
397
+ "server_url": "https://app.chiligumvideos.com"
398
+ },
399
+ "timestamp": "2020-06-30T18:08:59-03:00",
400
+ }
401
+ ```
402
+
403
+ Notice that ChiliLogger uses the Singleton pattern. So, to call its methods, you must first access the instance method. Calling ChiliLogger.new will return an error:
404
+ ```ruby
405
+ ChiliLogger.publish_instant_log(desc, agent, log)
406
+ # undefined method `publish_instant_log' for ChiliLogger:Class
407
+
408
+ ChiliLogger.new.publish_instant_log(desc, agent, log)
409
+ # NoMethodError: private method `new' called for ChiliLogger:Class
410
+
411
+ ChiliLogger.instance.publish_instant_log(desc, agent, log)
412
+ # works fine
413
+ ```
414
+
415
+
416
+ ## Advanced Usage
417
+ It will often happen that the information needed for a log is distributed in many parts of the application and can't be accessed in a single moment nor in a single place of the code base. For instance, you may want to set the log's agent with information about the current_user that's only available in the Controllers Layer. And you might want to add data to the log every time a DB record is modified, which is easier to do in the Models Layer. For these use cases, ChiliLogger's `publish_instant_log` will not be suitable and you should use more advanced features.
418
+
419
+ ChiliLogger takes advantage of Ruby's Thread class and allows you to start a log and keep adding information to it during the current thread's lifespan. This is particularly useful when logging requests to a controller, since these requests will run from beginning to end in a single thread.
420
+
421
+ Once a log is started, it can be accessed and modified by using ChiliLogger's `start_new_log` method and `current_log` accessor.
422
+
423
+ #### Advanced Code Example
424
+ ```ruby
425
+ # controllers/videos_controller.rb
426
+ class VideosController
427
+ def create
428
+ desc = { type: 'transaction', service: 'videos', action: 'create' }
429
+ agent = { user: current_user.as_json, company: current_user.as_json }
430
+ current_log = ChiliLogger.instance.start_new_log(desc, agent) # Step One
431
+
432
+ Video.create(title: 'Lateralus') # Step Two happens in the Video model
433
+
434
+ current_log.publish # Step Three
435
+ end
436
+ end
437
+
438
+ # models/video.rb
439
+ class Video < ApplicationRecord
440
+ after_create :add_modified_record_to_log
441
+
442
+ def add_modified_record_to_log
443
+ current_log = ChiliLogger.instance.current_log
444
+ modified_record = self.as_json
445
+
446
+ current_log.add_modified_record(self.class.table_name, modified_record) # Step Two
447
+ end
448
+ end
449
+
450
+ # resulting log (simplified)
451
+ {
452
+ "env": "development",
453
+ "layer": "creatives",
454
+ "type": "transaction",
455
+ "service": "videos",
456
+ "action": "create",
457
+ "desc": "development.creatives.transaction.videos.create",
458
+ "agent": {
459
+ "user": {
460
+ "id": 88,
461
+ "email": 'exemple@test.com'
462
+ },
463
+ "company": {
464
+ "id": 75,
465
+ "name": 'Chiligum'
466
+ }
467
+ },
468
+ "transaction": {
469
+ "modified_records": {
470
+ "videos": [{ "id": 42, "title": 'Lateralus' }]
471
+ },
472
+ "backtrace": [
473
+ "app/views/dashboard/controllers/videos_controller.rb:17 in block 'create'"
474
+ ]
475
+ },
476
+ "ops_metadata": {
477
+ "server_url": "https://app.chiligumvideos.com"
478
+ },
479
+ "timestamp": "2020-06-30T18:08:59-03:00",
480
+ }
481
+ ```
482
+
483
+ Notice that the log is started in VideosController#create with some initial info about the agent user and the log description; following, the same log has its main_content customized in Video.add_modified_record_to_log, by accessing ChiliLogger's `current_log` and calling its `add_modified_record` method. And, afterwards, the same log is finished and published with `current_log.publish`, again in VideosController#create.
484
+
485
+ Check the [Copy-Paste Snippets For Quick Logging](##copy-paste-examples-for-quick-logging) section to see how this functionality can be used to quickly set an all-embracing logging system for your application.
486
+
487
+ #### Error Logging
488
+ The controller code above could be improved by adding some logic to log unexpected errors:
489
+ ```ruby
490
+ # controllers/videos_controller.rb
491
+ class VideosController
492
+ def create
493
+ desc = { type: 'transaction', service: 'videos', action: 'create' }
494
+ agent = { user: current_user.as_json, company: current_user.as_json } # .as_json converts ActiveReacords into Hash
495
+ current_log = ChiliLogger.instance.start_new_log(desc, agent)
63
496
 
64
- ## Contributing
497
+ Video.create(title: 'Lateralus')
65
498
 
66
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/chili_logger.
499
+ current_log.publish
67
500
 
501
+ rescue StandardError => error
502
+ #changes log type and publishes it
503
+ current_log.add_to_main_content({ error: error.as_json })
504
+ current_log.overwrite_type('uncaught_error')
505
+ current_log.publish
506
+ end
507
+ end
508
+ ```
509
+ A [full list of methods](###customizing-a-started-log) for customizing a `current_log` is available
510
+
511
+ ### Customizing a Started Log
512
+ Once a log is started, its main attributes can be customized by accessing the `current_log` and calling one of its many customizing methods.
513
+ #### Accessing the current_log
514
+ ```ruby
515
+ current_log = ChiliLogger.instance.current_log
516
+ current_log.agent # returns currently set agent
517
+ current_log.overwrite_agent({ user: { name: 'new_agent' }) # sets new agent
518
+ ```
519
+ #### agent
520
+ returns log's currently set agent
521
+
522
+ ```ruby
523
+ current_log.agent
524
+ # { user: { name: 'not_specified' } }
525
+ ```
526
+
527
+ ---
528
+
529
+ #### overwrite_agent
530
+ overwrites the currently set agent
531
+
532
+ ```ruby
533
+ current_log.agent
534
+ # { user: { name: 'not_specified' } }
535
+
536
+ current_log.overwrite_agent({ user: { name: 'new_agent' } })
537
+ current_log.agent
538
+ # { user: { name: 'new_agent' }
539
+ ```
540
+
541
+ ---
542
+
543
+ #### desc
544
+ returns currently set log's description. Notice that, for the time being, `env` and `layer` can't be overwritten, these description attributes can only be set during [ChiliLogger's configuration](##installation)
545
+
546
+ ```ruby
547
+ current_log.desc
548
+ # { type: 'not_specified', service: 'not_specified', action: 'not_specified' }
549
+ ```
550
+
551
+ ---
552
+
553
+ #### overwrite_type
554
+ overwrites the type attribute currently set in the log's description
555
+
556
+ ```ruby
557
+ current_log.desc
558
+ # { type: 'not_specified', service: 'not_specified', action: 'not_specified' }
559
+
560
+ current_log.overwrite_type('transaction')
561
+ current_log.desc
562
+ # { type: 'transaction', service: 'not_specified', action: 'not_specified' }
563
+ ```
564
+
565
+ ---
566
+
567
+ #### overwrite_service
568
+ overwrites the service attribute currently set in the log's description
569
+
570
+ ```ruby
571
+ current_log.desc
572
+ # { type: 'not_specified', service: 'not_specified', action: 'not_specified' }
573
+
574
+ current_log.overwrite_service('videos')
575
+ current_log.desc
576
+ # { type: 'not_specified', service: 'videos', action: 'not_specified' }
577
+ ```
578
+
579
+ ---
580
+
581
+ #### overwrite_action
582
+ overwrites the action attribute currently set in the log's description
583
+
584
+ ```ruby
585
+ current_log.desc
586
+ # { type: 'not_specified', service: 'not_specified', action: 'not_specified' }
587
+
588
+ current_log.overwrite_action('delete')
589
+ current_log.desc
590
+ # { type: 'not_specified', service: 'not_specified', action: 'delete' }
591
+ ```
592
+
593
+ ---
594
+
595
+ #### overwrite_desc
596
+ overwrites the currently set log's description
597
+
598
+ ```ruby
599
+ current_log.desc
600
+ # { type: 'not_specified', service: 'not_specified', action: 'not_specified' }
601
+
602
+ current_log.overwrite_desc({ type: 'uncaught_error', action: 'create' })
603
+ current_log.desc
604
+ # { type: 'uncaught_error', service: 'not_specified', action: 'create' }
605
+ ```
606
+
607
+ ---
608
+
609
+ #### main_content
610
+ returns the currently set main_content
611
+
612
+ ```ruby
613
+ current_log.main_content
614
+ # { modified_records: {} }
615
+
616
+ ```
617
+
618
+ ---
619
+
620
+ #### overwrite_main_content
621
+ overwrites the currently set main_content
622
+
623
+ ```ruby
624
+ current_log.main_content
625
+ # { modified_records: {} }
626
+
627
+ current_log.overwrite_main_content({ meaning_of_life: 42 })
628
+ current_log.main_content
629
+ # { meaning_of_life: 42 }
630
+ ```
631
+
632
+ ---
633
+
634
+ #### add_to_main_content
635
+ merges hash with currently set main_content
636
+
637
+ ```ruby
638
+ current_log.main_content
639
+ # { modified_records: {} }
640
+
641
+ current_log.add_to_main_content({ meaning_of_life: 42 })
642
+ current_log.main_content
643
+ # { modified_records: {}, meaning_of_life: 42 }
644
+ ```
645
+
646
+ ---
647
+
648
+ #### modified_records
649
+ returns the modified_records stored in the main_content
650
+
651
+ ```ruby
652
+ current_log.modified_records
653
+ # {}
654
+ ```
655
+
656
+ ---
657
+
658
+ #### overwrite_modified_records
659
+ overwrites the modified_records stored in the main_content
660
+
661
+ ```ruby
662
+ current_log.modified_records
663
+ # {}
664
+
665
+ current_log.overwrite_modified_records({ tracks: [{ id: 88, title: 'Lateralus' }] })
666
+ current_log.modified_records
667
+ # { tracks: [{ id: 88, title: 'Lateralus' }] }
668
+ ```
669
+
670
+ ---
671
+
672
+ #### add_modified_record
673
+ merges hash with currently set modified_records
674
+
675
+ ```ruby
676
+ current_log.modified_records
677
+ # { tracks: [{ id: 88, title: 'Lateralus' }] }
678
+
679
+ current_log.overwrite_modified_records(
680
+ {
681
+ tracks: [{ id: 89, title: "Hips Don't lie" }],
682
+ videos: [{id: 42, title: 'Life Of Brian' }]
683
+ }
684
+ )
685
+ current_log.modified_records
686
+ # {
687
+ # tracks: [{ id: 88, title: 'Lateralus' }, { id: 89, title: "Hips Don't lie" }],
688
+ # videos: [{id: 42, title: 'Life Of Brian' }]
689
+ # }
690
+ ```
691
+
692
+ ---
693
+
694
+ #### clear_log_info
695
+ sets agent, desc, and main_content to their default values.
696
+
697
+
698
+ ## Copy-Paste Snippets for Quick Logging
699
+ Following are a series of snippets that can be copied and pasted to your project for quickly setting up some basic logging functionality.
700
+
701
+ ### Papertrail Optional Use
702
+ **Please notice that the snippets in this section use the [paper_trail gem](https://github.com/paper-trail-gem/paper_trail/blob/v9.2.0/README.md#1c-basic-usage) for improving logs.**
703
+
704
+ > ChiliLogger works just fine without paper_trail. If you don't want to use paper_trail or if your application doesn't use ActiveRecords, you can skip the following code.
705
+ >
706
+ > Papertrail allows us to keep a history of changes made to ActiveRecords entities, which is great for logging. To install and setup `paper_trail`, add the following code:
707
+
708
+ // optional code if you want to use Papertrail to enrich logs
709
+ $ bundle add paper_trail
710
+ $ bundle install
711
+ $ bundle exec rails generate paper_trail:install
712
+ $ bundle exec rake db:migrate
713
+ and then:
714
+ ```ruby
715
+ # optional code if you want to use Papertrail to enrich logs
716
+
717
+ # models/application_record.rb
718
+ class ApplicationRecord < ActiveRecord::Base
719
+ self.abstract_class = true
720
+
721
+ has_paper_trail # add this line!
722
+ end
723
+ ```
68
724
 
69
- ## License
70
725
 
71
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
726
+ ### Logging Transactions
727
+ <!-- TODO -->
728
+ <!-- TODO -->
729
+ <!-- TODO -->
730
+ <!-- TODO -->
731
+ <!-- TODO -->
732
+ <!-- TODO -->
733
+ <!-- TODO -->
734
+ <!-- TODO -->
735
+ <!-- TODO -->
736
+ ### Logging Rake Tasks
737
+ <!-- TODO -->
738
+ <!-- TODO -->
739
+ <!-- TODO -->
740
+ <!-- TODO -->
741
+ <!-- TODO -->
742
+ <!-- TODO -->
743
+ <!-- TODO -->
744
+ <!-- TODO -->
745
+ <!-- TODO -->
746
+ ### A Note About ActiveRecords
747
+ <!-- TODO -->
748
+ <!-- TODO -->
749
+ <!-- TODO -->
750
+ <!-- TODO -->
751
+ <!-- TODO -->
752
+ <!-- TODO -->
753
+ <!-- TODO -->
754
+ <!-- TODO -->
755
+ <!-- TODO -->
756
+
757
+ ## Coverage
758
+ <!-- TODO -->
759
+ <!-- TODO -->
760
+ <!-- TODO -->
761
+ <!-- TODO -->
762
+ <!-- TODO -->
763
+ <!-- TODO -->
764
+ <!-- TODO -->
765
+ <!-- TODO -->
766
+ <!-- TODO -->