logtail 0.1.0 → 0.1.5

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: 1be8318ce433573150e153c4def0b1583a7ff79cb23e66160b3a07662fe40346
4
- data.tar.gz: 0ef24a7258f255d275ed26d18de1c1d31948c270eacbb93b85a1ff8fa1d635f9
3
+ metadata.gz: 9958b51a6821adf3e371746dde1dee7d661e4f9b40e492f69e249ed85d22ca80
4
+ data.tar.gz: 2b9db72344079fe012179853c192385a51419f082d44f1883b35401aba6bc869
5
5
  SHA512:
6
- metadata.gz: 856d92f191144d4b827b5088fedc0963ef1a96a7e4ec8631e5883cb275ad9b7f5928dcfff0c2047dafb75ee0d8a40d798750457bbc3028b148fb3b6c1cf15320
7
- data.tar.gz: 0ab7a3f459aaaffe7494e626f535190f2bb643b458b17cd9209ba4b399f6daf336fb8707851272c5b928050b573b5d29db61c4cf020ed3997ead233ca865b193
6
+ metadata.gz: 9826d94548c436782434831a5161267a6b1d0e8814441cde1b2cb51f53257d015b42c6ff7dabc422b42bd045432d34d8bf241e4305cad4a59d5cc0ec156a8f6d
7
+ data.tar.gz: f71fb5e1af5d3cb71b50880c120f91e1eb573bd72fe671a32ec96b90a093b0986433db76a7c0f892051a1a1131c15a432018247a90d836a255ffaf162a98f130
data/CHANGELOG.md CHANGED
@@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [1.0.0] - 2021-02-11
10
+ ## [0.1.0] - 2021-02-11
11
11
 
12
12
  - The first version of the client.
13
+
14
+ ## [0.1.2] - 2021-04-22
15
+
16
+ - Fixed string encoding.
17
+
18
+ ## [0.1.3] - 2021-06-11
19
+
20
+ - Fixed detection of the frame that calls the logger.
data/Rakefile CHANGED
@@ -14,7 +14,7 @@ def puts_with_level(message, level = :info)
14
14
  end
15
15
  end
16
16
 
17
- task :test_the_pipes, [:api_key] do |t, args|
17
+ task :test_the_pipes, [:source_token] do |t, args|
18
18
  support_email = "support@logtail.com"
19
19
  # Do not modify below this line. It's important to keep the `Logtail::Logger`
20
20
  # because it provides an API for logging structured data and capturing context.
@@ -26,7 +26,7 @@ task :test_the_pipes, [:api_key] do |t, args|
26
26
 
27
27
  current_context = Logtail::CurrentContext.instance.snapshot
28
28
  entry = Logtail::LogEntry.new(:info, Time.now, nil, "Testing the pipes (click the inspect icon to view more details)", current_context, nil)
29
- http_device = Logtail::LogDevices::HTTP.new(args.api_key, flush_continuously: false)
29
+ http_device = Logtail::LogDevices::HTTP.new(args.source_token, flush_continuously: false)
30
30
  response = http_device.deliver_one(entry)
31
31
  if response.is_a?(Exception)
32
32
  message = <<~HEREDOC
@@ -36,7 +36,7 @@ module Logtail
36
36
  # you can drop the log messages instead by passing a {DroppingSizedQueue} via the
37
37
  # `:request_queue` option.
38
38
  #
39
- # @param api_key [String] The API key provided to you after you add your application to
39
+ # @param source_token [String] The API key provided to you after you add your application to
40
40
  # [Logtail](https://logtail.com).
41
41
  # @param [Hash] options the options to create a HTTP log device with.
42
42
  # @option attributes [Symbol] :batch_size (1000) Determines the maximum of log lines in
@@ -64,13 +64,13 @@ module Logtail
64
64
  # The default is set via {LOGTAIL_HOST}.
65
65
  #
66
66
  # @example Basic usage
67
- # Logtail::Logger.new(Logtail::LogDevices::HTTP.new("my_logtail_api_key"))
67
+ # Logtail::Logger.new(Logtail::LogDevices::HTTP.new("my_logtail_source_token"))
68
68
  #
69
69
  # @example Apply back pressure instead of dropping messages
