txcatcher 0.1.79 → 0.1.80

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d59cc16bf5e45f94d7e985ae349c9df72c0a07c3
4
- data.tar.gz: 4c38a0be61645875a0103e421d6ca6b5119368c1
3
+ metadata.gz: 7d2ec242dcffc29ac886983fba7ab7059465a9c4
4
+ data.tar.gz: f6846d3b51cfbd75e255349869aea3f0025b8162
5
5
  SHA512:
6
- metadata.gz: ace16685e76eca3039acf6b4b91e1f625a431e05a5ea2c07cf5a2dfd11072843a8e027668d2accc25ea776e893d616200003408a5db5552eba49b426368fffea
7
- data.tar.gz: 228a20993a98902b88ce773e2efa25d7442214ccc6f9bfb68ac804e9f3732eefd4e4e96b9629ddd4aceeeb3de220617ce681d688f1787e52b7b0aab4cc62a539
6
+ metadata.gz: '0832cce4418a861b4953ac158c346608f5d8e72ae5cd89a7a08ae89216321c1f0f2770140e3e561e95277da3c3629606b8d3e12f5569e586711ca802d3933d79'
7
+ data.tar.gz: 5a1105b971ec62a6598781eee994e90993cd780fab2cf495dcb05c305dd34766ae9944624390af047fce31340967c1b8d9a29868800ffcbb9795821d67244e1b
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gem "goliath"
4
4
  gem "sequel"
5
5
  gem "ffi-rzmq"
6
6
  gem "satoshi-unit"
7
+ gem "sentry-raven"
7
8
 
8
9
  group :development do
9
10
  gem "bundler"
data/Gemfile.lock CHANGED
@@ -95,6 +95,8 @@ GEM
95
95
  rspec-support (3.7.1)
96
96
  satoshi-unit (0.2.2)
97
97
  semver2 (3.4.2)
98
+ sentry-raven (2.6.3)
99
+ faraday (>= 0.7.6, < 1.0)
98
100
  sequel (5.5.0)
99
101
  sqlite3 (1.3.13)
100
102
  thread_safe (0.3.6)
@@ -109,6 +111,7 @@ DEPENDENCIES
109
111
  jeweler
110
112
  rspec
111
113
  satoshi-unit
114
+ sentry-raven
112
115
  sequel
113
116
  sqlite3
114
117
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.79
1
+ 0.1.80
@@ -9,7 +9,7 @@ module TxCatcher
9
9
  @sockets = {}
10
10
 
11
11
  {'rawtx' => "#{socket}#{name}.rawtx", 'hashblock' => "#{socket}#{name}.hashblock"}.each do |channel, address|
12
- puts "Start listening on #{name} #{channel}... (#{address})"
12
+ Logger.report "Start listening on #{name} #{channel}... (#{address})"
13
13
  listen_to_zeromq_message(channel: channel, address: address)
14
14
  end
15
15
  end
@@ -32,18 +32,16 @@ module TxCatcher
32
32
  # before we start listening to any messages from ZeroMQ.
33
33
  queue_thread = Thread.new do
34
34
  loop do
35
- puts "in #{channel} queue: #{@queue[channel].size}"
35
+ Logger.report "in #{channel} queue: #{@queue[channel].size}" if Config["logger"]["log_queue_info"]
36
36
  if @queue[channel].empty?
37
37
  sleep 1
38
38
  else
39
39
  begin
40
40
  @queue[channel].pop.call
41
41
  rescue Sequel::ValidationFailed => e
42
- $stdout.puts "[WARNING #{Time.now.to_s}] #{e.class} #{e.to_s}\n"
42
+ Logger.report e, :warn, timestamp: true
43
43
  rescue StandardError => e
44
- File.open(TxCatcher::Config.config_dir + "/error.log", "a") do |f|
45
- f.puts "[ERROR #{Time.now.to_s}] #{e.class} #{e.to_s}\n #{e.backtrace.join("\n ")}\n\n"
46
- end
44
+ Logger.report e, :error, timestamp: true
47
45
  end
48
46
  end
49
47
  end
@@ -77,14 +75,14 @@ module TxCatcher
77
75
  end # listen_to_zeromq_message
78
76
 
79
77
  def handle_rawtx(txhex)
