makit 0.0.138 → 0.0.140

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.
Files changed (151) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +41 -41
  3. data/exe/makit +5 -5
  4. data/lib/makit/apache.rb +28 -28
  5. data/lib/makit/auto.rb +48 -48
  6. data/lib/makit/cli/build_commands.rb +500 -500
  7. data/lib/makit/cli/generators/base_generator.rb +74 -74
  8. data/lib/makit/cli/generators/dotnet_generator.rb +50 -50
  9. data/lib/makit/cli/generators/generator_factory.rb +49 -49
  10. data/lib/makit/cli/generators/node_generator.rb +50 -50
  11. data/lib/makit/cli/generators/ruby_generator.rb +77 -77
  12. data/lib/makit/cli/generators/rust_generator.rb +50 -50
  13. data/lib/makit/cli/generators/templates/dotnet_templates.rb +167 -167
  14. data/lib/makit/cli/generators/templates/node_templates.rb +161 -161
  15. data/lib/makit/cli/generators/templates/ruby/gemfile.rb +26 -26
  16. data/lib/makit/cli/generators/templates/ruby/gemspec.rb +40 -40
  17. data/lib/makit/cli/generators/templates/ruby/main_lib.rb +33 -33
  18. data/lib/makit/cli/generators/templates/ruby/rakefile.rb +35 -35
  19. data/lib/makit/cli/generators/templates/ruby/readme.rb +63 -63
  20. data/lib/makit/cli/generators/templates/ruby/test.rb +39 -39
  21. data/lib/makit/cli/generators/templates/ruby/test_helper.rb +29 -29
  22. data/lib/makit/cli/generators/templates/ruby/version.rb +29 -29
  23. data/lib/makit/cli/generators/templates/rust_templates.rb +128 -128
  24. data/lib/makit/cli/main.rb +69 -67
  25. data/lib/makit/cli/project_commands.rb +868 -868
  26. data/lib/makit/cli/repository_commands.rb +661 -661
  27. data/lib/makit/cli/strategy_commands.rb +51 -0
  28. data/lib/makit/cli/utility_commands.rb +521 -521
  29. data/lib/makit/commands/factory.rb +359 -359
  30. data/lib/makit/commands/middleware/base.rb +73 -73
  31. data/lib/makit/commands/middleware/cache.rb +248 -248
  32. data/lib/makit/commands/middleware/command_logger.rb +312 -312
  33. data/lib/makit/commands/middleware/validator.rb +269 -269
  34. data/lib/makit/commands/request.rb +316 -316
  35. data/lib/makit/commands/result.rb +323 -323
  36. data/lib/makit/commands/runner.rb +385 -385
  37. data/lib/makit/commands/strategies/base.rb +171 -171
  38. data/lib/makit/commands/strategies/child_process.rb +1 -1
  39. data/lib/makit/commands/strategies/synchronous.rb +139 -139
  40. data/lib/makit/commands.rb +50 -50
  41. data/lib/makit/configuration/dotnet_project.rb +12 -12
  42. data/lib/makit/configuration/gitlab_helper.rb +58 -58
  43. data/lib/makit/configuration/project.rb +168 -168
  44. data/lib/makit/configuration/rakefile_helper.rb +43 -43
  45. data/lib/makit/configuration/step.rb +34 -34
  46. data/lib/makit/configuration/timeout.rb +74 -0
  47. data/lib/makit/configuration.rb +15 -14
  48. data/lib/makit/content/default_gitignore.rb +7 -7
  49. data/lib/makit/content/default_gitignore.txt +225 -225
  50. data/lib/makit/content/default_rakefile.rb +13 -13
  51. data/lib/makit/content/gem_rakefile.rb +16 -16
  52. data/lib/makit/context.rb +1 -1
  53. data/lib/makit/data.rb +49 -49
  54. data/lib/makit/directories.rb +140 -140
  55. data/lib/makit/directory.rb +262 -262
  56. data/lib/makit/docs/files.rb +89 -89
  57. data/lib/makit/docs/rake.rb +102 -102
  58. data/lib/makit/dotnet/cli.rb +69 -69
  59. data/lib/makit/dotnet/project.rb +217 -217
  60. data/lib/makit/dotnet/solution.rb +38 -38
  61. data/lib/makit/dotnet/solution_classlib.rb +239 -239
  62. data/lib/makit/dotnet/solution_console.rb +264 -264
  63. data/lib/makit/dotnet/solution_maui.rb +354 -354
  64. data/lib/makit/dotnet/solution_wasm.rb +275 -275
  65. data/lib/makit/dotnet/solution_wpf.rb +304 -304
  66. data/lib/makit/dotnet.rb +102 -102
  67. data/lib/makit/email.rb +90 -90
  68. data/lib/makit/environment.rb +142 -142
  69. data/lib/makit/examples/runner.rb +370 -370
  70. data/lib/makit/exceptions.rb +45 -45
  71. data/lib/makit/fileinfo.rb +24 -24
  72. data/lib/makit/files.rb +43 -43
  73. data/lib/makit/gems.rb +40 -40
  74. data/lib/makit/git/cli.rb +54 -54
  75. data/lib/makit/git/repository.rb +90 -90
  76. data/lib/makit/git.rb +98 -98
  77. data/lib/makit/gitlab_runner.rb +59 -59
  78. data/lib/makit/humanize.rb +137 -137
  79. data/lib/makit/indexer.rb +47 -47
  80. data/lib/makit/logging/configuration.rb +308 -308
  81. data/lib/makit/logging/format_registry.rb +84 -84
  82. data/lib/makit/logging/formatters/base.rb +39 -39
  83. data/lib/makit/logging/formatters/console_formatter.rb +140 -140
  84. data/lib/makit/logging/formatters/json_formatter.rb +65 -65
  85. data/lib/makit/logging/formatters/plain_text_formatter.rb +71 -71
  86. data/lib/makit/logging/formatters/text_formatter.rb +64 -64
  87. data/lib/makit/logging/log_request.rb +119 -119
  88. data/lib/makit/logging/logger.rb +199 -199
  89. data/lib/makit/logging/sinks/base.rb +91 -91
  90. data/lib/makit/logging/sinks/console.rb +72 -72
  91. data/lib/makit/logging/sinks/file_sink.rb +92 -92
  92. data/lib/makit/logging/sinks/structured.rb +123 -123
  93. data/lib/makit/logging/sinks/unified_file_sink.rb +296 -296
  94. data/lib/makit/logging.rb +565 -565
  95. data/lib/makit/markdown.rb +75 -75
  96. data/lib/makit/mp/basic_object_mp.rb +17 -17
  97. data/lib/makit/mp/command_mp.rb +13 -13
  98. data/lib/makit/mp/command_request.mp.rb +17 -17
  99. data/lib/makit/mp/project_mp.rb +199 -199
  100. data/lib/makit/mp/string_mp.rb +191 -191
  101. data/lib/makit/nuget.rb +74 -74
  102. data/lib/makit/port.rb +32 -32
  103. data/lib/makit/process.rb +163 -163
  104. data/lib/makit/protoc.rb +107 -107
  105. data/lib/makit/rake/cli.rb +196 -196
  106. data/lib/makit/rake.rb +80 -80
  107. data/lib/makit/ruby/cli.rb +185 -185
  108. data/lib/makit/ruby.rb +25 -25
  109. data/lib/makit/secrets.rb +51 -51
  110. data/lib/makit/serializer.rb +130 -130
  111. data/lib/makit/services/builder.rb +186 -186
  112. data/lib/makit/services/error_handler.rb +226 -226
  113. data/lib/makit/services/repository_manager.rb +231 -231
  114. data/lib/makit/services/validator.rb +112 -112
  115. data/lib/makit/setup/classlib.rb +101 -101
  116. data/lib/makit/setup/gem.rb +268 -268
  117. data/lib/makit/setup/razorclasslib.rb +101 -101
  118. data/lib/makit/setup/runner.rb +54 -54
  119. data/lib/makit/setup.rb +5 -5
  120. data/lib/makit/show.rb +110 -110
  121. data/lib/makit/storage.rb +126 -126
  122. data/lib/makit/symbols.rb +170 -170
  123. data/lib/makit/task_info.rb +130 -130
  124. data/lib/makit/tasks/at_exit.rb +15 -15
  125. data/lib/makit/tasks/build.rb +22 -22
  126. data/lib/makit/tasks/clean.rb +13 -13
  127. data/lib/makit/tasks/configure.rb +10 -10
  128. data/lib/makit/tasks/format.rb +10 -10
  129. data/lib/makit/tasks/hook_manager.rb +443 -443
  130. data/lib/makit/tasks/init.rb +49 -49
  131. data/lib/makit/tasks/integrate.rb +29 -29
  132. data/lib/makit/tasks/pull_incoming.rb +13 -13
  133. data/lib/makit/tasks/setup.rb +13 -13
  134. data/lib/makit/tasks/sync.rb +17 -17
  135. data/lib/makit/tasks/tag.rb +16 -16
  136. data/lib/makit/tasks/task_monkey_patch.rb +81 -81
  137. data/lib/makit/tasks/test.rb +22 -22
  138. data/lib/makit/tasks/update.rb +18 -18
  139. data/lib/makit/tasks.rb +20 -20
  140. data/lib/makit/test_cache.rb +239 -239
  141. data/lib/makit/tree.rb +37 -37
  142. data/lib/makit/v1/makit.v1_pb.rb +35 -35
  143. data/lib/makit/v1/makit.v1_services_pb.rb +27 -27
  144. data/lib/makit/version.rb +99 -99
  145. data/lib/makit/version_util.rb +21 -21
  146. data/lib/makit/wix.rb +95 -95
  147. data/lib/makit/yaml.rb +29 -29
  148. data/lib/makit/zip.rb +17 -17
  149. data/lib/makit copy.rb +44 -44
  150. data/lib/makit.rb +42 -42
  151. metadata +3 -2
