Skip to content

Commit e75f60e

Browse files
krystophnyclaude
andauthored
Comprehensive logging system to eliminate unwanted console output (Issue #84) (#86)
* feat: Add comprehensive logging system to replace console output - Add fortplot_logging module with configurable log levels (SILENT, ERROR, WARNING, INFO, DEBUG) - Replace all print statements with appropriate log calls - Export logging interface through main fortplot module for user control - Default to WARNING level (errors and warnings only) - Users can set LOG_LEVEL_SILENT for production use with zero console output **Affected files:** - fortplot.f90: Replace error messages for unimplemented features - fortplot_png.f90: Replace compression failure and success messages - fortplot_pdf.f90: Replace file creation success message - fortplot_text.f90: Replace STB TrueType initialization errors - fortplot_animation.f90: Replace all animation progress and error messages - fortplot_gltf.f90: Replace GLTF/GLB file creation messages - fortplot_figure_core.f90: Replace user interaction prompts and warnings - fortplot_raster.f90: Replace backend usage error - fortplot_ascii.f90: Replace file save confirmation message **User Interface:** - call set_log_level(LOG_LEVEL_SILENT) for production apps with zero output - call set_log_level(LOG_LEVEL_INFO) for verbose operation - call set_log_level(LOG_LEVEL_DEBUG) for maximum verbosity Fixes #84 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * fix: resolve critical CI failures blocking PR merges - Fix line length violation in fortplot_figure_core.f90 (132 chars -> 88 char limit) - Restore CMakeLists.txt required for CI cmake builds - Autonomous fix during batch mode PR processing to enable merging 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 93a07dc commit e75f60e

12 files changed

+201
-38
lines changed

src/fortplot.f90

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ module fortplot
1818
use fortplot_figure, only: figure_t
1919
use fortplot_format_parser, only: parse_format_string, contains_format_chars
2020
use fortplot_animation, only: animation_t, FuncAnimation
21+
use fortplot_logging, only: set_log_level, log_error, log_warning, log_info, &
22+
LOG_LEVEL_SILENT, LOG_LEVEL_ERROR, &
23+
LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG
2124

2225
implicit none
2326

@@ -37,6 +40,10 @@ module fortplot
3740

3841
! Animation interface
3942
public :: animation_t, FuncAnimation
43+
44+
! Logging interface
45+
public :: set_log_level, LOG_LEVEL_SILENT, LOG_LEVEL_ERROR, &
46+
LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG
4047

4148
! Line style constants (pyplot-style)
4249
character(len=*), parameter, public :: LINESTYLE_SOLID = '-'
@@ -225,7 +232,7 @@ subroutine hist(data, bins, density, label, color)
225232
call ensure_global_figure_initialized()
226233
! TODO: Implement hist method in figure_core
227234
! call fig%hist(data, bins=bins, density=density, label=label, color=color)
228-
print *, "ERROR: hist() not yet implemented - please use main branch for histogram support"
235+
call log_error("hist() not yet implemented - please use main branch for histogram support")
229236
end subroutine hist
230237

231238
subroutine histogram(data, bins, density, label, color)
@@ -253,7 +260,7 @@ subroutine histogram(data, bins, density, label, color)
253260
call ensure_global_figure_initialized()
254261
! TODO: Implement hist method in figure_core
255262
! call fig%hist(data, bins=bins, density=density, label=label, color=color)
256-
print *, "ERROR: hist() not yet implemented - please use main branch for histogram support"
263+
call log_error("hist() not yet implemented - please use main branch for histogram support")
257264
end subroutine histogram
258265

259266
subroutine boxplot(data, position, width, label, show_outliers, horizontal, color)
@@ -285,7 +292,7 @@ subroutine boxplot(data, position, width, label, show_outliers, horizontal, colo
285292
! TODO: Implement boxplot method in figure_core
286293
! call fig%boxplot(data, position=position, width=width, label=label, &
287294
! show_outliers=show_outliers, horizontal=horizontal, color=color)
288-
print *, "ERROR: boxplot() not yet implemented - please use main branch for boxplot support"
295+
call log_error("boxplot() not yet implemented - please use main branch for boxplot support")
289296
end subroutine boxplot
290297

291298
subroutine show_data(x, y, label, title_text, xlabel_text, ylabel_text, blocking)
@@ -666,13 +673,13 @@ subroutine show_viewer_implementation(blocking)
666673
call execute_command_line(command, wait=.false., exitstat=stat)
667674

668675
if (stat /= 0) then
669-
print *, 'Warning: Failed to open plot viewer. Plot saved to: ', trim(temp_filename)
670-
print *, 'Please open the file manually with your preferred PDF viewer.'
676+
call log_warning('Failed to open plot viewer. Plot saved to: ' // trim(temp_filename))
677+
call log_info('Please open the file manually with your preferred PDF viewer.')
671678
else
672-
print *, 'Plot opened in default viewer. File: ', trim(temp_filename)
679+
call log_info('Plot opened in default viewer. File: ' // trim(temp_filename))
673680

674681
if (do_block) then
675-
print *, 'Press Enter to continue and clean up temporary file...'
682+
call log_info('Press Enter to continue and clean up temporary file...')
676683
read(*,*)
677684

678685
! Clean up temporary file
@@ -688,7 +695,7 @@ subroutine show_viewer_implementation(blocking)
688695
call execute_command_line(command)
689696
else
690697
! In non-blocking mode, just inform that file stays
691-
print *, 'Note: Temporary file will remain at: ', trim(temp_filename)
698+
call log_info('Note: Temporary file will remain at: ' // trim(temp_filename))
692699
end if
693700
end if
694701
end subroutine show_viewer_implementation

src/fortplot_animation.f90

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ module fortplot_animation
44
use fortplot_pipe, only: open_ffmpeg_pipe, write_png_to_pipe, close_ffmpeg_pipe
55
use fortplot_png, only: png_context, create_png_canvas, get_png_data
66
use fortplot_mpeg1_format, only: encode_animation_to_mpeg1
7+
use fortplot_logging, only: log_error, log_info, log_warning
78
implicit none
89
private
910

@@ -63,11 +64,11 @@ subroutine run(self)
6364
integer :: i
6465

6566
if (.not. associated(self%animate_func)) then
66-
print *, "Error: Animation callback function not associated"
67+
call log_error("Animation callback function not associated")
6768
return
6869
end if
6970

70-
print *, "Running animation with", self%frames, "frames..."
71+
call log_info("Running animation with frames...")
7172

7273
do i = 1, self%frames
7374
call self%animate_func(i)
@@ -78,7 +79,7 @@ subroutine run(self)
7879
end if
7980
end do
8081

81-
print *, "Animation completed."
82+
call log_info("Animation completed.")
8283
end subroutine run
8384

8485
subroutine set_save_frames(self, pattern)
@@ -119,13 +120,13 @@ subroutine save(self, filename, fps, status)
119120

120121
if (.not. is_video_format(extension)) then
121122
if (present(status)) status = -3
122-
print *, "Error: Unsupported file format. Use .mp4, .avi, or .mkv"
123+
call log_error("Unsupported file format. Use .mp4, .avi, or .mkv")
123124
return
124125
end if
125126

126127
if (.not. check_ffmpeg_available()) then
127128
if (present(status)) status = -1
128-
print *, "Error: ffmpeg not found. Please install ffmpeg to save animations."
129+
call log_error("ffmpeg not found. Please install ffmpeg to save animations.")
129130
return
130131
end if
131132

@@ -230,7 +231,7 @@ subroutine save_animation_with_ffmpeg_pipe(anim, filename, fps, status)
230231
if (should_use_native_encoder(anim, filename)) then
231232
call save_animation_with_native_mpeg1(anim, filename, fps, status)
232233
if (status == 0) return ! Native encoder succeeded
233-
print *, "Native MPEG-1 encoder failed, falling back to FFmpeg"
234+
call log_warning("Native MPEG-1 encoder failed, falling back to FFmpeg")
234235
end if
235236

236237
! Fall back to FFmpeg pipeline
@@ -327,23 +328,23 @@ subroutine save_animation_with_ffmpeg_pipeline(anim, filename, fps, status)
327328
stat = open_ffmpeg_pipe(filename, fps)
328329
if (stat /= 0) then
329330
status = -4
330-
print *, "Error: Could not open pipe to ffmpeg"
331+
call log_error("Could not open pipe to ffmpeg")
331332
return
332333
end if
333334

334335
do frame_idx = 1, anim%frames
335336
call generate_png_frame_data(anim, frame_idx, png_data, stat)
336337
if (stat /= 0) then
337338
status = -5
338-
print *, "Error: Failed to generate frame", frame_idx
339+
call log_error("Failed to generate frame")
339340
stat = close_ffmpeg_pipe()
340341
return
341342
end if
342343

343344
stat = write_png_to_pipe(png_data)
344345
if (stat /= 0) then
345346
status = -6
346-
print *, "Error: Failed to write frame to pipe", frame_idx
347+
call log_error("Failed to write frame to pipe")
347348
stat = close_ffmpeg_pipe()
348349
return
349350
end if
@@ -358,7 +359,7 @@ subroutine save_animation_with_ffmpeg_pipeline(anim, filename, fps, status)
358359
status = 0
359360
else
360361
status = -7
361-
print *, "Error: Generated video failed validation"
362+
call log_error("Generated video failed validation")
362363
end if
363364
end subroutine save_animation_with_ffmpeg_pipeline
364365

src/fortplot_ascii.f90

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ module fortplot_ascii
88
!! Author: fortplot contributors
99

1010
use fortplot_context
11+
use fortplot_logging, only: log_info
1112
use fortplot_latex_parser
1213
use fortplot_unicode
1314
use, intrinsic :: iso_fortran_env, only: wp => real64
@@ -251,7 +252,7 @@ subroutine ascii_finalize(this, filename)
251252
open(newunit=unit, file=filename, status='replace')
252253
call output_to_file(this, unit)
253254
close(unit)
254-
print *, "Unicode plot saved to '", trim(filename), "'"
255+
call log_info("Unicode plot saved to '" // trim(filename) // "'")
255256
end if
256257
end subroutine ascii_finalize
257258

src/fortplot_figure_core.f90

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ module fortplot_figure_core
1313
use fortplot_scales
1414
use fortplot_utils
1515
use fortplot_axes
16+
use fortplot_logging, only: log_warning, log_info
1617
use fortplot_gltf, only: gltf_context
1718
use fortplot_colormap
1819
use fortplot_pcolormesh
@@ -631,11 +632,11 @@ subroutine savefig(self, filename, blocking)
631632
call self%backend%save(filename)
632633
end select
633634

634-
write(*, '(A, A, A)') 'Saved figure: ', trim(filename)
635+
call log_info('Saved figure: ' // trim(filename))
635636

636637
! If blocking requested, wait for user input
637638
if (do_block) then
638-
print *, "Press Enter to continue..."
639+
call log_info("Press Enter to continue...")
639640
read(*,*)
640641
end if
641642
end subroutine savefig
@@ -664,7 +665,7 @@ subroutine show(self, blocking)
664665

665666
! If blocking requested, wait for user input
666667
if (do_block) then
667-
print *, "Press Enter to continue..."
668+
call log_info("Press Enter to continue...")
668669
read(*,*)
669670
end if
670671
end subroutine show
@@ -2537,23 +2538,22 @@ subroutine set_ydata(self, plot_index, y_new)
25372538
real(wp), intent(in) :: y_new(:)
25382539

25392540
if (plot_index < 1 .or. plot_index > self%plot_count) then
2540-
print *, "Warning: Invalid plot index", plot_index, "for set_ydata"
2541+
call log_warning("Invalid plot index for set_ydata")
25412542
return
25422543
end if
25432544

25442545
if (self%plots(plot_index)%plot_type /= PLOT_TYPE_LINE) then
2545-
print *, "Warning: set_ydata only supported for line plots"
2546+
call log_warning("set_ydata only supported for line plots")
25462547
return
25472548
end if
25482549

25492550
if (.not. allocated(self%plots(plot_index)%y)) then
2550-
print *, "Warning: Plot", plot_index, "has no y data to update"
2551+
call log_warning("Plot has no y data to update")
25512552
return
25522553
end if
25532554

25542555
if (size(y_new) /= size(self%plots(plot_index)%y)) then
2555-
print *, "Warning: New y data size", size(y_new), &
2556-
"does not match existing size", size(self%plots(plot_index)%y)
2556+
call log_warning("New y data size does not match existing size")
25572557
return
25582558
end if
25592559

@@ -2635,7 +2635,7 @@ subroutine ensure_directory_exists(filename)
26352635
write(command, '(A,A,A)') 'mkdir -p "', trim(dir_path), '"'
26362636
call execute_command_line(command, exitstat=status)
26372637
if (status /= 0) then
2638-
print *, "Warning: Could not create directory: ", trim(dir_path)
2638+
call log_warning("Could not create directory: " // trim(dir_path))
26392639
end if
26402640
end if
26412641
end subroutine ensure_directory_exists

src/fortplot_gltf.f90

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module fortplot_gltf
66
use fortplot_context, only: plot_context, setup_canvas
77
use fortplot_gltf_base
88
use fortplot_gltf_writer
9+
use fortplot_logging, only: log_info, log_error
910
use fortplot_gltf_geometry
1011
use fortplot_gltf_buffer
1112
use fortplot_glb_writer
@@ -76,9 +77,9 @@ subroutine save_gltf(this, filename)
7677
! Write GLB binary format
7778
if (allocated(this%buffer_data)) then
7879
call write_glb_file(filename, json, this%buffer_data)
79-
print *, "GLB file '" // trim(filename) // "' created successfully!"
80+
call log_info("GLB file '" // trim(filename) // "' created successfully!")
8081
else
81-
print *, "Error: No binary data for GLB file"
82+
call log_error("No binary data for GLB file")
8283
end if
8384
else
8485
! Write GLTF text format
@@ -87,9 +88,9 @@ subroutine save_gltf(this, filename)
8788
if (iostat == 0) then
8889
write(unit, '(A)') json
8990
close(unit)
90-
print *, "GLTF file '" // trim(filename) // "' created successfully!"
91+
call log_info("GLTF file '" // trim(filename) // "' created successfully!")
9192
else
92-
print *, "Error: Failed to create GLTF file"
93+
call log_error("Failed to create GLTF file")
9394
end if
9495
end if
9596

src/fortplot_logging.f90

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
module fortplot_logging
2+
!! Simple logging facility for fortplot library
3+
!! Allows control over console output verbosity
4+
5+
implicit none
6+
private
7+
8+
public :: set_log_level, log_info, log_warning, log_error, log_debug
9+
public :: LOG_LEVEL_SILENT, LOG_LEVEL_ERROR, LOG_LEVEL_WARNING, LOG_LEVEL_INFO, LOG_LEVEL_DEBUG
10+
11+
! Log levels (in increasing verbosity)
12+
integer, parameter :: LOG_LEVEL_SILENT = 0
13+
integer, parameter :: LOG_LEVEL_ERROR = 1
14+
integer, parameter :: LOG_LEVEL_WARNING = 2
15+
integer, parameter :: LOG_LEVEL_INFO = 3
16+
integer, parameter :: LOG_LEVEL_DEBUG = 4
17+
18+
! Default log level (warnings and errors only)
19+
integer :: current_log_level = LOG_LEVEL_WARNING
20+
21+
contains
22+
23+
subroutine set_log_level(level)
24+
!! Set the global logging level
25+
!!
26+
!! Arguments:
27+
!! level: LOG_LEVEL_SILENT, LOG_LEVEL_ERROR, LOG_LEVEL_WARNING,
28+
!! LOG_LEVEL_INFO, or LOG_LEVEL_DEBUG
29+
integer, intent(in) :: level
30+
current_log_level = level
31+
end subroutine set_log_level
32+
33+
subroutine log_info(message)
34+
!! Log an informational message
35+
character(len=*), intent(in) :: message
36+
if (current_log_level >= LOG_LEVEL_INFO) then
37+
print *, "[INFO] ", trim(message)
38+
end if
39+
end subroutine log_info
40+
41+
subroutine log_warning(message)
42+
!! Log a warning message
43+
character(len=*), intent(in) :: message
44+
if (current_log_level >= LOG_LEVEL_WARNING) then
45+
print *, "[WARNING] ", trim(message)
46+
end if
47+
end subroutine log_warning
48+
49+
subroutine log_error(message)
50+
!! Log an error message
51+
character(len=*), intent(in) :: message
52+
if (current_log_level >= LOG_LEVEL_ERROR) then
53+
print *, "[ERROR] ", trim(message)
54+
end if
55+
end subroutine log_error
56+
57+
subroutine log_debug(message)
58+
!! Log a debug message
59+
character(len=*), intent(in) :: message
60+
if (current_log_level >= LOG_LEVEL_DEBUG) then
61+
print *, "[DEBUG] ", trim(message)
62+
end if
63+
end subroutine log_debug
64+
65+
end module fortplot_logging

src/fortplot_pdf.f90

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module fortplot_pdf
33
use fortplot_vector, only: vector_stream_writer, vector_graphics_state
44
use fortplot_latex_parser
55
use fortplot_unicode
6+
use fortplot_logging, only: log_info
67
use fortplot_margins, only: plot_margins_t, plot_area_t, calculate_plot_area, get_axis_tick_positions
78
use fortplot_ticks, only: generate_scale_aware_tick_labels, find_nice_tick_locations, format_tick_value_smart
89
use fortplot_label_positioning, only: calculate_x_label_position, calculate_y_label_position, &
@@ -427,7 +428,7 @@ subroutine write_pdf_file(this, filename)
427428

428429
call finalize_pdf_stream(this)
429430
call create_pdf_document(unit, filename, this)
430-
print *, "PDF file '", trim(filename), "' created successfully!"
431+
call log_info("PDF file '" // trim(filename) // "' created successfully!")
431432
end subroutine write_pdf_file
432433

433434
subroutine normalize_to_pdf_coords(ctx, x, y, pdf_x, pdf_y)

src/fortplot_png.f90

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module fortplot_png
33
use fortplot_context, only: setup_canvas
44
use fortplot_raster, only: raster_context, create_raster_canvas, draw_axes_and_labels, draw_rotated_ylabel_raster
55
use fortplot_zlib, only: zlib_compress, crc32_calculate
6+
use fortplot_logging, only: log_error, log_info
67
use, intrinsic :: iso_fortran_env, only: wp => real64, int8, int32
78
implicit none
89

@@ -57,7 +58,7 @@ subroutine generate_png_data(width, height, image_data, png_buffer)
5758
compressed_data = zlib_compress(image_data, data_size, compressed_size)
5859

5960
if (.not. allocated(compressed_data) .or. compressed_size <= 0) then
60-
print *, "Compression failed"
61+
call log_error("PNG compression failed")
6162
return
6263
end if
6364

@@ -124,7 +125,7 @@ subroutine write_png_file(filename, width, height, image_data)
124125
close(png_unit)
125126

126127
deallocate(png_buffer)
127-
print *, "PNG file '", trim(filename), "' created successfully!"
128+
call log_info("PNG file '" // trim(filename) // "' created successfully!")
128129
end subroutine write_png_file
129130

130131
! Public wrapper for getting PNG data

0 commit comments

Comments
 (0)