80
- $stdout.print "received tx hex: #{txhex[0..50]}...\n"
78
+ Logger.report "received tx hex: #{txhex[0..50]}..."
81
79
 
82
80
  @queue["rawtx"] << ( Proc.new {
83
81
  tx = TxCatcher::Transaction.new(hex: txhex)
84
82
  tx.save
85
- $stdout.puts "tx #{tx.txid} saved (id: #{tx.id}), deposits (outputs):"
83
+ Logger.report "tx #{tx.txid} saved (id: #{tx.id}), deposits (outputs):"
86
84
  tx.deposits.each do |d|
87
- $stdout.puts " id: #{d.id}, addr: #{d.address.address}, amount: #{Satoshi.new(d.amount, from_unit: :satoshi).to_btc}"
85
+ Logger.report " id: #{d.id}, addr: #{d.address.address}, amount: #{Satoshi.new(d.amount, from_unit: :satoshi).to_btc}"
88
86
  end
89
87
  })
90
88
  end
@@ -93,7 +91,7 @@ module TxCatcher
93
91
  block_hash = TxCatcher.rpc_node.getblock(block_hex)
94
92
  transactions = block_hash["tx"]
95
93
  height = TxCatcher.current_block_height = block_hash["height"].to_i
96
- $stdout.puts "*** Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
94
+ Logger.report "*** Block #{height} mined, transactions received:\n #{transactions.join(" \n")}"
97
95
  @queue["hashblock"] << ( Proc.new {
98
96
  Transaction.where(txid: transactions).update(block_height: height)
99
97
  })
@@ -19,14 +19,14 @@ module TxCatcher
19
19
  else
20
20
  TxCatcher::Transaction.count
21
21
  end
22
- $stdout.print "Cleaning transactions in DB..."
23
- $stdout.print "#{db_tx_count} now, needs to be below #{Config.max_db_transactions_stored}\n"
22
+ Logger.report "Cleaning transactions in DB..."
23
+ Logger.report "#{db_tx_count} now, needs to be below #{Config.max_db_transactions_stored}\n"
24
24
  if db_tx_count > Config.max_db_transactions_stored
25
25
  number_to_delete = (db_tx_count - Config.max_db_transactions_stored) + (Config.max_db_transactions_stored*0.1).round
26
26
  clean_transactions(number_to_delete)
27
- $stdout.puts "DB cleaned: #{@@cleaning_counter.to_s}"
27
+ Logger.report "DB cleaned: #{@@cleaning_counter.to_s}"
28
28
  else
29
- $stdout.puts "Nothing to be cleaned from DB, size is below threshold."
29
+ Logger.report "Nothing to be cleaned from DB, size is below threshold."
30
30
  end
31
31
  if @stop || run_once
32
32
  @@stopped = true
@@ -58,7 +58,7 @@ module TxCatcher
58
58
  t.times do
59
59
  limit = n <= 100 ? n : 100
60
60
  transactions.limit(limit).each do |t|
61
- $stdout.puts "- Removing tx #{t.txid}"
61
+ Logger.report "- Removing tx #{t.txid}"
62
62
  clean_deposits(t)
63
63
  TxCatcher.db_connection[:transactions].filter(id: t.id).delete
64
64
  @@cleaning_counter[:transactions] += 1
@@ -18,17 +18,19 @@ module TxCatcher
18
18
  # Determine config dir or set default. Useful when we want to
19
19
  # have different settings for production or staging or development environments.
20
20
  def set!(path=nil)
21
- @@config_file = path and return if path
22
- @@config_file = ENV['HOME'] + '/.txcatcher/config.yml'
23
- ARGV.each_with_index do |a,i|
24
- if a =~ /\A--config-file=.+/
25
- @@config_file = File.expand_path(a.sub('--config-file=', ''))
26
- ARGV.delete_at(i)
27
- break
28
- elsif a =~ /\A-c\Z/
29
- @@config_file = File.expand_path(ARGV[1])
30
- ARGV.delete_at(i) and ARGV.delete_at(i+1)
31
- break
21
+ @@config_file = path
22
+ unless path
23
+ @@config_file = ENV['HOME'] + '/.txcatcher/config.yml'
24
+ ARGV.each_with_index do |a,i|
25
+ if a =~ /\A--config-file=.+/
26
+ @@config_file = File.expand_path(a.sub('--config-file=', ''))
27
+ ARGV.delete_at(i)
28
+ break
29
+ elsif a =~ /\A-c\Z/
30
+ @@config_file = File.expand_path(ARGV[1])
31
+ ARGV.delete_at(i) and ARGV.delete_at(i+1)
32
+ break
33
+ end
32
34
  end
