Skip to content

A wrapper for Fortran-stdlib logger that enhances log messages with purpose classification and message categorization.

License

Notifications You must be signed in to change notification settings

degawa/flavanaly

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

22 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Flavanaly

A wrapper for Fortran-stdlib logger that enhances log messages with purpose classification and message categorization.

Features

  • Adds purpose tags to log messages (monitor/report/trace) to clarify the logging intent
  • Supports message categorization for better log organization
  • Fully compatible with Fortran-stdlib logger
  • Extensible - easily add custom purposes and log levels

Motivation

Traditional log levels (e.g. debug/info/warn/error) focus on message severity, but often fail to capture why we're logging in the first place. Flavanaly adds two key dimensions to logging:

  • Purpose tags clarify why we're logging (e.g., monitoring system state, reporting results, or tracing execution flow)
  • Categories help organize logs by subsystem or feature

This information makes logs more meaningful and more straightforward to filter/analyze.

Getting started

Requirements

  • Modern Fortran compiler
    • The compilers and versions listed below have been used to develop catechin.
    • gfortran 11.2 bundled with quickstart Fortran on Windows
    • Intel Fortran Classic 2021.5.0 Build 20211109_000000
    • NAG Fortran 7.1 Build 7117
  • Fortran-stdlib
    • catechin is a wrapper for logger provided by the Fortran-stdlib.
  • Fortran Package Manager (fpm) 0.7.0 alpha or later
    • catechin is created as an fpm project.
  • test-drive 0.4.0 or later
  • FORD (optional)

Get the code

To get the code, execute the following commnad:

git clone https://github.com/degawa/flavanaly.git
cd flavanaly

Build with fpm

To build the library using fpm, execute the following command:

fpm build

Then, install the library using:

fpm install --prefix path/to/your/libdir

Reference from your project

Add the following use statement to modules or procedures calling Flavanaly.

use :: flavanaly

Reference as a fpm project's dependency

To use Flavanaly in your fpm project, add the following to the fpm.toml.

[dependencies]
flavanaly = {git = "https://github.com/degawa/flavanaly.git"}

Usage

Basic Usage

Flavanaly provides a logging subroutine.

  • logging(purpose, level, message, module, procedure, category)
    • monitor, report, and trace are provided as specifiers for the purpose.
    • debug, info, warn, and error are provided as specifiers for the level.
    • message, module, procedure, category are strings.

An example is shown below:

use :: flavanaly

call logging(trace, info, "message", "main", category="IO")
! yyyy-mm-dd hh:mm:ss.xxx: main: INFO: (trace): [IO]: message

The purpose and category are added at the begging of the log message.

Practical Examples

  1. Monitoring system state:
! Monitor memory usage
call logging(monitor, info, "Current memory usage: 80%", "memory_manager", category="System.Resources")
! yyyy-mm-dd hh:mm:ss.xxx: memory_manager: INFO: (monitor): [System.Resources]: Current memory usage: 80%

! Report calculation results
call logging(report, info, "Optimization converged after 100 iterations", "optimizer", category="Algorithm.Convergence")
! yyyy-mm-dd hh:mm:ss.xxx: optimizer: INFO: (report): [Algorithm.Convergence]: Optimization converged after 100 iterations

Customizing Log Format

Configuration

Since Flavanaly's purpose loggers (monitor, report, and trace) extend the logger_type defined in the stdlib_logger module, they can be configured using procedures bound to the logger_type:

  • add_log_file
  • add_log_unit
  • configure
  • remove_log_unit

Also, the current configuration can be confirmed using the type-bound procedures configuration and log_units_assigned.

program main
    use, intrinsic :: iso_fortran_env, only: int32, output_unit
    use :: flavanaly
    use :: stdlib_logger, only:debug_level, information_level
    implicit none

    integer(int32) :: log_unit

    call monitor%configure(level=debug_level)
    call monitor%configure(time_stamp=.false.)

    open (newunit=log_unit, file="monitor.log")

    call monitor%add_log_unit(log_unit)
    call monitor%add_log_unit(output_unit)

    block
        logical :: time_stamp
        integer(int32) :: level, num_units
        integer(int32), allocatable :: units(:)

        call monitor%configuration(time_stamp=time_stamp, level=level, log_units=units)
        num_units = monitor%log_units_assigned()
        print *, "current status"
        print *, "add time stamp: ", time_stamp
        print *, "minimum level for printing log message: ", level
        print *, "number of units assigned to monitor logger: ", num_units
        print *, "log output units: ", units(:)
        ! current status
        ! add time stamp:  F
        ! minimum level for printing log message:           10
        ! number of units assigned to monitor logger:            2
        ! log output units:          -10           6
        ! ^ Log output unit numbers may change depending on the compiler and environment.
    end block

    call logging(monitor, debug, "message", category="TEST.IO.LOG")
    !DEBUG: (monitor): [TEST.IO.LOG]: message

    call monitor%remove_log_unit(log_unit, close_unit=.true.)
    call monitor%configure(level=information_level)
    call monitor%configure(time_stamp=.true.)