70
- # http_log_device = Logtail::LogDevices::HTTP.new("my_logtail_api_key", request_queue: SizedQueue.new(25))
70
+ # http_log_device = Logtail::LogDevices::HTTP.new("my_logtail_source_token", request_queue: SizedQueue.new(25))
71
71
  # Logtail::Logger.new(http_log_device)
72
- def initialize(api_key, options = {})
73
- @api_key = api_key || raise(ArgumentError.new("The api_key parameter cannot be blank"))
72
+ def initialize(source_token, options = {})
73
+ @source_token = source_token || raise(ArgumentError.new("The source_token parameter cannot be blank"))
74
74
  @logtail_host = options[:logtail_host] || ENV['LOGTAIL_HOST'] || LOGTAIL_HOST
75
75
  @logtail_port = options[:logtail_port] || ENV['LOGTAIL_PORT'] || LOGTAIL_PORT
76
76
  @logtail_scheme = options[:logtail_scheme] || ENV['LOGTAIL_SCHEME'] || LOGTAIL_SCHEME
@@ -168,7 +168,7 @@ MESSAGE
168
168
  end
169
169
 
170
170
  raise <<-MESSAGE
171
-
171
+
172
172
  Log delivery failed! No request was made.
173
173
 
174
174
  You can enable internal debug logging with the following:
@@ -201,10 +201,20 @@ MESSAGE
201
201
  req['Authorization'] = authorization_payload
202
202
  req['Content-Type'] = CONTENT_TYPE
203
203
  req['User-Agent'] = USER_AGENT
204
- req.body = msgs.to_msgpack
204
+ req.body = msgs.map { |msg| force_utf8_encoding(msg.to_hash) }.to_msgpack
205
205
  req
206
206
  end
207
207
 
208
+ def force_utf8_encoding(data)
209
+ if data.respond_to?(:force_encoding)
210
+ data.dup.force_encoding('UTF-8')
211
+ elsif data.respond_to?(:transform_values)
212
+ data.transform_values { |val| force_utf8_encoding(val) }
213
+ else
214
+ data
215
+ end
216
+ end
217
+
208
218
  # Flushes the message buffer asynchronously. The reason we provide this
209
219
  # method is because the message buffer limit is constricted by the
210
220
  # Logtail API. The application limit is multiples of the buffer limit,
@@ -361,7 +371,7 @@ MESSAGE
361
371
 
362
372
  # Builds the `Authorization` header value for HTTP delivery to the Logtail API.
363
373
  def authorization_payload
364
- "Bearer #{@api_key}"
374
+ "Bearer #{@source_token}"
365
375
  end
366
376
  end
367
377
  end
@@ -1,5 +1,6 @@
1
1
  require "socket"
2
2
  require "time"
3
+ require "pathname"
3
4
 
4
5
  require "logtail/contexts"
5
6
  require "logtail/events"
@@ -11,6 +12,7 @@ module Logtail
11
12
  BINARY_LIMIT_THRESHOLD = 1_000.freeze
12
13
  DT_PRECISION = 6.freeze
13
14
  MESSAGE_MAX_BYTES = 8192.freeze
15
+ LOGGER_FILE = '/logtail/logger.rb'.freeze
14
16
 
15
17
  attr_reader :context_snapshot, :event, :level, :message, :progname, :tags, :time
16
18
 
@@ -37,6 +39,7 @@ module Logtail
37
39
  @tags = options[:tags]
38
40
  @context_snapshot = context_snapshot
39
41
  @event = event
42
+ @runtime_context = current_runtime_context || {}
40
43
  end
41
44
 
42
45
  # Builds a hash representation containing simple objects, suitable for serialization (JSON).
@@ -45,7 +48,7 @@ module Logtail
45
48
  hash = {
46
49
  :level => level,
47
50
  :dt => formatted_dt,
48
- :message => message
51
+ :message => message,
49
52
  }
50
53
 
51
54
  if !tags.nil? && tags.length > 0
@@ -60,6 +63,10 @@ module Logtail
60
63
  hash[:context] = context_snapshot
61
64
  end
62
65
 