33
35
  end
34
36
  TxCatcher::Config.config_dir = File.dirname(@@config_file)
@@ -45,6 +47,7 @@ module TxCatcher
45
47
 
46
48
  def prepare
47
49
  ConfigFile.set!
50
+ initialize_sentry
48
51
  create_config_files
49
52
  read_config_file
50
53
  connect_to_db
@@ -149,6 +152,13 @@ module TxCatcher
149
152
  !Sequel::Migrator.is_current?(TxCatcher.db_connection, MIGRATIONS_ROOT)
150
153
  end
151
154
 
155
+ def initialize_sentry
156
+ Raven.configure do |config|
157
+ config.dsn = Config["sentry"]["dsn"]
158
+ config.environments = ["production"]
159
+ end if Config["sentry"] && Config["sentry"]["dsn"]
160
+ end
161
+
152
162
  end
153
163
 
154
164
  end
@@ -0,0 +1,63 @@
1
+ module TxCatcher
2
+
3
+ class Logger
4
+
5
+ LOG_LEVELS = {
6
+ info: 1,
7
+ warn: 2,
8
+ error: 3,
9
+ fatal: 4,
10
+ unknown: 5
11
+ }
12
+
13
+ class << self
14
+
15
+ def report(message, log_level=:info, data: nil, timestamp: false)
16
+ [:logfile, :stdout, :sentry].each do |out|
17
+ if LOG_LEVELS[log_level] >= LOG_LEVELS[Config["logger"]["#{out}_level"].to_sym]
18
+ send("report_to_#{out}", message, log_level, data: data, timestamp: timestamp)
19
+ end
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def report_to_stdout(message, log_level, data: nil, timestamp: timestamp)
26
+ $stdout.print prepare_message(message, timestamp: timestamp) + "\n"
27
+ $stdout.print " additional data: #{data.to_s}" if data
28
+ $stdout.print("\n\n") if LOG_LEVELS[log_level] >= LOG_LEVELS[:error]
29
+ end
30
+
31
+ def report_to_logfile(message, log_level, data: nil, timestamp: timestamp)
32
+ fn = LOG_LEVELS[log_level] >= LOG_LEVELS[:error] ? "error.log" : "txcatcher.log"
33
+ fn = TxCatcher::Config.config_dir + "/#{fn}"
34
+
35
+ File.open(fn, "a") do |f|
36
+ f.print "[#{log_level.to_s.upcase} #{Time.now.to_s}] #{prepare_message(message, timestamp: timestamp)}\n"
37
+ f.print " additional data: #{data.to_s}" if data
38
+ f.print("\n\n") if LOG_LEVELS[log_level] >= LOG_LEVELS[:error]
39
+ end
40
+ end
41
+
42
+ def report_to_sentry(e, log_level, data: nil, timestamp: timestamp)
43
+ e.kind_of?(Exception) ? Raven.capture_exception(e, extra: data) : Raven.capture_message(e, extra: data)
44
+ end
45
+
46
+ def prepare_message(e, timestamp: false)
47
+ result = if e.kind_of?(Exception)
48
+ result = e.to_s + "\n"
49
+ result += e.message + "\n\n" if e.message != e.to_s
50
+ result += (e.backtrace&.join("\n") || "[no backtrace]")
51
+ result
52
+ else
53
+ e
54
+ end
55
+ result = "[#{Time.now}] #{result}" if timestamp
56
+ result
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -62,9 +62,9 @@ module TxCatcher
62
62
  block_hash = TxCatcher.rpc_node.getblockhash(TxCatcher.current_block_height - i)
63
63
  block = TxCatcher.rpc_node.getblock(block_hash)
64
64
  i += 1
