debugtrace 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a9dbd0ded97b4a2115a8f5b86cbac05a7db29da09cefae334091cfbe8652ad1b
4
+ data.tar.gz: 7e121c90bbeecf411b3c7079bf900ac646998a3ec08a3a3f71c11faa56e6fc0d
5
+ SHA512:
6
+ metadata.gz: ec993125ba921db2f15a4079c57a23735a49a820566b6100e79a3387c9b111a711c4d143eb4350a06913397c3d3a589a5290e4e36bd71bcb352fa16ad591d0ff
7
+ data.tar.gz: 32f7121dfb3f6c8fc125a775db3c60eecf53590925df24ef1cbfb0d73327f3c9d830063d1c1058cc1718b561f4ed3f2a332ad25c5dc0c2c36c4e93179cbfacdd
data/.rubocop.yml ADDED
@@ -0,0 +1,8 @@
1
+ AllCops:
2
+ TargetRubyVersion: 3.1
3
+
4
+ Style/StringLiterals:
5
+ EnforcedStyle: double_quotes
6
+
7
+ Style/StringLiteralsInInterpolation:
8
+ EnforcedStyle: double_quotes
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2022-12-10
4
+
5
+ - Initial release
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2025 TODO: Write your name
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,40 @@
1
+ # DebugTrace-rb
2
+ A Ruby library to output logs for debugging.
3
+
4
+ # DebugTrace
5
+
6
+ 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/debugtrace`. To experiment with that code, run `bin/console` for an interactive prompt.
7
+
8
+ TODO: Delete this and the text above, and describe your gem
9
+
10
+ ## Installation
11
+
12
+ Install the gem and add to the application's Gemfile by executing:
13
+
14
+ ```bash
15
+ $ bundle add debugtrace
16
+ ```
17
+
18
+ If bundler is not being used to manage dependencies, install the gem by executing:
19
+
20
+ ```bash
21
+ $ gem install debugtrace
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ TODO: Write usage instructions here
27
+
28
+ ## Development
29
+
30
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
31
+
32
+ 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 the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
33
+
34
+ ## Contributing
35
+
36
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/debugtrace.
37
+
38
+ ## License
39
+
40
+ 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,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "minitest/test_task"
5
+
6
+ Minitest::TestTask.create
7
+
8
+ require "rubocop/rake_task"
9
+
10
+ RuboCop::RakeTask.new
11
+
12
+ task default: %i[test rubocop]
@@ -0,0 +1,27 @@
1
+ # common.rb
2
+ # (C) 2023 Masato Kokubo
3
+ module Common
4
+ def self.check_type(value_name, value, type)
5
+ if !(value_name.is_a? String); raise "Argument value_name (=#{value_name}) must be a String"; end
6
+ if !(type.is_a? Class); raise "Argument type (=#{type}) must be a Class"; end
7
+
8
+ error = false
9
+ if type == FalseClass || type == TrueClass
10
+ # false or true
11
+ if value.class != FalseClass && value.class != TrueClass
12
+ check_error = true
13
+ end
14
+ else
15
+ error = value.class != type
16
+ end
17
+
18
+ if error
19
+ value_string = value.class == String ? "\"#{value}\"" : "#{value}"
20
+ top_type_name = type.name.slice(0).upcase
21
+ a = top_type_name == 'A' || top_type_name == 'I' || top_type_name == 'U' ||
22
+ top_type_name == 'E' || top_type_name == 'O' ? 'an' : 'a'
23
+ raise "Argument #{value_name} (=#{value_string}) must be #{a} #{type}"
24
+ end
25
+ value
26
+ end
27
+ end
@@ -0,0 +1,92 @@
1
+ # config.rb
2
+ # (C) 2023 Masato Kokubo
3
+ require 'yaml'
4
+ require_relative 'common'
5
+
6
+ class Config
7
+ def initialize(config_path)
8
+ @config_path = Common::check_type('config_path', config_path, String)
9
+ if File.exist?(@config_path)
10
+ @config = YAML.load_file(@config_path)
11
+ else
12
+ @config_path = '<No config file>'
13
+ @config = nil
14
+ end
15
+ @logger_name = _get_config_value 'logger' , 'stderr'
16
+ @logging_destination = _get_config_value 'logging_destination' , 'STDERR'
17
+ @logging_format = _get_config_value 'logging_format' , "%2$s %1$s %4$s\n"
18
+ @logging_datetime_format = _get_config_value 'logging_datetime_format' , '%Y-%m-%d %H:%M:%S.%L%:z'
19
+ @enabled = _get_config_value 'enabled?' , true
20
+ @enter_format = _get_config_value 'enter_format' , 'Enter %1$s (%2$s:%3$d) <- %4$s (%5$s:%6$d)'
21
+ @leave_format = _get_config_value 'leave_format' , 'Leave %1$s (%2$s:%3$d) duration: %4$s'
22
+ @thread_boundary_format = _get_config_value 'thread_boundary_format' , '______________________________ %1$s #%2$s ______________________________'
23
+ @maximum_indents = _get_config_value 'maximum_indents' , 32
24
+ @indent_string = _get_config_value 'indent_string' , '| '
25
+ @data_indent_string = _get_config_value 'data_indent_string' , ' '
26
+ @limit_string = _get_config_value 'limit_string' , '...'
27
+ @non_output_string = _get_config_value 'non_output_string' , '...'
28
+ @cyclic_reference_string = _get_config_value 'cyclic_reference_string' , '*** Cyclic Reference ***'
29
+ @varname_value_separator = _get_config_value 'varname_value_separator' , ' = '
30
+ @key_value_separator = _get_config_value 'key_value_separator' , ': '
31
+ @print_suffix_format = _get_config_value 'print_suffix_format' , ' (%2$s:%3$d)'
32
+ @count_format = _get_config_value 'count_format' , 'count:%d'
33
+ @minimum_output_count = _get_config_value 'minimum_output_count' , 16
34
+ @length_format = _get_config_value 'length_format' , 'length:%d'
35
+ @minimum_output_length = _get_config_value 'minimum_output_length' , 16
36
+ @maximum_data_output_width = _get_config_value 'maximum_data_output_width' , 70
37
+ @bytes_count_in_line = _get_config_value 'bytes_count_in_line' , 16
38
+ @collection_limit = _get_config_value 'collection_limit' , 128
39
+ @bytes_limit = _get_config_value 'bytes_limit' , 256
40
+ @string_limit = _get_config_value 'string_limit' , 256
41
+ @reflection_nest_limit = _get_config_value 'reflection_nest_limit' , 4
42
+ end
43
+
44
+ def config_path ; @config_path ; end
45
+ def logger_name ; @logger_name ; end
46
+ def logging_destination ; @logging_destination ; end
47
+ def logging_format ; @logging_format ; end
48
+ def logging_datetime_format ; @logging_datetime_format ; end
49
+ def enabled? ; @enabled ; end
50
+ def enter_format ; @enter_format ; end
51
+ def leave_format ; @leave_format ; end
52
+ def thread_boundary_format ; @thread_boundary_format ; end
53
+ def maximum_indents ; @maximum_indents ; end
54
+ def indent_string ; @indent_string ; end
55
+ def data_indent_string ; @data_indent_string ; end
56
+ def limit_string ; @limit_string ; end
57
+ def non_output_string ; @non_output_string ; end
58
+ def cyclic_reference_string ; @cyclic_reference_string ; end
59
+ def varname_value_separator ; @varname_value_separator ; end
60
+ def key_value_separator ; @key_value_separator ; end
61
+ def print_suffix_format ; @print_suffix_format ; end
62
+ def count_format ; @count_format ; end
63
+ def minimum_output_count ; @minimum_output_count ; end
64
+ def length_format ; @length_format ; end
65
+ def minimum_output_length ; @minimum_output_length ; end
66
+ def maximum_data_output_width; @maximum_data_output_width; end
67
+ def bytes_count_in_line ; @bytes_count_in_line ; end
68
+ def collection_limit ; @collection_limit ; end
69
+ def bytes_limit ; @bytes_limit ; end
70
+ def string_limit ; @string_limit ; end
71
+ def reflection_nest_limit ; @reflection_nest_limit ; end
72
+
73
+ private
74
+
75
+ # Gets the value related the key from debugtrace.ini file.
76
+ # @param key (String): The key
77
+ # @param defalut_value (Object): Value to return when the value related the key is undefined
78
+ # @return Object: Value related the key
79
+ def _get_config_value(key, defalut_value)
80
+ Common::check_type('key', key, String)
81
+ value = defalut_value
82
+ if @config != nil
83
+ value = @config[key]
84
+ if value == nil
85
+ value = defalut_value
86
+ else
87
+ Common::check_type("config[#{key}]", value, defalut_value.class)
88
+ end
89
+ end
90
+ value
91
+ end
92
+ end
@@ -0,0 +1,125 @@
1
+ # log_buffer.rb
2
+ # (C) 2023 Masato Kokubo
3
+ require_relative 'common'
4
+
5
+ # Buffers logs.
6
+ class LogBuffer
7
+ class LevelAndLog
8
+ # Initializes this object.
9
+ def initialize(nest_level, log)
10
+ @nest_level = Common.check_type('nest_level', nest_level, Integer)
11
+ @log = Common.check_type('log', log, String)
12
+ end
13
+
14
+ def nest_level
15
+ @nest_level
16
+ end
17
+
18
+ def log
19
+ @log
20
+ end
21
+
22
+ def to_s
23
+ "(LogBuffer.LevelAndLog){nest_level: #{@nest_level}, log: \"#{@log}\"}"
24
+ end
25
+ end
26
+
27
+ # Initializes this object.
28
+ def initialize(maximum_data_output_width)
29
+ @maximum_data_output_width = Common.check_type('maximum_data_output_width', maximum_data_output_width, Integer)
30
+ @nest_level = 0
31
+ @append_nest_level = 0
32
+
33
+ # tuples of data indentation level && log string
34
+ @lines = []
35
+
36
+ # buffer for a line of logs
37
+ @last_line = ""
38
+ end
39
+
40
+ # Breaks the current line.
41
+ def line_feed
42
+ @lines << LevelAndLog.new(@nest_level + @append_nest_level, @last_line.rstrip())
43
+ @append_nest_level = 0
44
+ @last_line = ''
45
+ end
46
+
47
+ # Ups the data nest level.
48
+ def up_nest
49
+ @nest_level += 1
50
+ end
51
+
52
+ # Downs the data nest level.
53
+ def down_nest
54
+ @nest_level -= 1
55
+ end
56
+
57
+ # Appends a string representation of the value.
58
+ # @param value (Object): The value to append
59
+ # @param nest_level (int, optional): The nest level of the value. Defaults to 0
60
+ # @param no_break (bool, optional): If true, does not break even if the maximum width is exceeded.
61
+ # Defaults to false
62
+ # @return LogBuffer: This object
63
+ def append(value, nest_level = 0, no_break = false)
64
+ Common.check_type('nest_level', nest_level, Integer)
65
+ Common.check_type('no_break', no_break, TrueClass)
66
+ if value != nil
67
+ string = value.to_s
68
+ if !no_break && length > 0 && length + string.length > @maximum_data_output_width
69
+ line_feed()
70
+ end
71
+ @append_nest_level = nest_level
72
+ @last_line += string
73
+ end
74
+ self
75
+ end
76
+
77
+ # Appends a string representation of the value.
78
+ # Does not break even if the maximum width is exceeded.
79
+ # @param value (Object): The value to append
80
+ # @return LogBuffer: This object
81
+ def no_break_append(value)
82
+ append(value, 0, true)
83
+ end
84
+
85
+ # Appends lines of another LogBuffer.
86
+ # @param
87
+ # @param separator (String): The separator string to append if not ""
88
+ # @param buff (LogBuffer): Another LogBuffer
89
+ # @returns LogBuffer: This object
90
+ def append_buffer(separator, buff)
91
+ Common.check_type('separator', separator, String)
92
+ Common.check_type('buff', buff, LogBuffer)
93
+ if separator != ""
94
+ append(separator, 0, true)
95
+ end
96
+ index = 0
97
+ for line in buff.lines
98
+ if index > 0
99
+ line_feed()
100
+ end
101
+ append(line.nest_level, line.log, index == 0 && separator != "")
102
+ index += 1
103
+ end
104
+ self
105
+ end
106
+
107
+ # The length of the last line.
108
+ def length
109
+ @last_line.length
110
+ end
111
+
112
+ # true if multiple line, false otherwise.
113
+ def multi_lines?
114
+ @lines.length > 1 || @lines.length == 1 && length > 0
115
+ end
116
+
117
+ # A list of tuple of data indentation level && log string.
118
+ def lines
119
+ lines = @lines.dup
120
+ if length > 0
121
+ lines << LevelAndLog.new(@nest_level, @last_line)
122
+ end
123
+ lines
124
+ end
125
+ end
@@ -0,0 +1,131 @@
1
+ # loggers.rb
2
+ # (C) 2023 Masato Kokubo
3
+ require 'logger'
4
+ require_relative 'common'
5
+ require_relative 'config'
6
+
7
+ # Abstract base class for logger classes.
8
+ class LoggerBase
9
+ # Outputs the message.
10
+ # @param message (String): The message to output
11
+ def print(message)
12
+ raise 'LoggerBase.print is an abstract method.'
13
+ end
14
+ end
15
+
16
+ # Abstract base class for StdOut and StdErr classes.
17
+ class StdLogger < LoggerBase
18
+ # Initializes this object.
19
+ # @param iostream: Output destination
20
+ def initialize(config, iostream)
21
+ @config = Common::check_type("config", config, Config)
22
+ @iostream = iostream
23
+ end
24
+
25
+ # Outputs the message.
26
+ # @param message (String): The message to output
27
+ def print(message)
28
+ Common::check_type("message", message, String)
29
+ datetime_str = Time.now().strftime(@config.logging_datetime_format)
30
+ @iostream.puts "#{datetime_str} #{message}"
31
+ end
32
+
33
+ end
34
+
35
+ # A logger class that outputs to $stdout.
36
+ class StdOutLogger < StdLogger
37
+ # Initializes this object.
38
+ def initialize(config)
39
+ super(config, $stdout)
40
+ end
41
+
42
+ # Returns a string representation of this object.
43
+ # @return String: A string representation of this object
44
+ def to_s
45
+ '$stdout logger'
46
+ end
47
+ end
48
+
49
+ # A logger class that outputs to $stderr.
50
+ class StdErrLogger < StdLogger
51
+ # Initializes this object.
52
+ def initialize(config)
53
+ super(config, $stderr)
54
+ end
55
+
56
+ # Returns a string representation of this object.
57
+ # @return String: A string representation of this object
58
+ def to_s
59
+ '$stderr logger'
60
+ end
61
+ end
62
+
63
+ # A logger class that outputs using the logging library.
64
+ class LoggerLogger
65
+ private
66
+
67
+ class Formatter
68
+ def initialize(config)
69
+ @config = config
70
+ end
71
+
72
+ def call(severity, datetime, progname, msg)
73
+ datetime_str = datetime.strftime(@config.logging_datetime_format)
74
+ format(@config.logging_format, severity, datetime_str, progname, msg)
75
+ end
76
+ end
77
+
78
+ public
79
+
80
+ def initialize(config)
81
+ @config = Common::check_type("config", config, Config)
82
+ @logger = Logger.new(
83
+ @config.logging_destination,
84
+ formatter: Formatter.new(@config),
85
+ datetime_format: @config.logging_datetime_format)
86
+ end
87
+
88
+ # Outputs the message.
89
+ # @param message (String): The message to output
90
+ def print(message)
91
+ Common::check_type("message", message, String)
92
+ @logger.log(Logger::Severity::DEBUG, message, 'DebugTrace-rb')
93
+ end
94
+
95
+ # Returns a string representation of this object.
96
+ # @return String: A string representation of this object
97
+ def to_s
98
+ 'logging.Logger logger'
99
+ end
100
+ end
101
+
102
+ # A logger class that outputs the file.
103
+ class FileLogger < LoggerBase
104
+ def initialize(config, log_path)
105
+ @config = Common::check_type("config", config, Config)
106
+ Common::check_type("log_path", log_path, String)
107
+ dir_path = File.dirname(log_path)
108
+ if Dir.exist?(dir_path)
109
+ @log_path = log_path
110
+ else
111
+ $stderr.puts "DebugTrace-rb: FileLogger: The directory '#{dir_path}' cannot be found."
112
+ @log_path = ''
113
+ end
114
+ end
115
+
116
+ def print(message)
117
+ Common::check_type("message", message, String)
118
+ if @log_path != ''
119
+ File.open(@log_path, 'a') {|file|
120
+ datetime_str = Time.now().strftime(@config.logging_datetime_format)
121
+ file.puts "#{datetime_str} #{message}"
122
+ }
123
+ end
124
+ end
125
+
126
+ # Returns a string representation of this object.
127
+ # @return String: A string representation of this object
128
+ def to_s
129
+ "File logger: '#{@log_path}"
130
+ end
131
+ end
@@ -0,0 +1,24 @@
1
+ # print_options.rb
2
+ # (C) 2023 Masato Kokubo
3
+ # Hold output option values.
4
+ class PrintOptions
5
+ # Initializes this object.
6
+ # @param force_reflection (bool): If true, outputs using reflection even if it has a @_str__or @_repr__ method
7
+ # @param output_private (bool): If true, also outputs private members when using reflection
8
+ # @param output_method (bool): If true, also outputs method members when using reflection
9
+ # @param collection_limit (Integer): Output limit of collection elements (overrides debugtarace.ini value)
10
+ # @param string_limit (Integer): Output limit of string characters (overrides debugtarace.ini value)
11
+ # @param bytes_limit (Integer): Output limit of byte array elements (overrides debugtarace.ini value)
12
+ # @param reflection_nest_limit (Integer): Nest limits when using reflection (overrides debugtarace.ini value)
13
+ def initialize(
14
+ force_reflection, output_private, output_method,
15
+ collection_limit, string_limit, bytes_limit, reflection_nest_limit)
16
+ @force_reflection = force_reflection
17
+ @output_private = output_private
18
+ @output_method = output_method
19
+ @collection_limit = collection_limit == -1 ? @config.collection_limit : collection_limit
20
+ @string_limit = string_limit == -1 ? @config.string_limit : string_limit
21
+ @bytes_limit = bytes_limit == -1 ? @config.bytes_limit : bytes_limit
22
+ @reflection_nest_limit = reflection_nest_limit == -1 ? @config.reflection_nest_limit : reflection_nest_limit
23
+ end
24
+ end
@@ -0,0 +1,65 @@
1
+ # state.rb
2
+ # (C) 2023 Masato Kokubo
3
+ require_relative 'common'
4
+
5
+ # Have the trace state for a thread
6
+ class State
7
+ def initialize(thread_id)
8
+ @thread_id = Common::check_type('thread_id', thread_id, Integer)
9
+ reset()
10
+ end
11
+
12
+ # @return the thread id.
13
+ def thread_id
14
+ @thread_id
15
+ end
16
+
17
+ # @return the nest level.
18
+ def nest_level
19
+ @nest_level
20
+ end
21
+
22
+ # @return the previous nest level.
23
+ def previous_nest_level
24
+ @previous_nest_level
25
+ end
26
+
27
+ # @return the previous line count.
28
+ def previous_line_count
29
+ @previous_line_count
30
+ end
31
+
32
+ # Sets the previous line count.
33
+ # @param value the previous line count
34
+ def previous_line_count=(value)
35
+ @previous_line_count = Common::check_type('value', value, Integer)
36
+ end
37
+
38
+ def reset
39
+ @nest_level = 0
40
+ @previous_nest_level = 0
41
+ @previous_line_count = 0
42
+ @times = []
43
+ end
44
+
45
+ def to_s()
46
+ "(State){thread_id: #{@thread_id}, nest_level: #{@nest_level}, previous_nest_level: #{@previous_nest_level}, previous_line_count: #{@previous_line_count}, times: #{@times}}"
47
+ end
48
+
49
+ # Ups the nest level.
50
+ def up_nest
51
+ @previous_nest_level = @nest_level
52
+ if (@nest_level >= 0)
53
+ @times.push(Time.now)
54
+ end
55
+ @nest_level += 1
56
+ end
57
+
58
+ #Downs the nest level.
59
+ # @return Time: The time when the corresponding upNest method was invoked
60
+ def down_nest
61
+ @previous_nest_level = @nest_level
62
+ @nest_level -= 1
63
+ return @times.length > 0 ? @times.pop() : Time.now
64
+ end
65
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DebugTrace
4
+ VERSION = '0.1.0'
5
+ end