From 51cbe09217027684384df76fe940081428362f59 Mon Sep 17 00:00:00 2001 From: nialljames Date: Mon, 15 Sep 2025 08:01:26 +0100 Subject: [PATCH 1/2] overhaul ansi text colours & strip colour when saving to file --- CHANGELOG.md | 15 ++++++ README.md | 63 ++++++++++------------ lib/dvla/herodotus/multi_writer.rb | 9 +++- lib/dvla/herodotus/string.rb | 68 ++++++++++++++++++++---- lib/dvla/herodotus/version.rb | 2 +- spec/dvla/herodotus/multi_writer_spec.rb | 12 +++++ spec/dvla/herodotus/string_spec.rb | 48 ++++++++++++++--- 7 files changed, 162 insertions(+), 55 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..217b59f --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [2.2.1] - 2025-09-15 +- Fixed issue with ANSI exit codes breaking on string interpolation +- Added strip_colour method to String which we now call when sending logs to file +- Added colours/styles: yellow, bg_yellow, bg_white, bg_bright_red, bg_bright_green, bg_bright_blue, bg_bright_magenta, bg_bright_cyan, dim +- Removed colours/styles: bright_black (that's just gray), bright_yellow (that's just yellow), blink (inconsistent/unsupported) +- Added a changelog + + +## [2.2.0] - 2025-09-08 +- Added colourise method to handle colour exist codes +- Added alias for colour/color +- Added alias for brown/yellow diff --git a/README.md b/README.md index 63fe3c2..573d6c0 100644 --- a/README.md +++ b/README.md @@ -29,7 +29,9 @@ You can get a logger by calling the following once Herodotus is installed: logger = DVLA::Herodotus.logger('') ``` -You can also log out to a file. If you want all the logs in a single file, provide a string of the path to that output file and it will be logged to simultaneously with standard console logger +You can also log out to a file. If you want all the logs in a single file, provide a string of the path to that output file and it will be logged to simultaneously with standard console logger. + +**Note:** Log messages are stripped of colour codes before being saved to file. ```ruby logger = DVLA::Herodotus.logger('', output_path: 'logs.txt') @@ -82,45 +84,34 @@ logger.new_scenario('Scenario Id') ### Strings -Also included is a series of additional methods on `String` that allow you to modify the colour and style of logs. As these exist on `String`, you can call them on any string such as: +Also included is a series of additional methods on `String` that allow you to modify the colour and style of logs. +You can stack multiple method calls to add additional styling and use string interpolation to style different parts of the string + ```ruby -example_string = 'Multicoloured String'.blue.bg_red.bold + example_string = "#{'H'.red}#{'E'.bright_red}#{'R'.yellow}#{'O'.green}#{'D'.blue}#{'O'.bright_blue}#{'T'.magenta}#{'U'.bright_magenta}#{'S'.cyan}".bold.reverse_colour ``` -| Method | Function | -|---------------|--------------------------------------------------| -| blue | Sets the string's colour to blue | -| red | Sets the string's colour to red | -| green | Sets the string's colour to green | -| brown | Sets the string's colour to brown | -| blue | Sets the string's colour to blue | -| magenta | Sets the string's colour to magenta | -| cyan | Sets the string's colour to cyan | -| gray | Sets the string's colour to gray | -| grey | Sets the string's colour to grey (alias of gray) | -| bright_blue | Sets the string's colour to bright blue | -| bright_red | Sets the string's colour to bright red | -| bright_green | Sets the string's colour to bright green | -| bright_yellow | Sets the string's colour to bright yellow | -| bright_blue | Sets the string's colour to bright blue | -| bright_magenta| Sets the string's colour to bright magenta | -| bright_cyan | Sets the string's colour to bright cyan | -| white | Sets the string's colour to white | -| bg_blue | Sets the string's background colour to blue | -| bg_red | Sets the string's background colour to red | -| bg_green | Sets the string's background colour to green | -| bg_brown | Sets the string's background colour to brown | -| bg_blue | Sets the string's background colour to blue | -| bg_magenta | Sets the string's background colour to magenta | -| bg_cyan | Sets the string's background colour to cyan | -| bg_gray | Sets the string's background colour to gray | -| bold | Sets the string to be bold | -| italic | Sets the string to be italic | -| underline | Sets the string to be underline | -| blink | Sets the string to blink | -| reverse_color | Reverses the colour of the string | +#### Available String Methods + +| Type | Examples | +|------|----------| +| Text Styles | **bold** dim *italic* underline | +| Colors | black red green brown yellow blue magenta cyan gray white | +| Bright Colors | bright_red bright_green bright_blue bright_magenta bright_cyan | +| Background Colors | bg_black bg_red bg_green bg_brown bg_yellow bg_blue bg_magenta bg_cyan bg_gray bg_white | +| Bright Background Colors | bg_bright_red bg_bright_green bg_bright_blue bg_bright_magenta bg_bright_cyan | +| Utility | strip_colour reverse_colour | + +#### To handle differences in spelling the following methods have been given aliases: +| Original | Alias | +|---------------|----------------| +| bg_grey | bg_gray | +| colorize | colourise | +| grey | gray | +| reverse_color | reverse_colour | +| strip_color | strip_colour | ## Development -Herodotus is very lightweight. Currently all code to generate a new logger can be found in `herodotus.rb` and the code for the logger is in `herodotus_logger.rb` so that is the best place to start with any modifications +Herodotus is very lightweight. Currently, all code to generate a new logger can be found in `herodotus.rb` and the code for the logger is in `herodotus_logger.rb` so that is the best place to start with any modifications diff --git a/lib/dvla/herodotus/multi_writer.rb b/lib/dvla/herodotus/multi_writer.rb index 24c81f9..a8eba5b 100644 --- a/lib/dvla/herodotus/multi_writer.rb +++ b/lib/dvla/herodotus/multi_writer.rb @@ -8,7 +8,14 @@ def initialize(*targets) end def write(*args) - @targets.each { |t| t.write(*args) } + @targets.each do |target| + # If we're writing to a file we remove the colour + if target != $stdout && args[0].respond_to?(:strip_colour) + target.write(args[0].strip_colour, *args[1..]) + else + target.write(*args) + end + end end def close diff --git a/lib/dvla/herodotus/string.rb b/lib/dvla/herodotus/string.rb index bd0109b..49ca61e 100644 --- a/lib/dvla/herodotus/string.rb +++ b/lib/dvla/herodotus/string.rb @@ -1,43 +1,91 @@ class String def colourise(code, reset_code = 39) - "\e[#{code}m#{self}\e[#{reset_code}m" + # Making sure we align the correct reset codes + if self.include?("\e[#{reset_code}m") + result = self.gsub("\e[#{reset_code}m", "\e[#{reset_code}m\e[#{code}m") + "\e[#{code}m#{result}\e[#{reset_code}m" + else + "\e[#{code}m#{self}\e[#{reset_code}m" + end end - alias colorize colourise + + def white = colourise(97) def black = colourise(30) + def red = colourise(31) + def green = colourise(32) + def brown = colourise(33) - alias yellow brown + + def yellow = colourise(93) def blue = colourise(34) + def magenta = colourise(35) + def cyan = colourise(36) + def gray = colourise(37) - alias grey gray - def bright_black = colourise(90) def bright_red = colourise(91) + def bright_green = colourise(92) - def bright_yellow = colourise(93) + def bright_blue = colourise(94) + def bright_magenta = colourise(95) + def bright_cyan = colourise(96) - def white = colourise(97) def bg_black = colourise(40, 49) + def bg_red = colourise(41, 49) + def bg_green = colourise(42, 49) + def bg_brown = colourise(43, 49) + + def bg_yellow = colourise(103, 49) + def bg_blue = colourise(44, 49) + def bg_magenta = colourise(45, 49) + def bg_cyan = colourise(46, 49) + def bg_gray = colourise(47, 49) + def bg_white = colourise(107, 49) + + def bg_bright_red = colourise(101, 49) + + def bg_bright_green = colourise(102, 49) + + def bg_bright_blue = colourise(104, 49) + + def bg_bright_magenta = colourise(105, 49) + + def bg_bright_cyan = colourise(106, 49) + def bold = colourise(1, 22) + + def dim = colourise(2, 22) + def italic = colourise(3, 23) + def underline = colourise(4, 24) - def blink = colourise(5, 25) - def reverse_color = colourise(7, 27) - alias reverse_colour reverse_color + + def reverse_colour = colourise(7, 27) + + def strip_colour + gsub(/\e\[[0-9;]*m/, '') + end + + alias bg_grey bg_gray + alias colorize colourise + alias grey gray + alias reverse_color reverse_colour + alias strip_color strip_colour end diff --git a/lib/dvla/herodotus/version.rb b/lib/dvla/herodotus/version.rb index 86f55ec..65683fb 100644 --- a/lib/dvla/herodotus/version.rb +++ b/lib/dvla/herodotus/version.rb @@ -1,5 +1,5 @@ module DVLA module Herodotus - VERSION = '2.2.0'.freeze + VERSION = '2.2.1'.freeze end end diff --git a/spec/dvla/herodotus/multi_writer_spec.rb b/spec/dvla/herodotus/multi_writer_spec.rb index 0c8f413..f9fce11 100644 --- a/spec/dvla/herodotus/multi_writer_spec.rb +++ b/spec/dvla/herodotus/multi_writer_spec.rb @@ -32,4 +32,16 @@ expect($stdout.closed?).to be false end + + it 'strips colours from messages when writing to non-stdout targets' do + file_output = StringIO.new + coloured_message = 'test message'.red.underline.bg_blue + + allow($stdout).to receive(:write) + + multi_writer = DVLA::Herodotus::MultiWriter.new($stdout, file_output) + multi_writer.write(coloured_message) + + expect(file_output.string).to eq('test message') + end end diff --git a/spec/dvla/herodotus/string_spec.rb b/spec/dvla/herodotus/string_spec.rb index cb179f1..b1cceb7 100644 --- a/spec/dvla/herodotus/string_spec.rb +++ b/spec/dvla/herodotus/string_spec.rb @@ -6,33 +6,37 @@ { method: :red, expected_output: "\e[31mTest String\e[39m" }, { method: :green, expected_output: "\e[32mTest String\e[39m" }, { method: :brown, expected_output: "\e[33mTest String\e[39m" }, - { method: :yellow, expected_output: "\e[33mTest String\e[39m" }, + { method: :yellow, expected_output: "\e[93mTest String\e[39m" }, { method: :blue, expected_output: "\e[34mTest String\e[39m" }, { method: :magenta, expected_output: "\e[35mTest String\e[39m" }, { method: :cyan, expected_output: "\e[36mTest String\e[39m" }, { method: :gray, expected_output: "\e[37mTest String\e[39m" }, - { method: :grey, expected_output: "\e[37mTest String\e[39m" }, - { method: :bright_black, expected_output: "\e[90mTest String\e[39m" }, + { method: :white, expected_output: "\e[97mTest String\e[39m" }, { method: :bright_red, expected_output: "\e[91mTest String\e[39m" }, { method: :bright_green, expected_output: "\e[92mTest String\e[39m" }, - { method: :bright_yellow, expected_output: "\e[93mTest String\e[39m" }, { method: :bright_blue, expected_output: "\e[94mTest String\e[39m" }, { method: :bright_magenta, expected_output: "\e[95mTest String\e[39m" }, { method: :bright_cyan, expected_output: "\e[96mTest String\e[39m" }, - { method: :white, expected_output: "\e[97mTest String\e[39m" }, { method: :bg_black, expected_output: "\e[40mTest String\e[49m" }, { method: :bg_red, expected_output: "\e[41mTest String\e[49m" }, { method: :bg_green, expected_output: "\e[42mTest String\e[49m" }, { method: :bg_brown, expected_output: "\e[43mTest String\e[49m" }, + { method: :bg_yellow, expected_output: "\e[103mTest String\e[49m" }, { method: :bg_blue, expected_output: "\e[44mTest String\e[49m" }, { method: :bg_magenta, expected_output: "\e[45mTest String\e[49m" }, { method: :bg_cyan, expected_output: "\e[46mTest String\e[49m" }, { method: :bg_gray, expected_output: "\e[47mTest String\e[49m" }, + { method: :bg_white, expected_output: "\e[107mTest String\e[49m" }, + { method: :bg_bright_red, expected_output: "\e[101mTest String\e[49m" }, + { method: :bg_bright_green, expected_output: "\e[102mTest String\e[49m" }, + { method: :bg_bright_blue, expected_output: "\e[104mTest String\e[49m" }, + { method: :bg_bright_magenta, expected_output: "\e[105mTest String\e[49m" }, + { method: :bg_bright_cyan, expected_output: "\e[106mTest String\e[49m" }, { method: :bold, expected_output: "\e[1mTest String\e[22m" }, + { method: :dim, expected_output: "\e[2mTest String\e[22m" }, { method: :italic, expected_output: "\e[3mTest String\e[23m" }, { method: :underline, expected_output: "\e[4mTest String\e[24m" }, - { method: :blink, expected_output: "\e[5mTest String\e[25m" }, - { method: :reverse_color, expected_output: "\e[7mTest String\e[27m" }, + { method: :reverse_colour, expected_output: "\e[7mTest String\e[27m" }, ].each do |testcase| it 'applies the expected formatting to a string' do result = 'Test String'.send(testcase[:method]) @@ -40,4 +44,34 @@ expect(result).to eq(testcase[:expected_output]) end end + + describe 'strip_colour' do + it 'removes ANSI colour codes from strings' do + coloured_string = "\e[31m\e[1mRed Bold Text\e[22m\e[39m" + expect(coloured_string.strip_colour).to eq('Red Bold Text') + end + + it 'handles complex nested colours' do + complex_string = "\e[31mRed \e[32mGreen\e[39m Red Again\e[39m" + expect(complex_string.strip_colour).to eq('Red Green Red Again') + end + end + + describe 'colour combinations' do + it 'chains multiple styles correctly' do + expect('Test'.red.bold.underline).to eq "\e[4m\e[1m\e[31mTest\e[39m\e[22m\e[24m" + end + + it 'combines foreground and background colours' do + expect('Test'.white.bg_red).to eq "\e[41m\e[97mTest\e[39m\e[49m" + end + + it 'handles bright colours with styles' do + expect('Test'.bright_blue.bold.italic).to eq "\e[3m\e[1m\e[94mTest\e[39m\e[22m\e[23m" + end + + it 'works with reverse and other styles' do + expect('Test'.red.bg_yellow.reverse_colour.bold).to eq "\e[1m\e[7m\e[103m\e[31mTest\e[39m\e[49m\e[27m\e[22m" + end + end end From 90b71f0416857d417b13e0e3a192c01022845f78 Mon Sep 17 00:00:00 2001 From: nialljames Date: Mon, 15 Sep 2025 08:07:30 +0100 Subject: [PATCH 2/2] readme fix --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 573d6c0..2fb7ae6 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,7 @@ You can stack multiple method calls to add additional styling and use string int | Utility | strip_colour reverse_colour | #### To handle differences in spelling the following methods have been given aliases: -| Original | Alias | +| Alias | Original | |---------------|----------------| | bg_grey | bg_gray | | colorize | colourise |