65
- $stdout.puts "--- checking block #{TxCatcher.current_block_height - i} for tx #{self.txid}"
65
+ Logger.report "--- checking block #{TxCatcher.current_block_height - i} for tx #{self.txid}"
66
66
  if block["tx"] && block["tx"].include?(self.txid)
67
- $stdout.puts "tx #{self.txid} block height updated to #{block["height"]}"
67
+ Logger.report "tx #{self.txid} block height updated to #{block["height"]}"
68
68
  self.update(block_height: block["height"])
69
69
  return block["height"].to_i
70
70
  end
data/lib/txcatcher.rb CHANGED
@@ -4,6 +4,7 @@ require 'json'
4
4
  require 'thread'
5
5
  require 'satoshi-unit'
6
6
  require 'sequel'
7
+ require 'sentry-raven'
7
8
  Sequel.extension :migration
8
9
 
9
10
  require_relative 'txcatcher/utils/hash_string_to_sym_keys'
@@ -12,6 +13,7 @@ require_relative 'txcatcher/config'
12
13
  require_relative 'txcatcher/initializer'
13
14
  require_relative 'txcatcher/catcher'
14
15
  require_relative 'txcatcher/cleaner'
16
+ require_relative 'txcatcher/logger'
15
17
 
16
18
  include TxCatcher::Initializer
17
19
  prepare
@@ -17,7 +17,17 @@ rpcnode:
17
17
  host: 127.0.0.1
18
18
  port: 8332
19
19
 
20
+ logger:
21
+ log_queue_info: false
22
+ stdout_level: info
23
+ logfile_level: info
24
+ sentry_level: info
25
+ sentry:
26
+ dsn: null
27
+ client_dsn: null
28
+
20
29
  # zeromq: bitcoind
21
30
  max_db_transactions_stored: 10
22
31
  db_clean_period_seconds: 300
23
32
  protected_transactions: true
33
+ environment: development
@@ -0,0 +1,64 @@
1
+ require 'rubygems'
2
+ require_relative 'spec_helper'
3
+
4
+ RSpec.describe TxCatcher::Logger do
5
+
6
+ describe "logging to logfiles" do
7
+
8
+ it "writes info messages to txcatcher.log" do
9
+ TxCatcher::Logger.report "example info message", :info
10
+ expect(File.read(LOGFILE)).to include("example info message")
11
+ end
12
+
13
+ it "writes errors into a separate error.log file" do
14
+ TxCatcher::Logger.report "example error message", :error
15
+ expect(File.read(ERRFILE)).to include("example error message")
16
+ end
17
+
18
+ end
19
+
20
+ describe "sending to Sentry" do
21
+
22
+ it "logs messages to Sentry" do
23
+ expect(Raven).to receive(:capture_message).once
24
+ TxCatcher::Logger.report "example info message", :info
25
+ TxCatcher::Logger.report "example error message", :error
26
+ end
27
+
28
+ it "logs errors to Sentry" do
29
+ expect(Raven).to receive(:capture_exception).once
30
+ TxCatcher::Logger.report StandardError.new, :error
31
+ end
32
+
33
+ end
34
+
35
+ it "logs to stdout" do
36
+ expect($stdout).to receive(:print).exactly(3).times
37
+ TxCatcher::Logger.report "example info message", :info
38
+ TxCatcher::Logger.report "example error message", :error
39
+ end
40
+
41
+ it "doesn't log an message if its loglevel is below current threshold" do
42
+ TxCatcher::Config["logger"]["stdout_level"] = "fatal"
43
+ expect($stdout).to receive(:print).exactly(2).times
44
+ TxCatcher::Logger.report "example info message", :info
45
+ TxCatcher::Logger.report "example warn message", :warn
46
+ TxCatcher::Logger.report "example error message", :error
47
+ TxCatcher::Logger.report "example fatal message", :fatal
48
+ TxCatcher::Config["logger"]["stdout_level"] = "info"
49
+ end
50
+
51
+ it "converts Exception into a text for logging" do
52
+ expect($stdout).to receive(:print).with("StandardError\n[no backtrace]\n")
53
+ expect($stdout).to receive(:print).with("\n\n")
54
+ TxCatcher::Logger.report StandardError.new, :error
55
+ end
56
+
57
+ it "prints additional data passed along" do
58
+ expect($stdout).to receive(:print).with("example error message\n")
59
+ expect($stdout).to receive(:print).with(' additional data: {:hello=>"world"}')
60
+ expect($stdout).to receive(:print).with("\n\n")
61
+ TxCatcher::Logger.report "example error message", :error, data: { hello: "world" }
62
+ end
63
+
64
+ end
data/spec/spec_helper.rb CHANGED
@@ -3,11 +3,13 @@ require 'yaml'
3
3
  require 'json'