66
+ hash[:context] ||= {}
67
+ hash[:context][:runtime] ||= {}
68
+ hash[:context][:runtime].merge!(@runtime_context)
69
+
63
70
  if options[:only]
64
71
  hash.select do |key, _value|
65
72
  options[:only].include?(key)
@@ -106,5 +113,42 @@ module Logtail
106
113
  rescue Exception
107
114
  nil
108
115
  end
116
+
117
+ def current_runtime_context
118
+ last_logger_invocation_index = caller_locations.rindex { |frame| logtail_logger_frame?(frame) }
119
+ return {} if last_logger_invocation_index.nil?
120
+
121
+ calling_frame_index = last_logger_invocation_index + 1
122
+ frame = caller_locations[calling_frame_index]
123
+
124
+ return convert_to_runtime_context(frame)
125
+ end
126
+
127
+ def convert_to_runtime_context(frame)
128
+ {
129
+ file: path_relative_to_app_root(frame),
130
+ line: frame.lineno,
131
+ frame_label: frame.label,
132
+ }
133
+ end
134
+
135
+ def logtail_logger_frame?(frame)
136
+ !frame.absolute_path.nil? && frame.absolute_path.end_with?(LOGGER_FILE)
137
+ end
138
+
139
+ def path_relative_to_app_root(frame)
140
+ Pathname.new(frame.absolute_path).relative_path_from(root_path).to_s
141
+ end
142
+
143
+ def root_path
144
+ if Object.const_defined?('Rails')
145
+ Rails.root
146
+ elsif Object.const_defined?('Rack::Directory')
147
+ Pathname.new(Rack::Directory.new('').root)
148
+ else
149
+ base_file = caller_locations.last.absolute_path
150
+ Pathname.new(File.dirname(base_file || '/'))
151
+ end
152
+ end
109
153
  end
110
154
  end
@@ -34,14 +34,13 @@ module Logtail
34
34
  def build_log_entry(severity, time, progname, logged_obj)
35
35
  context_snapshot = CurrentContext.instance.snapshot
36
36
  level = SEVERITY_MAP.fetch(severity)
37
- tags = extract_active_support_tagged_logging_tags
37
+ tags = extract_active_support_tagged_logging_tags.clone
38
38
 
39
39
  if logged_obj.is_a?(Event)
40
40
  LogEntry.new(level, time, progname, logged_obj.message, context_snapshot, logged_obj,
41
41
  tags: tags)
42
42
  elsif logged_obj.is_a?(Hash)
43
43
  # Extract the tags
44
- tags = tags.clone
45
44
  tags.push(logged_obj.delete(:tag)) if logged_obj.key?(:tag)
46
45
  tags.concat(logged_obj.delete(:tags)) if logged_obj.key?(:tags)
47
46
  tags.uniq!
@@ -56,7 +55,8 @@ module Logtail
56
55
 
57
56
  # Because of all the crazy ways Rails has attempted tags, we need this crazy method.
58
57
  def extract_active_support_tagged_logging_tags
59
- Thread.current[:activesupport_tagged_logging_tags] ||
58
+ @current_tags ||
59
+ Thread.current[:activesupport_tagged_logging_tags] ||
60
60
  Thread.current[tagged_logging_object_key_name] ||
61
61
  EMPTY_ARRAY
62
62
  end
@@ -135,7 +135,7 @@ module Logtail
135
135
  # logger = Logtail::Logger.new(STDOUT)
136
136
  #
137
137
  # @example Logging to the Logtail HTTP device
138
- # http_device = Logtail::LogDevices::HTTP.new("my-logtail-api-key")
138
+ # http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token")
139
139
  # logger = Logtail::Logger.new(http_device)
140
140
  #
141
141
  # @example Logging to a file (with rotation)
@@ -143,7 +143,7 @@ module Logtail
143
143
  # logger = Logtail::Logger.new(file_device)
144
144
  #
145
145
  # @example Logging to a file and the Logtail HTTP device (multiple log devices)
146
- # http_device = Logtail::LogDevices::HTTP.new("my-logtail-api-key")
146
+ # http_device = Logtail::LogDevices::HTTP.new("my-logtail-source-token")
147
147
  # file_logger = ::Logger.new("path/to/file.log")
148
148
  # logger = Logtail::Logger.new(http_device, file_logger)