end program main

Preprocessor Macros

Flavanaly provides several preprocessor macros to customize the log format at compile time.

String Length for Purpose

#define FLAVAN_PURPOSE_LENGTH 16  ! default value

Defines the maximum length of strings storing log purposes. Must be a positive integer. The default value is 16. If undefined or set to less than 1, it will be set to 16.

Brackets for Purpose and Category

#define FLAVAN_PURPOSE_BRACKET "("    ! default value
#define FLAVAN_CATEGORY_BRACKET "["   ! default value

Define the opening brackets used for purpose and category tags in log messages.

  • FLAVAN_PURPOSE_BRACKET: Opening bracket for purpose tags (default: "(")
  • FLAVAN_CATEGORY_BRACKET: Opening bracket for category tags (default: "[")

The corresponding closing brackets are automatically determined based on the following pairs:

  • (), <>, [], {}: Each opening bracket is paired with its standard closing bracket
  • Other characters: The same character is used for both opening and closing brackets

Example log outputs with different bracket settings:

! Default settings
call logging(monitor, info, "message", category="System")
! Output: ... (monitor): [System]: message

! Custom settings
#define FLAVAN_PURPOSE_BRACKET "<"
#define FLAVAN_CATEGORY_BRACKET "{"

call logging(monitor, info, "message", category="System")
! Output: ... <monitor>: {System}: message

How to Set Macros

  • Using compiler flags:
# gfortran example
gfortran -DFLAVAN_PURPOSE_LENGTH=32 -DFLAVAN_PURPOSE_BRACKET="{" source.f90

# Intel Fortran example
ifort /DFLAVAN_PURPOSE_LENGTH=32 /DFLAVAN_PURPOSE_BRACKET="{" source.f90
  • Using fpm build command with the --flag option:
fpm build --flag "-DFLAVAN_PURPOSE_LENGTH=32 -DFLAVAN_PURPOSE_BRACKET='{'"

Note:

  • All macro values must be set before including any Flavanaly modules
  • Changes to macro values require recompiling the code
  • Invalid bracket characters will default to using the same character for both opening and closing brackets

Extension

Adding New Purposes

  1. Declare a purposeful_logger_type instance
  2. Specify a custom purpose name
use :: flavanaly, only:purposeful_logger_type

type(purposeful_logger_type), public :: detail = purposeful_logger_type(purpose="detail")

Adding New Levels

  1. Define a new type extending level_logger_atype
  2. Implement the required logging type-bound procedure
  3. Declare an instance for use as a specifier
    use :: flavanaly, only:level_logger_atype
    ! use clause must be described because it conflicts with logging procedures.
    implicit none

    type, private, extends(level_logger_atype) :: fatal_level_logger_type
    contains
        procedure, public, nopass :: logging
    end type fatal_level_logger_type

    type(fatal_level_logger_type), public :: fatal
contains
    subroutine logging(logger, message, module, procedure)
        use :: stdlib_logger, only:stdlib_logger_type => logger_type
        class(stdlib_logger_type)   , intent(in)            :: logger
        character(*)                , intent(in)            :: message
        character(*)                , intent(in), optional  :: module
        character(*)                , intent(in), optional  :: procedure

        call logger%log_message(message, module, procedure, prefix="FATAL")
    end subroutine logging

Passing the defined detail and fatal to logging procedure yields the following output.

call logging(detail, fatal, "fatal error", "main", category="HID.controller.buttom")
! yyyy-mm-dd hh:mm:ss.xxx: main: FATAL: (detail): [HID.controller.buttom]: fatal error

About

A wrapper for Fortran-stdlib logger that enhances log messages with purpose classification and message categorization.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published