4
4
  require 'satoshi-unit'
5
5
  require 'sequel'
6
+ require 'sentry-raven'
6
7
  Sequel.extension :migration
7
8
 
8
9
  db_file = File.dirname(__FILE__) + "/config/txcatcher_test.db"
9
10
  File.delete(db_file) if File.exists?(db_file)
10
11
 
12
+ require_relative '../lib/txcatcher/logger'
11
13
  require_relative '../lib/txcatcher/utils/hash_string_to_sym_keys'
12
14
  require_relative '../lib/txcatcher/config'
13
15
  require_relative '../lib/txcatcher/initializer'
@@ -17,6 +19,7 @@ include TxCatcher::Initializer
17
19
  ConfigFile.set!(File.dirname(__FILE__) + "/config/config.yml")
18
20
 
19
21
  read_config_file
22
+ initialize_sentry
20
23
  connect_to_db
21
24
  run_migrations if migrations_pending?
22
25
  connect_to_rpc_node
@@ -31,8 +34,10 @@ end
31
34
 
32
35
  RSpec.configure do |config|
33
36
 
34
- config.default_formatter = 'doc'
37
+ LOGFILE = File.expand_path(File.dirname(__FILE__) + "/config/txcatcher.log")
38
+ ERRFILE = File.expand_path(File.dirname(__FILE__) + "/config/error.log")
35
39
 
40
+ config.default_formatter = 'doc'
36
41
  config.before(:all) do
37
42
  end
38
43
 
@@ -45,4 +50,10 @@ RSpec.configure do |config|
45
50
  TxCatcher::Address.select_all.delete
46
51
  end
47
52
 
53
+ config.after(:each) do
54
+ [LOGFILE, ERRFILE].each do |f|
55
+ FileUtils.rm(f) if File.exist?(f)
56
+ end
57
+ end
58
+
48
59
  end
data/templates/config.yml CHANGED
@@ -17,8 +17,18 @@ rpcnode:
17
17
  host: 127.0.0.1
18
18
  port: 8332
19
19
 
20
+ logger:
21
+ log_queue_info: false
22
+ stdout_level: info
23
+ logfile_level: warn
24
+ sentry_level: error
25
+ sentry:
26
+ dsn: null
27
+ client_dsn: null
28
+
20
29
  zeromq: bitcoind
21
30
  max_db_transactions_stored: 100000
22
31
  db_clean_period_seconds: 300
23
32
  server_port: 9498
24
33
  daemonize: false
34
+ environment: production
data/txcatcher.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: txcatcher 0.1.79 ruby lib
5
+ # stub: txcatcher 0.1.80 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "txcatcher"
9
- s.version = "0.1.79"
9
+ s.version = "0.1.80"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: txcatcher
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.79
4
+ version: 0.1.80
5
5
  platform: ruby
6
6
  authors:
7
7
  - Roman Snitko
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-01 00:00:00.000000000 Z
11
+ date: 2018-03-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: goliath
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sentry-raven
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -130,6 +144,7 @@ files:
130
144
  - lib/txcatcher/cleaner.rb
131
145
  - lib/txcatcher/config.rb
132
146
  - lib/txcatcher/initializer.rb
147
+ - lib/txcatcher/logger.rb
133
148
  - lib/txcatcher/models/address.rb
134
149
  - lib/txcatcher/models/deposit.rb
135
150
  - lib/txcatcher/models/transaction.rb
@@ -140,6 +155,7 @@ files:
140
155
  - spec/config/config.yml.sample
141
156
  - spec/fixtures/transaction.txt
142
157
  - spec/fixtures/transaction_decoded_no_outputs.txt
158
+ - spec/logger_spec.rb
143
159
  - spec/models/transaction_spec.rb
144
160
  - spec/spec_helper.rb
145
161
  - templates/config.yml