Skip to content

Develop an API for a STDLIB logging system #227

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wclodius2 opened this issue Aug 7, 2020 · 18 comments
Closed

Develop an API for a STDLIB logging system #227

wclodius2 opened this issue Aug 7, 2020 · 18 comments
Labels
implementation Implementation in experimental and submission of a PR topic: IO Common input/output related features

Comments

@wclodius2
Copy link
Contributor

It is common for a library of code to have the equivalent of one or more error logging modules. SLATEC has its XERROR codes, Futility has its ExceptionHandler.f90 and FileTypeLog.f90, and FLIBS has its reporting, m_logger, m_multilog, and m_exception modules. I propose that STDLIB have its own logger module that I propose naming STDLIB_LOGGER. I propose that the logger have options to:

  • be able to send the same text to multiple logical units;
  • be able to configure which units receive the text;
  • by default have one of those units be one of OUTPUT_UNIT or ERROR_UNIT;
  • precede messages by a blank line;
  • precede messages by a time stamp of the form yyyy-mm-dd hh:mm:ss.sss;
  • precede a message with module and procedure names;
  • follow a message with the STAT and ERRMSG of the statement that prompted the log message;
  • follow a message with the IOSTAT and IOMSG of the I/O statement that prompted the log message;
  • label a message with one of 'INFORMATION: ', 'WARNING: ', or 'ERROR: ';
  • indent subsequent lines of the messages; and
  • format the text to fit within a maximum column width.

The maximum number of logical units to be supported at one time is a minimum of two: one of either OUTPUT_UNIT or ERROR_UNIT and an additional formatted file, but by using an array of logical units it should be possible to support more, such as the five supported by SLATEC's XERROR. While the initial implementation will use Fortran's standard file interface through logical units, I hope to hide enough of the details that wrappers to the logical units can be deployed if desired. The above proposal prompts the following questions:

  1. Is the general outline of the proposal a good one?
  2. Is STDLIB ready for such a proposal at this time?
  3. Which should be the default logical unit for the logger: OUTPUT_UNIT or ERROR_UNIT?
  4. Should you be able to output to both OUTPUT_UNIT and ERROR_UNIT at the same time?
  5. How many output units should be supported at one time?
  6. Should output of the labels be determined by the name of the procedure, e.g., LOG_ERROR, LOG_WARNING, and LOG_INFO, or by a flag?
  7. Are there any other questions I am missing?
@milancurcic
Copy link
Member

Thank you @wclodius2. Note #193.

Is the general outline of the proposal a good one?

I think so.

Is STDLIB ready for such a proposal at this time?

Yes. There may not be as much excitement or intense discussion on the topic as you'd like, but we should still go forward with it and iterate as issues come up.

Which should be the default logical unit for the logger: OUTPUT_UNIT or ERROR_UNIT?

I think stdout should be default.

Should you be able to output to both OUTPUT_UNIT and ERROR_UNIT at the same time?

Yes.

How many output units should be supported at one time?

As I understand it, it seems to me the choice is between 1 and as many as you want, depending on the implementation. Multiple loggers in the same application seems like it'd be useful for multi-component programs.

Should output of the labels be determined by the name of the procedure, e.g., LOG_ERROR, LOG_WARNING, and LOG_INFO, or by a flag?

This seems like a matter of style. Personally I think having separate procedures log_error(), log_warning(), and log_info() makes for a nicer and easier to learn API. They can be just thin wrappers around a generic log() to avoid duplication of code.

Let's discuss it on our call next week: https://fortran-lang.discourse.group/t/fortran-monthly-call-august-2020

@wclodius2
Copy link
Contributor Author

The tentative API I have developed is described below.

The module STDLIB_LOGGER

Introduction

This module defines procedures to be used for reporting by the Fortran
Standard Library of errors and other information. The reportsnormally
go to formatted files represented by the array of logical unit,
LOG_UNITS. An arbitrary number of log units can be open at a
time. LOG_UNITS may be associated with files dedicated to
STDLIB_LOGGER or may be associated with previously opened files. If
LOG_UNITS is empty then the output goes to OUTPUT_UNIT of
ISO_FORTRAN_ENV.

