Messaging and Logging
HEC-DSS provides informational messages for operations. The default message level includes file open statements, write statements and error messages, but not read messages. You can set the message level to have HEC-DSS write more or less message information, or turn off messaging all together (although not recommended.) Generally, users need to review the HEC-DSS messages when issues or errors come up; often something as simple as a miss-spelled pathname part.
To better accommodate users, messages are generally directed to a log file, which can be displayed when the user wants to review that file. This is done by calling function zopenLog(const char *logFileName) at the beginning of the program (before other DSS calls.) Before you exit the program, call void zcloseLog().
Message Levels
HEC-DSS version 7 has additional functionality to display messages for specific method types a pre-set levels, so one doesn't get lost in the trees while looking for the forest. Version 7's messaging allows messaging for actions to be elevated without elevating all messaging, so issues can be more easily isolated.
To set the message level for a method set, call the function zsetMessageLevel:
void zsetMessageLevel(int methodID, int levelID);
For example, suppose you were having issues reading time series data. To help diagnose this, you could call zsetMessageLevel like as follows:
zsetMessageLevel(zmessaging_tsread_ID, MESS_LEVEL_USER_DIAG);
….
(DSS time series read calls)
….
zsetMessageLevel(zmessaging_tsread_ID, MESS_LEVEL_GENERAL);
Generally, it is not helpful to go to a level higher than user diagnostics; internal messages usually will clutter output for users trying to determine what issues are. Also, be sure to set the message level back to General when you are out of the area of concern.
Functions
zopenLog
Opens (and creates, if needed) a text file to write all DSS messages to.
Declaration
int zopenLog(const char *logFileName);
Parameters
const char * logFileName
Returns
int status
STATUS_OKAY for successful operation.
System error code (a non-zero number) if an error occurred. Not a DSS error code.
zcloseLog
Flushes text to the log file, then closes it.
Declaration
void zcloseLog();
zmessage
Writes a text message to the log file. The message is pre-pended with the date and time, and a CR/NL is added to the end of the message
Declaration
void zmessage(long long *ifltab, const char *message);
Parameters
long long *ifltab
const char *message
Convience Functions
Writes a number after the message
void zmessageInt(long long *ifltab, const char *message, int number);
void zmessageLong(long long *ifltab, const char *message, long long number);
zsetMessageLevel
Controls diagonistic and informational messages from HEC-DSS. Messages are written to the set output, which is either a log file or standard out or another file, if set by the programmer. You can turn on diagnostics for specific kinds of operations using the Method ID.
Declaration
void zsetMessageLevel(int methodID, int levelID);
Parameters
int methodID
int levelID
Method / Group ID | Description |
MESS_METHOD_GENERAL | All methods for both DSS version 6 and 7 |
MESS_METHOD_GLOBAL | All methods for DSS version 7 |
MESS_METHOD_GET | Low-level read I/O |
MESS_METHOD_PUT | Low-level write I/O |
MESS_METHOD_READ | Read methods, except time series |
MESS_METHOD_WRITE | Write methods, except time series |
MESS_METHOD_PERM | Operations for the file header |
MESS_METHOD_OPEN | Opening and creating a DSS file |
MESS_METHOD_CHECK | Checking for records |
MESS_METHOD_LOCKING | Locking and unlocking methods |
MESS_METHOD_TS_READ | Time series read operations |
MESS_METHOD_TS_WRITE | Time series write operations |
MESS_METHOD_ALIAS | Record alias methods |
MESS_METHOD_COPY | Record copying functions |
MESS_METHOD_UTILITY | General utility functions (rename, delete, etc.) |
MESS_METHOD_CATALOG | Cataloging |
MESS_METHOD_FILE_CHECK | Checking file integrity |
MESS_METHOD_JNI | Java Native Interface |
Level ID | Description |
MESS_LEVEL_NONE | No messages, including errors (not guaranteed). Highly discourage |
MESS_LEVEL_CRITICAL | Critical (Error) messages only. Use discouraged. |
MESS_LEVEL_TERSE | Minimal (terse) output: zopen, zclose, critical errors. |
MESS_LEVEL_GENERAL | General Log Messages. Default. |
MESS_LEVEL_USER_DIAG | Diagnostic User Messages (e.g., input parameters) |
MESS_LEVEL_INTERNAL_DIAG_1 | Diagnostic Internal Messages level 1 (debug). Not recommended for users |
MESS_LEVEL_INTERNAL_DIAG_2 | Diagnostic Internal Messages level 2 (full debug) |
Example HEC-DSS Logging
#include "heclib.h"
int ExampleLogging()
{
long long ifltab[250];
int status;
int DEBUG = 1;
status = zopenLog("C:/temp/Example.log");
if (status != 0) return status;
zmessage(ifltab, "Beginning start of program");
// Do DSS stuff
status = zopen(ifltab, "C:/temp/Example.dss");
if (DEBUG) {
zmessageInt(ifltab, "Open status: ", status);
}
if (status != STATUS_OKAY) return status;
// Turn message level up
zsetMessageLevel(MESS_METHOD_GENERAL_ID, MESS_LEVEL_USER_DIAG);
zclose(ifltab);
// Close log file
zcloseLog();
return 0;
}
Error Handling
Most HEC-DSS functions return a status flag to indicate a successful operation or not. Generally, a return status of 0 (zero, or STATUS_OKAY) indicates a successful operation. A status of -1 (minus one, or STATUS_NOT_OKAY) usually indicates that operation could not be preformed because the record to operate on was not found or similar. DSS does not consider this an "error", just an operation that could not be preformed.
A return status of less than 0 (actually a large negative number) indicates an error, and information about that error is encoded in the status. When such an error is encountered, the calling program needs to take an appropriate action.
The error code will have the format as follows:
-a bb cc dd ee
Where:
- "a" (0–9) indicates the error severity, with 9 being the highest
- "bb" (0-99) indicates the highest function called when the error occurred (e.g., ztsStore)
- "cc" (0-99) indicates the function where the error actually occurred (e.g., zput)
- "dd" (0-99) is the actual error
- "ee" (0-99) is the system error code, if the error was returned by the OS
The severity levels and apporopriate actions are as follows:
Severity Level | Value | Meaning | Example | Action |
INFORMATION | 1 | An inconsequential action failed | Unable to unlock a record | None |
WARNING | 2 | Unable to compete the request given | Pathname does not exist | Cannot complete the request given (e.g., data was not read) |
INVALID_ARGUMENT | 3 | An incorrect argument was passed to a function | No pathname provided | Inform user and have them change |
WARNING_NO_WRITE_ACCESS | 4 | You cannot write to the file | You do not have write permission | Inform user and have them change |
WARNING_NO_FILE_ACCESS | 5 | You cannot read or write to the file | Do not have permission | Inform user and have them change |
WRITE_ERROR | 6 | An error was thrown on a write action. | out of disk space | Do not attempt to access the DSS file |
READ_ERROR | 7 | An error was thrown on a read action. The file maybe damaged. It might be recovered by a squeeze, at the user's discretion. | The file maybe damaged. | It might be recovered by a squeeze, at the user's discretion. |
CORRUPT_FILE | 8 | Flags and pointers are not correct, indicating that something has changed the file outside of DSS | Bad ftp transfer or truncated file | Do not attempt to access the DSS file |
MEMORY_ERROR | 9 | Memory exhausted or flags and pointers in memory are not correct | Array out of bounds | Exit, if program did not crash. |
Generally, zdssErrorSeverity.WARNING_NO_FILE_ACCESS can be handled by the program and is often associated with a permissions error or network drive that is not available. WRITE_ERROR through CORRUPT_FILE can indicate a bad file or other significant issue. MEMORY_ERROR or CRITICAL_ERROR indicates that something very nasty has occurred and you should exit the program.
HEC-DSS will print any error messages to the log file specified, it the message level is set appropriately. A convenience function "zisError(int status)" will indicate if the status is an error. zerrorCheck will check if an error occurred without needing the status. If an error has occurred, the error information can be obtained by calling function zerror.
Functions
zerrorCheck
zerrorCheck can be called at any time to determine if an error has occurred.
Declaration
int zerrorCheck();
Returns
int status
0 (Zero) - no error has occurred.
Severity (1-9) of the error.
zisError
zisError determines if there was an error from the status returned from a DSS function, as opposed to no error or just a warning (such as a record does not exist). If this returns positive, then the program must take appropriate action.
Declaration
int zisError(int status);
Parameters
int status
The status returned from a DSS function
Returns
int boolError
0 (Zero) - no error has occurred.
1 (One) - there has been an error.
Use
If (zisError(status)) {
// Process the error here
}
zerror
zerror provides all the known information about the last error that occurred. If the message level is set appropriately, this is the same as what has been sent to the log file or standard out. .
Declaration
int zerror(hec_zdssLastError *errorStruct);
Parameters
hec_zdssLastError *errorStruct
The address of an error struct defined in zerrorCodes.h. The program must create this struct. The struct filled out with information about the last error.
Returns
int severity
0 (Zero) - no error has occurred.
Severity (1-9) of the error.
errorStruct
The struct filled out with information about the last error
Error Struct Definition
typedef struct {
int errorCode;
int severity;
int errorNumber;
int errorType;
int systemError;
long long lastAddress;
int functionID;
int calledByFunction;
char errorMessage[MAX_LEN_ERROR_MESS];
char systemErrorMessage[MAX_LEN_ERROR_MESS];
char lastPathname[MAX_PATHNAME_SIZE];
char filename[MAX_FILENAME_LENGTH];
} hec_zdssLastError;
Example: Simple Error Processing
function zcheck returns either STATUS_RECORD_FOUND, STATUS_RECORD_NOT_FOUND or an error code.
status = zcheck(ifltab, pathname);
if (zisError(status)) {
zerror(&errorStruct);
notifyUser(&errorStruct); // (e.g., display an error dialog)
if (zerrorSeverity(status) >= zdssErrorSeverity.MEMORY_ERROR) exitProg();
return status;
}
If(status == STATUS_RECORD_NOT_FOUND) {
printf("Record does not exist: %s\n", pathname);
return status;
}
// Process data set
Example: Error Processing
You may want to copy portions of this example directly into your program.
#include <string.h>
#include "heclib.h"
void notifyUser(hec_zdssLastError *errorStruct);
int ExampleErrorHandling()
{
long long ifltab[250];
int status;
hec_zdssLastError errorStruct;
zStructTimeSeries *tss;
// Open a log file for messages and transactions
status = zopenLog("C:/temp/Example.log");
if (status != 0) return status;
// Create some errors to show process
// Bad drive or file name
status = zopen(ifltab, "Z:/temp/Example7.dss");
if (zisError(status)) {
// The error message is automatically written to the log file,
// so the following 2 lines are optional
zerror(&errorStruct);
notifyUser(&errorStruct); // (e.g., display an error dialog)
if (zerrorSeverity(status) >= zdssErrorSeverity.MEMORY_ERROR) exit(0);
// return status;
}
// Okay, open a valid file
zerrorStructClear();
printf("\n\n");
status = zopen(ifltab, "C:/temp/Example7.dss");
if (zisError(status)) {
zerror(&errorStruct);
notifyUser(&errorStruct);
if (zerrorSeverity(status) >= zdssErrorSeverity.MEMORY_ERROR) exit(0);
// return status;
}
// Create a new error by writing a time series struct with no pathname
tss = zstructTsNew("");
status = ztsStore(ifltab, tss, 0);
if (zisError(status)) {
// The error message is automatically written to the log file,
// so the following 2 lines are optional
zerror(&errorStruct);
notifyUser(&errorStruct); // (e.g., display an error dialog)
zstructFree(tss);
if (zerrorSeverity(status) >= zdssErrorSeverity.MEMORY_ERROR) exit(0);
// return status;
}
// Do stuff with struct, then free
zstructFree(tss);
// When all done (near end of program), close the file
zclose(ifltab);
zcloseLog();
return 0;
}
void notifyUser(hec_zdssLastError *errorStruct)
{
if (errorStruct->severity == 0) {
return;
}
else if (errorStruct->severity < zdssErrorSeverity.INVALID_ARGUMENT) {
printf("*** Warning ***\n");
}
else if (errorStruct->severity < zdssErrorSeverity.WRITE_ERROR) {
printf("*** File access error ***\n");
}
else if (errorStruct->severity == zdssErrorSeverity.WRITE_ERROR) {
printf("*** Write error ***\n");
}
else if (errorStruct->severity < zdssErrorSeverity.MEMORY_ERROR) {
printf("*** Read or File corruption error ***\n");
}
else {
printf("*** Critical error ***\n");
}
if (strlen(errorStruct->errorMessage) > 2) {
printf("Error: %s\n", errorStruct→errorMessage);
}
if (strlen(errorStruct->systemErrorMessage) > 2) {
printf("System error: %s\n", errorStruct→systemErrorMessage);
}
if (strlen(errorStruct->lastPathname) > 2) {
printf("Last pathname: %s\n", errorStruct→lastPathname);
}
if (strlen(errorStruct->filename) > 2) {
printf("File: %s\n", errorStruct→filename);
}
}
Output:
*** File access error ***
Error: *****DSS*** ERROR in function zopen: Unable to create file
File
Z:\temp\Example7.dss
System error: No such file or directory
*** File access error ***
Error: -----DSS--- ztsStore Warning: Invalid pathname
pathname:
Invalid pathname
File: C:\temp\Example7.dss
Mulitple Processes Access
A principal feature of HEC-DSS is that several processes (multi-user) can read and write data to a single database at the same time. The multi-user access capability is implemented with system record locking and flushing functions. There is no daemon or other background program managing accesses to a database. A database may also exist on a Windows or Unix server, which can be accessed by users on PC's or other computers via the network.
Multi-user access is automatic and needs no action from the programmer or user. When HEC-DSS detects another processes accessing a file, writes by either (any) process to that file are flushed to disk to ensure that the other processes coordinate with that. This allows one program to access what another program wrote. However, this flushing to disk on the Windows OS takes significantly longer (on the order of 100 times) than single user access. This is not true on some Unix workstations. It is recommended, when possible, to keep in a single user access mode on Windows PCs by accessing a file from only one vitural machine.
Reading from a DSS file being written to by another process
There is little measurable impact reading from or listening to a HEC-DSS file that is being written to by another process. You can watch a DSS file for changes, and read those changes, with little time impact. Refer to the listener discussion and example later in this document.
Common Utility Functions
zpathnameForm
zpathnameForm builds a pathname from the 6 (input) parts.
Declaration
int zpathnameForm(const char *aPart, const char *bPart, const char *cPart, const char *dPart,
const char *ePart, const char *fPart, char *pathname, size_t sizeofPathname);
Parameters
const char *aPart - const char *fPart
The six pathname parts to be formed into a pathname \
char *pathname
A character string to return the pathname in
size_t sizeofPathname
The size of pathname in bytes.
Returns
int length
0 (Zero) - no error has occurred.
1 (One) - there has been an error.
Use
If (zisError(status)) {
// Process the error here
}