149
149
  def initialize(*io_devices_and_loggers)
@@ -1,3 +1,3 @@
1
1
  module Logtail
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.5"
3
3
  end
@@ -102,7 +102,7 @@ describe Logtail::LogDevices::HTTP do
102
102
  request_queue = http.instance_variable_get(:@request_queue)
103
103
  request_attempt = request_queue.deq
104
104
  expect(request_attempt.request).to be_kind_of(Net::HTTP::Post)
105
- expect(request_attempt.request.body).to start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT"))
105
+ expect(request_attempt.request.body).to start_with("\x92\x84\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT"))
106
106
 
107
107
  message_queue = http.instance_variable_get(:@msg_queue)
108
108
  expect(message_queue.size).to eq(0)
@@ -127,7 +127,7 @@ describe Logtail::LogDevices::HTTP do
127
127
  it "should deliver requests on an interval" do
128
128
  stub = stub_request(:post, "https://in.logtail.com/").
129
129
  with(
130
- :body => start_with("\x92\x83\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT")),
130
+ :body => start_with("\x92\x84\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z\xA7message\xB2test log message 1".force_encoding("ASCII-8BIT")),
131
131
  :headers => {
132
132
  'Authorization' => 'Bearer MYKEY',
133
133
  'Content-Type' => 'application/msgpack',
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Logtail::LogEntry do
4
- let(:time) { Time.utc(2016, 9, 1, 12, 0, 0) }
4
+ let(:time) { Time.utc(2021, 06, 11, 12, 0, 0) }
5
5
 
6
6
  describe "#to_msgpack" do
7
7
  it "should encode properly with an event and context" do
@@ -16,7 +16,20 @@ describe Logtail::LogEntry do
16
16
  context = {custom: {a: "b"}}
17
17
  log_entry = described_class.new("INFO", time, nil, "log message", context, event)
18
18
  msgpack = log_entry.to_msgpack
19
- expect(msgpack).to start_with("\x85\xA5level\xA4INFO\xA2dt\xBB2016-09-01T12:00:00.000000Z".force_encoding("ASCII-8BIT"))
19
+ expect(msgpack).to start_with("\x85\xA5level\xA4INFO\xA2dt\xBB2021-06-11T12:00:00.000000Z".force_encoding("ASCII-8BIT"))
20
+ end
21
+ end
22
+
23
+ describe "#to_hash" do
24
+ it "should include runtime context information" do
25
+ log_entry = Logtail::Logger::PassThroughFormatter.new.call("INFO", time, "", "log message")
26
+
27
+ hash = log_entry.to_hash
28
+ expect(hash[:context]).to_not be_nil
29
+ expect(hash[:context][:runtime]).to_not be_nil
30
+ expect(hash[:context][:runtime][:file]).to end_with('/spec/logtail/log_entry_spec.rb')
31
+ expect(hash[:context][:runtime][:line]).to be(25)
32
+ expect(hash[:context][:runtime][:frame_label]).to_not be_nil
20
33
  end
21
34
  end
22
35
  end
@@ -161,7 +161,7 @@ describe Logtail::Logger do
161
161
  end
162
162
 
163
163
  context "with the HTTP log device" do
164
- let(:io) { Logtail::LogDevices::HTTP.new("my_key") }
164
+ let(:io) { Logtail::LogDevices::HTTP.new("my_source_token") }
165
165
 
166
166
  it "should use the PassThroughFormatter" do
167
167
  expect(logger.formatter).to be_kind_of(Logtail::Logger::PassThroughFormatter)
@@ -189,7 +189,7 @@ describe Logtail::Logger do
189
189
 
190
190
  describe "#formatter=" do
191
191
  it "should not allow changing the formatter when the device is HTTP" do
192
- http_device = Logtail::LogDevices::HTTP.new("api_key")
192
+ http_device = Logtail::LogDevices::HTTP.new("source_token")
193
193
  logger = Logtail::Logger.new(http_device)
194
194
  expect { logger.formatter = ::Logger::Formatter.new }.to raise_error(ArgumentError)
195
195
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logtail
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Logtail
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-26 00:00:00.000000000 Z
11
+ date: 2021-06-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: msgpack