The logger has the options to:

  • change which units receive the log messages;
  • report which units recieve the log messages;
  • precede messages by a blank line;
  • precede messages by a time stamp of the form
    yyyy-mm-dd hh:mm:ss.sss;
  • precede messages with the names of a module and procedure;
  • follow a message with the STAT and ERRMSG of the error report
    that prompted the log message;
  • follow a message with the IOSTAT and IOMSG of the I/O error
    report that prompted the log message;
  • label a message with one of 'INFORMATION: ', 'WARNING: ', or
    'ERROR: ';
  • indent subsequent lines of the messages; and
  • format the text to fit within a maximum column width.

The STDLIB_LOGGER constants

The module defines seven distinct named integer constants for
reporting errors in STAT arguments. SUCCESS,
INVALID_INDEX_ERROR, OPEN_FAILURE, READ_ONLY_ERROR,
SEQUENTIAL_ACCESS_ERROR, UNFORMATTED_IN_ERROR, and
UNOPENED_IN_ERROR. SUCCESS indicates that no
error has occurred. INVALID_INDEX_ERROR indicates that
START_INDEX was invalid for the given LINE. OPEN_FAILURE
indicates that an OPEN statement failed. READ_ONLY_ERROR indicates
that an output unit did not have a WRITE or READWRITE
action. SEQUENTIAL_ACCESS_ERROR indicates that the unit did not have
SEQUENTIAL access. UNFORMATTED_IN_ERROR indicates that the unit
did not have a FORM of FORMATTED. Finally, UNOPENED_IN_ERROR
indicates that the unit was not opened.

The STDLIB_LOGGER module procedures

Overview of the procedures

The module defines thirteen public procedures: one function and twelve
subroutines. The one function is an inquiry function:

LOG_UNITS_ASSIGNED
: verifies whether LOG_UNITS has been assigned one or more logical
units.

There are three subroutines for manipulating LOG_UNITS:

ADD_LOG_FILE
: opens a file using NEWUNIT, and adds the resulting logical unit
number to the LOG_UNITS list.

ADD_LOG_UNIT
: adds an existing logical unit number to the LOG_UNITS list.

REMOVE_LOG_UNIT
: remove the UNIT from th LOG_UNITS array and optionally closes
it.

There are seven subroutines for sending messages to the LOG_UNITS:

ASSERT
: tests a logical condition and if .FALSE. sends a log
message and stops processing.

LOG_ERROR
: sends a message prepended by 'ERROR: ' optionally followed by a
STAT or ERRMSG or both.

LOG_INFORMATION
: sends a message prepended by 'INFORMATION: '

LOG_IO_ERROR
: sends a message prepended by 'I/O ERROR: 'optionally followed by
an IOSTAT or IOMSG or both.

LOG_MESSAGE
: sends a message.

LOG_TEXT_ERROR
: sends a message describing an error found in a line of text.

LOG_WARNING
: sends a message prepended by 'WARNING: '.

There is one subroutine for configuring the logger:

CONFIGURE_LOGGING
: configures the details of the logging process.

There is one subroutine for reporting the details of the logging
configuration.

LOGGING_CONFIGURATION
: reports the details of the logging configuration.

@milancurcic
Copy link
Member

Thank you, @wclodius2. Can you make it to the call today? https://fortran-lang.discourse.group/t/fortran-monthly-call-august-2020/260/2

It'd be good to discuss it live and get a temperature on what people think about it.

@wclodius2
Copy link
Contributor Author

wclodius2 commented Aug 20, 2020 via email

@wclodius2
Copy link
Contributor Author