@@ -1,312 +1,312 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative "base"
4
- require_relative "../../logging/logger"
5
-
6
- module Makit
7
- module Commands
8
- module Middleware
9
- # Command-specific logging middleware with enhanced output handling
10
- #
11
- # This middleware provides specialized logging for command execution with
12
- # enhanced output handling, performance tracking, and structured data
13
- # logging. It's designed to work alongside the base Logger middleware
14
- # to provide comprehensive command execution logging.
15
- #
16
- # @example Basic usage
17
- # logger = CommandLogger.new
18
- # runner = Runner.new(middleware: [logger])
19
- # result = runner.execute("git --version")
20
- #
21
- # @example With custom configuration
22
- # logger = CommandLogger.new(
23
- # log_stdout: true,
24
- # log_stderr: true,
25
- # log_performance: true,
26
- # max_output_lines: 50
27
- # )
28
- class CommandLogger < Base
29
- # @!attribute [r] logger
30
- # @return [Makit::Logging::Logger] the logging instance
31
- attr_reader :logger
32
-
33
- # Initialize command logging middleware.
34
- #
35
- # @param log_stdout [Boolean] whether to log stdout output
36
- # @param log_stderr [Boolean] whether to log stderr output
37
- # @param log_performance [Boolean] whether to log performance metrics
38
- # @param max_output_lines [Integer] maximum lines of output to log
39
- def initialize(
40
- log_stdout: false,
41
- log_stderr: true,
42
- log_performance: true,
43
- max_output_lines: 100
44
- )
45
- @log_stdout = log_stdout
46
- @log_stderr = log_stderr
47
- @log_performance = log_performance
48
- @max_output_lines = max_output_lines
49
-
50
- # Use the default logger for all logging
51
- @logger = Makit::Logging.default_logger
52
- end
53
-
54
- # Execute command with enhanced logging.
55
- #
56
- # @param request [Request] command request to execute
57
- # @yield [Request] yields request to next middleware
58
- # @return [Result] execution result
59
- def call(request, &block)
60
- start_time = Time.now
61
- command_id = generate_command_id
62
-
63
- # Log command start with enhanced context
64
- log_command_start(request, command_id)
65
-
66
- begin
67
- # Execute the command
68
- result = block.call(request)
69
- duration = Time.now - start_time
70
-
71
- # Log command completion with performance data
72
- log_command_completion(request, result, duration, command_id)
73
-
74
- # Log command output if configured
75
- log_command_output(request, result, command_id)
76
-
77
- result
78
- rescue StandardError => e
79
- duration = Time.now - start_time
80
-
81
- # Log command error with full context
82
- log_command_error(request, e, duration, command_id)
83
-
84
- raise
85
- end
86
- end
87
-
88
- # Check if this middleware applies to the given request.
89
- #
90
- # @param request [Request] command request
91
- # @return [Boolean] true if middleware applies
92
- def applicable?(_request)
93
- # Apply to all requests
94
- true
95
- end
96
-
97
- # Get middleware configuration.
98
- #
99
- # @return [Hash] middleware configuration
100
- def config
101
- {
102
- name: "CommandLogger",
103
- description: "Command execution logging with performance tracking and output handling",
104
- log_stdout: @log_stdout,
105
- log_stderr: @log_stderr,
106
- log_performance: @log_performance,
107
- max_output_lines: @max_output_lines,
108
- logger_class: @logger.class.name,
109
- }
110
- end
111
-
112
- private
113
-
114
- # Log command execution start with enhanced context.
115
- #
116
- # @param request [Request] command request
117
- # @param command_id [String] unique command identifier
118
- def log_command_start(request, command_id)
119
- log_data = {
120
- command_id: command_id,
121
- command: request.command,
122
- arguments: request.arguments,
123
- working_directory: request.directory,
124
- timeout: request.timeout,
125
- environment: request.environment&.keys || [],
126
- timestamp: Time.now.iso8601,
127
- }
128
-
129
- @logger.debug("Command execution started", log_data)
130
- end
131
-
132
- # Log command execution completion with performance data.
133
- #
134
- # @param request [Request] command request
135
- # @param result [Result] execution result
136
- # @param duration [Float] execution duration
137
- # @param command_id [String] unique command identifier
138
- def log_command_completion(request, result, duration, command_id)
139
- log_data = {
140
- command_id: command_id,
141
- command: request.command,
142
- success: result.success?,
143
- exit_code: result.exit_code,
144
- duration: duration,
145
- }
146
-
147
- # Add performance metrics if enabled
148
- if @log_performance
149
- log_data.merge!(
150
- memory_usage: get_memory_usage,
151
- timestamp: Time.now.iso8601,
152
- output_size: calculate_output_size(result),
153
- )
154
- end
155
-
156
- # Log detailed information at debug level
157
- @logger.debug("Command completed successfully", log_data) if result.success?
158
- @logger.debug("Command completed with errors", log_data) unless result.success?
159
-
160
- # Log performance warnings
161
- log_performance_warnings(request, result, duration, command_id) if @log_performance
162
- end
163
-
164
- # Log command output (stdout/stderr) with line limits.
165
- #
166
- # @param request [Request] command request
167
- # @param result [Result] execution result
168
- # @param command_id [String] unique command identifier
169
- def log_command_output(request, result, command_id)
170
- # Log stdout if enabled and present
171
- if @log_stdout && result.stdout && !result.stdout.empty?
172
- output_lines = result.stdout.split("\n")
173
- truncated = output_lines.length > @max_output_lines
174
-
175
- log_data = {
176
- command_id: command_id,
177
- command: request.command,
178
- output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stdout,
179
- total_lines: output_lines.length,
180
- truncated: truncated,
181
- }
182
-
183
- @logger.debug("Command stdout", log_data)
184
- end
185
-
186
- # Log stderr if enabled and present
187
- return unless @log_stderr && result.stderr && !result.stderr.empty?
188
-
189
- output_lines = result.stderr.split("\n")
190
- truncated = output_lines.length > @max_output_lines
191
-
192
- {
193
- command_id: command_id,
194
- command: request.command,
195
- output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stderr,
196
- total_lines: output_lines.length,
197
- truncated: truncated,
198
- }
199
-
200
- # @logger.warn("Command stderr", log_data)
201
- end
202
-
203
- # Log command execution error with full context.
204
- #
205
- # @param request [Request] command request
206
- # @param error [Exception] execution error
207
- # @param duration [Float] execution duration
208
- # @param command_id [String] unique command identifier
209
- def log_command_error(request, error, duration, command_id)
210
- log_data = {
211
- command_id: command_id,
212
- command: request.command,
213
- arguments: request.arguments,
214
- working_directory: request.directory,
215
- environment: request.environment&.keys || [],
216
- error_class: error.class.name,
217
- error_message: error.message,
218
- duration: duration,
219
- backtrace: error.backtrace&.first(10),
220
- timestamp: Time.now.iso8601,
221
- }
222
-
223
- @logger.error("Command execution failed", log_data)
224
- end
225
-
226
- # Log performance warnings for slow or resource-intensive commands.
227
- #
228
- # @param request [Request] command request
229
- # @param result [Result] execution result
230
- # @param duration [Float] execution duration
231
- # @param command_id [String] unique command identifier
232
- def log_performance_warnings(request, result, duration, command_id)
233
- duration_threshold_seconds = 60.0
234
- # Log slow commands
235
- if duration > duration_threshold_seconds
236
- @logger.warn("Slow command detected",
237
- command_id: command_id,
238
- command: request.command,
239
- duration: duration,
240
- threshold: "5.0s")
241
- end
242
-
243
- # Log commands with large output
244
- output_size = calculate_output_size(result)
245
- if output_size > 100_000 # 100KB
246
- @logger.warn("Command with large output detected",
247
- command_id: command_id,
248
- command: request.command,
249
- output_size: "#{output_size / 1024}KB",
250
- threshold: "100KB")
251
- end
252
-
253
- # Log commands with high exit codes (potential errors)
254
- return unless result.exit_code.positive?
255
-
256
- @logger.debug("Command completed with non-zero exit code",
257
- command_id: command_id,
258
- command: request.command,
259
- exit_code: result.exit_code,
260
- success: result.success?)
261
- end
262
-
263
- # Calculate total output size (stdout + stderr).
264
- #
265
- # @param result [Result] execution result
266
- # @return [Integer] total output size in bytes
267
- def calculate_output_size(result)
268
- size = 0
269
- size += result.stdout&.bytesize || 0
270
- size += result.stderr&.bytesize || 0
271
- size
272
- end
273
-
274
- # Generate a unique command identifier.
275
- #
276
- # @return [String] unique command ID
277
- def generate_command_id
278
- "#{::Process.pid}-#{Time.now.to_f}-#{rand(1000)}"
279
- end
280
-
281
- # Get current memory usage (if available).
282
- #
283
- # @return [String, nil] memory usage string or nil if unavailable
284
- def get_memory_usage
285
- # Try to get memory usage on different platforms
286
- if RUBY_PLATFORM.match?(/mswin|mingw/)
287
- # Windows - try to get memory usage
288
- begin
289
- # This is a simplified approach for Windows
290
- "Unknown"
291
- rescue StandardError
292
- nil
293
- end
294
- else
295
- # Unix-like systems
296
- begin
297
- # Try to get memory usage from /proc/self/status
298
- if File.exist?("/proc/self/status")
299
- status = File.read("/proc/self/status")
300
- if (match = status.match(/VmRSS:\s+(\d+)\s+kB/))
301
- "#{match[1].to_i / 1024}MB"
302
- end
303
- end
304
- rescue StandardError
305
- nil
306
- end
307
- end
308
- end
309
- end
310
- end
311
- end
312
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "base"
4
+ require_relative "../../logging/logger"
5
+
6
+ module Makit
7
+ module Commands
8
+ module Middleware
9
+ # Command-specific logging middleware with enhanced output handling
10
+ #
11
+ # This middleware provides specialized logging for command execution with
12
+ # enhanced output handling, performance tracking, and structured data
13
+ # logging. It's designed to work alongside the base Logger middleware
14
+ # to provide comprehensive command execution logging.
15
+ #
16
+ # @example Basic usage
17
+ # logger = CommandLogger.new
18
+ # runner = Runner.new(middleware: [logger])
19
+ # result = runner.execute("git --version")
20
+ #
21
+ # @example With custom configuration
22
+ # logger = CommandLogger.new(
23
+ # log_stdout: true,
24
+ # log_stderr: true,
25
+ # log_performance: true,
26
+ # max_output_lines: 50
27
+ # )
28
+ class CommandLogger < Base
29
+ # @!attribute [r] logger
30
+ # @return [Makit::Logging::Logger] the logging instance
31
+ attr_reader :logger
32
+
33
+ # Initialize command logging middleware.
34
+ #
35
+ # @param log_stdout [Boolean] whether to log stdout output
36
+ # @param log_stderr [Boolean] whether to log stderr output
37
+ # @param log_performance [Boolean] whether to log performance metrics
38
+ # @param max_output_lines [Integer] maximum lines of output to log
39
+ def initialize(
40
+ log_stdout: false,
41
+ log_stderr: true,
42
+ log_performance: true,
43
+ max_output_lines: 100
44
+ )
45
+ @log_stdout = log_stdout
46
+ @log_stderr = log_stderr
47
+ @log_performance = log_performance
48
+ @max_output_lines = max_output_lines
49
+
50
+ # Use the default logger for all logging
51
+ @logger = Makit::Logging.default_logger
52
+ end
53
+
54
+ # Execute command with enhanced logging.
55
+ #
56
+ # @param request [Request] command request to execute
57
+ # @yield [Request] yields request to next middleware
58
+ # @return [Result] execution result
59
+ def call(request, &block)
60
+ start_time = Time.now
61
+ command_id = generate_command_id
62
+
63
+ # Log command start with enhanced context
64
+ log_command_start(request, command_id)
65
+
66
+ begin
67
+ # Execute the command
68
+ result = block.call(request)
69
+ duration = Time.now - start_time
70
+
71
+ # Log command completion with performance data
72
+ log_command_completion(request, result, duration, command_id)
73
+
74
+ # Log command output if configured
75
+ log_command_output(request, result, command_id)
76
+
77
+ result
78
+ rescue StandardError => e
79
+ duration = Time.now - start_time
80
+
81
+ # Log command error with full context
82
+ log_command_error(request, e, duration, command_id)
83
+
84
+ raise
85
+ end
86
+ end
87
+
88
+ # Check if this middleware applies to the given request.
89
+ #
90
+ # @param request [Request] command request
91
+ # @return [Boolean] true if middleware applies
92
+ def applicable?(_request)
93
+ # Apply to all requests
94
+ true
95
+ end
96
+
97
+ # Get middleware configuration.
98
+ #
99
+ # @return [Hash] middleware configuration
100
+ def config
101
+ {
102
+ name: "CommandLogger",
103
+ description: "Command execution logging with performance tracking and output handling",
104
+ log_stdout: @log_stdout,
105
+ log_stderr: @log_stderr,
106
+ log_performance: @log_performance,
107
+ max_output_lines: @max_output_lines,
108
+ logger_class: @logger.class.name,
109
+ }
110
+ end
111
+
112
+ private
113
+
114
+ # Log command execution start with enhanced context.
115
+ #
116
+ # @param request [Request] command request
117
+ # @param command_id [String] unique command identifier
118
+ def log_command_start(request, command_id)
119
+ log_data = {
120
+ command_id: command_id,
121
+ command: request.command,
122
+ arguments: request.arguments,
123
+ working_directory: request.directory,
124
+ timeout: request.timeout,
125
+ environment: request.environment&.keys || [],
126
+ timestamp: Time.now.iso8601,
127
+ }
128
+
129
+ @logger.debug("Command execution started", log_data)
130
+ end
131
+
132
+ # Log command execution completion with performance data.
133
+ #
134
+ # @param request [Request] command request
135
+ # @param result [Result] execution result
136
+ # @param duration [Float] execution duration
137
+ # @param command_id [String] unique command identifier
138
+ def log_command_completion(request, result, duration, command_id)
139
+ log_data = {
140
+ command_id: command_id,
141
+ command: request.command,
142
+ success: result.success?,
143
+ exit_code: result.exit_code,
144
+ duration: duration,
145
+ }
146
+
147
+ # Add performance metrics if enabled
148
+ if @log_performance
149
+ log_data.merge!(
150
+ memory_usage: get_memory_usage,
151
+ timestamp: Time.now.iso8601,
152
+ output_size: calculate_output_size(result),
153
+ )
154
+ end
155
+
156
+ # Log detailed information at debug level
157
+ @logger.debug("Command completed successfully", log_data) if result.success?
158
+ @logger.debug("Command completed with errors", log_data) unless result.success?
159
+
160
+ # Log performance warnings
161
+ log_performance_warnings(request, result, duration, command_id) if @log_performance
162
+ end
163
+
164
+ # Log command output (stdout/stderr) with line limits.
165
+ #
166
+ # @param request [Request] command request
167
+ # @param result [Result] execution result
168
+ # @param command_id [String] unique command identifier
169
+ def log_command_output(request, result, command_id)
170
+ # Log stdout if enabled and present
171
+ if @log_stdout && result.stdout && !result.stdout.empty?
172
+ output_lines = result.stdout.split("\n")
173
+ truncated = output_lines.length > @max_output_lines
174
+
175
+ log_data = {
176
+ command_id: command_id,
177
+ command: request.command,
178
+ output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stdout,
179
+ total_lines: output_lines.length,
180
+ truncated: truncated,
181
+ }
182
+
183
+ @logger.debug("Command stdout", log_data)
184
+ end
185
+
186
+ # Log stderr if enabled and present
187
+ return unless @log_stderr && result.stderr && !result.stderr.empty?
188
+
189
+ output_lines = result.stderr.split("\n")
190
+ truncated = output_lines.length > @max_output_lines
191
+
192
+ {
193
+ command_id: command_id,
194
+ command: request.command,
195
+ output: truncated ? output_lines.first(@max_output_lines).join("\n") : result.stderr,
196
+ total_lines: output_lines.length,
197
+ truncated: truncated,
198
+ }
199
+
200
+ # @logger.warn("Command stderr", log_data)
201
+ end
202
+
203
+ # Log command execution error with full context.
204
+ #
205
+ # @param request [Request] command request
206
+ # @param error [Exception] execution error
207
+ # @param duration [Float] execution duration
208
+ # @param command_id [String] unique command identifier
209
+ def log_command_error(request, error, duration, command_id)
210
+ log_data = {
211
+ command_id: command_id,
212
+ command: request.command,
213
+ arguments: request.arguments,
214
+ working_directory: request.directory,
215
+ environment: request.environment&.keys || [],
216
+ error_class: error.class.name,
217
+ error_message: error.message,
218
+ duration: duration,
219
+ backtrace: error.backtrace&.first(10),
220
+ timestamp: Time.now.iso8601,
221
+ }
222
+
223
+ @logger.error("Command execution failed", log_data)
224
+ end
225
+
226
+ # Log performance warnings for slow or resource-intensive commands.
227
+ #
228
+ # @param request [Request] command request
229
+ # @param result [Result] execution result
230
+ # @param duration [Float] execution duration
231
+ # @param command_id [String] unique command identifier
232
+ def log_performance_warnings(request, result, duration, command_id)
233
+ duration_threshold_seconds = 60.0
234
+ # Log slow commands
235
+ if duration > duration_threshold_seconds
236
+ @logger.warn("Slow command detected",
237
+ command_id: command_id,
238
+ command: request.command,
239
+ duration: duration,
240
+ threshold: "5.0s")
241
+ end
242
+
243
+ # Log commands with large output
244
+ output_size = calculate_output_size(result)
245
+ if output_size > 100_000 # 100KB
246
+ @logger.warn("Command with large output detected",
247
+ command_id: command_id,
248
+ command: request.command,
249
+ output_size: "#{output_size / 1024}KB",
250
+ threshold: "100KB")
251
+ end
252
+
253
+ # Log commands with high exit codes (potential errors)
254
+ return unless result.exit_code.positive?
255
+
256
+ @logger.debug("Command completed with non-zero exit code",
257
+ command_id: command_id,
258
+ command: request.command,
259
+ exit_code: result.exit_code,
260
+ success: result.success?)
261
+ end
262
+
263
+ # Calculate total output size (stdout + stderr).
264
+ #
265
+ # @param result [Result] execution result
266
+ # @return [Integer] total output size in bytes
267
+ def calculate_output_size(result)
268
+ size = 0
269
+ size += result.stdout&.bytesize || 0
270
+ size += result.stderr&.bytesize || 0
271
+ size
272
+ end
273
+
274
+ # Generate a unique command identifier.
275
+ #
276
+ # @return [String] unique command ID
277
+ def generate_command_id
278
+ "#{::Process.pid}-#{Time.now.to_f}-#{rand(1000)}"
279
+ end
280
+
281
+ # Get current memory usage (if available).
282
+ #
283
+ # @return [String, nil] memory usage string or nil if unavailable
284
+ def get_memory_usage
285
+ # Try to get memory usage on different platforms
286
+ if RUBY_PLATFORM.match?(/mswin|mingw/)
287
+ # Windows - try to get memory usage
288
+ begin
289
+ # This is a simplified approach for Windows
290
+ "Unknown"
291
+ rescue StandardError
292
+ nil
293
+ end
294
+ else
295
+ # Unix-like systems
296
+ begin
297
+ # Try to get memory usage from /proc/self/status
298
+ if File.exist?("/proc/self/status")
299
+ status = File.read("/proc/self/status")
300
+ if (match = status.match(/VmRSS:\s+(\d+)\s+kB/))
301
+ "#{match[1].to_i / 1024}MB"
302
+ end
303
+ end
304
+ rescue StandardError
305
+ nil
306
+ end
307
+ end
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end