Given that the response seemed positive at the call I will now proceed. Next should be a detailed outline of the proposed API, followed by an implementation of any changes in the API, then testing, then a checkout of the STDLIB source, and then a PR for the implementation and tests. What follows is a detailed summary of the procedures in a form that parallels the format used in the standard. Possible sources of controversy are:

  1. the lack of STAT arguments for most of the LOG_* subroutines,
  2. the use of the language keywords, MODULE and PROCEDURE, as arguments to the LOG_* and ASSERT subroutines,
  3. the presence of the ASSERT subroutine,
  4. the number of LOG_* subroutines,
  5. the use of MESSAGE as an argument rather than the shorter MSG,
  6. the presence of the LOG_TEXT_ERROR subroutine, and
  7. that the INDENT argument of the configuration routines is a logical argument and not a numeric one

My response to the above are

  1. The only obvious source of problems with the LOG_* routines is a failure with the WRITE statements. I don't know of a good way of handling such an error in the error reporting routines.
  2. They seemed the most obvious names for their intended semantics. MODULE_NAME, and PROCEDURE_NAME seemed too wordy, MOD had a similar conflict with the Standard's intrinsic function.
  3. It seemed useful and appropriate
  4. All of them seemed useful
  5. I have no strong feelings on this
  6. It seemed useful to me
  7. I have no strong feelings on this.

Specification of the Procedures

ADD_LOG_FILE( FILENAME, UNIT [, ACTION, POSITION, STATUS, STAT ] )

Description. Sets FILENAME to be a file in LOG_UNITS and
returns the associated I/O unit number, UNIT. The file will be a
formatted sequential access file. STAT, if present, has the value
SUCCESS if FILENAME could be opened, and OPEN_FAILURE
otherwise. If STAT is absent and FILENAME could not be opened then
processing will stop with a write to LOG_UNITS.

Class. Subroutine

Arguments.

FILENAME shall be a scalar default CHARACTER expression. It is
an INTENT(IN) argument. It shall be the name of the file to be
opened,

UNIT shall be a scalar default INTEGER variable. It is an
INTENT(OUT) argument. It will be the unit number returned by the
NEWUNIT specifier of the OPEN statement for FILENAME.

ACTION (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It shall be the ACTION
specifier of the OPEN statement and must have one of the values
'WRITE' or 'READWRITE'. It has the default value of 'WRITE'.

POSITION (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It shall be the
POSITION specifier of the OPEN statement and must have one of
the values 'ASIS', 'REWIND', or 'APPEND'. It has the default
value of 'REWIND'.

STATUS (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It shall be the
STATUS specifier of the OPEN statement and must have one of
the values 'OLD', 'NEW', 'REPLACE', or 'UNKNOWN'. It has the
default value of 'REPLACE'.

STAT (optional) shall be a scalar default INTEGER variable. It
is an INTENT(OUT) argument. If present, on return it will have the
value SUCCESS if FILENAME could be opened, or the value
OPEN_FAILURE if it could not be opened. If absent and FILENAME
could not be opened then processing will stop with amessage to
LOG_UNITS.

ADD_LOG_UNIT( UNIT [, STAT ] )

Description Adds UNIT to the array of LOG_UNITS. UNIT shall
be the UNIT number for an opened, sequential, formatted file with an
action specifier of WRITE or READWRITE. Failure of UNIT to meet
those requirements will result cause STAT, if present, to not be
SUCCESS and UNIT not to be added to LOG_UNITS, or, if STAT is
not present, cause processing to stop with an informative string as
the stop code.

Class. Subroutine.

Arguments.

UNIT shall be a scalar default INTEGER expression. It is an
INTENT(IN) argument. It shall be the UNIT number for an opened,
sequential, formatted file with an action specifier of WRITE or
READWRITE.

STAT (optional) shaa be a scalar default INTEGER variable. It is
an INTENT(OUT) argument. If present it will have the value of one
of the module's integer constants indicating any errors found with
UNIT. The constants are

  • SUCCESS - no problems found
  • READ_ONLY_ERROR - UNIT had an ACTION specifier of 'READ'
    when it needs a specifier of 'WRITE' or 'READWRITE'
  • SEQUENTIAL_ACCESS_ERROR - UNIT did not have an ACCESS of
    SEQUENTIAL
  • UNFORMATTED_IN_ERROR - UNIT did not have a FORM of
    FORMATTED
  • UNOPENED_IN_ERROR - UNIT was not opened
ASSERT( TEST, MESSAGE [, MODULE, PROCEDURE ] )

Description. Checks the value of TEST and if TEST is
.FALSE. writes output to the LOG_UNITS and stops processing,
otherwise it returns with no effect.

Behavior. If TEST is .FALSE. ASSERT will write to
LOG_UNITS, otherwise nothing is written. If time stamps are active
then the time stamp will be written first. Then if MODULE and
PROCEDURE are present then they will be written. Finally MESSAGE,
will be written prepended by the string 'ASSERTION FAILURE: '.

Class. Subroutine.

Arguments.

TEST shall be a scalar default LOGICAL expression. It is an
INTENT(IN) argument.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument. Normally it is a representation of TEST.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the ASSERT call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the ASSERT call.

CONFIGURE_LOGGING( [ ADD_LINE, INDENT, MAX_WIDTH, TIME_STAMP ] )

Description. Configures the logging process.

Class. Subroutine.

Arguments.

ADD_LINE (optional) shall be a scalar default LOGICAL
expression. It is an INTENT(IN) argument. Set to .TRUE. to start
output with a blank line, and to .FALSE. otherwise. ADD_LINE has
a default value of .TRUE..

INDENT (optional) shall be a scalar default LOGICAL
expression. It is an INTENT(IN) argument. Set to .TRUE. to
indent subsequent lines by four spaces, and to .FALSE.
otherwise. INDENT has a default value of .TRUE..

MAX_WIDTH (optional) shall be a scalar default INTEGER
expression. It is an INTENT(IN) argument. Set to a positive value
bigger than four to define the maximum width of the output,
otherwise there is no maximum width. MAX_WIDTH has a default value
of 80.

TIME_STAMP (optional) shall be a scalar default LOGICAL
expression. It is an INTENT(IN) argument. Set to .TRUE. to
precede output with a time stamp of the form 'yyyy-mm-dd
hh:mm:ss.sss', and to .FALSE. otherwise. TIME_STAMP has a
default value of .TRUE..

LOG_ERROR( MESSAGE [, MODULE, PROCEDURE, STAT, ERRMSG ] )

Description. Writes the string MESSAGE to LOG_UNITS with
optional additional text.

Behavior. If time stamps are active, a time stamp is written
first. Then if MODULE or PROCEDURE are present, they are
written. Then MESSAGE is written with the prefix 'ERROR: '. Then
if STAT or ERRMSG are present they are written.

Class. Subroutine.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the LOG_ERROR call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the LOG_ERROR call.

STAT (optional) shall be a scalar default INTEGER expression. It
is an INTENT(IN) argument. It should be the STAT specifier of
the subroutine call or intrinsic statement that prompted the
LOG_ERROR call.

ERRMSG (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the
ERRMSG specifier of the subroutine call or intrinsic statement
that prompted the LOG_ERROR call.

LOG_INFORMATION( MESSAGE [, MODULE, PROCEDURE ] )

Description. Writes the string MESSAGE to LOG_UNITS with
optional additional text.

Behavior. If time stamps are active, a time stamp is written
first. Then if MODULE or PROCEDURE are present, they are
written. Then MESSAGE is written with the prefix
INFORMATION: '.

Class. Subroutine.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the LOG_INFORMATION call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the LOG_INFORMATION call.

LOG_IO_ERROR( MESSAGE [, MODULE, PROCEDURE, IOSTAT, IOMSG ] )

Description. Writes the string MESSAGE to LOG_UNITS with
optional additional text.

Behavior. If time stamps are active, a time stamp is written
first. Then if MODULE or PROCEDURE are present, they are
written. Then MESSAGE is written with the prefix
'I/O ERROR: '. Then if IOSTAT or IOMSG are present they are
written.

Class. Subroutine.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the LOG_IO_ERROR call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the LOG_IO_ERROR call.

IOSTAT (optional) shall be a scalar default INTEGER
expression. It is an INTENT(IN) argument. It should be the
IOSTAT specifier of the subroutine call or intrinsic statement
that prompted the LOG_IO_ERROR call.

IOMSG (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the
IOMSG specifier of the subroutine call or intrinsic statement
that prompted the LOG_IO_ERROR call.

LOG_MESSAGE( MESSAGE [, MODULE, PROCEDURE ] )

Description. Writes the string MESSAGE to LOG_UNITS with
optional additional text.

Behavior. If time stamps are active, a time stamp is written
first. Then if MODULE or PROCEDURE are present, they are
written. Then MESSAGE is written with no prefix.

Class. Subroutine.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the LOG_MESSAGE call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the LOG_MESSAGE call.

LOG_TEXT_ERROR( LINE, COLUMN, DESCRIP [, FILENAME, LINE_NUMBER, STAT ] )

Description. LOG_TEXT_ERROR sends a message to LOG_UNITS
describing an error found in a line of text.

Behavior. If time stamps are active first a time stamp is
written. Then if FILENAME or LINE_NUMBER are present they are
written. Then if LEVEL is present a string is written indicating the
level of the error. Then LINE is written. Then a caret, '^', is
written below LINE at the column indicated by COLUMN. Then
DESCRIP is written below the caret.

Class. Subroutine.

Arguments.

LINE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument. It should be the line of text in which the
error was found.

COLUMN shall be a scalar default INTEGER expression. It is an
INTENT(IN) argument. It should be the one's based column at which
the error location begins.

DESCRIP shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument. It should be the description of the error in
LINE.

FILENAME (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the file, if any, in which LINE was found.

LINE_NUMBER (optional) shall be a scalar default INTEGER
expression. It is an INTENT(IN) argument. It should be the line
number in FILENAME associated with LINE.

STAT (optional) shall be a scalar default CHARACTER variable. It
is an INTENT(OUT) argument. If present will have the value of
SUCCESS if no errors were encountered, the value
INDEX_INVALID_ERROR if COLUMN is less than one or greater than
LEN(LINE)+1, or the value WRITE_FAULT if the writes to any of
LOG)UNITS failed. If STAT is absent and would not have the value
SUCCESS then processing will stop with an informative message.

LOG_UNITS_ASIGNED()

Description. Returns .TRUE. if LOG_UNITS has at least one
logical unit assigned and .FALSE. otherwise.

Class. Function.

Result Character. The result is a scalar default LOGICAL.

Result Value. The result is .TRUE. if LOG_UNITS has at least
one logical unit assigned and .FALSE. otherwise.

LOG_WARNING( MESSAGE [, MODULE, PROCEDURE ] )

Description. Writes the string MESSAGE to LOG_UNITS with
optional additional text.

Behavior. If time stamps are active, a time stamp is written
first. Then if MODULE or PROCEDURE are present, they are
written. Then MESSAGE is written with the prefix
WARNING: '.

Class. Subroutine.

MESSAGE shall be a scalar default CHARACTER expression. It is an
INTENT(IN) argument.

MODULE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the module containing the LOG_WARNING call.

PROCEDURE (optional) shall be a scalar default CHARACTER
expression. It is an INTENT(IN) argument. It should be the name of
the procedure containing the LOG_WARNING call.

LOGGING_CONFIGURATION( [ ADD_LINE, INDENT, MAX_WIDTH, TIME_STAMP, LOG_UNITS ] )

Description. Reports the logging configuration.

Class. Subroutine.

Arguments.

ADD_LINE (optional) shall be a scalar default LOGICAL
variable. It is an INTENT(OUT) argument. A value of .TRUE.
starts output with a blank line, and .FALSE. otherwise.

INDENT (optional) shall be a scalar default LOGICAL variable. It
is an INTENT(OUT) argument. A value of .TRUE. indents subsequent
lines by four spaces, and .FALSE. otherwise.

MAX_WIDTH (optional) shall be a scalar default INTEGER
variable. It is an INTENT(OUT) argument. A positive value bigger
than four defines the maximum width of the output, otherwise there
is no maximum width.

TIME_STAMP (optional) shall be a scalar default LOGICAL
variable. It is an INTENT(OUT) argument. A value of .TRUE.
precedes output with a time stamp of the form 'yyyy-mm-dd
hh:mm:ss.sss', and .FALSE. otherwise.

LOG_UNITS (optional) shall be a rank one allocatable array
variable of type default INTEGER. It is an INTENT(OUT)
argument. On return it shall be the elements of the LOG_UNITS
array.

REMOVE_LOG_UNIT( UNIT [, CLOSE_UNIT ] )

Description. Remove UNIT from the LOG_UNITS list. If
CLOSE_UNIT is present and .TRUE. then the corresponding file is
closed. If UNIT is not in LOG_UNITS then nothing is done.

Class. Subroutine.

Arguments

UNIT shall be a scalar default INTEGER expression. It is an
INTENT(IN) argument. It should be one of the I/O UNIT numbers
in LOG_UNITS. If it is not, then nothing is done.

CLOSE_UNIT (optional) shall be a scalar default LOGICAL
expression. It is an INTENT(IN) argument. If .TRUE and UNIT is
in LOG_UNITS then UNIT will be closed, otherwise the I/O UNIT
will be unaffected.

@milancurcic
Copy link
Member

Hi @wclodius2, thank you for the detailed spec. At this time I'll only comment on the proposal of assert. Stdlib already has a subroutine check in the stdlib_error module (see docs) that has a broader but overlapping scope as your assert. So I suggest that we omit it from the logging module.

I think you can go ahead with the PR and I will assist you with the edits and testing. The PR should also include the spec following an existing format (see examples here). It looks like you already have all the information that goes into the spec, it just needs to be re-formatted. Also, please follow the Style guide.

@wclodius2 wclodius2 mentioned this issue Aug 26, 2020
@wclodius2
Copy link
Contributor Author

@milancurcic sorry for the delay. I had ben working on the BITSETS code, and wanted it to successfully compile before I put put it aside.

I have decided to convert the logger into a derived type, logger_t, so it can be used both as a global and a local logger. It will take about a day to do the conversion. Then it will take about another day to convert and extend my testing. I will also comment out the ASSERT code.

The existing specs only cover procedures, and I will have to cover more than that so there will be new ground: the derived type, module defined constants, and a module variable, global_logger. My existing procedure summaries are similar in structure to the existing specs so that part should go quickly. My personal style is compatible with the Style guide, with the occasional exception of a larger use of abbreviations. However, for the logger I have only been using abbreviations which the standard uses: STAT, IOSTAT, ERRMSG, and IOMSG.

@wclodius2
Copy link
Contributor Author

@milancurcic I have the stdlib_logger module and the test_stdlib_logger test code successfully compiling and running. The test code output is a bit messy, but so is the API it is testing so I don't see how to make it much cleaner. I think I am testing everything except the failures that result in an error stop. If testing of those is desired I will probably need a separate test code for each error stop.

I also have a markdown document documenting the API.

Before I check in the code and document I want to test it out using Cmake. I am having problems integrating Cmake and fypp. CMake out of the box cannot find fypp on my Mac. It is obvious that Cmake uses find_program to locate fypp. I find CMake documentation idiomatic, and I don't use it often enough to internalize the idioms, so I am having problems with the find_program documentation. It has too many options with too many qualifiers. Do you use a Unix environment (Linux, Mac, or BSD) and, if you do use it, how do you specify the path to fypp so that CMake can find it?

@wclodius2
Copy link
Contributor Author

Never mind the integrating of fypp and CMake. Adding the appropriate directory to the PATH worked fine.

@wclodius2
Copy link
Contributor Author

I have finished testing the code with cmake. Now how do I submit a PR for stdlib? I have tried to push the revised code but I get the error message
git push
Username for 'https://github.com': wclodius2
Password for 'https://[email protected]':
remote: Permission to fortran-lang/stdlib.git denied to wclodius2.
fatal: unable to access 'https://github.com/fortran-lang/stdlib/': The requested URL returned error: 403

The internet suggests that the most common cause of the 403 error is a bad password, but changing the password to something known to be bad now gives a different message :
git push
Username for 'https://github.com': wclodius2
Password for 'https://[email protected]':
remote: Invalid username or password.
fatal: Authentication failed for 'https://github.com/fortran-lang/stdlib/'

Do you know how to fix the 403 error? FWIW I have an older fork of stdlib here, but I can't seem to merge recent changes in the source directories. Should I upload the revised files there anyway and do a PR from there?

@milancurcic
Copy link
Member

Hi William, yes, you need to create a new branch on your fork (wclodius2/stdlib), then open a new Pull Request from the fortran-lang/stdlib page (there will be a yellow prompt near the top).

As an aside, 403 means Forbidden, which indicates correct password but lack of permission to write to fortran-lang/stdlib. In your second case, the error is 401 Unauthorized. That's why you get a different answer in those two attempts.

@wclodius2
Copy link
Contributor Author

Hi Milan. I have done a PR from my fork. The main files to concentrate on are the doc/specs/stdlib_logger.md, src/stdlib_logger.f90, and src/tests/logger/test_stdlib_logger.f90. I have also provided mods of the pertinent CMakeLists.txt and Makefile.manual files to be compatible with my fork, but I didn't know if you wanted those to be compatible with my fork, or with the current main branch, or with whatever branch you work with. Fortunately, they are easy to modify to be compatible with what you want. I am fairly happy with the stdlib_logger.md and stdlib_logger.f90 files. I am less happy with the test_stdlib_logger.f90 file, but it does what needs to be done.

@jvdp1 jvdp1 added implementation Implementation in experimental and submission of a PR topic: utilities containers, strings, files, OS/environment integration, unit testing, assertions, logging, ... labels Sep 29, 2020
@w6ws
Copy link

w6ws commented May 7, 2021

Besides the log_io_error method, may I suggest adding log_alloc_error and log_dealloc_error methods? ALLOCATE and DEALLOCATE also have optional stat= error returns.

We included specific methods for alloc, and dealloc, and also a NETCDF variant, in the ESMF logger class:

http://earthsystemmodeling.org/docs/release/ESMF_8_1_1/ESMF_refdoc/node6.html#SECTION060100000000000000000

Probably should have done a MPI variant as well. But in ESMF, the vast majority of MPI calls are in the C++ code.

I should note that we wrote several of the methods as 'found' functions with logical results - e.g., ESMF_LogFoundAllocError, as they could then be combined with the inevitable IF statement which follows an error checked statement. Made the error checking code a little cleaner and more compact.

@wclodius2
Copy link
Contributor Author

How is having log_alloc_error and log_dealloc_error an improvement over the optional stat and errmsg arguments to log_error?

@w6ws
Copy link

w6ws commented May 7, 2021

How is having log_alloc_error and log_dealloc_error an improvement over the optional stat and errmsg arguments to log_error?

In the case of ESMF, we issued specific memory allocation/deallocation messages, and also specific error 'return codes' to pass up the call chain.

@w6ws
Copy link

w6ws commented May 7, 2021

With the NETCDF variant, we called the NETCDF error message routines (e.g., NF90_STRERROR) and included their messages in our log as well.

@w6ws
Copy link

w6ws commented May 7, 2021

Should also note that in ESMF, we wanted to distinguish between our library 'return codes' (rc=) and Fortran-specific iostat/stat status returns. So the different entry points into the Log class reflected that as well.

@awvwgk awvwgk added topic: IO Common input/output related features and removed topic: utilities containers, strings, files, OS/environment integration, unit testing, assertions, logging, ... labels Sep 18, 2021
@14NGiestas
Copy link
Member

This issue was addressed in stdlib_logger a while ago so it's API discussion can be safely closed (I can't find the exact PR).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
implementation Implementation in experimental and submission of a PR topic: IO Common input/output related features
Projects
None yet
Development

No branches or pull requests

6 participants